• 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 "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