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 | */ |
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 | |
41 | jpeg_compress_struct* cinfo() { return &fCInfo; } |
42 | |
43 | skjpeg_error_mgr* errorMgr() { return &fErrMgr; } |
44 | |
45 | transform_scanline_proc proc() const { return fProc; } |
46 | |
47 | ~SkJpegEncoderMgr() { |
48 | jpeg_destroy_compress(&fCInfo); |
49 | } |
50 | |
51 | private: |
52 | |
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 | |
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 | |
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 | |
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 | |
210 | SkJpegEncoder::~SkJpegEncoder() {} |
211 | |
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 | |
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 | |