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

Side by Side Diff: third_party/WebKit/Source/platform/image-encoders/JPEGImageEncoder.cpp

Issue 2878333004: Use SkJpegEncoder in WebKit platform (Closed)
Patch Set: Create instead of Make Created 3 years, 7 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
(Empty)
1 /*
2 * Copyright (c) 2010, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "platform/image-encoders/JPEGImageEncoder.h"
32
33 #include <memory>
34 #include "SkColorPriv.h"
35 #include "platform/geometry/IntSize.h"
36 #include "platform/graphics/ImageBuffer.h"
37 #include "platform/image-encoders/RGBAtoRGB.h"
38 #include "platform/wtf/CurrentTime.h"
39 #include "platform/wtf/PtrUtil.h"
40
41 extern "C" {
42 #include <setjmp.h>
43 #include <stdio.h> // jpeglib.h needs stdio.h FILE
44 #include "jpeglib.h"
45 }
46
47 namespace blink {
48
49 void RGBAtoRGBScalar(const unsigned char* pixels,
50 unsigned pixel_count,
51 unsigned char* output) {
52 // Per <canvas> spec, composite the input image pixels source-over on black.
53 for (; pixel_count-- > 0; pixels += 4) {
54 unsigned char alpha = pixels[3];
55 if (alpha != 255) {
56 *output++ = SkMulDiv255Round(pixels[0], alpha);
57 *output++ = SkMulDiv255Round(pixels[1], alpha);
58 *output++ = SkMulDiv255Round(pixels[2], alpha);
59 } else {
60 *output++ = pixels[0];
61 *output++ = pixels[1];
62 *output++ = pixels[2];
63 }
64 }
65 }
66
67 // TODO(cavalcantii): use regular macro, see https://crbug.com/673067.
68 #ifdef __ARM_NEON__
69 void RGBAtoRGBNeon(const unsigned char* input,
70 const unsigned pixel_count,
71 unsigned char* output) {
72 const unsigned kPixelsPerLoad = 16;
73 const unsigned kRgbaStep = kPixelsPerLoad * 4, kRgbStep = kPixelsPerLoad * 3;
74 // Input registers.
75 uint8x16x4_t rgba;
76 // Output registers.
77 uint8x16x3_t rgb;
78 // Intermediate registers.
79 uint8x8_t low, high;
80 uint8x16_t result;
81 unsigned counter;
82 auto transform_color = [&](size_t channel) {
83 // Extracts the low/high part of the 128 bits.
84 low = vget_low_u8(rgba.val[channel]);
85 high = vget_high_u8(rgba.val[channel]);
86 // Scale the color and combine.
87 uint16x8_t temp = vmull_u8(low, vget_low_u8(rgba.val[3]));
88 low = vraddhn_u16(temp, vrshrq_n_u16(temp, 8));
89 temp = vmull_u8(high, vget_high_u8(rgba.val[3]));
90 high = vraddhn_u16(temp, vrshrq_n_u16(temp, 8));
91 result = vcombine_u8(low, high);
92 // Write back the channel to a 128 bits register.
93 rgb.val[channel] = result;
94 };
95
96 for (counter = 0; counter + kPixelsPerLoad <= pixel_count;
97 counter += kPixelsPerLoad) {
98 // Reads 16 pixels at once, each color channel in a different
99 // 128 bits register.
100 rgba = vld4q_u8(input);
101
102 transform_color(0);
103 transform_color(1);
104 transform_color(2);
105
106 // Write back (interleaved) results to output.
107 vst3q_u8(output, rgb);
108
109 // Advance to next elements (could be avoided loading register with
110 // increment after i.e. "vld4 {vector}, [r1]!").
111 input += kRgbaStep;
112 output += kRgbStep;
113 }
114
115 // Handle the tail elements.
116 unsigned remaining = pixel_count;
117 remaining -= counter;
118 if (remaining != 0) {
119 RGBAtoRGBScalar(input, remaining, output);
120 }
121 }
122 #endif
123
124 struct JPEGOutputBuffer : public jpeg_destination_mgr {
125 DISALLOW_NEW();
126 Vector<unsigned char>* output;
127 Vector<unsigned char> buffer;
128 };
129
130 class JPEGImageEncoderStateImpl final : public JPEGImageEncoderState {
131 public:
132 JPEGImageEncoderStateImpl() {}
133 ~JPEGImageEncoderStateImpl() override {
134 jpeg_destroy_compress(&cinfo_);
135 cinfo_.client_data = 0;
136 }
137 JPEGOutputBuffer* OutputBuffer() { return &output_buffer_; }
138 jpeg_compress_struct* Cinfo() { return &cinfo_; }
139 jpeg_error_mgr* GetError() { return &error_; }
140
141 private:
142 JPEGOutputBuffer output_buffer_;
143 jpeg_compress_struct cinfo_;
144 jpeg_error_mgr error_;
145 };
146
147 static void PrepareOutput(j_compress_ptr cinfo) {
148 JPEGOutputBuffer* out = static_cast<JPEGOutputBuffer*>(cinfo->dest);
149 const size_t kInternalBufferSize = 8192;
150 out->buffer.resize(kInternalBufferSize);
151 out->next_output_byte = out->buffer.data();
152 out->free_in_buffer = out->buffer.size();
153 }
154
155 static boolean WriteOutput(j_compress_ptr cinfo) {
156 JPEGOutputBuffer* out = static_cast<JPEGOutputBuffer*>(cinfo->dest);
157 out->output->Append(out->buffer.data(), out->buffer.size());
158 out->next_output_byte = out->buffer.data();
159 out->free_in_buffer = out->buffer.size();
160 return TRUE;
161 }
162
163 static void FinishOutput(j_compress_ptr cinfo) {
164 JPEGOutputBuffer* out = static_cast<JPEGOutputBuffer*>(cinfo->dest);
165 const size_t size = out->buffer.size() - out->free_in_buffer;
166 out->output->Append(out->buffer.data(), size);
167 }
168
169 static void HandleError(j_common_ptr common) {
170 jmp_buf* jump_buffer_ptr = static_cast<jmp_buf*>(common->client_data);
171 longjmp(*jump_buffer_ptr, -1);
172 }
173
174 static void DisableSubsamplingForHighQuality(jpeg_compress_struct* cinfo,
175 int quality) {
176 if (quality < 100)
177 return;
178
179 for (int i = 0; i < MAX_COMPONENTS; ++i) {
180 cinfo->comp_info[i].h_samp_factor = 1;
181 cinfo->comp_info[i].v_samp_factor = 1;
182 }
183 }
184
185 #define SET_JUMP_BUFFER(jpeg_compress_struct_ptr, what_to_return) \
186 jmp_buf jump_buffer; \
187 jpeg_compress_struct_ptr->client_data = &jump_buffer; \
188 if (setjmp(jump_buffer)) { \
189 return what_to_return; \
190 }
191
192 std::unique_ptr<JPEGImageEncoderState> JPEGImageEncoderState::Create(
193 const IntSize& image_size,
194 const double& quality,
195 Vector<unsigned char>* output) {
196 if (image_size.Width() <= 0 || image_size.Height() <= 0)
197 return nullptr;
198
199 std::unique_ptr<JPEGImageEncoderStateImpl> encoder_state =
200 WTF::MakeUnique<JPEGImageEncoderStateImpl>();
201
202 jpeg_compress_struct* cinfo = encoder_state->Cinfo();
203 jpeg_error_mgr* error = encoder_state->GetError();
204 cinfo->err = jpeg_std_error(error);
205 error->error_exit = HandleError;
206
207 SET_JUMP_BUFFER(cinfo, nullptr);
208
209 JPEGOutputBuffer* destination = encoder_state->OutputBuffer();
210 destination->output = output;
211
212 jpeg_create_compress(cinfo);
213 cinfo->dest = destination;
214 cinfo->dest->init_destination = PrepareOutput;
215 cinfo->dest->empty_output_buffer = WriteOutput;
216 cinfo->dest->term_destination = FinishOutput;
217
218 cinfo->image_height = image_size.Height();
219 cinfo->image_width = image_size.Width();
220 cinfo->in_color_space = JCS_RGB;
221 cinfo->input_components = 3;
222
223 jpeg_set_defaults(cinfo);
224 int compression_quality =
225 JPEGImageEncoder::ComputeCompressionQuality(quality);
226 jpeg_set_quality(cinfo, compression_quality, TRUE);
227 DisableSubsamplingForHighQuality(cinfo, compression_quality);
228 jpeg_start_compress(cinfo, TRUE);
229
230 cinfo->client_data = 0;
231 return std::move(encoder_state);
232 }
233
234 int JPEGImageEncoder::ComputeCompressionQuality(const double& quality) {
235 int compression_quality = JPEGImageEncoder::kDefaultCompressionQuality;
236 if (quality >= 0.0 && quality <= 1.0)
237 compression_quality = static_cast<int>(quality * 100 + 0.5);
238 return compression_quality;
239 }
240
241 int JPEGImageEncoder::ProgressiveEncodeRowsJpegHelper(
242 JPEGImageEncoderState* encoder_state,
243 unsigned char* data,
244 int current_rows_completed,
245 const double slack_before_deadline,
246 double deadline_seconds) {
247 JPEGImageEncoderStateImpl* encoder_state_impl =
248 static_cast<JPEGImageEncoderStateImpl*>(encoder_state);
249 Vector<JSAMPLE> row(encoder_state_impl->Cinfo()->image_width *
250 encoder_state_impl->Cinfo()->input_components);
251 SET_JUMP_BUFFER(encoder_state_impl->Cinfo(), kProgressiveEncodeFailed);
252
253 const size_t pixel_row_stride = encoder_state_impl->Cinfo()->image_width * 4;
254 unsigned char* pixels = data + pixel_row_stride * current_rows_completed;
255
256 while (encoder_state_impl->Cinfo()->next_scanline <
257 encoder_state_impl->Cinfo()->image_height) {
258 JSAMPLE* row_data = row.data();
259 RGBAtoRGB(pixels, encoder_state_impl->Cinfo()->image_width, row_data);
260 jpeg_write_scanlines(encoder_state_impl->Cinfo(), &row_data, 1);
261 pixels += pixel_row_stride;
262 current_rows_completed++;
263
264 if (deadline_seconds - slack_before_deadline -
265 MonotonicallyIncreasingTime() <=
266 0) {
267 return current_rows_completed;
268 }
269 }
270
271 jpeg_finish_compress(encoder_state_impl->Cinfo());
272 return current_rows_completed;
273 }
274
275 bool JPEGImageEncoder::EncodeWithPreInitializedState(
276 std::unique_ptr<JPEGImageEncoderState> encoder_state,
277 const unsigned char* input_pixels,
278 int num_rows_completed) {
279 JPEGImageEncoderStateImpl* encoder_state_impl =
280 static_cast<JPEGImageEncoderStateImpl*>(encoder_state.get());
281
282 Vector<JSAMPLE> row;
283 row.resize(encoder_state_impl->Cinfo()->image_width *
284 encoder_state_impl->Cinfo()->input_components);
285
286 SET_JUMP_BUFFER(encoder_state_impl->Cinfo(), false);
287
288 const size_t pixel_row_stride = encoder_state_impl->Cinfo()->image_width * 4;
289 unsigned char* pixels = const_cast<unsigned char*>(input_pixels) +
290 pixel_row_stride * num_rows_completed;
291 while (encoder_state_impl->Cinfo()->next_scanline <
292 encoder_state_impl->Cinfo()->image_height) {
293 JSAMPLE* row_data = row.data();
294 RGBAtoRGB(pixels, encoder_state_impl->Cinfo()->image_width, row_data);
295 jpeg_write_scanlines(encoder_state_impl->Cinfo(), &row_data, 1);
296 pixels += pixel_row_stride;
297 }
298
299 jpeg_finish_compress(encoder_state_impl->Cinfo());
300 return true;
301 }
302
303 bool JPEGImageEncoder::Encode(const ImageDataBuffer& image_data,
304 const double& quality,
305 Vector<unsigned char>* output) {
306 if (!image_data.Pixels())
307 return false;
308
309 std::unique_ptr<JPEGImageEncoderState> encoder_state =
310 JPEGImageEncoderState::Create(image_data.size(), quality, output);
311 if (!encoder_state)
312 return false;
313
314 return JPEGImageEncoder::EncodeWithPreInitializedState(
315 std::move(encoder_state), image_data.Pixels());
316 }
317
318 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698