• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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