• 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/images/SkImageEncoderPriv.h"
9 
10 #ifdef SK_ENCODE_JPEG
11 
12 #include "include/core/SkStream.h"
13 #include "include/encode/SkJpegEncoder.h"
14 #include "include/private/SkColorData.h"
15 #include "include/private/SkImageInfoPriv.h"
16 #include "include/private/SkTemplates.h"
17 #include "src/core/SkMSAN.h"
18 #include "src/images/SkImageEncoderFns.h"
19 #include "src/images/SkJPEGWriteUtility.h"
20 
21 #include <stdio.h>
22 
23 extern "C" {
24     #include "jpeglib.h"
25     #include "jerror.h"
26 }
27 
28 class SkJpegEncoderMgr final : SkNoncopyable {
29 public:
30 
31     /*
32      * Create the decode manager
33      * Does not take ownership of stream
34      */
Make(SkWStream * stream)35     static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
36         return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
37     }
38 
39     bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
40 
cinfo()41     jpeg_compress_struct* cinfo() { return &fCInfo; }
42 
errorMgr()43     skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
44 
proc() const45     transform_scanline_proc proc() const { return fProc; }
46 
~SkJpegEncoderMgr()47     ~SkJpegEncoderMgr() {
48         jpeg_destroy_compress(&fCInfo);
49     }
50 
51 private:
52 
SkJpegEncoderMgr(SkWStream * stream)53     SkJpegEncoderMgr(SkWStream* stream)
54         : fDstMgr(stream)
55         , fProc(nullptr)
56     {
57         fCInfo.err = jpeg_std_error(&fErrMgr);
58         fErrMgr.error_exit = skjpeg_error_exit;
59         jpeg_create_compress(&fCInfo);
60         fCInfo.dest = &fDstMgr;
61     }
62 
63     jpeg_compress_struct    fCInfo;
64     skjpeg_error_mgr        fErrMgr;
65     skjpeg_destination_mgr  fDstMgr;
66     transform_scanline_proc fProc;
67 };
68 
setParams(const SkImageInfo & srcInfo,const SkJpegEncoder::Options & options)69 bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options)
70 {
71     auto chooseProc8888 = [&]() {
72         if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
73                 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
74             return transform_scanline_to_premul_legacy;
75         }
76         return (transform_scanline_proc) nullptr;
77     };
78 
79     J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
80     int numComponents = 0;
81     switch (srcInfo.colorType()) {
82         case kRGBA_8888_SkColorType:
83             fProc = chooseProc8888();
84             jpegColorType = JCS_EXT_RGBA;
85             numComponents = 4;
86             break;
87         case kBGRA_8888_SkColorType:
88             fProc = chooseProc8888();
89             jpegColorType = JCS_EXT_BGRA;
90             numComponents = 4;
91             break;
92         case kRGB_565_SkColorType:
93             fProc = transform_scanline_565;
94             jpegColorType = JCS_RGB;
95             numComponents = 3;
96             break;
97         case kARGB_4444_SkColorType:
98             if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
99                 return false;
100             }
101 
102             fProc = transform_scanline_444;
103             jpegColorType = JCS_RGB;
104             numComponents = 3;
105             break;
106         case kGray_8_SkColorType:
107             SkASSERT(srcInfo.isOpaque());
108             jpegColorType = JCS_GRAYSCALE;
109             numComponents = 1;
110             break;
111         case kRGBA_F16_SkColorType:
112             if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
113                     options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
114                 fProc = transform_scanline_F16_to_premul_8888;
115             } else {
116                 fProc = transform_scanline_F16_to_8888;
117             }
118             jpegColorType = JCS_EXT_RGBA;
119             numComponents = 4;
120             break;
121         default:
122             return false;
123     }
124 
125     fCInfo.image_width = srcInfo.width();
126     fCInfo.image_height = srcInfo.height();
127     fCInfo.in_color_space = jpegColorType;
128     fCInfo.input_components = numComponents;
129     jpeg_set_defaults(&fCInfo);
130 
131     if (kGray_8_SkColorType != srcInfo.colorType()) {
132         switch (options.fDownsample) {
133             case SkJpegEncoder::Downsample::k420:
134                 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
135                 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
136                 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
137                 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
138                 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
139                 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
140                 break;
141             case SkJpegEncoder::Downsample::k422:
142                 fCInfo.comp_info[0].h_samp_factor = 2;
143                 fCInfo.comp_info[0].v_samp_factor = 1;
144                 fCInfo.comp_info[1].h_samp_factor = 1;
145                 fCInfo.comp_info[1].v_samp_factor = 1;
146                 fCInfo.comp_info[2].h_samp_factor = 1;
147                 fCInfo.comp_info[2].v_samp_factor = 1;
148                 break;
149             case SkJpegEncoder::Downsample::k444:
150                 fCInfo.comp_info[0].h_samp_factor = 1;
151                 fCInfo.comp_info[0].v_samp_factor = 1;
152                 fCInfo.comp_info[1].h_samp_factor = 1;
153                 fCInfo.comp_info[1].v_samp_factor = 1;
154                 fCInfo.comp_info[2].h_samp_factor = 1;
155                 fCInfo.comp_info[2].v_samp_factor = 1;
156                 break;
157         }
158     }
159 
160     // Tells libjpeg-turbo to compute optimal Huffman coding tables
161     // for the image.  This improves compression at the cost of
162     // slower encode performance.
163     fCInfo.optimize_coding = TRUE;
164     return true;
165 }
166 
Make(SkWStream * dst,const SkPixmap & src,const Options & options)167 std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
168                                                const Options& options) {
169     if (!SkPixmapIsValid(src)) {
170         return nullptr;
171     }
172 
173     std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
174 
175     skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
176     if (setjmp(jmp)) {
177         return nullptr;
178     }
179 
180     if (!encoderMgr->setParams(src.info(), options)) {
181         return nullptr;
182     }
183 
184     jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
185     jpeg_start_compress(encoderMgr->cinfo(), TRUE);
186 
187     sk_sp<SkData> icc = icc_from_color_space(src.info());
188     if (icc) {
189         // Create a contiguous block of memory with the icc signature followed by the profile.
190         sk_sp<SkData> markerData =
191                 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
192         uint8_t* ptr = (uint8_t*) markerData->writable_data();
193         memcpy(ptr, kICCSig, sizeof(kICCSig));
194         ptr += sizeof(kICCSig);
195         *ptr++ = 1; // This is the first marker.
196         *ptr++ = 1; // Out of one total markers.
197         memcpy(ptr, icc->data(), icc->size());
198 
199         jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
200     }
201 
202     return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
203 }
204 
SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,const SkPixmap & src)205 SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
206     : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
207     , fEncoderMgr(std::move(encoderMgr))
208 {}
209 
~SkJpegEncoder()210 SkJpegEncoder::~SkJpegEncoder() {}
211 
onEncodeRows(int numRows)212 bool SkJpegEncoder::onEncodeRows(int numRows) {
213     skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
214     if (setjmp(jmp)) {
215         return false;
216     }
217 
218     const size_t srcBytes = SkColorTypeBytesPerPixel(fSrc.colorType()) * fSrc.width();
219     const size_t jpegSrcBytes = fEncoderMgr->cinfo()->input_components * fSrc.width();
220 
221     const void* srcRow = fSrc.addr(0, fCurrRow);
222     for (int i = 0; i < numRows; i++) {
223         JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
224         if (fEncoderMgr->proc()) {
225             sk_msan_assert_initialized(srcRow, SkTAddOffset<const void>(srcRow, srcBytes));
226             fEncoderMgr->proc()((char*)fStorage.get(),
227                                 (const char*)srcRow,
228                                 fSrc.width(),
229                                 fEncoderMgr->cinfo()->input_components);
230             jpegSrcRow = fStorage.get();
231             sk_msan_assert_initialized(jpegSrcRow,
232                                        SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
233         } else {
234             // Same as above, but this repetition allows determining whether a
235             // proc was used when msan asserts.
236             sk_msan_assert_initialized(jpegSrcRow,
237                                        SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
238         }
239 
240         jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
241         srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
242     }
243 
244     fCurrRow += numRows;
245     if (fCurrRow == fSrc.height()) {
246         jpeg_finish_compress(fEncoderMgr->cinfo());
247     }
248 
249     return true;
250 }
251 
Encode(SkWStream * dst,const SkPixmap & src,const Options & options)252 bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
253     auto encoder = SkJpegEncoder::Make(dst, src, options);
254     return encoder.get() && encoder->encodeRows(src.height());
255 }
256 
257 #endif
258