1
// Copyright 2009, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
#include "gears/desktop/drag_and_drop_utils_common.h"
28
#if GEARS_DRAG_AND_DROP_API_IS_SUPPORTED_FOR_THIS_PLATFORM
30
#include "gears/desktop/meta_data_extraction.h"
32
#include "gears/base/common/js_types.h"
33
#include "gears/blob/blob_interface.h"
34
#include "third_party/libpng/png.h"
37
#include "third_party/libjpeg/jpeglib.h"
41
// Make an arbitrary limit (of 2^16 pixels wide, or 2^16 pixels high) for what
42
// images we deem reasonable to deal with in Gears.
43
static const unsigned int kMaxReasonableImageWidthHeight = 65536;
46
//-----------------------------------------------------------------------------
47
// JPEG meta-data extraction functions
50
struct JpegBlobErrorMgr {
51
jpeg_error_mgr parent_implementation;
52
jmp_buf setjmp_buffer;
56
static void JpegBlobErrorExit(j_common_ptr c) {
57
JpegBlobErrorMgr *jbem = reinterpret_cast<JpegBlobErrorMgr*>(c->err);
58
longjmp(jbem->setjmp_buffer, 1);
62
static const size_t kJpegBlobReadContextBufferSize = 1024;
63
struct JpegBlobReadContext {
66
uint8 buffer[kJpegBlobReadContextBufferSize];
70
static void JpegBlobInitSource(j_decompress_ptr jd) {
71
JpegBlobReadContext *context =
72
reinterpret_cast<JpegBlobReadContext*>(jd->client_data);
73
jd->src->next_input_byte = context->buffer;
74
jd->src->bytes_in_buffer = 0;
78
static boolean JpegBlobFillInputBuffer(j_decompress_ptr jd) {
79
JpegBlobReadContext *context =
80
reinterpret_cast<JpegBlobReadContext*>(jd->client_data);
81
int bytes_read = static_cast<int>(context->blob->Read(
82
context->buffer, context->offset, kJpegBlobReadContextBufferSize));
83
if (bytes_read <= 0) {
86
assert(bytes_read >= 0 &&
87
bytes_read <= static_cast<int>(kJpegBlobReadContextBufferSize));
88
context->offset += bytes_read;
89
jd->src->next_input_byte = context->buffer;
90
jd->src->bytes_in_buffer = bytes_read;
95
static void JpegBlobSkipInputData(j_decompress_ptr jd, long num_bytes) {
99
JpegBlobReadContext *context =
100
reinterpret_cast<JpegBlobReadContext*>(jd->client_data);
101
assert(jd->src->bytes_in_buffer >= 0 &&
102
jd->src->bytes_in_buffer <= kJpegBlobReadContextBufferSize);
103
int extra_bytes_needed =
104
num_bytes - static_cast<int>(jd->src->bytes_in_buffer);
105
if (extra_bytes_needed < 0) {
106
jd->src->next_input_byte += num_bytes;
107
jd->src->bytes_in_buffer -= num_bytes;
109
context->offset += extra_bytes_needed;
110
jd->src->next_input_byte = context->buffer;
111
jd->src->bytes_in_buffer = 0;
116
static void JpegBlobTermSource(j_decompress_ptr jd) {
121
static bool ExtractMetaDataJpeg(BlobInterface *blob, JsObject *result) {
122
static const int kJpegMagicHeaderLength = 2;
123
if (blob->Length() < kJpegMagicHeaderLength) {
126
uint8 magic_header[kJpegMagicHeaderLength];
127
if (kJpegMagicHeaderLength !=
128
blob->Read(magic_header, 0, kJpegMagicHeaderLength)) {
131
// The JPEG Start-Of-Image (SOI) marker is 0xFFD8.
132
if (magic_header[0] != 0xFF || magic_header[1] != 0xD8) {
136
JpegBlobReadContext context;
140
jpeg_decompress_struct jd;
141
memset(&jd, 0, sizeof(jd));
142
jpeg_create_decompress(&jd);
143
jd.client_data = &context;
145
JpegBlobErrorMgr jbem;
146
memset(&jbem, 0, sizeof(jbem));
147
jd.err = jpeg_std_error(&jbem.parent_implementation);
148
jd.err->error_exit = JpegBlobErrorExit;
149
if (setjmp(jbem.setjmp_buffer)) {
150
jpeg_destroy_decompress(&jd);
155
memset(&jsm, 0, sizeof(jsm));
157
jsm.init_source = JpegBlobInitSource;
158
jsm.fill_input_buffer = JpegBlobFillInputBuffer;
159
jsm.skip_input_data = JpegBlobSkipInputData;
160
jsm.resync_to_restart = jpeg_resync_to_restart;
161
jsm.term_source = JpegBlobTermSource;
163
jpeg_read_header(&jd, TRUE);
165
if ((jd.image_width > kMaxReasonableImageWidthHeight) ||
166
(jd.image_height > kMaxReasonableImageWidthHeight)) {
167
jpeg_destroy_decompress(&jd);
170
result->SetPropertyInt(
171
std::string16(STRING16(L"imageWidth")),
172
static_cast<int>(jd.image_width));
173
result->SetPropertyInt(
174
std::string16(STRING16(L"imageHeight")),
175
static_cast<int>(jd.image_height));
176
result->SetPropertyString(
177
std::string16(STRING16(L"mimeType")),
178
std::string16(STRING16(L"image/jpeg")));
180
jpeg_destroy_decompress(&jd);
185
//-----------------------------------------------------------------------------
186
// PNG meta-data extraction functions
189
struct PngBlobReadContext {
195
static void PngBlobReadFunction(
196
png_structp png_ptr, png_bytep data, png_size_t length) {
197
PngBlobReadContext *context =
198
reinterpret_cast<PngBlobReadContext*>(png_get_io_ptr(png_ptr));
199
int64 read = context->blob->Read(data, context->offset, length);
200
if (read < 0 || length != static_cast<uint64>(read)) {
201
png_error(png_ptr, "Incomplete read from Blob");
203
context->offset += length;
207
static bool ExtractMetaDataPng(BlobInterface *blob, JsObject *result) {
208
static const int kPngMagicHeaderLength = 8;
209
if (blob->Length() < kPngMagicHeaderLength) {
212
uint8 magic_header[kPngMagicHeaderLength];
213
if (kPngMagicHeaderLength !=
214
blob->Read(magic_header, 0, kPngMagicHeaderLength)) {
217
if (png_sig_cmp(magic_header, 0, kPngMagicHeaderLength) != 0) {
221
png_structp png_ptr = png_create_read_struct(
222
PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
226
png_infop info_ptr = png_create_info_struct(png_ptr);
228
png_destroy_read_struct(&png_ptr, NULL, NULL);
231
if (setjmp(png_jmpbuf(png_ptr))) {
232
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
236
PngBlobReadContext context;
238
context.offset = kPngMagicHeaderLength;
239
png_set_sig_bytes(png_ptr, kPngMagicHeaderLength);
240
png_set_read_fn(png_ptr, &context, PngBlobReadFunction);
241
png_read_info(png_ptr, info_ptr);
243
png_uint_32 width, height;
244
int bit_depth, color_type;
245
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
247
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
249
if ((width > kMaxReasonableImageWidthHeight) ||
250
(height > kMaxReasonableImageWidthHeight)) {
253
result->SetPropertyInt(
254
std::string16(STRING16(L"imageWidth")), static_cast<int>(width));
255
result->SetPropertyInt(
256
std::string16(STRING16(L"imageHeight")), static_cast<int>(height));
257
result->SetPropertyString(
258
std::string16(STRING16(L"mimeType")),
259
std::string16(STRING16(L"image/png")));
264
//-----------------------------------------------------------------------------
268
void ExtractMetaData(BlobInterface *blob, JsObject *result) {
269
if (ExtractMetaDataJpeg(blob, result)) return;
270
if (ExtractMetaDataPng(blob, result)) return;
272
// If we didn't match an explicit mime-type, above, then we fall back on
273
// the default "application/octet-stream".
274
result->SetPropertyString(
275
std::string16(STRING16(L"mimeType")),
276
std::string16(STRING16(L"application/octet-stream")));
279
#endif // GEARS_DRAG_AND_DROP_API_IS_SUPPORTED_FOR_THIS_PLATFORM