~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/desktop/meta_data_extraction.cc

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2009, Google Inc.
 
2
//
 
3
// Redistribution and use in source and binary forms, with or without
 
4
// modification, are permitted provided that the following conditions are met:
 
5
//
 
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.
 
14
//
 
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.
 
25
 
 
26
#include "gears/desktop/drag_and_drop_utils_common.h"
 
27
 
 
28
#if GEARS_DRAG_AND_DROP_API_IS_SUPPORTED_FOR_THIS_PLATFORM
 
29
 
 
30
#include "gears/desktop/meta_data_extraction.h"
 
31
 
 
32
#include "gears/base/common/js_types.h"
 
33
#include "gears/blob/blob_interface.h"
 
34
#include "third_party/libpng/png.h"
 
35
 
 
36
extern "C" {
 
37
#include "third_party/libjpeg/jpeglib.h"
 
38
}
 
39
 
 
40
 
 
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;
 
44
 
 
45
 
 
46
//-----------------------------------------------------------------------------
 
47
// JPEG meta-data extraction functions
 
48
 
 
49
 
 
50
struct JpegBlobErrorMgr {
 
51
  jpeg_error_mgr parent_implementation;
 
52
  jmp_buf setjmp_buffer;
 
53
};
 
54
 
 
55
 
 
56
static void JpegBlobErrorExit(j_common_ptr c) {
 
57
  JpegBlobErrorMgr *jbem = reinterpret_cast<JpegBlobErrorMgr*>(c->err);
 
58
  longjmp(jbem->setjmp_buffer, 1);
 
59
}
 
60
 
 
61
 
 
62
static const size_t kJpegBlobReadContextBufferSize = 1024;
 
63
struct JpegBlobReadContext {
 
64
  BlobInterface *blob;
 
65
  int64 offset;
 
66
  uint8 buffer[kJpegBlobReadContextBufferSize];
 
67
};
 
68
 
 
69
 
 
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;
 
75
}
 
76
 
 
77
 
 
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) {
 
84
    return FALSE;
 
85
  }
 
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;
 
91
  return TRUE;
 
92
}
 
93
 
 
94
 
 
95
static void JpegBlobSkipInputData(j_decompress_ptr jd, long num_bytes) {
 
96
  if (num_bytes <= 0) {
 
97
    return;
 
98
  }
 
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;
 
108
  } else {
 
109
    context->offset += extra_bytes_needed;
 
110
    jd->src->next_input_byte = context->buffer;
 
111
    jd->src->bytes_in_buffer = 0;
 
112
  }
 
113
}
 
114
 
 
115
 
 
116
static void JpegBlobTermSource(j_decompress_ptr jd) {
 
117
  // No-op.
 
118
}
 
119
 
 
120
 
 
121
static bool ExtractMetaDataJpeg(BlobInterface *blob, JsObject *result) {
 
122
  static const int kJpegMagicHeaderLength = 2;
 
123
  if (blob->Length() < kJpegMagicHeaderLength) {
 
124
    return false;
 
125
  }
 
126
  uint8 magic_header[kJpegMagicHeaderLength];
 
127
  if (kJpegMagicHeaderLength !=
 
128
          blob->Read(magic_header, 0, kJpegMagicHeaderLength)) {
 
129
    return false;
 
130
  }
 
131
  // The JPEG Start-Of-Image (SOI) marker is 0xFFD8.
 
132
  if (magic_header[0] != 0xFF || magic_header[1] != 0xD8) {
 
133
    return false;
 
134
  }
 
135
 
 
136
  JpegBlobReadContext context;
 
137
  context.blob = blob;
 
138
  context.offset = 0;
 
139
 
 
140
  jpeg_decompress_struct jd;
 
141
  memset(&jd, 0, sizeof(jd));
 
142
  jpeg_create_decompress(&jd);
 
143
  jd.client_data = &context;
 
144
 
 
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);
 
151
    return false;
 
152
  }
 
153
 
 
154
  jpeg_source_mgr jsm;
 
155
  memset(&jsm, 0, sizeof(jsm));
 
156
  jd.src = &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;
 
162
 
 
163
  jpeg_read_header(&jd, TRUE);
 
164
 
 
165
  if ((jd.image_width > kMaxReasonableImageWidthHeight) ||
 
166
      (jd.image_height > kMaxReasonableImageWidthHeight)) {
 
167
    jpeg_destroy_decompress(&jd);
 
168
    return false;
 
169
  }
 
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")));
 
179
 
 
180
  jpeg_destroy_decompress(&jd);
 
181
  return true;
 
182
}
 
183
 
 
184
 
 
185
//-----------------------------------------------------------------------------
 
186
// PNG meta-data extraction functions
 
187
 
 
188
 
 
189
struct PngBlobReadContext {
 
190
  BlobInterface *blob;
 
191
  int64 offset;
 
192
};
 
193
 
 
194
 
 
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");
 
202
  }
 
203
  context->offset += length;
 
204
}
 
205
 
 
206
 
 
207
static bool ExtractMetaDataPng(BlobInterface *blob, JsObject *result) {
 
208
  static const int kPngMagicHeaderLength = 8;
 
209
  if (blob->Length() < kPngMagicHeaderLength) {
 
210
    return false;
 
211
  }
 
212
  uint8 magic_header[kPngMagicHeaderLength];
 
213
  if (kPngMagicHeaderLength !=
 
214
          blob->Read(magic_header, 0, kPngMagicHeaderLength)) {
 
215
    return false;
 
216
  }
 
217
  if (png_sig_cmp(magic_header, 0, kPngMagicHeaderLength) != 0) {
 
218
    return false;
 
219
  }
 
220
 
 
221
  png_structp png_ptr = png_create_read_struct(
 
222
      PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
223
  if (!png_ptr) {
 
224
    return false;
 
225
  }
 
226
  png_infop info_ptr = png_create_info_struct(png_ptr);
 
227
  if (!info_ptr) {
 
228
    png_destroy_read_struct(&png_ptr, NULL, NULL);
 
229
    return false;
 
230
  }
 
231
  if (setjmp(png_jmpbuf(png_ptr))) {
 
232
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
233
    return false;
 
234
  }
 
235
 
 
236
  PngBlobReadContext context;
 
237
  context.blob = blob;
 
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);
 
242
 
 
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,
 
246
      NULL, NULL, NULL);
 
247
  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
248
 
 
249
  if ((width > kMaxReasonableImageWidthHeight) ||
 
250
      (height > kMaxReasonableImageWidthHeight)) {
 
251
    return false;
 
252
  }
 
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")));
 
260
  return true;
 
261
}
 
262
 
 
263
 
 
264
//-----------------------------------------------------------------------------
 
265
// Public API
 
266
 
 
267
 
 
268
void ExtractMetaData(BlobInterface *blob, JsObject *result) {
 
269
  if (ExtractMetaDataJpeg(blob, result)) return;
 
270
  if (ExtractMetaDataPng(blob, result)) return;
 
271
 
 
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")));
 
277
}
 
278
 
 
279
#endif  // GEARS_DRAG_AND_DROP_API_IS_SUPPORTED_FOR_THIS_PLATFORM