Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Side by Side Diff: src/codec/SkJpegCodec.cpp

Issue 1076923002: SkJpegCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@gif-real
Patch Set: Added comment Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/codec/SkJpegCodec.h ('k') | src/codec/SkJpegDecoderMgr.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 Google Inc.
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 "SkCodec.h"
9 #include "SkJpegCodec.h"
10 #include "SkJpegDecoderMgr.h"
11 #include "SkJpegUtility.h"
12 #include "SkCodecPriv.h"
13 #include "SkColorPriv.h"
14 #include "SkStream.h"
15 #include "SkTemplates.h"
16 #include "SkTypes.h"
17
18 // stdio is needed for jpeglib
19 #include <stdio.h>
20
21 extern "C" {
22 #include "jerror.h"
23 #include "jmorecfg.h"
24 #include "jpegint.h"
25 #include "jpeglib.h"
26 }
27
28 // ANDROID_RGB
29 // If this is defined in the jpeg headers it indicates that jpeg offers
30 // support for two additional formats: JCS_RGBA_8888 and JCS_RGB_565.
31
32 /*
33 * Get the source configuarion for the swizzler
34 */
35 SkSwizzler::SrcConfig get_src_config(const jpeg_decompress_struct& dinfo) {
36 if (JCS_CMYK == dinfo.out_color_space) {
37 // We will need to perform a manual conversion
38 return SkSwizzler::kRGBX;
39 }
40 if (3 == dinfo.out_color_components && JCS_RGB == dinfo.out_color_space) {
41 return SkSwizzler::kRGB;
42 }
43 #ifdef ANDROID_RGB
44 if (JCS_RGBA_8888 == dinfo.out_color_space) {
45 return SkSwizzler::kRGBX;
46 }
47
48 if (JCS_RGB_565 == dinfo.out_color_space) {
49 return SkSwizzler::kRGB_565;
50 }
51 #endif
52 if (1 == dinfo.out_color_components && JCS_GRAYSCALE == dinfo.out_color_spac e) {
53 return SkSwizzler::kGray;
54 }
55 return SkSwizzler::kUnknown;
56 }
57
58 /*
59 * Convert a row of CMYK samples to RGBX in place.
60 * Note that this method moves the row pointer.
61 * @param width the number of pixels in the row that is being converted
62 * CMYK is stored as four bytes per pixel
63 */
64 static void convert_CMYK_to_RGB(uint8_t* row, uint32_t width) {
65 // We will implement a crude conversion from CMYK -> RGB using formulas
66 // from easyrgb.com.
67 //
68 // CMYK -> CMY
69 // C = C * (1 - K) + K
70 // M = M * (1 - K) + K
71 // Y = Y * (1 - K) + K
72 //
73 // libjpeg actually gives us inverted CMYK, so we must subtract the
74 // original terms from 1.
75 // CMYK -> CMY
76 // C = (1 - C) * (1 - (1 - K)) + (1 - K)
77 // M = (1 - M) * (1 - (1 - K)) + (1 - K)
78 // Y = (1 - Y) * (1 - (1 - K)) + (1 - K)
79 //
80 // Simplifying the above expression.
81 // CMYK -> CMY
82 // C = 1 - CK
83 // M = 1 - MK
84 // Y = 1 - YK
85 //
86 // CMY -> RGB
87 // R = (1 - C) * 255
88 // G = (1 - M) * 255
89 // B = (1 - Y) * 255
90 //
91 // Therefore the full conversion is below. This can be verified at
92 // www.rapidtables.com (assuming inverted CMYK).
93 // CMYK -> RGB
94 // R = C * K * 255
95 // G = M * K * 255
96 // B = Y * K * 255
97 //
98 // As a final note, we have treated the CMYK values as if they were on
99 // a scale from 0-1, when in fact they are 8-bit ints scaling from 0-255.
100 // We must divide each CMYK component by 255 to obtain the true conversion
101 // we should perform.
102 // CMYK -> RGB
103 // R = C * K / 255
104 // G = M * K / 255
105 // B = Y * K / 255
106 for (uint32_t x = 0; x < width; x++, row += 4) {
107 row[0] = SkMulDiv255Round(row[0], row[3]);
108 row[1] = SkMulDiv255Round(row[1], row[3]);
109 row[2] = SkMulDiv255Round(row[2], row[3]);
110 row[3] = 0xFF;
111 }
112 }
113
114 bool SkJpegCodec::IsJpeg(SkStream* stream) {
115 static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF };
116 char buffer[sizeof(jpegSig)];
117 return stream->read(buffer, sizeof(jpegSig)) == sizeof(jpegSig) &&
118 !memcmp(buffer, jpegSig, sizeof(jpegSig));
119 }
120
121 bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
122 JpegDecoderMgr** decoderMgrOut) {
123
124 // Create a JpegDecoderMgr to own all of the decompress information
125 SkAutoTDelete<JpegDecoderMgr> decoderMgr(SkNEW_ARGS(JpegDecoderMgr, (stream) ));
126
127 // libjpeg errors will be caught and reported here
128 if (setjmp(decoderMgr->getJmpBuf())) {
129 return decoderMgr->returnFalse("setjmp");
130 }
131
132 // Initialize the decompress info and the source manager
133 decoderMgr->init();
134
135 // Read the jpeg header
136 if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) {
137 return decoderMgr->returnFalse("read_header");
138 }
139
140 if (NULL != codecOut) {
141 // Recommend the color type to decode to
142 const SkColorType colorType = decoderMgr->getColorType();
143
144 // Create image info object and the codec
145 const SkImageInfo& imageInfo = SkImageInfo::Make(decoderMgr->dinfo()->im age_width,
146 decoderMgr->dinfo()->image_height, colorType, kOpaque_SkAlphaTyp e);
147 *codecOut = SkNEW_ARGS(SkJpegCodec, (imageInfo, stream, decoderMgr.detac h()));
148 } else {
149 SkASSERT(NULL != decoderMgrOut);
150 *decoderMgrOut = decoderMgr.detach();
151 }
152 return true;
153 }
154
155 SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) {
156 SkAutoTDelete<SkStream> streamDeleter(stream);
157 SkCodec* codec = NULL;
158 if (ReadHeader(stream, &codec, NULL)) {
159 // Codec has taken ownership of the stream, we do not need to delete it
160 SkASSERT(codec);
161 streamDeleter.detach();
162 return codec;
163 }
164 return NULL;
165 }
166
167 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
168 JpegDecoderMgr* decoderMgr)
169 : INHERITED(srcInfo, stream)
170 , fDecoderMgr(decoderMgr)
171 {}
172
173 /*
174 * Return a valid set of output dimensions for this decoder, given an input scal e
175 */
176 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
177 // libjpeg supports scaling by 1/1, 1/2, 1/4, and 1/8, so we will support th ese as well
178 long scale;
179 if (desiredScale > 0.75f) {
180 scale = 1;
181 } else if (desiredScale > 0.375f) {
182 scale = 2;
183 } else if (desiredScale > 0.1875f) {
184 scale = 4;
185 } else {
186 scale = 8;
187 }
188
189 // Set up a fake decompress struct in order to use libjpeg to calculate outp ut dimensions
190 jpeg_decompress_struct dinfo;
191 dinfo.image_width = this->getInfo().width();
192 dinfo.image_height = this->getInfo().height();
193 dinfo.global_state = DSTATE_READY;
194 dinfo.num_components = 0;
195 dinfo.scale_num = 1;
196 dinfo.scale_denom = scale;
197 jpeg_calc_output_dimensions(&dinfo);
198
199 // Return the calculated output dimensions for the given scale
200 return SkISize::Make(dinfo.output_width, dinfo.output_height);
201 }
202
203 /*
204 * Checks if the conversion between the input image and the requested output
205 * image has been implemented
206 */
207 static bool conversion_possible(const SkImageInfo& dst,
208 const SkImageInfo& src) {
209 // Ensure that the profile type is unchanged
210 if (dst.profileType() != src.profileType()) {
211 return false;
212 }
213
214 // Ensure that the alpha type is opaque
215 if (kOpaque_SkAlphaType != dst.alphaType()) {
216 return false;
217 }
218
219 // Always allow kN32 as the color type
220 if (kN32_SkColorType == dst.colorType()) {
221 return true;
222 }
223
224 // Otherwise require that the destination color type match our recommendatio n
225 return dst.colorType() == src.colorType();
226 }
227
228 /*
229 * Performs the jpeg decode
230 */
231 SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
232 void* dst, size_t dstRowBytes,
233 const Options& options, SkPMColor*, int *) {
234 // Rewind the stream if needed
235 SkCodec::RewindState rewindState = this->rewindIfNeeded();
236 if (rewindState == kCouldNotRewind_RewindState) {
237 return kCouldNotRewind;
238 } else if (rewindState == kRewound_RewindState) {
239 JpegDecoderMgr* decoderMgr = NULL;
240 if (!ReadHeader(this->stream(), NULL, &decoderMgr)) {
241 return kCouldNotRewind;
242 }
243 SkASSERT(NULL != decoderMgr);
244 fDecoderMgr.reset(decoderMgr);
245 }
246
247 // Get a pointer to the decompress info since we will use it quite frequentl y
248 jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
249
250 // Set the jump location for libjpeg errors
251 if (setjmp(fDecoderMgr->getJmpBuf())) {
252 return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
253 }
254
255 // Check if we can decode to the requested destination
256 if (!conversion_possible(dstInfo, this->getInfo())) {
257 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion);
258 }
259 // Check if we can scale to the requested dimensions
260 // libjpeg can scale to 1/1, 1/2, 1/4, and 1/8
261 SkASSERT(1 == dinfo->scale_num);
262 SkASSERT(1 == dinfo->scale_denom);
263 jpeg_calc_output_dimensions(dinfo);
264 const uint32_t dstWidth = dstInfo.width();
265 const uint32_t dstHeight = dstInfo.height();
266 while (dinfo->output_width != dstWidth || dinfo->output_height != dstHeight) {
267
268 // Return a failure if we have tried all of the possible scales
269 if (8 == dinfo->scale_denom ||
270 dstWidth > dinfo->output_width ||
271 dstHeight > dinfo->output_height) {
272 return fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale);
273 }
274
275 // Try the next scale
276 dinfo->scale_denom *= 2;
277 jpeg_calc_output_dimensions(dinfo);
278 }
279
280 // Now, given valid output dimensions, we can start the decompress
281 if (!jpeg_start_decompress(dinfo)) {
282 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
283 }
284
285 // Create the swizzler
286 SkSwizzler::SrcConfig srcConfig = get_src_config(*dinfo);
287 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(srcConfig, NUL L, dstInfo, dst,
288 dstRowBytes, options.fZeroInitialized));
289 if (NULL == swizzler) {
290 return fDecoderMgr->returnFailure("getSwizzler", kInvalidInput);
291 }
292 const uint32_t srcBytesPerPixel = SkSwizzler::BytesPerPixel(srcConfig);
293
294 // This is usually 1, but can also be 2 or 4.
295 // If we wanted to always read one row at a time, we could, but we will save space and time
296 // by using the recommendation from libjpeg.
297 const uint32_t rowsPerDecode = dinfo->rec_outbuf_height;
298 SkASSERT(rowsPerDecode <= 4);
299
300 // Create a buffer to contain decoded rows (libjpeg requires a 2D array)
301 const uint32_t srcRowBytes = srcBytesPerPixel * dstWidth;
302 SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, srcRowBytes * row sPerDecode));
303 JSAMPLE* srcRows[4];
304 uint8_t* srcPtr = srcBuffer.get();
305 for (uint8_t i = 0; i < rowsPerDecode; i++) {
306 srcRows[i] = (JSAMPLE*) srcPtr;
307 srcPtr += srcRowBytes;
308 }
309
310 // Ensure that we loop enough times to decode all of the rows
311 // libjpeg will prevent us from reading past the bottom of the image
312 for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) {
313 // Read rows of the image
314 uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode );
315
316 // Convert to RGB if necessary
317 if (JCS_CMYK == dinfo->out_color_space) {
318 convert_CMYK_to_RGB(srcRows[0], dstWidth * rowsDecoded);
319 }
320
321 // Swizzle to output destination
322 for (uint32_t i = 0; i < rowsDecoded; i++) {
323 swizzler->next(srcRows[i]);
324 }
325
326 // If we cannot read enough rows, assume the input is incomplete
327 if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) {
328 // Fill the remainder of the image with black. This error handling
329 // behavior is unspecified but SkCodec consistently uses black as
330 // the fill color for opaque images. If the destination is kGray,
331 // the low 8 bits of SK_ColorBLACK will be used. Conveniently,
332 // these are zeros, which is the representation for black in kGray.
333 SkSwizzler::Fill(swizzler->getDstRow(), dstInfo, dstRowBytes,
334 dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL);
335
336 // Prevent libjpeg from failing on incomplete decode
337 dinfo->output_scanline = dstHeight;
338
339 // Finish the decode and indicate that the input was incomplete.
340 jpeg_finish_decompress(dinfo);
341 return fDecoderMgr->returnFailure("Incomplete image data", kIncomple teInput);
342 }
343 }
344 jpeg_finish_decompress(dinfo);
345
346 return kSuccess;
347 }
OLDNEW
« no previous file with comments | « src/codec/SkJpegCodec.h ('k') | src/codec/SkJpegDecoderMgr.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698