1 /*
2 * Copyright 2007 The Android Open Source Project
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
8 #include "src/encode/SkJpegEncoderImpl.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPixmap.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkStream.h"
18 #include "include/core/SkYUVAInfo.h"
19 #include "include/core/SkYUVAPixmaps.h"
20 #include "include/encode/SkEncoder.h"
21 #include "include/encode/SkJpegEncoder.h"
22 #include "include/private/base/SkAssert.h"
23 #include "include/private/base/SkDebug.h"
24 #include "include/private/base/SkNoncopyable.h"
25 #include "include/private/base/SkTemplates.h"
26 #include "src/base/SkMSAN.h"
27 #include "src/codec/SkJpegConstants.h"
28 #include "src/codec/SkJpegPriv.h"
29 #include "src/encode/SkImageEncoderFns.h"
30 #include "src/encode/SkImageEncoderPriv.h"
31 #include "src/encode/SkJPEGWriteUtility.h"
32 #include "src/image/SkImage_Base.h"
33
34 #include <csetjmp>
35 #include <cstdint>
36 #include <cstring>
37 #include <memory>
38 #include <utility>
39
40 class GrDirectContext;
41 class SkColorSpace;
42 class SkImage;
43
44 extern "C" {
45 #include "jpeglib.h" // NO_G3_REWRITE
46 }
47
48 class SkJpegEncoderMgr final : SkNoncopyable {
49 public:
50 /*
51 * Create the decode manager
52 * Does not take ownership of stream.
53 */
Make(SkWStream * stream)54 static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
55 return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
56 }
57
58 bool initializeRGB(const SkImageInfo&,
59 const SkJpegEncoder::Options&,
60 const SkJpegMetadataEncoder::SegmentList&);
61 bool initializeYUV(const SkYUVAPixmapInfo&,
62 const SkJpegEncoder::Options&,
63 const SkJpegMetadataEncoder::SegmentList&);
64
cinfo()65 jpeg_compress_struct* cinfo() { return &fCInfo; }
66
errorMgr()67 skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
68
proc() const69 transform_scanline_proc proc() const { return fProc; }
70
~SkJpegEncoderMgr()71 ~SkJpegEncoderMgr() { jpeg_destroy_compress(&fCInfo); }
72
73 private:
SkJpegEncoderMgr(SkWStream * stream)74 SkJpegEncoderMgr(SkWStream* stream) : fDstMgr(stream), fProc(nullptr) {
75 fCInfo.err = jpeg_std_error(&fErrMgr);
76 fErrMgr.error_exit = skjpeg_error_exit;
77 jpeg_create_compress(&fCInfo);
78 fCInfo.dest = &fDstMgr;
79 }
80 void initializeCommon(const SkJpegEncoder::Options&, const SkJpegMetadataEncoder::SegmentList&);
81
82 jpeg_compress_struct fCInfo;
83 skjpeg_error_mgr fErrMgr;
84 skjpeg_destination_mgr fDstMgr;
85 transform_scanline_proc fProc;
86 };
87
initializeRGB(const SkImageInfo & srcInfo,const SkJpegEncoder::Options & options,const SkJpegMetadataEncoder::SegmentList & metadataSegments)88 bool SkJpegEncoderMgr::initializeRGB(const SkImageInfo& srcInfo,
89 const SkJpegEncoder::Options& options,
90 const SkJpegMetadataEncoder::SegmentList& metadataSegments) {
91 auto chooseProc8888 = [&]() {
92 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
93 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
94 return transform_scanline_to_premul_legacy;
95 }
96 return (transform_scanline_proc) nullptr;
97 };
98
99 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
100 int numComponents = 0;
101 switch (srcInfo.colorType()) {
102 case kRGBA_8888_SkColorType:
103 fProc = chooseProc8888();
104 jpegColorType = JCS_EXT_RGBA;
105 numComponents = 4;
106 break;
107 case kBGRA_8888_SkColorType:
108 fProc = chooseProc8888();
109 jpegColorType = JCS_EXT_BGRA;
110 numComponents = 4;
111 break;
112 case kRGB_565_SkColorType:
113 fProc = transform_scanline_565;
114 jpegColorType = JCS_RGB;
115 numComponents = 3;
116 break;
117 case kARGB_4444_SkColorType:
118 if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
119 return false;
120 }
121
122 fProc = transform_scanline_444;
123 jpegColorType = JCS_RGB;
124 numComponents = 3;
125 break;
126 case kGray_8_SkColorType:
127 case kAlpha_8_SkColorType:
128 case kR8_unorm_SkColorType:
129 jpegColorType = JCS_GRAYSCALE;
130 numComponents = 1;
131 break;
132 case kRGBA_F16_SkColorType:
133 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
134 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
135 fProc = transform_scanline_F16_to_premul_8888;
136 } else {
137 fProc = transform_scanline_F16_to_8888;
138 }
139 jpegColorType = JCS_EXT_RGBA;
140 numComponents = 4;
141 break;
142 default:
143 return false;
144 }
145
146 fCInfo.image_width = srcInfo.width();
147 fCInfo.image_height = srcInfo.height();
148 fCInfo.in_color_space = jpegColorType;
149 fCInfo.input_components = numComponents;
150 jpeg_set_defaults(&fCInfo);
151
152 if (numComponents != 1) {
153 switch (options.fDownsample) {
154 case SkJpegEncoder::Downsample::k420:
155 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
156 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
157 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
158 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
159 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
160 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
161 break;
162 case SkJpegEncoder::Downsample::k422:
163 fCInfo.comp_info[0].h_samp_factor = 2;
164 fCInfo.comp_info[0].v_samp_factor = 1;
165 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
166 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
167 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
168 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
169 break;
170 case SkJpegEncoder::Downsample::k444:
171 fCInfo.comp_info[0].h_samp_factor = 1;
172 fCInfo.comp_info[0].v_samp_factor = 1;
173 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
174 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
175 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
176 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
177 break;
178 }
179 }
180
181 initializeCommon(options, metadataSegments);
182 return true;
183 }
184
185 // Convert a row of an SkYUVAPixmaps to a row of Y,U,V triples.
186 // TODO(ccameron): This is horribly inefficient.
yuva_copy_row(const SkYUVAPixmaps & src,int row,uint8_t * dst)187 static void yuva_copy_row(const SkYUVAPixmaps& src, int row, uint8_t* dst) {
188 int width = src.plane(0).width();
189 switch (src.yuvaInfo().planeConfig()) {
190 case SkYUVAInfo::PlaneConfig::kY_U_V: {
191 auto [ssWidthU, ssHeightU] = src.yuvaInfo().planeSubsamplingFactors(1);
192 auto [ssWidthV, ssHeightV] = src.yuvaInfo().planeSubsamplingFactors(2);
193 const uint8_t* srcY = reinterpret_cast<const uint8_t*>(src.plane(0).addr(0, row));
194 const uint8_t* srcU =
195 reinterpret_cast<const uint8_t*>(src.plane(1).addr(0, row / ssHeightU));
196 const uint8_t* srcV =
197 reinterpret_cast<const uint8_t*>(src.plane(2).addr(0, row / ssHeightV));
198 for (int col = 0; col < width; ++col) {
199 dst[3 * col + 0] = srcY[col];
200 dst[3 * col + 1] = srcU[col / ssWidthU];
201 dst[3 * col + 2] = srcV[col / ssWidthV];
202 }
203 break;
204 }
205 case SkYUVAInfo::PlaneConfig::kY_UV: {
206 auto [ssWidthUV, ssHeightUV] = src.yuvaInfo().planeSubsamplingFactors(1);
207 const uint8_t* srcY = reinterpret_cast<const uint8_t*>(src.plane(0).addr(0, row));
208 const uint8_t* srcUV =
209 reinterpret_cast<const uint8_t*>(src.plane(1).addr(0, row / ssHeightUV));
210 for (int col = 0; col < width; ++col) {
211 dst[3 * col + 0] = srcY[col];
212 dst[3 * col + 1] = srcUV[2 * (col / ssWidthUV) + 0];
213 dst[3 * col + 2] = srcUV[2 * (col / ssWidthUV) + 1];
214 }
215 break;
216 }
217 default:
218 break;
219 }
220 }
221
initializeYUV(const SkYUVAPixmapInfo & srcInfo,const SkJpegEncoder::Options & options,const SkJpegMetadataEncoder::SegmentList & metadataSegments)222 bool SkJpegEncoderMgr::initializeYUV(const SkYUVAPixmapInfo& srcInfo,
223 const SkJpegEncoder::Options& options,
224 const SkJpegMetadataEncoder::SegmentList& metadataSegments) {
225 fCInfo.image_width = srcInfo.yuvaInfo().width();
226 fCInfo.image_height = srcInfo.yuvaInfo().height();
227 fCInfo.in_color_space = JCS_YCbCr;
228 fCInfo.input_components = 3;
229 jpeg_set_defaults(&fCInfo);
230
231 // Support no color space conversion.
232 if (srcInfo.yuvColorSpace() != kJPEG_Full_SkYUVColorSpace) {
233 return false;
234 }
235
236 // Support only 8-bit data.
237 switch (srcInfo.dataType()) {
238 case SkYUVAPixmapInfo::DataType::kUnorm8:
239 break;
240 default:
241 return false;
242 }
243
244 // Support only Y,U,V and Y,UV configurations (they are the only ones supported by
245 // yuva_copy_row).
246 switch (srcInfo.yuvaInfo().planeConfig()) {
247 case SkYUVAInfo::PlaneConfig::kY_U_V:
248 case SkYUVAInfo::PlaneConfig::kY_UV:
249 break;
250 default:
251 return false;
252 }
253
254 // Specify to the encoder to use the same subsampling as the input image. The U and V planes
255 // always have a sampling factor of 1.
256 auto [ssHoriz, ssVert] = SkYUVAInfo::SubsamplingFactors(srcInfo.yuvaInfo().subsampling());
257 fCInfo.comp_info[0].h_samp_factor = ssHoriz;
258 fCInfo.comp_info[0].v_samp_factor = ssVert;
259
260 initializeCommon(options, metadataSegments);
261 return true;
262 }
263
initializeCommon(const SkJpegEncoder::Options & options,const SkJpegMetadataEncoder::SegmentList & metadataSegments)264 void SkJpegEncoderMgr::initializeCommon(
265 const SkJpegEncoder::Options& options,
266 const SkJpegMetadataEncoder::SegmentList& metadataSegments) {
267 // Tells libjpeg-turbo to compute optimal Huffman coding tables
268 // for the image. This improves compression at the cost of
269 // slower encode performance.
270 fCInfo.optimize_coding = TRUE;
271
272 jpeg_set_quality(&fCInfo, options.fQuality, TRUE);
273 jpeg_start_compress(&fCInfo, TRUE);
274
275 for (const auto& segment : metadataSegments) {
276 jpeg_write_marker(&fCInfo,
277 segment.fMarker,
278 segment.fParameters->bytes(),
279 segment.fParameters->size());
280 }
281 }
282
MakeYUV(SkWStream * dst,const SkYUVAPixmaps & srcYUVA,const SkColorSpace * srcYUVAColorSpace,const SkJpegEncoder::Options & options,const SkJpegMetadataEncoder::SegmentList & metadataSegments)283 std::unique_ptr<SkEncoder> SkJpegEncoderImpl::MakeYUV(
284 SkWStream* dst,
285 const SkYUVAPixmaps& srcYUVA,
286 const SkColorSpace* srcYUVAColorSpace,
287 const SkJpegEncoder::Options& options,
288 const SkJpegMetadataEncoder::SegmentList& metadataSegments) {
289 if (!srcYUVA.isValid()) {
290 return nullptr;
291 }
292 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
293 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
294 if (setjmp(jmp)) {
295 return nullptr;
296 }
297
298 if (!encoderMgr->initializeYUV(srcYUVA.pixmapsInfo(), options, metadataSegments)) {
299 return nullptr;
300 }
301 return std::unique_ptr<SkJpegEncoderImpl>(
302 new SkJpegEncoderImpl(std::move(encoderMgr), srcYUVA));
303 }
304
MakeRGB(SkWStream * dst,const SkPixmap & src,const SkJpegEncoder::Options & options,const SkJpegMetadataEncoder::SegmentList & metadataSegments)305 std::unique_ptr<SkEncoder> SkJpegEncoderImpl::MakeRGB(
306 SkWStream* dst,
307 const SkPixmap& src,
308 const SkJpegEncoder::Options& options,
309 const SkJpegMetadataEncoder::SegmentList& metadataSegments) {
310 if (!SkPixmapIsValid(src)) {
311 return nullptr;
312 }
313 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
314 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
315 if (setjmp(jmp)) {
316 return nullptr;
317 }
318
319 if (!encoderMgr->initializeRGB(src.info(), options, metadataSegments)) {
320 return nullptr;
321 }
322 return std::unique_ptr<SkJpegEncoderImpl>(new SkJpegEncoderImpl(std::move(encoderMgr), src));
323 }
324
SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,const SkPixmap & src)325 SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,
326 const SkPixmap& src)
327 : SkEncoder(src,
328 encoderMgr->proc() ? encoderMgr->cinfo()->input_components * src.width() : 0)
329 , fEncoderMgr(std::move(encoderMgr)) {}
330
SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,const SkYUVAPixmaps & src)331 SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,
332 const SkYUVAPixmaps& src)
333 : SkEncoder(src.plane(0), encoderMgr->cinfo()->input_components * src.yuvaInfo().width())
334 , fEncoderMgr(std::move(encoderMgr))
335 , fSrcYUVA(src) {}
336
~SkJpegEncoderImpl()337 SkJpegEncoderImpl::~SkJpegEncoderImpl() {}
338
onEncodeRows(int numRows)339 bool SkJpegEncoderImpl::onEncodeRows(int numRows) {
340 skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
341 if (setjmp(jmp)) {
342 return false;
343 }
344
345 if (fSrcYUVA) {
346 // TODO(ccameron): Consider using jpeg_write_raw_data, to avoid having to re-pack the data.
347 for (int i = 0; i < numRows; i++) {
348 yuva_copy_row(*fSrcYUVA, fCurrRow + i, fStorage.get());
349 JSAMPLE* jpegSrcRow = fStorage.get();
350 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
351 }
352 } else {
353 const size_t srcBytes = SkColorTypeBytesPerPixel(fSrc.colorType()) * fSrc.width();
354 const size_t jpegSrcBytes = fEncoderMgr->cinfo()->input_components * fSrc.width();
355 const void* srcRow = fSrc.addr(0, fCurrRow);
356 for (int i = 0; i < numRows; i++) {
357 JSAMPLE* jpegSrcRow = (JSAMPLE*)(const_cast<void*>(srcRow));
358 if (fEncoderMgr->proc()) {
359 sk_msan_assert_initialized(srcRow, SkTAddOffset<const void>(srcRow, srcBytes));
360 fEncoderMgr->proc()((char*)fStorage.get(),
361 (const char*)srcRow,
362 fSrc.width(),
363 fEncoderMgr->cinfo()->input_components);
364 jpegSrcRow = fStorage.get();
365 sk_msan_assert_initialized(jpegSrcRow,
366 SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
367 } else {
368 // Same as above, but this repetition allows determining whether a
369 // proc was used when msan asserts.
370 sk_msan_assert_initialized(jpegSrcRow,
371 SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
372 }
373
374 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
375 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
376 }
377 }
378
379 fCurrRow += numRows;
380 if (fCurrRow == fSrc.height()) {
381 jpeg_finish_compress(fEncoderMgr->cinfo());
382 }
383
384 return true;
385 }
386
387 namespace SkJpegEncoder {
388
Encode(SkWStream * dst,const SkPixmap & src,const Options & options)389 bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
390 auto encoder = Make(dst, src, options);
391 return encoder.get() && encoder->encodeRows(src.height());
392 }
393
Encode(SkWStream * dst,const SkYUVAPixmaps & src,const SkColorSpace * srcColorSpace,const Options & options)394 bool Encode(SkWStream* dst,
395 const SkYUVAPixmaps& src,
396 const SkColorSpace* srcColorSpace,
397 const Options& options) {
398 auto encoder = Make(dst, src, srcColorSpace, options);
399 return encoder.get() && encoder->encodeRows(src.yuvaInfo().height());
400 }
401
Encode(GrDirectContext * ctx,const SkImage * img,const Options & options)402 sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) {
403 if (!img) {
404 return nullptr;
405 }
406 SkBitmap bm;
407 if (!as_IB(img)->getROPixels(ctx, &bm)) {
408 return nullptr;
409 }
410 SkDynamicMemoryWStream stream;
411 if (Encode(&stream, bm.pixmap(), options)) {
412 return stream.detachAsData();
413 }
414 return nullptr;
415 }
416
Make(SkWStream * dst,const SkPixmap & src,const Options & options)417 std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src, const Options& options) {
418 SkJpegMetadataEncoder::SegmentList metadataSegments;
419 SkJpegMetadataEncoder::AppendXMPStandard(metadataSegments, options.xmpMetadata);
420 SkJpegMetadataEncoder::AppendICC(metadataSegments, options, src.colorSpace());
421 if (options.fOrigin.has_value()) {
422 SkJpegMetadataEncoder::AppendOrigin(metadataSegments, options.fOrigin.value());
423 }
424 return SkJpegEncoderImpl::MakeRGB(dst, src, options, metadataSegments);
425 }
426
Make(SkWStream * dst,const SkYUVAPixmaps & src,const SkColorSpace * srcColorSpace,const Options & options)427 std::unique_ptr<SkEncoder> Make(SkWStream* dst,
428 const SkYUVAPixmaps& src,
429 const SkColorSpace* srcColorSpace,
430 const Options& options) {
431 SkJpegMetadataEncoder::SegmentList metadataSegments;
432 SkJpegMetadataEncoder::AppendXMPStandard(metadataSegments, options.xmpMetadata);
433 SkJpegMetadataEncoder::AppendICC(metadataSegments, options, srcColorSpace);
434 if (options.fOrigin.has_value()) {
435 SkJpegMetadataEncoder::AppendOrigin(metadataSegments, options.fOrigin.value());
436 }
437 return SkJpegEncoderImpl::MakeYUV(dst, src, srcColorSpace, options, metadataSegments);
438 }
439
440 } // namespace SkJpegEncoder
441
442 namespace SkJpegMetadataEncoder {
443
AppendICC(SegmentList & segmentList,const SkJpegEncoder::Options & options,const SkColorSpace * colorSpace)444 void AppendICC(SegmentList& segmentList,
445 const SkJpegEncoder::Options& options,
446 const SkColorSpace* colorSpace) {
447 sk_sp<SkData> icc =
448 icc_from_color_space(colorSpace, options.fICCProfile, options.fICCProfileDescription);
449 if (!icc) {
450 return;
451 }
452
453 // TODO(ccameron): This limits ICC profile size to a single segment's parameters (less than
454 // 64k). Split larger profiles into more segments.
455 SkDynamicMemoryWStream s;
456 s.write(kICCSig, sizeof(kICCSig));
457 s.write8(1); // This is the first marker.
458 s.write8(1); // Out of one total markers.
459 s.write(icc->data(), icc->size());
460 segmentList.emplace_back(kICCMarker, s.detachAsData());
461 }
462
AppendXMPStandard(SegmentList & segmentList,const SkData * xmpMetadata)463 void AppendXMPStandard(SegmentList& segmentList, const SkData* xmpMetadata) {
464 if (!xmpMetadata) {
465 return;
466 }
467
468 // TODO(ccameron): Split this into a standard and extended XMP segment if needed.
469 SkDynamicMemoryWStream s;
470 s.write(kXMPStandardSig, sizeof(kXMPStandardSig));
471 s.write(xmpMetadata->data(), xmpMetadata->size());
472 segmentList.emplace_back(kXMPMarker, s.detachAsData());
473 }
474
AppendOrigin(SegmentList & segmentList,SkEncodedOrigin origin)475 void AppendOrigin(SegmentList& segmentList, SkEncodedOrigin origin) {
476 if (origin < kDefault_SkEncodedOrigin || origin > kLast_SkEncodedOrigin) {
477 SkDebugf("Origin is not a valid value.\n");
478 return;
479 }
480 sk_sp<SkData> exif = exif_from_origin(origin);
481 if (!exif) {
482 return;
483 }
484 SkDynamicMemoryWStream s;
485 s.write(kExifSig, sizeof(kExifSig));
486 s.write8(0);
487 s.write(exif->data(), exif->size());
488 segmentList.emplace_back(kExifMarker, s.detachAsData());
489 }
490
491 } // namespace SkJpegMetadataEncoder
492