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