Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2010, Google Inc. All rights reserved. | 2 * Copyright (c) 2010, Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "platform/image-encoders/JPEGImageEncoder.h" | 31 #include "platform/image-encoders/JPEGImageEncoder.h" |
| 32 | 32 |
| 33 #include "SkColorPriv.h" | 33 #include "SkColorPriv.h" |
| 34 #include "platform/geometry/IntSize.h" | 34 #include "platform/geometry/IntSize.h" |
| 35 #include "platform/graphics/ImageBuffer.h" | 35 #include "platform/graphics/ImageBuffer.h" |
| 36 #include "platform/image-encoders/RGBAtoRGB.h" | |
| 36 #include "wtf/CurrentTime.h" | 37 #include "wtf/CurrentTime.h" |
| 37 #include "wtf/PtrUtil.h" | 38 #include "wtf/PtrUtil.h" |
| 38 #include <memory> | 39 #include <memory> |
| 39 | 40 |
| 40 extern "C" { | 41 extern "C" { |
| 41 #include <setjmp.h> | 42 #include <setjmp.h> |
| 42 #include <stdio.h> // jpeglib.h needs stdio.h FILE | 43 #include <stdio.h> // jpeglib.h needs stdio.h FILE |
| 43 #include "jpeglib.h" | 44 #include "jpeglib.h" |
| 44 } | 45 } |
| 45 | 46 |
| 46 namespace blink { | 47 namespace blink { |
| 47 | 48 |
| 49 void RGBAtoRGBScalar(const unsigned char* pixels, | |
|
msarett1
2016/12/16 13:07:54
libjpeg-turbo actually supports RGBA input (in add
cavalcantii1
2016/12/17 02:35:33
Acknowledged.
| |
| 50 unsigned pixelCount, | |
| 51 unsigned char* output) { | |
| 52 // Per <canvas> spec, composite the input image pixels source-over on black. | |
| 53 for (; pixelCount-- > 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 pixelCount, | |
| 71 unsigned char* output) { | |
| 72 const unsigned pixelsPerLoad = 16; | |
| 73 const unsigned rgbaStep = pixelsPerLoad * 4, rgbStep = pixelsPerLoad * 3; | |
| 74 // Input registers. | |
| 75 uint8x16x4_t rgba; | |
| 76 // Output registers. | |
| 77 uint8x16x3_t rgb; | |
| 78 // Intermediate registers. | |
| 79 uint16x8_t low, high; | |
| 80 uint8x16_t result; | |
| 81 unsigned counter; | |
| 82 | |
| 83 for (counter = 0; counter + pixelsPerLoad <= pixelCount; | |
| 84 counter += pixelsPerLoad) { | |
| 85 // Reads 16 pixels at once, each color channel in a different | |
| 86 // 128 bits register. | |
| 87 rgba = vld4q_u8(input); | |
| 88 | |
| 89 // Extracts the low/high part of the 128 bits, multiplying by the | |
| 90 // respective alpha channel. | |
| 91 low = vmull_u8(vget_low_u8(rgba.val[0]), vget_low_u8(rgba.val[3])); | |
| 92 high = vmull_u8(vget_high_u8(rgba.val[0]), vget_high_u8(rgba.val[3])); | |
| 93 | |
| 94 // Original Skia formula is: (x + (x >> 8)) >> 8, where x = a*b + 128. | |
| 95 // This shifts and accumulates following by rounding in a single | |
| 96 // instruction. | |
| 97 low = vrsraq_n_u16(low, low, 8); | |
|
msarett1
2016/12/16 13:07:54
Skia has a NEON implementation of "mul and rounded
cavalcantii1
2016/12/17 02:35:33
I searched for SkSwizzler_opts.h and it seems it i
| |
| 98 high = vrsraq_n_u16(high, high, 8); | |
| 99 | |
| 100 // And now to the last shift and combining the vector. | |
| 101 result = vcombine_u8(vqrshrn_n_u16(low, 8), vqrshrn_n_u16(high, 8)); | |
| 102 | |
| 103 // Write back the Red channel to the first 128 bits register. | |
| 104 rgb.val[0] = result; | |
| 105 | |
| 106 // Now the Green channel (don't trust the compiler to unroll the loop). | |
|
msarett1
2016/12/16 13:07:54
What about using an inline helper function? Ex: m
cavalcantii1
2016/12/17 02:35:33
I tested moving the pixel manipulation code to a l
| |
| 107 low = vmull_u8(vget_low_u8(rgba.val[1]), vget_low_u8(rgba.val[3])); | |
| 108 high = vmull_u8(vget_high_u8(rgba.val[1]), vget_high_u8(rgba.val[3])); | |
| 109 low = vrsraq_n_u16(low, low, 8); | |
| 110 high = vrsraq_n_u16(high, high, 8); | |
| 111 result = vcombine_u8(vqrshrn_n_u16(low, 8), vqrshrn_n_u16(high, 8)); | |
| 112 rgb.val[1] = result; | |
| 113 | |
| 114 // Finally the Blue channel. | |
| 115 low = vmull_u8(vget_low_u8(rgba.val[2]), vget_low_u8(rgba.val[3])); | |
| 116 high = vmull_u8(vget_high_u8(rgba.val[2]), vget_high_u8(rgba.val[3])); | |
| 117 low = vrsraq_n_u16(low, low, 8); | |
| 118 high = vrsraq_n_u16(high, high, 8); | |
| 119 result = vcombine_u8(vqrshrn_n_u16(low, 8), vqrshrn_n_u16(high, 8)); | |
| 120 rgb.val[2] = result; | |
| 121 | |
| 122 // Write back (interleaved) results to output. | |
| 123 vst3q_u8(output, rgb); | |
| 124 | |
| 125 // Advance to next elements (could be avoided loading register with | |
| 126 // increment after i.e. "vld4 {vector}, [r1]!"). | |
| 127 input += rgbaStep; | |
| 128 output += rgbStep; | |
| 129 } | |
| 130 | |
| 131 // Handle the tail elements. | |
| 132 unsigned remaining = pixelCount; | |
| 133 remaining -= counter; | |
| 134 if (remaining != 0) { | |
| 135 RGBAtoRGBScalar(input, remaining, output); | |
| 136 } | |
| 137 } | |
| 138 #endif | |
| 139 | |
| 48 struct JPEGOutputBuffer : public jpeg_destination_mgr { | 140 struct JPEGOutputBuffer : public jpeg_destination_mgr { |
| 49 DISALLOW_NEW(); | 141 DISALLOW_NEW(); |
| 50 Vector<unsigned char>* output; | 142 Vector<unsigned char>* output; |
| 51 Vector<unsigned char> buffer; | 143 Vector<unsigned char> buffer; |
| 52 }; | 144 }; |
| 53 | 145 |
| 54 class JPEGImageEncoderStateImpl final : public JPEGImageEncoderState { | 146 class JPEGImageEncoderStateImpl final : public JPEGImageEncoderState { |
| 55 public: | 147 public: |
| 56 JPEGImageEncoderStateImpl() {} | 148 JPEGImageEncoderStateImpl() {} |
| 57 ~JPEGImageEncoderStateImpl() override { | 149 ~JPEGImageEncoderStateImpl() override { |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 88 JPEGOutputBuffer* out = static_cast<JPEGOutputBuffer*>(cinfo->dest); | 180 JPEGOutputBuffer* out = static_cast<JPEGOutputBuffer*>(cinfo->dest); |
| 89 const size_t size = out->buffer.size() - out->free_in_buffer; | 181 const size_t size = out->buffer.size() - out->free_in_buffer; |
| 90 out->output->append(out->buffer.data(), size); | 182 out->output->append(out->buffer.data(), size); |
| 91 } | 183 } |
| 92 | 184 |
| 93 static void handleError(j_common_ptr common) { | 185 static void handleError(j_common_ptr common) { |
| 94 jmp_buf* jumpBufferPtr = static_cast<jmp_buf*>(common->client_data); | 186 jmp_buf* jumpBufferPtr = static_cast<jmp_buf*>(common->client_data); |
| 95 longjmp(*jumpBufferPtr, -1); | 187 longjmp(*jumpBufferPtr, -1); |
| 96 } | 188 } |
| 97 | 189 |
| 98 static void RGBAtoRGB(const unsigned char* pixels, | |
| 99 unsigned pixelCount, | |
| 100 unsigned char* output) { | |
| 101 // Per <canvas> spec, composite the input image pixels source-over on black. | |
| 102 | |
| 103 for (; pixelCount-- > 0; pixels += 4) { | |
| 104 unsigned char alpha = pixels[3]; | |
| 105 if (alpha != 255) { | |
| 106 *output++ = SkMulDiv255Round(pixels[0], alpha); | |
| 107 *output++ = SkMulDiv255Round(pixels[1], alpha); | |
| 108 *output++ = SkMulDiv255Round(pixels[2], alpha); | |
| 109 } else { | |
| 110 *output++ = pixels[0]; | |
| 111 *output++ = pixels[1]; | |
| 112 *output++ = pixels[2]; | |
| 113 } | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 static void disableSubsamplingForHighQuality(jpeg_compress_struct* cinfo, | 190 static void disableSubsamplingForHighQuality(jpeg_compress_struct* cinfo, |
| 118 int quality) { | 191 int quality) { |
| 119 if (quality < 100) | 192 if (quality < 100) |
| 120 return; | 193 return; |
| 121 | 194 |
| 122 for (int i = 0; i < MAX_COMPONENTS; ++i) { | 195 for (int i = 0; i < MAX_COMPONENTS; ++i) { |
| 123 cinfo->comp_info[i].h_samp_factor = 1; | 196 cinfo->comp_info[i].h_samp_factor = 1; |
| 124 cinfo->comp_info[i].v_samp_factor = 1; | 197 cinfo->comp_info[i].v_samp_factor = 1; |
| 125 } | 198 } |
| 126 } | 199 } |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 std::unique_ptr<JPEGImageEncoderState> encoderState = | 323 std::unique_ptr<JPEGImageEncoderState> encoderState = |
| 251 JPEGImageEncoderState::create(imageData.size(), quality, output); | 324 JPEGImageEncoderState::create(imageData.size(), quality, output); |
| 252 if (!encoderState) | 325 if (!encoderState) |
| 253 return false; | 326 return false; |
| 254 | 327 |
| 255 return JPEGImageEncoder::encodeWithPreInitializedState( | 328 return JPEGImageEncoder::encodeWithPreInitializedState( |
| 256 std::move(encoderState), imageData.pixels()); | 329 std::move(encoderState), imageData.pixels()); |
| 257 } | 330 } |
| 258 | 331 |
| 259 } // namespace blink | 332 } // namespace blink |
| OLD | NEW |