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