Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(773)

Side by Side Diff: src/codec/SkJpegCodec.cpp

Issue 1260673002: SkScaledCodec class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Add get_row_bytes function to SkJpegCodec Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCodec.h" 8 #include "SkCodec.h"
9 #include "SkJpegCodec.h" 9 #include "SkJpegCodec.h"
10 #include "SkJpegDecoderMgr.h" 10 #include "SkJpegDecoderMgr.h"
11 #include "SkJpegUtility_codec.h" 11 #include "SkJpegUtility_codec.h"
12 #include "SkCodecPriv.h" 12 #include "SkCodecPriv.h"
13 #include "SkColorPriv.h" 13 #include "SkColorPriv.h"
14 #include "SkScaledCodec.h"
14 #include "SkScanlineDecoder.h" 15 #include "SkScanlineDecoder.h"
15 #include "SkStream.h" 16 #include "SkStream.h"
16 #include "SkTemplates.h" 17 #include "SkTemplates.h"
17 #include "SkTypes.h" 18 #include "SkTypes.h"
18 19
19 // stdio is needed for libjpeg-turbo 20 // stdio is needed for libjpeg-turbo
20 #include <stdio.h> 21 #include <stdio.h>
21 22
22 extern "C" { 23 extern "C" {
23 #include "jpeglibmangler.h" 24 #include "jpeglibmangler.h"
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 streamDeleter.detach(); 140 streamDeleter.detach();
140 return codec; 141 return codec;
141 } 142 }
142 return NULL; 143 return NULL;
143 } 144 }
144 145
145 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, 146 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
146 JpegDecoderMgr* decoderMgr) 147 JpegDecoderMgr* decoderMgr)
147 : INHERITED(srcInfo, stream) 148 : INHERITED(srcInfo, stream)
148 , fDecoderMgr(decoderMgr) 149 , fDecoderMgr(decoderMgr)
150 , fPartialNum(1)
151 , fPartialDenom(1)
149 {} 152 {}
150 153
151 /* 154 /*
155 * Return the row bytes of a particular image type and width
156 */
157 static int get_row_bytes(j_decompress_ptr dinfo) {
scroggo 2015/08/05 19:10:04 can this parameter be const?
emmaleer 2015/08/06 13:45:46 Acknowledged.
158 int colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 : dinfo->out_col or_components;
159 return dinfo->output_width * colorBytes;
160
161 }
162 /*
152 * Return a valid set of output dimensions for this decoder, given an input scal e 163 * Return a valid set of output dimensions for this decoder, given an input scal e
153 */ 164 */
154 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const { 165 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
155 // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will 166 // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will
156 // support these as well 167 // support these as well
157 long num; 168 long num;
158 long denom = 8; 169 long denom = 8;
159 if (desiredScale > 0.875f) { 170 if (desiredScale > 0.875f) {
160 num = 8; 171 num = 8;
161 } else if (desiredScale > 0.75f) { 172 } else if (desiredScale > 0.75f) {
(...skipping 20 matching lines...) Expand all
182 dinfo.global_state = DSTATE_READY; 193 dinfo.global_state = DSTATE_READY;
183 dinfo.num_components = 0; 194 dinfo.num_components = 0;
184 dinfo.scale_num = num; 195 dinfo.scale_num = num;
185 dinfo.scale_denom = denom; 196 dinfo.scale_denom = denom;
186 turbo_jpeg_calc_output_dimensions(&dinfo); 197 turbo_jpeg_calc_output_dimensions(&dinfo);
187 198
188 // Return the calculated output dimensions for the given scale 199 // Return the calculated output dimensions for the given scale
189 return SkISize::Make(dinfo.output_width, dinfo.output_height); 200 return SkISize::Make(dinfo.output_width, dinfo.output_height);
190 } 201 }
191 202
203 // The swizzler is only used for sampling in the x direction
204 // when sampling a scanlineDecoder is used. The swizzler is not used in onGetPix els
205 SkCodec::Result SkJpegCodec::initializeSwizzler(const SkImageInfo& info,
206 const Options& options) {
scroggo 2015/08/05 19:10:04 nit: spacing. This should line up with const, abov
emmaleer 2015/08/06 13:45:46 Acknowledged.
207 SkSwizzler::SrcConfig srcConfig;
208 switch (info.colorType()) {
209 case kGray_8_SkColorType:
210 srcConfig = SkSwizzler::kGray;
211 break;
212 case kRGBA_8888_SkColorType:
213 srcConfig = SkSwizzler::kRGBX;
214 break;
215 case kBGRA_8888_SkColorType:
216 srcConfig = SkSwizzler::kBGRX;
217 break;
218 case kRGB_565_SkColorType:
219 srcConfig = SkSwizzler::kRGB_565;
220 break;
221 default:
222 //would have exited before now if the colorType was supported by jpe g
223 SkASSERT(false);
224 }
225
226 fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, NULL, info, options.fZ eroInitialized,
227 this->getInfo().width()));
228 if (!fSwizzler) {
229 // FIXME: CreateSwizzler could fail for another reason.
230 return kUnimplemented;
231 }
232 return kSuccess;
233 }
234
192 /* 235 /*
193 * Handles rewinding the input stream if it is necessary 236 * Handles rewinding the input stream if it is necessary
194 */ 237 */
195 bool SkJpegCodec::handleRewind() { 238 bool SkJpegCodec::handleRewind() {
196 switch(this->rewindIfNeeded()) { 239 switch(this->rewindIfNeeded()) {
197 case kCouldNotRewind_RewindState: 240 case kCouldNotRewind_RewindState:
198 return fDecoderMgr->returnFalse("could not rewind"); 241 return fDecoderMgr->returnFalse("could not rewind");
199 case kRewound_RewindState: { 242 case kRewound_RewindState: {
200 JpegDecoderMgr* decoderMgr = NULL; 243 JpegDecoderMgr* decoderMgr = NULL;
201 if (!ReadHeader(this->stream(), NULL, &decoderMgr)) { 244 if (!ReadHeader(this->stream(), NULL, &decoderMgr)) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 // much faster than decoding to color and then converting 308 // much faster than decoding to color and then converting
266 fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; 309 fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
267 } 310 }
268 return true; 311 return true;
269 default: 312 default:
270 return false; 313 return false;
271 } 314 }
272 } 315 }
273 316
274 /* 317 /*
275 * Checks if we can scale to the requested dimensions and scales the dimensions 318 * Checks if we can natively scale to the requested dimensions and natively scal es the
276 * if possible 319 * dimensions if possible
277 */ 320 */
278 bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) { 321 bool SkJpegCodec::nativelyScaleToDimensions(uint32_t dstWidth, uint32_t dstHeigh t) {
279 // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1 322 // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1
280 fDecoderMgr->dinfo()->scale_denom = 8; 323 fDecoderMgr->dinfo()->scale_denom = 8;
281 fDecoderMgr->dinfo()->scale_num = 8; 324 fDecoderMgr->dinfo()->scale_num = 8;
282 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo()); 325 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
283 while (fDecoderMgr->dinfo()->output_width != dstWidth || 326 while (fDecoderMgr->dinfo()->output_width != dstWidth ||
284 fDecoderMgr->dinfo()->output_height != dstHeight) { 327 fDecoderMgr->dinfo()->output_height != dstHeight) {
285 328
286 // Return a failure if we have tried all of the possible scales 329 // Return a failure if we have tried all of the possible scales
287 if (1 == fDecoderMgr->dinfo()->scale_num || 330 if (1 == fDecoderMgr->dinfo()->scale_num ||
288 dstWidth > fDecoderMgr->dinfo()->output_width || 331 dstWidth > fDecoderMgr->dinfo()->output_width ||
289 dstHeight > fDecoderMgr->dinfo()->output_height) { 332 dstHeight > fDecoderMgr->dinfo()->output_height) {
290 return fDecoderMgr->returnFalse("could not scale to requested dimens ions"); 333 // reset native scale settings on failure because this may be suppor ted by the swizzler
334 this->fDecoderMgr->dinfo()->scale_num = 8;
335 turbo_jpeg_calc_output_dimensions(this->fDecoderMgr->dinfo());
336 return false;
291 } 337 }
292 338
293 // Try the next scale 339 // Try the next scale
294 fDecoderMgr->dinfo()->scale_num -= 1; 340 fDecoderMgr->dinfo()->scale_num -= 1;
295 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo()); 341 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
296 } 342 }
297 return true; 343 return true;
298 } 344 }
299 345
300 /* 346 /*
(...skipping 19 matching lines...) Expand all
320 if (setjmp(fDecoderMgr->getJmpBuf())) { 366 if (setjmp(fDecoderMgr->getJmpBuf())) {
321 return fDecoderMgr->returnFailure("setjmp", kInvalidInput); 367 return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
322 } 368 }
323 369
324 // Check if we can decode to the requested destination and set the output co lor space 370 // Check if we can decode to the requested destination and set the output co lor space
325 if (!this->setOutputColorSpace(dstInfo)) { 371 if (!this->setOutputColorSpace(dstInfo)) {
326 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion); 372 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion);
327 } 373 }
328 374
329 // Perform the necessary scaling 375 // Perform the necessary scaling
330 if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) { 376 if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
331 return fDecoderMgr->returnFailure("cannot scale to requested dims", kInv alidScale); 377 return fDecoderMgr->returnFailure("cannot scale to requested dims", kInv alidScale);
332 } 378 }
333 379
334 // Now, given valid output dimensions, we can start the decompress 380 // Now, given valid output dimensions, we can start the decompress
335 if (!turbo_jpeg_start_decompress(dinfo)) { 381 if (!turbo_jpeg_start_decompress(dinfo)) {
336 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); 382 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
337 } 383 }
338 384
339 // The recommended output buffer height should always be 1 in high quality m odes. 385 // The recommended output buffer height should always be 1 in high quality m odes.
340 // If it's not, we want to know because it means our strategy is not optimal . 386 // If it's not, we want to know because it means our strategy is not optimal .
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 /* 433 /*
388 * Enable scanline decoding for jpegs 434 * Enable scanline decoding for jpegs
389 */ 435 */
390 class SkJpegScanlineDecoder : public SkScanlineDecoder { 436 class SkJpegScanlineDecoder : public SkScanlineDecoder {
391 public: 437 public:
392 SkJpegScanlineDecoder(const SkImageInfo& dstInfo, SkJpegCodec* codec, 438 SkJpegScanlineDecoder(const SkImageInfo& dstInfo, SkJpegCodec* codec,
393 const SkCodec::Options& opts) 439 const SkCodec::Options& opts)
394 : INHERITED(dstInfo) 440 : INHERITED(dstInfo)
395 , fCodec(codec) 441 , fCodec(codec)
396 , fOpts(opts) 442 , fOpts(opts)
397 {} 443 {
444 if(fCodec->fSwizzler) {
445 fStorage.reset(get_row_bytes(codec->fDecoderMgr->dinfo()));
446 fSrcRow = static_cast<uint8_t*>(fStorage.get());
447 } else {
448 fSrcRow = NULL;
449 }
450 }
398 451
399 virtual ~SkJpegScanlineDecoder() { 452 virtual ~SkJpegScanlineDecoder() {
400 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 453 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
401 SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n"); 454 SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n");
402 return; 455 return;
403 } 456 }
404 457
405 // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a 458 // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a
406 // partial decode. 459 // partial decode.
407 fCodec->fDecoderMgr->dinfo()->output_scanline = fCodec->getInfo().height (); 460 fCodec->fDecoderMgr->dinfo()->output_scanline = fCodec->getInfo().height ();
408 turbo_jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo()); 461 turbo_jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo());
409 } 462 }
410 463
411 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de { 464 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de {
412 // Set the jump location for libjpeg errors 465 // Set the jump location for libjpeg errors
413 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 466 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
414 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput); 467 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput);
415 } 468 }
469 // Read rows one at a time
470 JSAMPLE* dstRow;
471 if (fCodec->fSwizzler) {
472 // write data to storage row, then sample using swizzler
473 dstRow = fSrcRow;
474 } else {
475 // write data directly to dst
476 dstRow = (JSAMPLE*) dst;
477 }
416 478
417 // Read rows one at a time
418 JSAMPLE* dstRow = (JSAMPLE*) dst;
419 for (int y = 0; y < count; y++) { 479 for (int y = 0; y < count; y++) {
420 // Read row of the image 480 // Read row of the image
421 uint32_t rowsDecoded = 481 uint32_t rowsDecoded =
422 turbo_jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &dst Row, 1); 482 turbo_jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &dst Row, 1);
423 if (rowsDecoded != 1) { 483 if (rowsDecoded != 1) {
424 if (SkCodec::kNo_ZeroInitialized == fOpts.fZeroInitialized || 484 if (SkCodec::kNo_ZeroInitialized == fOpts.fZeroInitialized ||
425 kN32_SkColorType == this->dstInfo().colorType()) { 485 kN32_SkColorType == this->dstInfo().colorType()) {
426 SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes, 486 SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes,
427 count - y, SK_ColorBLACK, NULL); 487 count - y, SK_ColorBLACK, NULL);
428 } 488 }
429 fCodec->fDecoderMgr->dinfo()->output_scanline = this->dstInfo(). height(); 489 fCodec->fDecoderMgr->dinfo()->output_scanline = this->dstInfo(). height();
430 return SkCodec::kIncompleteInput; 490 return SkCodec::kIncompleteInput;
431 } 491 }
432 492
433 // Convert to RGBA if necessary 493 // Convert to RGBA if necessary
434 if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) { 494 if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) {
435 convert_CMYK_to_RGBA(dstRow, this->dstInfo().width()); 495 convert_CMYK_to_RGBA(dstRow, fCodec->fDecoderMgr->dinfo()->outpu t_width);
436 } 496 }
437 497
438 // Move to the next row 498 if(fCodec->fSwizzler) {
439 dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); 499 // use swizzler to sample row
500 fCodec->fSwizzler->swizzle(dst, dstRow);
501 dst = SkTAddOffset<JSAMPLE>(dst, rowBytes);
502 } else {
503 dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes);
504 }
440 } 505 }
441
442 return SkCodec::kSuccess; 506 return SkCodec::kSuccess;
443 } 507 }
444 508
445 #ifndef TURBO_HAS_SKIP 509 #ifndef TURBO_HAS_SKIP
446 #define turbo_jpeg_skip_scanlines(dinfo, count) \ 510 #define turbo_jpeg_skip_scanlines(dinfo, count) \
447 SkAutoMalloc storage(dinfo->output_width * dinfo->out_color_components); \ 511 SkAutoMalloc storage(get_row_bytes(dinfo)); \
448 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); \ 512 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); \
449 for (int y = 0; y < count; y++) { \ 513 for (int y = 0; y < count; y++) { \
450 turbo_jpeg_read_scanlines(dinfo, &storagePtr, 1); \ 514 turbo_jpeg_read_scanlines(dinfo, &storagePtr, 1); \
451 } 515 }
452 #endif 516 #endif
453 517
454 SkCodec::Result onSkipScanlines(int count) override { 518 SkCodec::Result onSkipScanlines(int count) override {
455 // Set the jump location for libjpeg errors 519 // Set the jump location for libjpeg errors
456 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 520 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
457 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput); 521 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput);
458 } 522 }
459 523
460 turbo_jpeg_skip_scanlines(fCodec->fDecoderMgr->dinfo(), count); 524 turbo_jpeg_skip_scanlines(fCodec->fDecoderMgr->dinfo(), count);
461 525
462 return SkCodec::kSuccess; 526 return SkCodec::kSuccess;
463 } 527 }
464 528
465 private: 529 private:
466 SkAutoTDelete<SkJpegCodec> fCodec; 530 SkAutoTDelete<SkJpegCodec> fCodec;
531 SkAutoMalloc fStorage; // Only used if sampling is needed
532 uint8_t* fSrcRow; // Only used if sampling is needed
467 const SkCodec::Options& fOpts; 533 const SkCodec::Options& fOpts;
468 534
469 typedef SkScanlineDecoder INHERITED; 535 typedef SkScanlineDecoder INHERITED;
470 }; 536 };
471 537
472 SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, 538 SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
473 const Options& options, SkPMColor ctable[], int* ctableCount) { 539 const Options& options, SkPMColor ctable[], int* ctableCount) {
474 540
475 // Rewind the stream if needed 541 // Rewind the stream if needed
476 if (!this->handleRewind()) { 542 if (!this->handleRewind()) {
(...skipping 15 matching lines...) Expand all
492 if (!codec) { 558 if (!codec) {
493 return NULL; 559 return NULL;
494 } 560 }
495 561
496 // Check if we can decode to the requested destination and set the output co lor space 562 // Check if we can decode to the requested destination and set the output co lor space
497 if (!codec->setOutputColorSpace(dstInfo)) { 563 if (!codec->setOutputColorSpace(dstInfo)) {
498 SkCodecPrintf("Cannot convert to output type\n"); 564 SkCodecPrintf("Cannot convert to output type\n");
499 return NULL; 565 return NULL;
500 } 566 }
501 567
568 SkImageInfo scaledInfo = dstInfo;
569
502 // Perform the necessary scaling 570 // Perform the necessary scaling
503 if (!codec->scaleToDimensions(dstInfo.width(), dstInfo.height())) { 571 if (!codec->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
504 SkCodecPrintf("Cannot scale to output dimensions\n"); 572 // full native scaling to dstInfo dimensions not supported
505 return NULL; 573
574 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstI nfo)) {
575 return NULL;
576 }
577 // create swizzler for sampling
578 if (codec->initializeSwizzler(dstInfo, options) != kSuccess) {
579 SkCodecPrintf("failed to initialize the swizzler.\n");
580 return NULL;
581 }
506 } 582 }
507 583
508 // Now, given valid output dimensions, we can start the decompress 584 // Now, given valid output dimensions, we can start the decompress
509 if (!turbo_jpeg_start_decompress(codec->fDecoderMgr->dinfo())) { 585 if (!turbo_jpeg_start_decompress(codec->fDecoderMgr->dinfo())) {
510 SkCodecPrintf("start decompress failed\n"); 586 SkCodecPrintf("start decompress failed\n");
511 return NULL; 587 return NULL;
512 } 588 }
513 589
514 // Return the new scanline decoder 590 // Return the new scanline decoder
515 return SkNEW_ARGS(SkJpegScanlineDecoder, (dstInfo, codec.detach(), options)) ; 591 return SkNEW_ARGS(SkJpegScanlineDecoder, (scaledInfo, codec.detach(), option s));
516 } 592 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698