OLD | NEW |
| (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 | |
OLD | NEW |