2 * Copyright (c) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
34 #include "SkColorPriv.h"
35 #include "platform/geometry/IntSize.h"
36 #include "platform/graphics/ImageBuffer.h"
40 #include <stdio.h> // jpeglib.h needs stdio.h FILE
46 struct JPEGOutputBuffer
: public jpeg_destination_mgr
{
47 Vector
<unsigned char>* output
;
48 Vector
<unsigned char> buffer
;
51 static void prepareOutput(j_compress_ptr cinfo
)
53 JPEGOutputBuffer
* out
= static_cast<JPEGOutputBuffer
*>(cinfo
->dest
);
54 const size_t internalBufferSize
= 8192;
55 out
->buffer
.resize(internalBufferSize
);
56 out
->next_output_byte
= out
->buffer
.data();
57 out
->free_in_buffer
= out
->buffer
.size();
60 static boolean
writeOutput(j_compress_ptr cinfo
)
62 JPEGOutputBuffer
* out
= static_cast<JPEGOutputBuffer
*>(cinfo
->dest
);
63 out
->output
->append(out
->buffer
.data(), out
->buffer
.size());
64 out
->next_output_byte
= out
->buffer
.data();
65 out
->free_in_buffer
= out
->buffer
.size();
69 static void finishOutput(j_compress_ptr cinfo
)
71 JPEGOutputBuffer
* out
= static_cast<JPEGOutputBuffer
*>(cinfo
->dest
);
72 const size_t size
= out
->buffer
.size() - out
->free_in_buffer
;
73 out
->output
->append(out
->buffer
.data(), size
);
76 static void handleError(j_common_ptr common
)
78 jmp_buf* jumpBufferPtr
= static_cast<jmp_buf*>(common
->client_data
);
79 longjmp(*jumpBufferPtr
, -1);
82 static void RGBAtoRGB(const unsigned char* pixels
, unsigned pixelCount
, unsigned char* output
)
84 // Per <canvas> spec, composite the input image pixels source-over on black.
86 for (; pixelCount
-- > 0; pixels
+= 4) {
87 unsigned char alpha
= pixels
[3];
89 *output
++ = SkMulDiv255Round(pixels
[0], alpha
);
90 *output
++ = SkMulDiv255Round(pixels
[1], alpha
);
91 *output
++ = SkMulDiv255Round(pixels
[2], alpha
);
93 *output
++ = pixels
[0];
94 *output
++ = pixels
[1];
95 *output
++ = pixels
[2];
100 static void disableSubsamplingForHighQuality(jpeg_compress_struct
* cinfo
, int quality
)
105 for (int i
= 0; i
< MAX_COMPONENTS
; ++i
) {
106 cinfo
->comp_info
[i
].h_samp_factor
= 1;
107 cinfo
->comp_info
[i
].v_samp_factor
= 1;
111 static bool encodePixels(IntSize imageSize
, const unsigned char* inputPixels
, int quality
, Vector
<unsigned char>* output
)
113 if (imageSize
.width() <= 0 || imageSize
.height() <= 0)
116 JPEGOutputBuffer destination
;
117 destination
.output
= output
;
120 jpeg_compress_struct cinfo
;
121 jpeg_error_mgr error
;
122 cinfo
.err
= jpeg_std_error(&error
);
123 error
.error_exit
= handleError
;
125 cinfo
.client_data
= &jumpBuffer
;
127 if (setjmp(jumpBuffer
)) {
128 jpeg_destroy_compress(&cinfo
);
132 jpeg_create_compress(&cinfo
);
133 cinfo
.dest
= &destination
;
134 cinfo
.dest
->init_destination
= prepareOutput
;
135 cinfo
.dest
->empty_output_buffer
= writeOutput
;
136 cinfo
.dest
->term_destination
= finishOutput
;
138 cinfo
.image_height
= imageSize
.height();
139 cinfo
.image_width
= imageSize
.width();
140 cinfo
.in_color_space
= JCS_RGB
;
141 cinfo
.input_components
= 3;
143 jpeg_set_defaults(&cinfo
);
144 jpeg_set_quality(&cinfo
, quality
, TRUE
);
145 disableSubsamplingForHighQuality(&cinfo
, quality
);
146 jpeg_start_compress(&cinfo
, TRUE
);
148 unsigned char* pixels
= const_cast<unsigned char*>(inputPixels
);
149 row
.resize(cinfo
.image_width
* cinfo
.input_components
);
150 const size_t pixelRowStride
= cinfo
.image_width
* 4;
151 while (cinfo
.next_scanline
< cinfo
.image_height
) {
152 JSAMPLE
* rowData
= row
.data();
153 RGBAtoRGB(pixels
, cinfo
.image_width
, rowData
);
154 jpeg_write_scanlines(&cinfo
, &rowData
, 1);
155 pixels
+= pixelRowStride
;
158 jpeg_finish_compress(&cinfo
);
159 jpeg_destroy_compress(&cinfo
);
163 bool JPEGImageEncoder::encode(const ImageDataBuffer
& imageData
, int quality
, Vector
<unsigned char>* output
)
165 if (!imageData
.pixels())
168 return encodePixels(IntSize(imageData
.width(), imageData
.height()), imageData
.pixels(), quality
, output
);