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