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

Side by Side Diff: ui/gfx/codec/jpeg_codec.cc

Issue 2895953003: Use SkJpegEncoder in gfx jpeg_codec (Closed)
Patch Set: Remove brackets Created 3 years, 6 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
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/codec/jpeg_codec.h" 5 #include "ui/gfx/codec/jpeg_codec.h"
6 6
7 #include <setjmp.h> 7 #include <setjmp.h>
8 8
9 #include <memory> 9 #include <memory>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "third_party/skia/include/core/SkBitmap.h" 12 #include "third_party/skia/include/core/SkBitmap.h"
13 #include "third_party/skia/include/core/SkColorPriv.h" 13 #include "third_party/skia/include/core/SkColorPriv.h"
14 #include "third_party/skia/include/core/SkStream.h"
15 #include "third_party/skia/include/encode/SkJpegEncoder.h"
14 16
15 extern "C" { 17 extern "C" {
16 #if defined(USE_SYSTEM_LIBJPEG) 18 #if defined(USE_SYSTEM_LIBJPEG)
17 #include <jpeglib.h> 19 #include <jpeglib.h>
18 #elif defined(USE_LIBJPEG_TURBO) 20 #elif defined(USE_LIBJPEG_TURBO)
19 #include "third_party/libjpeg_turbo/jpeglib.h" 21 #include "third_party/libjpeg_turbo/jpeglib.h"
20 #else 22 #else
21 #include "third_party/libjpeg/jpeglib.h" 23 #include "third_party/libjpeg/jpeglib.h"
22 #endif 24 #endif
23 } 25 }
(...skipping 13 matching lines...) Expand all
37 void ErrorExit(jpeg_common_struct* cinfo) { 39 void ErrorExit(jpeg_common_struct* cinfo) {
38 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err); 40 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
39 41
40 // Return control to the setjmp point. 42 // Return control to the setjmp point.
41 longjmp(err->setjmp_buffer, false); 43 longjmp(err->setjmp_buffer, false);
42 } 44 }
43 45
44 } // namespace 46 } // namespace
45 47
46 // Encoder --------------------------------------------------------------------- 48 // Encoder ---------------------------------------------------------------------
47 //
48 // This code is based on nsJPEGEncoder from Mozilla.
49 // Copyright 2005 Google Inc. (Brett Wilson, contributor)
50 49
51 namespace { 50 namespace {
52 51
53 // Initial size for the output buffer in the JpegEncoderState below. 52 class VectorWStream : public SkWStream {
54 static const int initial_output_buffer_size = 8192; 53 public:
55 54 VectorWStream(std::vector<unsigned char>* dst) : dst_(dst) {
56 struct JpegEncoderState { 55 DCHECK(dst_);
57 explicit JpegEncoderState(std::vector<unsigned char>* o) 56 DCHECK_EQ(0UL, dst_->size());
58 : out(o),
59 image_buffer_used(0) {
60 } 57 }
61 58
62 // Output buffer, of which 'image_buffer_used' bytes are actually used (this 59 bool write(const void* buffer, size_t size) override {
63 // will often be less than the actual size of the vector because we size it 60 const unsigned char* ptr = reinterpret_cast<const unsigned char*>(buffer);
64 // so that libjpeg can write directly into it. 61 dst_->insert(dst_->end(), ptr, ptr + size);
65 std::vector<unsigned char>* out; 62 return true;
63 }
66 64
67 // Number of bytes in the 'out' buffer that are actually used (see above). 65 size_t bytesWritten() const override { return dst_->size(); }
68 size_t image_buffer_used; 66
67 private:
68 // Does not have ownership.
69 std::vector<unsigned char>* dst_;
70 };
69 }; 71 };
70 72
71 // Initializes the JpegEncoderState for encoding, and tells libjpeg about where 73 bool JPEGCodec::Encode(const SkPixmap& src,
72 // the output buffer is. 74 int quality,
73 // 75 std::vector<unsigned char>* output) {
74 // From the JPEG library: 76 output->clear();
75 // "Initialize destination. This is called by jpeg_start_compress() before 77 VectorWStream dst(output);
76 // any data is actually written. It must initialize next_output_byte and
77 // free_in_buffer. free_in_buffer must be initialized to a positive value."
78 void InitDestination(jpeg_compress_struct* cinfo) {
79 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
80 DCHECK(state->image_buffer_used == 0) << "initializing after use";
81 78
82 state->out->resize(initial_output_buffer_size); 79 SkJpegEncoder::Options options;
83 state->image_buffer_used = 0; 80 options.fQuality = quality;
84 81 return SkJpegEncoder::Encode(&dst, src, options);
85 cinfo->dest->next_output_byte = &(*state->out)[0];
86 cinfo->dest->free_in_buffer = initial_output_buffer_size;
87 } 82 }
88 83
89 // Resize the buffer that we give to libjpeg and update our and its state. 84 bool JPEGCodec::Encode(const SkBitmap& src,
90 // 85 int quality,
91 // From the JPEG library: 86 std::vector<unsigned char>* output) {
92 // "Callback used by libjpeg whenever the buffer has filled (free_in_buffer 87 SkPixmap pixmap;
93 // reaches zero). In typical applications, it should write out the *entire* 88 if (!src.peekPixels(&pixmap)) {
94 // buffer (use the saved start address and buffer length; ignore the current
95 // state of next_output_byte and free_in_buffer). Then reset the pointer &
96 // count to the start of the buffer, and return TRUE indicating that the
97 // buffer has been dumped. free_in_buffer must be set to a positive value
98 // when TRUE is returned. A FALSE return should only be used when I/O
99 // suspension is desired (this operating mode is discussed in the next
100 // section)."
101 boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) {
102 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
103
104 // note the new size, the buffer is full
105 state->image_buffer_used = state->out->size();
106
107 // expand buffer, just double size each time
108 state->out->resize(state->out->size() * 2);
109
110 // tell libjpeg where to write the next data
111 cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used];
112 cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used;
113 return 1;
114 }
115
116 // Cleans up the JpegEncoderState to prepare for returning in the final form.
117 //
118 // From the JPEG library:
119 // "Terminate destination --- called by jpeg_finish_compress() after all data
120 // has been written. In most applications, this must flush any data
121 // remaining in the buffer. Use either next_output_byte or free_in_buffer to
122 // determine how much data is in the buffer."
123 void TermDestination(jpeg_compress_struct* cinfo) {
124 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
125 DCHECK(state->out->size() >= state->image_buffer_used);
126
127 // update the used byte based on the next byte libjpeg would write to
128 state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0];
129 DCHECK(state->image_buffer_used < state->out->size()) <<
130 "JPEG library busted, got a bad image buffer size";
131
132 // update our buffer so that it exactly encompases the desired data
133 state->out->resize(state->image_buffer_used);
134 }
135
136 // This class destroys the given jpeg_compress object when it goes out of
137 // scope. It simplifies the error handling in Encode (and even applies to the
138 // success case).
139 class CompressDestroyer {
140 public:
141 CompressDestroyer() : cinfo_(NULL) {
142 }
143 ~CompressDestroyer() {
144 DestroyManagedObject();
145 }
146 void SetManagedObject(jpeg_compress_struct* ci) {
147 DestroyManagedObject();
148 cinfo_ = ci;
149 }
150 void DestroyManagedObject() {
151 if (cinfo_) {
152 jpeg_destroy_compress(cinfo_);
153 cinfo_ = NULL;
154 }
155 }
156 private:
157 jpeg_compress_struct* cinfo_;
158 };
159
160 } // namespace
161
162 bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format,
163 int w, int h, int row_byte_width,
164 int quality, std::vector<unsigned char>* output) {
165 jpeg_compress_struct cinfo;
166 CompressDestroyer destroyer;
167 destroyer.SetManagedObject(&cinfo);
168 output->clear();
169
170 // We set up the normal JPEG error routines, then override error_exit.
171 // This must be done before the call to create_compress.
172 CoderErrorMgr errmgr;
173 cinfo.err = jpeg_std_error(&errmgr.pub);
174 errmgr.pub.error_exit = ErrorExit;
175
176 // Establish the setjmp return context for ErrorExit to use.
177 if (setjmp(errmgr.setjmp_buffer)) {
178 // If we get here, the JPEG code has signaled an error.
179 // MSDN notes: "if you intend your code to be portable, do not rely on
180 // correct destruction of frame-based objects when executing a nonlocal
181 // goto using a call to longjmp." So we delete the CompressDestroyer's
182 // object manually instead.
183 destroyer.DestroyManagedObject();
184 return false; 89 return false;
185 } 90 }
186 91
187 // The destroyer will destroy() cinfo on exit. 92 return JPEGCodec::Encode(pixmap, quality, output);
188 jpeg_create_compress(&cinfo);
189
190 cinfo.image_width = w;
191 cinfo.image_height = h;
192 cinfo.input_components = 4;
193 // Choose an input colorspace and return if it is an unsupported one. Since
194 // libjpeg-turbo supports all input formats used by Chromium (i.e. RGB, RGBA,
195 // and BGRA), we just map the input parameters to a colorspace used by
196 // libjpeg-turbo.
197 if (format == FORMAT_RGBA ||
198 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
199 cinfo.in_color_space = JCS_EXT_RGBX;
200 } else if (format == FORMAT_BGRA ||
201 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
202 cinfo.in_color_space = JCS_EXT_BGRX;
203 } else {
204 // We can exit this function without calling jpeg_destroy_compress() because
205 // CompressDestroyer automaticaly calls it.
206 NOTREACHED() << "Invalid pixel format";
207 return false;
208 }
209 cinfo.data_precision = 8;
210
211 jpeg_set_defaults(&cinfo);
212 jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100
213
214 // set up the destination manager
215 jpeg_destination_mgr destmgr;
216 destmgr.init_destination = InitDestination;
217 destmgr.empty_output_buffer = EmptyOutputBuffer;
218 destmgr.term_destination = TermDestination;
219 cinfo.dest = &destmgr;
220
221 JpegEncoderState state(output);
222 cinfo.client_data = &state;
223
224 jpeg_start_compress(&cinfo, 1);
225
226 // feed it the rows, doing necessary conversions for the color format
227 // This function already returns when the input format is not supported by
228 // libjpeg-turbo and needs conversion. Therefore, we just encode lines without
229 // conversions.
230 while (cinfo.next_scanline < cinfo.image_height) {
231 const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
232 jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
233 }
234
235 jpeg_finish_compress(&cinfo);
236 return true;
237 } 93 }
238 94
239 // Decoder -------------------------------------------------------------------- 95 // Decoder --------------------------------------------------------------------
240 96
241 namespace { 97 namespace {
242 98
243 struct JpegDecoderState { 99 struct JpegDecoderState {
244 JpegDecoderState(const unsigned char* in, size_t len) 100 JpegDecoderState(const unsigned char* in, size_t len)
245 : input_buffer(in), input_buffer_length(len) { 101 : input_buffer(in), input_buffer_length(len) {
246 } 102 }
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK) 237 if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK)
382 return false; 238 return false;
383 239
384 // we want to always get RGB data out 240 // we want to always get RGB data out
385 switch (cinfo.jpeg_color_space) { 241 switch (cinfo.jpeg_color_space) {
386 case JCS_GRAYSCALE: 242 case JCS_GRAYSCALE:
387 case JCS_RGB: 243 case JCS_RGB:
388 case JCS_YCbCr: 244 case JCS_YCbCr:
389 // Choose an output colorspace and return if it is an unsupported one. 245 // Choose an output colorspace and return if it is an unsupported one.
390 // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats 246 // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats
391 // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input 247 // used by Chromium (i.e. RGBA and BGRA) and we just map the input
392 // parameters to a colorspace. 248 // parameters to a colorspace.
393 if (format == FORMAT_RGBA || 249 if (format == FORMAT_RGBA ||
394 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) { 250 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
395 cinfo.out_color_space = JCS_EXT_RGBX; 251 cinfo.out_color_space = JCS_EXT_RGBX;
396 cinfo.output_components = 4; 252 cinfo.output_components = 4;
397 } else if (format == FORMAT_BGRA || 253 } else if (format == FORMAT_BGRA ||
398 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) { 254 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
399 cinfo.out_color_space = JCS_EXT_BGRX; 255 cinfo.out_color_space = JCS_EXT_BGRX;
400 cinfo.output_components = 4; 256 cinfo.output_components = 4;
401 } else { 257 } else {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
452 int data_length = w * h * 4; 308 int data_length = w * h * 4;
453 309
454 std::unique_ptr<SkBitmap> bitmap(new SkBitmap()); 310 std::unique_ptr<SkBitmap> bitmap(new SkBitmap());
455 bitmap->allocN32Pixels(w, h); 311 bitmap->allocN32Pixels(w, h);
456 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length); 312 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
457 313
458 return bitmap; 314 return bitmap;
459 } 315 }
460 316
461 } // namespace gfx 317 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698