Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
SkJpegCodec.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
14#include "include/core/SkData.h"
24#include "modules/skcms/skcms.h"
32
33#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
35#endif // SK_CODEC_DECODES_JPEG_GAINMAPS
36
37#include <array>
38#include <csetjmp>
39#include <cstring>
40#include <utility>
41
42using namespace skia_private;
43
44class SkSampler;
45struct SkGainmapInfo;
46
47// This warning triggers false postives way too often in here.
48#if defined(__GNUC__) && !defined(__clang__)
49 #pragma GCC diagnostic ignored "-Wclobbered"
50#endif
51
52extern "C" {
53 #include "jpeglib.h" // NO_G3_REWRITE
54}
55
56bool SkJpegCodec::IsJpeg(const void* buffer, size_t bytesRead) {
57 return bytesRead >= sizeof(kJpegSig) && !memcmp(buffer, kJpegSig, sizeof(kJpegSig));
58}
59
60SkJpegMarkerList get_sk_marker_list(jpeg_decompress_struct* dinfo) {
61 SkJpegMarkerList markerList;
62 for (auto* marker = dinfo->marker_list; marker; marker = marker->next) {
63 markerList.emplace_back(marker->marker,
64 SkData::MakeWithoutCopy(marker->data, marker->data_length));
65 }
66 return markerList;
67}
68
71 if (exifData && SkParseEncodedOrigin(exifData->bytes(), exifData->size(), &origin)) {
72 return origin;
73 }
75}
76
77SkCodec::Result SkJpegCodec::ReadHeader(
79 SkCodec** codecOut,
80 JpegDecoderMgr** decoderMgrOut,
81 std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
82 // Create a JpegDecoderMgr to own all of the decompress information
83 std::unique_ptr<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream));
84
85 // libjpeg errors will be caught and reported here
86 skjpeg_error_mgr::AutoPushJmpBuf jmp(decoderMgr->errorMgr());
87 if (setjmp(jmp)) {
88 return decoderMgr->returnFailure("ReadHeader", kInvalidInput);
89 }
90
91 // Initialize the decompress info and the source manager
92 decoderMgr->init();
93 auto* dinfo = decoderMgr->dinfo();
94
95 // Instruct jpeg library to save the markers that we care about. Since
96 // the orientation and color profile will not change, we can skip this
97 // step on rewinds.
98 if (codecOut) {
99 jpeg_save_markers(dinfo, kExifMarker, 0xFFFF);
100 jpeg_save_markers(dinfo, kICCMarker, 0xFFFF);
101 jpeg_save_markers(dinfo, kMpfMarker, 0xFFFF);
102 }
103
104 // Read the jpeg header
105 switch (jpeg_read_header(dinfo, true)) {
106 case JPEG_HEADER_OK:
107 break;
108 case JPEG_SUSPENDED:
109 return decoderMgr->returnFailure("ReadHeader", kIncompleteInput);
110 default:
111 return decoderMgr->returnFailure("ReadHeader", kInvalidInput);
112 }
113
114 if (codecOut) {
115 // Get the encoded color type
117 if (!decoderMgr->getEncodedColor(&color)) {
118 return kInvalidInput;
119 }
120
121 auto metadataDecoder =
122 std::make_unique<SkJpegMetadataDecoderImpl>(get_sk_marker_list(dinfo));
123
124 SkEncodedOrigin orientation =
125 get_exif_orientation(metadataDecoder->getExifMetadata(/*copyData=*/false));
126
127 std::unique_ptr<SkEncodedInfo::ICCProfile> profile;
128 if (auto iccProfileData = metadataDecoder->getICCProfileData(/*copyData=*/true)) {
129 profile = SkEncodedInfo::ICCProfile::Make(std::move(iccProfileData));
130 }
131 if (profile) {
132 auto type = profile->profile()->data_color_space;
133 switch (decoderMgr->dinfo()->jpeg_color_space) {
134 case JCS_CMYK:
135 case JCS_YCCK:
136 if (type != skcms_Signature_CMYK) {
137 profile = nullptr;
138 }
139 break;
140 case JCS_GRAYSCALE:
141 if (type != skcms_Signature_Gray &&
143 {
144 profile = nullptr;
145 }
146 break;
147 default:
148 if (type != skcms_Signature_RGB) {
149 profile = nullptr;
150 }
151 break;
152 }
153 }
154 if (!profile) {
155 profile = std::move(defaultColorProfile);
156 }
157
158 SkEncodedInfo info = SkEncodedInfo::Make(dinfo->image_width, dinfo->image_height,
160 std::move(profile));
161
162 SkJpegCodec* codec = new SkJpegCodec(std::move(info),
163 std::unique_ptr<SkStream>(stream),
164 decoderMgr.release(),
165 orientation);
166 *codecOut = codec;
167 } else {
168 SkASSERT(nullptr != decoderMgrOut);
169 *decoderMgrOut = decoderMgr.release();
170 }
171 return kSuccess;
172}
173
174std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
175 Result* result) {
176 return SkJpegCodec::MakeFromStream(std::move(stream), result, nullptr);
177}
178
179std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
180 Result* result, std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
182 if (!stream) {
184 return nullptr;
185 }
186 SkCodec* codec = nullptr;
187 *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorProfile));
188 if (kSuccess == *result) {
189 // Codec has taken ownership of the stream, we do not need to delete it
190 SkASSERT(codec);
191 stream.release();
192 return std::unique_ptr<SkCodec>(codec);
193 }
194 return nullptr;
195}
196
197SkJpegCodec::SkJpegCodec(SkEncodedInfo&& info,
198 std::unique_ptr<SkStream> stream,
199 JpegDecoderMgr* decoderMgr,
200 SkEncodedOrigin origin)
201 : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin)
202 , fDecoderMgr(decoderMgr)
203 , fReadyState(decoderMgr->dinfo()->global_state) {}
204SkJpegCodec::~SkJpegCodec() = default;
205
206/*
207 * Return the row bytes of a particular image type and width
208 */
209static size_t get_row_bytes(const j_decompress_ptr dinfo) {
210 const size_t colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 :
211 dinfo->out_color_components;
212 return dinfo->output_width * colorBytes;
213
214}
215
216/*
217 * Calculate output dimensions based on the provided factors.
218 *
219 * Not to be used on the actual jpeg_decompress_struct used for decoding, since it will
220 * incorrectly modify num_components.
221 */
222void calc_output_dimensions(jpeg_decompress_struct* dinfo, unsigned int num, unsigned int denom) {
223 dinfo->num_components = 0;
224 dinfo->scale_num = num;
225 dinfo->scale_denom = denom;
226 jpeg_calc_output_dimensions(dinfo);
227}
228
229/*
230 * Return a valid set of output dimensions for this decoder, given an input scale
231 */
233 // 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
234 // support these as well
235 unsigned int num;
236 unsigned int denom = 8;
237 if (desiredScale >= 0.9375) {
238 num = 8;
239 } else if (desiredScale >= 0.8125) {
240 num = 7;
241 } else if (desiredScale >= 0.6875f) {
242 num = 6;
243 } else if (desiredScale >= 0.5625f) {
244 num = 5;
245 } else if (desiredScale >= 0.4375f) {
246 num = 4;
247 } else if (desiredScale >= 0.3125f) {
248 num = 3;
249 } else if (desiredScale >= 0.1875f) {
250 num = 2;
251 } else {
252 num = 1;
253 }
254
255 // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
256 jpeg_decompress_struct dinfo;
257 sk_bzero(&dinfo, sizeof(dinfo));
258 dinfo.image_width = this->dimensions().width();
259 dinfo.image_height = this->dimensions().height();
260 dinfo.global_state = fReadyState;
261 calc_output_dimensions(&dinfo, num, denom);
262
263 // Return the calculated output dimensions for the given scale
264 return SkISize::Make(dinfo.output_width, dinfo.output_height);
265}
266
268 JpegDecoderMgr* decoderMgr = nullptr;
269 if (kSuccess != ReadHeader(this->stream(), nullptr, &decoderMgr, nullptr)) {
270 return fDecoderMgr->returnFalse("onRewind");
271 }
272 SkASSERT(nullptr != decoderMgr);
273 fDecoderMgr.reset(decoderMgr);
274
275 fSwizzler.reset(nullptr);
276 fSwizzleSrcRow = nullptr;
277 fColorXformSrcRow = nullptr;
278 fStorage.reset();
279
280 return true;
281}
282
283bool SkJpegCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque,
284 bool needsColorXform) {
285 SkASSERT(srcIsOpaque);
286
288 return false;
289 }
290
292 SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
293 "- it is being decoded as non-opaque, which will draw slower\n");
294 }
295
296 J_COLOR_SPACE encodedColorType = fDecoderMgr->dinfo()->jpeg_color_space;
297
298 // Check for valid color types and set the output color space
299 switch (dstInfo.colorType()) {
301 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
302 break;
304 if (needsColorXform) {
305 // Always using RGBA as the input format for color xforms makes the
306 // implementation a little simpler.
307 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
308 } else {
309 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA;
310 }
311 break;
313 if (needsColorXform) {
314 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
315 } else {
316 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE;
317 fDecoderMgr->dinfo()->out_color_space = JCS_RGB565;
318 }
319 break;
321 if (JCS_GRAYSCALE != encodedColorType) {
322 return false;
323 }
324
325 if (needsColorXform) {
326 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
327 } else {
328 fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
329 }
330 break;
334 SkASSERT(needsColorXform);
335 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
336 break;
337 default:
338 return false;
339 }
340
341 // Check if we will decode to CMYK. libjpeg-turbo does not convert CMYK to RGBA, so
342 // we must do it ourselves.
343 if (JCS_CMYK == encodedColorType || JCS_YCCK == encodedColorType) {
344 fDecoderMgr->dinfo()->out_color_space = JCS_CMYK;
345 }
346
347 return true;
348}
349
350/*
351 * Checks if we can natively scale to the requested dimensions and natively scales the
352 * dimensions if possible
353 */
355 skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr());
356 if (setjmp(jmp)) {
357 return fDecoderMgr->returnFalse("onDimensionsSupported");
358 }
359
360 const unsigned int dstWidth = size.width();
361 const unsigned int dstHeight = size.height();
362
363 // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
364 // FIXME: Why is this necessary?
365 jpeg_decompress_struct dinfo;
366 sk_bzero(&dinfo, sizeof(dinfo));
367 dinfo.image_width = this->dimensions().width();
368 dinfo.image_height = this->dimensions().height();
369 dinfo.global_state = fReadyState;
370
371 // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1
372 unsigned int num = 8;
373 const unsigned int denom = 8;
374 calc_output_dimensions(&dinfo, num, denom);
375 while (dinfo.output_width != dstWidth || dinfo.output_height != dstHeight) {
376
377 // Return a failure if we have tried all of the possible scales
378 if (1 == num || dstWidth > dinfo.output_width || dstHeight > dinfo.output_height) {
379 return false;
380 }
381
382 // Try the next scale
383 num -= 1;
384 calc_output_dimensions(&dinfo, num, denom);
385 }
386
387 fDecoderMgr->dinfo()->scale_num = num;
388 fDecoderMgr->dinfo()->scale_denom = denom;
389 return true;
390}
391
392int SkJpegCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count,
393 const Options& opts) {
394 // Set the jump location for libjpeg-turbo errors
395 skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr());
396 if (setjmp(jmp)) {
397 return 0;
398 }
399
400 // When fSwizzleSrcRow is non-null, it means that we need to swizzle. In this case,
401 // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer.
402 // We can never swizzle "in place" because the swizzler may perform sampling and/or
403 // subsetting.
404 // When fColorXformSrcRow is non-null, it means that we need to color xform and that
405 // we cannot color xform "in place" (many times we can, but not when the src and dst
406 // are different sizes).
407 // In this case, we will color xform from fColorXformSrcRow into the dst.
408 JSAMPLE* decodeDst = (JSAMPLE*) dst;
409 uint32_t* swizzleDst = (uint32_t*) dst;
410 size_t decodeDstRowBytes = rowBytes;
411 size_t swizzleDstRowBytes = rowBytes;
412 int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width();
413 if (fSwizzleSrcRow && fColorXformSrcRow) {
414 decodeDst = (JSAMPLE*) fSwizzleSrcRow;
415 swizzleDst = fColorXformSrcRow;
416 decodeDstRowBytes = 0;
417 swizzleDstRowBytes = 0;
418 dstWidth = fSwizzler->swizzleWidth();
419 } else if (fColorXformSrcRow) {
420 decodeDst = (JSAMPLE*) fColorXformSrcRow;
421 swizzleDst = fColorXformSrcRow;
422 decodeDstRowBytes = 0;
423 swizzleDstRowBytes = 0;
424 } else if (fSwizzleSrcRow) {
425 decodeDst = (JSAMPLE*) fSwizzleSrcRow;
426 decodeDstRowBytes = 0;
427 dstWidth = fSwizzler->swizzleWidth();
428 }
429
430 for (int y = 0; y < count; y++) {
431 uint32_t lines = jpeg_read_scanlines(fDecoderMgr->dinfo(), &decodeDst, 1);
432 if (0 == lines) {
433 return y;
434 }
435
436 if (fSwizzler) {
437 fSwizzler->swizzle(swizzleDst, decodeDst);
438 }
439
440 if (this->colorXform()) {
441 this->applyColorXform(dst, swizzleDst, dstWidth);
442 dst = SkTAddOffset<void>(dst, rowBytes);
443 }
444
445 decodeDst = SkTAddOffset<JSAMPLE>(decodeDst, decodeDstRowBytes);
446 swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
447 }
448
449 return count;
450}
451
452/*
453 * This is a bit tricky. We only need the swizzler to do format conversion if the jpeg is
454 * encoded as CMYK.
455 * And even then we still may not need it. If the jpeg has a CMYK color profile and a color
456 * xform, the color xform will handle the CMYK->RGB conversion.
457 */
458static inline bool needs_swizzler_to_convert_from_cmyk(J_COLOR_SPACE jpegColorType,
459 const skcms_ICCProfile* srcProfile,
460 bool hasColorSpaceXform) {
461 if (JCS_CMYK != jpegColorType) {
462 return false;
463 }
464
465 bool hasCMYKColorSpace = srcProfile && srcProfile->data_color_space == skcms_Signature_CMYK;
466 return !hasCMYKColorSpace || !hasColorSpaceXform;
467}
468
469/*
470 * Performs the jpeg decode
471 */
473 void* dst, size_t dstRowBytes,
474 const Options& options,
475 int* rowsDecoded) {
476 if (options.fSubset) {
477 // Subsets are not supported.
478 return kUnimplemented;
479 }
480
481 // Get a pointer to the decompress info since we will use it quite frequently
482 jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
483
484 // Set the jump location for libjpeg errors
485 skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr());
486 if (setjmp(jmp)) {
487 return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
488 }
489
490 if (!jpeg_start_decompress(dinfo)) {
491 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
492 }
493
494 // The recommended output buffer height should always be 1 in high quality modes.
495 // If it's not, we want to know because it means our strategy is not optimal.
496 SkASSERT(1 == dinfo->rec_outbuf_height);
497
498 if (needs_swizzler_to_convert_from_cmyk(dinfo->out_color_space,
499 this->getEncodedInfo().profile(), this->colorXform())) {
500 this->initializeSwizzler(dstInfo, options, true);
501 }
502
503 if (!this->allocateStorage(dstInfo)) {
504 return kInternalError;
505 }
506
507 int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
508 if (rows < dstInfo.height()) {
509 *rowsDecoded = rows;
510 return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput);
511 }
512
513 return kSuccess;
514}
515
516bool SkJpegCodec::allocateStorage(const SkImageInfo& dstInfo) {
517 int dstWidth = dstInfo.width();
518
519 size_t swizzleBytes = 0;
520 if (fSwizzler) {
521 swizzleBytes = get_row_bytes(fDecoderMgr->dinfo());
522 dstWidth = fSwizzler->swizzleWidth();
523 SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
524 }
525
526 size_t xformBytes = 0;
527
528 if (this->colorXform() && sizeof(uint32_t) != dstInfo.bytesPerPixel()) {
529 xformBytes = dstWidth * sizeof(uint32_t);
530 }
531
532 size_t totalBytes = swizzleBytes + xformBytes;
533 if (totalBytes > 0) {
534 if (!fStorage.reset(totalBytes)) {
535 return false;
536 }
537 fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
538 fColorXformSrcRow = (xformBytes > 0) ?
539 SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
540 }
541 return true;
542}
543
544void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
545 bool needsCMYKToRGB) {
546 Options swizzlerOptions = options;
547 if (options.fSubset) {
548 // Use fSwizzlerSubset if this is a subset decode. This is necessary in the case
549 // where libjpeg-turbo provides a subset and then we need to subset it further.
550 // Also, verify that fSwizzlerSubset is initialized and valid.
551 SkASSERT(!fSwizzlerSubset.isEmpty() && fSwizzlerSubset.x() <= options.fSubset->x() &&
552 fSwizzlerSubset.width() == options.fSubset->width());
553 swizzlerOptions.fSubset = &fSwizzlerSubset;
554 }
555
556 SkImageInfo swizzlerDstInfo = dstInfo;
557 if (this->colorXform()) {
558 // The color xform will be expecting RGBA 8888 input.
559 swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
560 }
561
562 if (needsCMYKToRGB) {
563 // The swizzler is used to convert to from CMYK.
564 // The swizzler does not use the width or height on SkEncodedInfo.
567 fSwizzler = SkSwizzler::Make(swizzlerInfo, nullptr, swizzlerDstInfo, swizzlerOptions);
568 } else {
569 int srcBPP = 0;
570 switch (fDecoderMgr->dinfo()->out_color_space) {
571 case JCS_EXT_RGBA:
572 case JCS_EXT_BGRA:
573 case JCS_CMYK:
574 srcBPP = 4;
575 break;
576 case JCS_RGB565:
577 srcBPP = 2;
578 break;
579 case JCS_GRAYSCALE:
580 srcBPP = 1;
581 break;
582 default:
583 SkASSERT(false);
584 break;
585 }
586 fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerDstInfo, swizzlerOptions);
587 }
588 SkASSERT(fSwizzler);
589}
590
591SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
592 if (!createIfNecessary || fSwizzler) {
593 SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
594 return fSwizzler.get();
595 }
596
597 bool needsCMYKToRGB = needs_swizzler_to_convert_from_cmyk(
598 fDecoderMgr->dinfo()->out_color_space, this->getEncodedInfo().profile(),
599 this->colorXform());
600 this->initializeSwizzler(this->dstInfo(), this->options(), needsCMYKToRGB);
601 if (!this->allocateStorage(this->dstInfo())) {
602 return nullptr;
603 }
604 return fSwizzler.get();
605}
606
607SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
608 const Options& options) {
609 // Set the jump location for libjpeg errors
610 skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr());
611 if (setjmp(jmp)) {
612 SkCodecPrintf("setjmp: Error from libjpeg\n");
613 return kInvalidInput;
614 }
615
616 if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
617 SkCodecPrintf("start decompress failed\n");
618 return kInvalidInput;
619 }
620
621 bool needsCMYKToRGB = needs_swizzler_to_convert_from_cmyk(
622 fDecoderMgr->dinfo()->out_color_space, this->getEncodedInfo().profile(),
623 this->colorXform());
624 if (options.fSubset) {
625 uint32_t startX = options.fSubset->x();
626 uint32_t width = options.fSubset->width();
627
628 // libjpeg-turbo may need to align startX to a multiple of the IDCT
629 // block size. If this is the case, it will decrease the value of
630 // startX to the appropriate alignment and also increase the value
631 // of width so that the right edge of the requested subset remains
632 // the same.
633 jpeg_crop_scanline(fDecoderMgr->dinfo(), &startX, &width);
634
635 SkASSERT(startX <= (uint32_t) options.fSubset->x());
636 SkASSERT(width >= (uint32_t) options.fSubset->width());
637 SkASSERT(startX + width >= (uint32_t) options.fSubset->right());
638
639 // Instruct the swizzler (if it is necessary) to further subset the
640 // output provided by libjpeg-turbo.
641 //
642 // We set this here (rather than in the if statement below), so that
643 // if (1) we don't need a swizzler for the subset, and (2) we need a
644 // swizzler for CMYK, the swizzler will still use the proper subset
645 // dimensions.
646 //
647 // Note that the swizzler will ignore the y and height parameters of
648 // the subset. Since the scanline decoder (and the swizzler) handle
649 // one row at a time, only the subsetting in the x-dimension matters.
650 fSwizzlerSubset.setXYWH(options.fSubset->x() - startX, 0,
652
653 // We will need a swizzler if libjpeg-turbo cannot provide the exact
654 // subset that we request.
655 if (startX != (uint32_t) options.fSubset->x() ||
656 width != (uint32_t) options.fSubset->width()) {
657 this->initializeSwizzler(dstInfo, options, needsCMYKToRGB);
658 }
659 }
660
661 // Make sure we have a swizzler if we are converting from CMYK.
662 if (!fSwizzler && needsCMYKToRGB) {
663 this->initializeSwizzler(dstInfo, options, true);
664 }
665
666 if (!this->allocateStorage(dstInfo)) {
667 return kInternalError;
668 }
669
670 return kSuccess;
671}
672
673int SkJpegCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
674 int rows = this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
675 if (rows < count) {
676 // This allows us to skip calling jpeg_finish_decompress().
677 fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height();
678 }
679
680 return rows;
681}
682
683bool SkJpegCodec::onSkipScanlines(int count) {
684 // Set the jump location for libjpeg errors
685 skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr());
686 if (setjmp(jmp)) {
687 return fDecoderMgr->returnFalse("onSkipScanlines");
688 }
689
690 return (uint32_t) count == jpeg_skip_scanlines(fDecoderMgr->dinfo(), count);
691}
692
693static bool is_yuv_supported(const jpeg_decompress_struct* dinfo,
694 const SkJpegCodec& codec,
695 const SkYUVAPixmapInfo::SupportedDataTypes* supportedDataTypes,
696 SkYUVAPixmapInfo* yuvaPixmapInfo) {
697 // Scaling is not supported in raw data mode.
698 SkASSERT(dinfo->scale_num == dinfo->scale_denom);
699
700 // I can't imagine that this would ever change, but we do depend on it.
701 static_assert(8 == DCTSIZE, "DCTSIZE (defined in jpeg library) should always be 8.");
702
703 if (JCS_YCbCr != dinfo->jpeg_color_space) {
704 return false;
705 }
706
707 SkASSERT(3 == dinfo->num_components);
708 SkASSERT(dinfo->comp_info);
709
710 // It is possible to perform a YUV decode for any combination of
711 // horizontal and vertical sampling that is supported by
712 // libjpeg/libjpeg-turbo. However, we will start by supporting only the
713 // common cases (where U and V have samp_factors of one).
714 //
715 // The definition of samp_factor is kind of the opposite of what SkCodec
716 // thinks of as a sampling factor. samp_factor is essentially a
717 // multiplier, and the larger the samp_factor is, the more samples that
718 // there will be. Ex:
719 // U_plane_width = image_width * (U_h_samp_factor / max_h_samp_factor)
720 //
721 // Supporting cases where the samp_factors for U or V were larger than
722 // that of Y would be an extremely difficult change, given that clients
723 // allocate memory as if the size of the Y plane is always the size of the
724 // image. However, this case is very, very rare.
725 if ((1 != dinfo->comp_info[1].h_samp_factor) ||
726 (1 != dinfo->comp_info[1].v_samp_factor) ||
727 (1 != dinfo->comp_info[2].h_samp_factor) ||
728 (1 != dinfo->comp_info[2].v_samp_factor))
729 {
730 return false;
731 }
732
733 // Support all common cases of Y samp_factors.
734 // TODO (msarett): As mentioned above, it would be possible to support
735 // more combinations of samp_factors. The issues are:
736 // (1) Are there actually any images that are not covered
737 // by these cases?
738 // (2) How much complexity would be added to the
739 // implementation in order to support these rare
740 // cases?
741 int hSampY = dinfo->comp_info[0].h_samp_factor;
742 int vSampY = dinfo->comp_info[0].v_samp_factor;
743 SkASSERT(hSampY == dinfo->max_h_samp_factor);
744 SkASSERT(vSampY == dinfo->max_v_samp_factor);
745
746 SkYUVAInfo::Subsampling tempSubsampling;
747 if (1 == hSampY && 1 == vSampY) {
748 tempSubsampling = SkYUVAInfo::Subsampling::k444;
749 } else if (2 == hSampY && 1 == vSampY) {
750 tempSubsampling = SkYUVAInfo::Subsampling::k422;
751 } else if (2 == hSampY && 2 == vSampY) {
752 tempSubsampling = SkYUVAInfo::Subsampling::k420;
753 } else if (1 == hSampY && 2 == vSampY) {
754 tempSubsampling = SkYUVAInfo::Subsampling::k440;
755 } else if (4 == hSampY && 1 == vSampY) {
756 tempSubsampling = SkYUVAInfo::Subsampling::k411;
757 } else if (4 == hSampY && 2 == vSampY) {
758 tempSubsampling = SkYUVAInfo::Subsampling::k410;
759 } else {
760 return false;
761 }
762 if (supportedDataTypes &&
763 !supportedDataTypes->supported(SkYUVAInfo::PlaneConfig::kY_U_V,
765 return false;
766 }
767 if (yuvaPixmapInfo) {
769 size_t rowBytes[SkYUVAPixmapInfo::kMaxPlanes];
770 for (int i = 0; i < 3; ++i) {
771 colorTypes[i] = kAlpha_8_SkColorType;
772 rowBytes[i] = dinfo->comp_info[i].width_in_blocks * DCTSIZE;
773 }
774 SkYUVAInfo yuvaInfo(codec.dimensions(),
776 tempSubsampling,
778 codec.getOrigin(),
781 *yuvaPixmapInfo = SkYUVAPixmapInfo(yuvaInfo, colorTypes, rowBytes);
782 }
783 return true;
784}
785
787 SkYUVAPixmapInfo* yuvaPixmapInfo) const {
788 jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
789 return is_yuv_supported(dinfo, *this, &supportedDataTypes, yuvaPixmapInfo);
790}
791
793 // Get a pointer to the decompress info since we will use it quite frequently
794 jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
795 if (!is_yuv_supported(dinfo, *this, nullptr, nullptr)) {
796 return fDecoderMgr->returnFailure("onGetYUVAPlanes", kInvalidInput);
797 }
798 // Set the jump location for libjpeg errors
799 skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr());
800 if (setjmp(jmp)) {
801 return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
802 }
803
804 dinfo->raw_data_out = TRUE;
805 if (!jpeg_start_decompress(dinfo)) {
806 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
807 }
808
809 const std::array<SkPixmap, SkYUVAPixmaps::kMaxPlanes>& planes = yuvaPixmaps.planes();
810
811#ifdef SK_DEBUG
812 {
813 // A previous implementation claims that the return value of is_yuv_supported()
814 // may change after calling jpeg_start_decompress(). It looks to me like this
815 // was caused by a bug in the old code, but we'll be safe and check here.
816 // Also check that pixmap properties agree with expectations.
818 SkASSERT(is_yuv_supported(dinfo, *this, nullptr, &info));
819 SkASSERT(info.yuvaInfo() == yuvaPixmaps.yuvaInfo());
820 for (int i = 0; i < info.numPlanes(); ++i) {
822 SkASSERT(info.planeInfo(i) == planes[i].info());
823 }
824 }
825#endif
826
827 // Build a JSAMPIMAGE to handle output from libjpeg-turbo. A JSAMPIMAGE has
828 // a 2-D array of pixels for each of the components (Y, U, V) in the image.
829 // Cheat Sheet:
830 // JSAMPIMAGE == JSAMPLEARRAY* == JSAMPROW** == JSAMPLE***
831 JSAMPARRAY yuv[3];
832
833 // Set aside enough space for pointers to rows of Y, U, and V.
834 JSAMPROW rowptrs[2 * DCTSIZE + DCTSIZE + DCTSIZE];
835 yuv[0] = &rowptrs[0]; // Y rows (DCTSIZE or 2 * DCTSIZE)
836 yuv[1] = &rowptrs[2 * DCTSIZE]; // U rows (DCTSIZE)
837 yuv[2] = &rowptrs[3 * DCTSIZE]; // V rows (DCTSIZE)
838
839 // Initialize rowptrs.
840 int numYRowsPerBlock = DCTSIZE * dinfo->comp_info[0].v_samp_factor;
841 static_assert(sizeof(JSAMPLE) == 1);
842 for (int i = 0; i < numYRowsPerBlock; i++) {
843 rowptrs[i] = static_cast<JSAMPLE*>(planes[0].writable_addr()) + i* planes[0].rowBytes();
844 }
845 for (int i = 0; i < DCTSIZE; i++) {
846 rowptrs[i + 2 * DCTSIZE] =
847 static_cast<JSAMPLE*>(planes[1].writable_addr()) + i* planes[1].rowBytes();
848 rowptrs[i + 3 * DCTSIZE] =
849 static_cast<JSAMPLE*>(planes[2].writable_addr()) + i* planes[2].rowBytes();
850 }
851
852 // After each loop iteration, we will increment pointers to Y, U, and V.
853 size_t blockIncrementY = numYRowsPerBlock * planes[0].rowBytes();
854 size_t blockIncrementU = DCTSIZE * planes[1].rowBytes();
855 size_t blockIncrementV = DCTSIZE * planes[2].rowBytes();
856
857 uint32_t numRowsPerBlock = numYRowsPerBlock;
858
859 // We intentionally round down here, as this first loop will only handle
860 // full block rows. As a special case at the end, we will handle any
861 // remaining rows that do not make up a full block.
862 const int numIters = dinfo->output_height / numRowsPerBlock;
863 for (int i = 0; i < numIters; i++) {
864 JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock);
865 if (linesRead < numRowsPerBlock) {
866 // FIXME: Handle incomplete YUV decodes without signalling an error.
867 return kInvalidInput;
868 }
869
870 // Update rowptrs.
871 for (int j = 0; j < numYRowsPerBlock; j++) {
872 rowptrs[j] += blockIncrementY;
873 }
874 for (int j = 0; j < DCTSIZE; j++) {
875 rowptrs[j + 2 * DCTSIZE] += blockIncrementU;
876 rowptrs[j + 3 * DCTSIZE] += blockIncrementV;
877 }
878 }
879
880 uint32_t remainingRows = dinfo->output_height - dinfo->output_scanline;
881 SkASSERT(remainingRows == dinfo->output_height % numRowsPerBlock);
882 SkASSERT(dinfo->output_scanline == numIters * numRowsPerBlock);
883 if (remainingRows > 0) {
884 // libjpeg-turbo needs memory to be padded by the block sizes. We will fulfill
885 // this requirement using an extra row buffer.
886 // FIXME: Should SkCodec have an extra memory buffer that can be shared among
887 // all of the implementations that use temporary/garbage memory?
888 AutoTMalloc<JSAMPLE> extraRow(planes[0].rowBytes());
889 for (int i = remainingRows; i < numYRowsPerBlock; i++) {
890 rowptrs[i] = extraRow.get();
891 }
892 int remainingUVRows = dinfo->comp_info[1].downsampled_height - DCTSIZE * numIters;
893 for (int i = remainingUVRows; i < DCTSIZE; i++) {
894 rowptrs[i + 2 * DCTSIZE] = extraRow.get();
895 rowptrs[i + 3 * DCTSIZE] = extraRow.get();
896 }
897
898 JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock);
899 if (linesRead < remainingRows) {
900 // FIXME: Handle incomplete YUV decodes without signalling an error.
901 return kInvalidInput;
902 }
903 }
904
905 return kSuccess;
906}
907
909 std::unique_ptr<SkStream>* gainmapImageStream) {
910#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
911 sk_sp<SkData> gainmap_data;
912 SkGainmapInfo gainmap_info;
913
914 auto metadataDecoder =
915 std::make_unique<SkJpegMetadataDecoderImpl>(get_sk_marker_list(fDecoderMgr->dinfo()));
916 if (!metadataDecoder->findGainmapImage(
917 fDecoderMgr->getSourceMgr(), gainmap_data, gainmap_info)) {
918 return false;
919 }
920
921 *info = gainmap_info;
922 *gainmapImageStream = SkMemoryStream::Make(gainmap_data);
923 return true;
924#else
925 return false;
926#endif // SK_CODEC_DECODES_JPEG_GAINMAPS
927}
928
929namespace SkJpegDecoder {
930bool IsJpeg(const void* data, size_t len) {
932}
933
934std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
935 SkCodec::Result* outResult,
937 SkCodec::Result resultStorage;
938 if (!outResult) {
939 outResult = &resultStorage;
940 }
941 return SkJpegCodec::MakeFromStream(std::move(stream), outResult);
942}
943
944std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
945 SkCodec::Result* outResult,
947 if (!data) {
948 if (outResult) {
949 *outResult = SkCodec::kInvalidInput;
950 }
951 return nullptr;
952 }
953 return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr);
954}
955
956} // namespace SkJpegDecoder
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
sk_bzero(glyphs, sizeof(glyphs))
int count
Definition: FontMgrTest.cpp:50
static const char marker[]
static constexpr bool SkIsAlign4(T x)
Definition: SkAlign.h:20
@ kUnknown_SkAlphaType
uninitialized
Definition: SkAlphaType.h:27
@ kOpaque_SkAlphaType
pixel is opaque
Definition: SkAlphaType.h:28
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkCodecPrintf(...)
Definition: SkCodecPriv.h:23
SkColorType
Definition: SkColorType.h:19
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition: SkColorType.h:38
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition: SkColorType.h:21
@ kGray_8_SkColorType
pixel with grayscale level in 8-bit byte
Definition: SkColorType.h:35
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition: SkColorType.h:22
@ kBGRA_10101010_XR_SkColorType
pixel with 10 bits each for blue, green, red, alpha; in 64-bit word, extended range
Definition: SkColorType.h:32
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
@ kBGR_101010x_XR_SkColorType
pixel with 10 bits each for blue, green, red; in 32-bit word, extended range
Definition: SkColorType.h:31
SkEncodedOrigin
@ kDefault_SkEncodedOrigin
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
@ kJPEG_Full_SkYUVColorSpace
describes full range
Definition: SkImageInfo.h:69
static size_t get_row_bytes(const j_decompress_ptr dinfo)
void calc_output_dimensions(jpeg_decompress_struct *dinfo, unsigned int num, unsigned int denom)
static bool is_yuv_supported(const jpeg_decompress_struct *dinfo, const SkJpegCodec &codec, const SkYUVAPixmapInfo::SupportedDataTypes *supportedDataTypes, SkYUVAPixmapInfo *yuvaPixmapInfo)
static SkEncodedOrigin get_exif_orientation(sk_sp< SkData > exifData)
Definition: SkJpegCodec.cpp:69
SkJpegMarkerList get_sk_marker_list(jpeg_decompress_struct *dinfo)
Definition: SkJpegCodec.cpp:60
static bool needs_swizzler_to_convert_from_cmyk(J_COLOR_SPACE jpegColorType, const skcms_ICCProfile *srcProfile, bool hasColorSpaceXform)
static constexpr uint32_t kExifMarker
static constexpr uint32_t kMpfMarker
static constexpr uint32_t kICCMarker
static constexpr uint8_t kJpegSig[]
std::vector< SkJpegMarker > SkJpegMarkerList
bool SkParseEncodedOrigin(const void *data, size_t data_length, SkEncodedOrigin *orientation)
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
GLenum type
SkISize dimensions() const
Definition: SkCodec.h:230
const SkImageInfo & dstInfo() const
Definition: SkCodec.h:878
SkStream * stream()
Definition: SkCodec.h:865
void applyColorXform(void *dst, const void *src, int count) const
Definition: SkCodec.cpp:853
Result
Definition: SkCodec.h:76
@ kIncompleteInput
Definition: SkCodec.h:84
@ kInvalidInput
Definition: SkCodec.h:109
@ kInternalError
Definition: SkCodec.h:118
@ kUnimplemented
Definition: SkCodec.h:123
@ kSuccess
Definition: SkCodec.h:80
bool colorXform() const
Definition: SkCodec.h:906
SkEncodedOrigin getOrigin() const
Definition: SkCodec.h:246
const Options & options() const
Definition: SkCodec.h:880
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition: SkData.h:116
const uint8_t * bytes() const
Definition: SkData.h:43
size_t size() const
Definition: SkData.h:30
static std::unique_ptr< ICCProfile > Make(sk_sp< SkData >)
bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes &, SkYUVAPixmapInfo *) const override
static bool IsJpeg(const void *, size_t)
Definition: SkJpegCodec.cpp:56
bool onRewind() override
Result onGetYUVAPlanes(const SkYUVAPixmaps &yuvaPixmaps) override
Result onGetPixels(const SkImageInfo &dstInfo, void *dst, size_t dstRowBytes, const Options &, int *) override
bool onDimensionsSupported(const SkISize &) override
SkISize onGetScaledDimensions(float desiredScale) const override
bool conversionSupported(const SkImageInfo &, bool, bool) override
bool onGetGainmapInfo(SkGainmapInfo *info, std::unique_ptr< SkStream > *gainmapImageStream) override
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, Result *)
~SkJpegCodec() override
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
static std::unique_ptr< SkSwizzler > MakeSimple(int srcBPP, const SkImageInfo &dstInfo, const SkCodec::Options &)
Definition: SkSwizzler.cpp:792
static std::unique_ptr< SkSwizzler > Make(const SkEncodedInfo &encodedInfo, const SkPMColor *ctable, const SkImageInfo &dstInfo, const SkCodec::Options &, const SkIRect *frame=nullptr)
Definition: SkSwizzler.cpp:821
@ kY_U_V
Plane 0: Y, Plane 1: U, Plane 2: V.
@ k440
1 set of UV values for each 1x2 block of Y values.
@ k420
1 set of UV values for each 2x2 block of Y values.
@ k410
1 set of UV values for each 4x2 block of Y values.
@ k411
1 set of UV values for each 4x1 block of Y values.
@ k422
1 set of UV values for each 2x1 block of Y values.
@ k444
No subsampling. UV values for each Y.
constexpr bool supported(PlaneConfig, DataType) const
@ kUnorm8
8 bit unsigned normalized
static constexpr auto kMaxPlanes
Definition: SkYUVAPixmaps.h:32
const SkYUVAInfo & yuvaInfo() const
const std::array< SkPixmap, kMaxPlanes > & planes() const
T * reset(size_t count=0)
Definition: SkTemplates.h:296
DlColor color
GAsyncResult * result
double y
void * DecodeContext
Definition: SkCodec.h:1047
SK_API bool IsJpeg(const void *, size_t)
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
Definition: ref_ptr.h:256
int32_t width
@ skcms_PixelFormat_RGBA_8888
Definition: skcms_public.h:287
@ skcms_Signature_RGB
Definition: skcms_public.h:266
@ skcms_Signature_Gray
Definition: skcms_public.h:265
@ skcms_Signature_CMYK
Definition: skcms_public.h:264
const SkIRect * fSubset
Definition: SkCodec.h:347
static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, int bitsPerComponent)
constexpr int32_t x() const
Definition: SkRect.h:141
constexpr int32_t height() const
Definition: SkRect.h:165
constexpr int32_t right() const
Definition: SkRect.h:127
constexpr int32_t width() const
Definition: SkRect.h:158
bool isEmpty() const
Definition: SkRect.h:202
void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height)
Definition: SkRect.h:268
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
int bytesPerPixel() const
Definition: SkImageInfo.h:492
int width() const
Definition: SkImageInfo.h:365
SkAlphaType alphaType() const
Definition: SkImageInfo.h:375
SkColorType colorType() const
Definition: SkImageInfo.h:373
int height() const
Definition: SkImageInfo.h:371
SkImageInfo makeColorType(SkColorType newColorType) const
Definition: SkImageInfo.h:475
uint32_t data_color_space
Definition: skcms_public.h:177
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63