[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / ui / gfx / codec / jpeg_codec.cc
blob5c13e5091d9b9ab2792fe31889ac383bc741b9f9
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/codec/jpeg_codec.h"
7 #include <setjmp.h>
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "third_party/skia/include/core/SkBitmap.h"
12 #include "third_party/skia/include/core/SkColorPriv.h"
14 extern "C" {
15 #if defined(USE_SYSTEM_LIBJPEG)
16 #include <jpeglib.h>
17 #elif defined(USE_LIBJPEG_TURBO)
18 #include "third_party/libjpeg_turbo/jpeglib.h"
19 #else
20 #include "third_party/libjpeg/jpeglib.h"
21 #endif
24 namespace gfx {
26 // Encoder/decoder shared stuff ------------------------------------------------
28 namespace {
30 // used to pass error info through the JPEG library
31 struct CoderErrorMgr {
32 jpeg_error_mgr pub;
33 jmp_buf setjmp_buffer;
36 void ErrorExit(jpeg_common_struct* cinfo) {
37 CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err);
39 // Return control to the setjmp point.
40 longjmp(err->setjmp_buffer, false);
43 } // namespace
45 // This method helps identify at run time which library chromium is using.
46 JPEGCodec::LibraryVariant JPEGCodec::JpegLibraryVariant() {
47 #if defined(USE_SYSTEM_LIBJPEG)
48 return SYSTEM_LIBJPEG;
49 #elif defined(USE_LIBJPEG_TURBO)
50 return LIBJPEG_TURBO;
51 #else
52 return IJG_LIBJPEG;
53 #endif
56 // Encoder ---------------------------------------------------------------------
58 // This code is based on nsJPEGEncoder from Mozilla.
59 // Copyright 2005 Google Inc. (Brett Wilson, contributor)
61 namespace {
63 // Initial size for the output buffer in the JpegEncoderState below.
64 static const int initial_output_buffer_size = 8192;
66 struct JpegEncoderState {
67 explicit JpegEncoderState(std::vector<unsigned char>* o)
68 : out(o),
69 image_buffer_used(0) {
72 // Output buffer, of which 'image_buffer_used' bytes are actually used (this
73 // will often be less than the actual size of the vector because we size it
74 // so that libjpeg can write directly into it.
75 std::vector<unsigned char>* out;
77 // Number of bytes in the 'out' buffer that are actually used (see above).
78 size_t image_buffer_used;
81 // Initializes the JpegEncoderState for encoding, and tells libjpeg about where
82 // the output buffer is.
84 // From the JPEG library:
85 // "Initialize destination. This is called by jpeg_start_compress() before
86 // any data is actually written. It must initialize next_output_byte and
87 // free_in_buffer. free_in_buffer must be initialized to a positive value."
88 void InitDestination(jpeg_compress_struct* cinfo) {
89 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
90 DCHECK(state->image_buffer_used == 0) << "initializing after use";
92 state->out->resize(initial_output_buffer_size);
93 state->image_buffer_used = 0;
95 cinfo->dest->next_output_byte = &(*state->out)[0];
96 cinfo->dest->free_in_buffer = initial_output_buffer_size;
99 // Resize the buffer that we give to libjpeg and update our and its state.
101 // From the JPEG library:
102 // "Callback used by libjpeg whenever the buffer has filled (free_in_buffer
103 // reaches zero). In typical applications, it should write out the *entire*
104 // buffer (use the saved start address and buffer length; ignore the current
105 // state of next_output_byte and free_in_buffer). Then reset the pointer &
106 // count to the start of the buffer, and return TRUE indicating that the
107 // buffer has been dumped. free_in_buffer must be set to a positive value
108 // when TRUE is returned. A FALSE return should only be used when I/O
109 // suspension is desired (this operating mode is discussed in the next
110 // section)."
111 boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) {
112 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
114 // note the new size, the buffer is full
115 state->image_buffer_used = state->out->size();
117 // expand buffer, just double size each time
118 state->out->resize(state->out->size() * 2);
120 // tell libjpeg where to write the next data
121 cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used];
122 cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used;
123 return 1;
126 // Cleans up the JpegEncoderState to prepare for returning in the final form.
128 // From the JPEG library:
129 // "Terminate destination --- called by jpeg_finish_compress() after all data
130 // has been written. In most applications, this must flush any data
131 // remaining in the buffer. Use either next_output_byte or free_in_buffer to
132 // determine how much data is in the buffer."
133 void TermDestination(jpeg_compress_struct* cinfo) {
134 JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data);
135 DCHECK(state->out->size() >= state->image_buffer_used);
137 // update the used byte based on the next byte libjpeg would write to
138 state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0];
139 DCHECK(state->image_buffer_used < state->out->size()) <<
140 "JPEG library busted, got a bad image buffer size";
142 // update our buffer so that it exactly encompases the desired data
143 state->out->resize(state->image_buffer_used);
146 #if !defined(JCS_EXTENSIONS)
147 // Converts RGBA to RGB (removing the alpha values) to prepare to send data to
148 // libjpeg. This converts one row of data in rgba with the given width in
149 // pixels the the given rgb destination buffer (which should have enough space
150 // reserved for the final data).
151 void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb)
153 for (int x = 0; x < pixel_width; x++) {
154 const unsigned char* pixel_in = &rgba[x * 4];
155 unsigned char* pixel_out = &rgb[x * 3];
156 pixel_out[0] = pixel_in[0];
157 pixel_out[1] = pixel_in[1];
158 pixel_out[2] = pixel_in[2];
162 // Converts BGRA to RGB by reordering the color components and dropping the
163 // alpha. This converts one row of data in rgba with the given width in
164 // pixels the the given rgb destination buffer (which should have enough space
165 // reserved for the final data).
166 void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
168 for (int x = 0; x < pixel_width; x++) {
169 const unsigned char* pixel_in = &bgra[x * 4];
170 unsigned char* pixel_out = &rgb[x * 3];
171 pixel_out[0] = pixel_in[2];
172 pixel_out[1] = pixel_in[1];
173 pixel_out[2] = pixel_in[0];
176 #endif // !defined(JCS_EXTENSIONS)
178 // This class destroys the given jpeg_compress object when it goes out of
179 // scope. It simplifies the error handling in Encode (and even applies to the
180 // success case).
181 class CompressDestroyer {
182 public:
183 CompressDestroyer() : cinfo_(NULL) {
185 ~CompressDestroyer() {
186 DestroyManagedObject();
188 void SetManagedObject(jpeg_compress_struct* ci) {
189 DestroyManagedObject();
190 cinfo_ = ci;
192 void DestroyManagedObject() {
193 if (cinfo_) {
194 jpeg_destroy_compress(cinfo_);
195 cinfo_ = NULL;
198 private:
199 jpeg_compress_struct* cinfo_;
202 } // namespace
204 bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format,
205 int w, int h, int row_byte_width,
206 int quality, std::vector<unsigned char>* output) {
207 jpeg_compress_struct cinfo;
208 CompressDestroyer destroyer;
209 destroyer.SetManagedObject(&cinfo);
210 output->clear();
211 #if !defined(JCS_EXTENSIONS)
212 unsigned char* row_buffer = NULL;
213 #endif
215 // We set up the normal JPEG error routines, then override error_exit.
216 // This must be done before the call to create_compress.
217 CoderErrorMgr errmgr;
218 cinfo.err = jpeg_std_error(&errmgr.pub);
219 errmgr.pub.error_exit = ErrorExit;
221 // Establish the setjmp return context for ErrorExit to use.
222 if (setjmp(errmgr.setjmp_buffer)) {
223 // If we get here, the JPEG code has signaled an error.
224 // MSDN notes: "if you intend your code to be portable, do not rely on
225 // correct destruction of frame-based objects when executing a nonlocal
226 // goto using a call to longjmp." So we delete the CompressDestroyer's
227 // object manually instead.
228 destroyer.DestroyManagedObject();
229 #if !defined(JCS_EXTENSIONS)
230 delete[] row_buffer;
231 #endif
232 return false;
235 // The destroyer will destroy() cinfo on exit.
236 jpeg_create_compress(&cinfo);
238 cinfo.image_width = w;
239 cinfo.image_height = h;
240 cinfo.input_components = 3;
241 #ifdef JCS_EXTENSIONS
242 // Choose an input colorspace and return if it is an unsupported one. Since
243 // libjpeg-turbo supports all input formats used by Chromium (i.e. RGB, RGBA,
244 // and BGRA), we just map the input parameters to a colorspace used by
245 // libjpeg-turbo.
246 if (format == FORMAT_RGB) {
247 cinfo.input_components = 3;
248 cinfo.in_color_space = JCS_RGB;
249 } else if (format == FORMAT_RGBA ||
250 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
251 cinfo.input_components = 4;
252 cinfo.in_color_space = JCS_EXT_RGBX;
253 } else if (format == FORMAT_BGRA ||
254 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
255 cinfo.input_components = 4;
256 cinfo.in_color_space = JCS_EXT_BGRX;
257 } else {
258 // We can exit this function without calling jpeg_destroy_compress() because
259 // CompressDestroyer automaticaly calls it.
260 NOTREACHED() << "Invalid pixel format";
261 return false;
263 #else
264 cinfo.in_color_space = JCS_RGB;
265 #endif
266 cinfo.data_precision = 8;
268 jpeg_set_defaults(&cinfo);
269 jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100
271 // set up the destination manager
272 jpeg_destination_mgr destmgr;
273 destmgr.init_destination = InitDestination;
274 destmgr.empty_output_buffer = EmptyOutputBuffer;
275 destmgr.term_destination = TermDestination;
276 cinfo.dest = &destmgr;
278 JpegEncoderState state(output);
279 cinfo.client_data = &state;
281 jpeg_start_compress(&cinfo, 1);
283 // feed it the rows, doing necessary conversions for the color format
284 #ifdef JCS_EXTENSIONS
285 // This function already returns when the input format is not supported by
286 // libjpeg-turbo and needs conversion. Therefore, we just encode lines without
287 // conversions.
288 while (cinfo.next_scanline < cinfo.image_height) {
289 const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
290 jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
292 #else
293 if (format == FORMAT_RGB) {
294 // no conversion necessary
295 while (cinfo.next_scanline < cinfo.image_height) {
296 const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
297 jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
299 } else {
300 // get the correct format converter
301 void (*converter)(const unsigned char* in, int w, unsigned char* rgb);
302 if (format == FORMAT_RGBA ||
303 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
304 converter = StripAlpha;
305 } else if (format == FORMAT_BGRA ||
306 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
307 converter = BGRAtoRGB;
308 } else {
309 NOTREACHED() << "Invalid pixel format";
310 return false;
313 // output row after converting
314 row_buffer = new unsigned char[w * 3];
316 while (cinfo.next_scanline < cinfo.image_height) {
317 converter(&input[cinfo.next_scanline * row_byte_width], w, row_buffer);
318 jpeg_write_scanlines(&cinfo, &row_buffer, 1);
320 delete[] row_buffer;
322 #endif
324 jpeg_finish_compress(&cinfo);
325 return true;
328 // Decoder --------------------------------------------------------------------
330 namespace {
332 struct JpegDecoderState {
333 JpegDecoderState(const unsigned char* in, size_t len)
334 : input_buffer(in), input_buffer_length(len) {
337 const unsigned char* input_buffer;
338 size_t input_buffer_length;
341 // Callback to initialize the source.
343 // From the JPEG library:
344 // "Initialize source. This is called by jpeg_read_header() before any data is
345 // actually read. May leave bytes_in_buffer set to 0 (in which case a
346 // fill_input_buffer() call will occur immediately)."
347 void InitSource(j_decompress_ptr cinfo) {
348 JpegDecoderState* state = static_cast<JpegDecoderState*>(cinfo->client_data);
349 cinfo->src->next_input_byte = state->input_buffer;
350 cinfo->src->bytes_in_buffer = state->input_buffer_length;
353 // Callback to fill the buffer. Since our buffer already contains all the data,
354 // we should never need to provide more data. If libjpeg thinks it needs more
355 // data, our input is probably corrupt.
357 // From the JPEG library:
358 // "This is called whenever bytes_in_buffer has reached zero and more data is
359 // wanted. In typical applications, it should read fresh data into the buffer
360 // (ignoring the current state of next_input_byte and bytes_in_buffer), reset
361 // the pointer & count to the start of the buffer, and return TRUE indicating
362 // that the buffer has been reloaded. It is not necessary to fill the buffer
363 // entirely, only to obtain at least one more byte. bytes_in_buffer MUST be
364 // set to a positive value if TRUE is returned. A FALSE return should only
365 // be used when I/O suspension is desired."
366 boolean FillInputBuffer(j_decompress_ptr cinfo) {
367 return false;
370 // Skip data in the buffer. Since we have all the data at once, this operation
371 // is easy. It is not clear if this ever gets called because the JPEG library
372 // should be able to do the skip itself (it has all the data).
374 // From the JPEG library:
375 // "Skip num_bytes worth of data. The buffer pointer and count should be
376 // advanced over num_bytes input bytes, refilling the buffer as needed. This
377 // is used to skip over a potentially large amount of uninteresting data
378 // (such as an APPn marker). In some applications it may be possible to
379 // optimize away the reading of the skipped data, but it's not clear that
380 // being smart is worth much trouble; large skips are uncommon.
381 // bytes_in_buffer may be zero on return. A zero or negative skip count
382 // should be treated as a no-op."
383 void SkipInputData(j_decompress_ptr cinfo, long num_bytes) {
384 if (num_bytes > static_cast<long>(cinfo->src->bytes_in_buffer)) {
385 // Since all our data should be in the buffer, trying to skip beyond it
386 // means that there is some kind of error or corrupt input data. A 0 for
387 // bytes left means it will call FillInputBuffer which will then fail.
388 cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer;
389 cinfo->src->bytes_in_buffer = 0;
390 } else if (num_bytes > 0) {
391 cinfo->src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
392 cinfo->src->next_input_byte += num_bytes;
396 // Our source doesn't need any cleanup, so this is a NOP.
398 // From the JPEG library:
399 // "Terminate source --- called by jpeg_finish_decompress() after all data has
400 // been read to clean up JPEG source manager. NOT called by jpeg_abort() or
401 // jpeg_destroy()."
402 void TermSource(j_decompress_ptr cinfo) {
405 #if !defined(JCS_EXTENSIONS)
406 // Converts one row of rgb data to rgba data by adding a fully-opaque alpha
407 // value.
408 void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
409 for (int x = 0; x < pixel_width; x++) {
410 const unsigned char* pixel_in = &rgb[x * 3];
411 unsigned char* pixel_out = &rgba[x * 4];
412 pixel_out[0] = pixel_in[0];
413 pixel_out[1] = pixel_in[1];
414 pixel_out[2] = pixel_in[2];
415 pixel_out[3] = 0xff;
419 // Converts one row of RGB data to BGRA by reordering the color components and
420 // adding alpha values of 0xff.
421 void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
423 for (int x = 0; x < pixel_width; x++) {
424 const unsigned char* pixel_in = &bgra[x * 3];
425 unsigned char* pixel_out = &rgb[x * 4];
426 pixel_out[0] = pixel_in[2];
427 pixel_out[1] = pixel_in[1];
428 pixel_out[2] = pixel_in[0];
429 pixel_out[3] = 0xff;
432 #endif // !defined(JCS_EXTENSIONS)
434 // This class destroys the given jpeg_decompress object when it goes out of
435 // scope. It simplifies the error handling in Decode (and even applies to the
436 // success case).
437 class DecompressDestroyer {
438 public:
439 DecompressDestroyer() : cinfo_(NULL) {
441 ~DecompressDestroyer() {
442 DestroyManagedObject();
444 void SetManagedObject(jpeg_decompress_struct* ci) {
445 DestroyManagedObject();
446 cinfo_ = ci;
448 void DestroyManagedObject() {
449 if (cinfo_) {
450 jpeg_destroy_decompress(cinfo_);
451 cinfo_ = NULL;
454 private:
455 jpeg_decompress_struct* cinfo_;
458 } // namespace
460 bool JPEGCodec::Decode(const unsigned char* input, size_t input_size,
461 ColorFormat format, std::vector<unsigned char>* output,
462 int* w, int* h) {
463 jpeg_decompress_struct cinfo;
464 DecompressDestroyer destroyer;
465 destroyer.SetManagedObject(&cinfo);
466 output->clear();
468 // We set up the normal JPEG error routines, then override error_exit.
469 // This must be done before the call to create_decompress.
470 CoderErrorMgr errmgr;
471 cinfo.err = jpeg_std_error(&errmgr.pub);
472 errmgr.pub.error_exit = ErrorExit;
473 // Establish the setjmp return context for ErrorExit to use.
474 if (setjmp(errmgr.setjmp_buffer)) {
475 // If we get here, the JPEG code has signaled an error.
476 // See note in JPEGCodec::Encode() for why we need to destroy the cinfo
477 // manually here.
478 destroyer.DestroyManagedObject();
479 return false;
482 // The destroyer will destroy() cinfo on exit. We don't want to set the
483 // destroyer's object until cinfo is initialized.
484 jpeg_create_decompress(&cinfo);
486 // set up the source manager
487 jpeg_source_mgr srcmgr;
488 srcmgr.init_source = InitSource;
489 srcmgr.fill_input_buffer = FillInputBuffer;
490 srcmgr.skip_input_data = SkipInputData;
491 srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine
492 srcmgr.term_source = TermSource;
493 cinfo.src = &srcmgr;
495 JpegDecoderState state(input, input_size);
496 cinfo.client_data = &state;
498 // fill the file metadata into our buffer
499 if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK)
500 return false;
502 // we want to always get RGB data out
503 switch (cinfo.jpeg_color_space) {
504 case JCS_GRAYSCALE:
505 case JCS_RGB:
506 case JCS_YCbCr:
507 #ifdef JCS_EXTENSIONS
508 // Choose an output colorspace and return if it is an unsupported one.
509 // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats
510 // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input
511 // parameters to a colorspace.
512 if (format == FORMAT_RGB) {
513 cinfo.out_color_space = JCS_RGB;
514 cinfo.output_components = 3;
515 } else if (format == FORMAT_RGBA ||
516 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
517 cinfo.out_color_space = JCS_EXT_RGBX;
518 cinfo.output_components = 4;
519 } else if (format == FORMAT_BGRA ||
520 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
521 cinfo.out_color_space = JCS_EXT_BGRX;
522 cinfo.output_components = 4;
523 } else {
524 // We can exit this function without calling jpeg_destroy_decompress()
525 // because DecompressDestroyer automaticaly calls it.
526 NOTREACHED() << "Invalid pixel format";
527 return false;
529 #else
530 cinfo.out_color_space = JCS_RGB;
531 #endif
532 break;
533 case JCS_CMYK:
534 case JCS_YCCK:
535 default:
536 // Mozilla errors out on these color spaces, so I presume that the jpeg
537 // library can't do automatic color space conversion for them. We don't
538 // care about these anyway.
539 return false;
541 #ifndef JCS_EXTENSIONS
542 cinfo.output_components = 3;
543 #endif
545 jpeg_calc_output_dimensions(&cinfo);
546 *w = cinfo.output_width;
547 *h = cinfo.output_height;
549 jpeg_start_decompress(&cinfo);
551 // FIXME(brettw) we may want to allow the capability for callers to request
552 // how to align row lengths as we do for the compressor.
553 int row_read_stride = cinfo.output_width * cinfo.output_components;
555 #ifdef JCS_EXTENSIONS
556 // Create memory for a decoded image and write decoded lines to the memory
557 // without conversions same as JPEGCodec::Encode().
558 int row_write_stride = row_read_stride;
559 output->resize(row_write_stride * cinfo.output_height);
561 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
562 unsigned char* rowptr = &(*output)[row * row_write_stride];
563 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
564 return false;
566 #else
567 if (format == FORMAT_RGB) {
568 // easy case, row needs no conversion
569 int row_write_stride = row_read_stride;
570 output->resize(row_write_stride * cinfo.output_height);
572 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
573 unsigned char* rowptr = &(*output)[row * row_write_stride];
574 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
575 return false;
577 } else {
578 // Rows need conversion to output format: read into a temporary buffer and
579 // expand to the final one. Performance: we could avoid the extra
580 // allocation by doing the expansion in-place.
581 int row_write_stride;
582 void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
583 if (format == FORMAT_RGBA ||
584 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
585 row_write_stride = cinfo.output_width * 4;
586 converter = AddAlpha;
587 } else if (format == FORMAT_BGRA ||
588 (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
589 row_write_stride = cinfo.output_width * 4;
590 converter = RGBtoBGRA;
591 } else {
592 NOTREACHED() << "Invalid pixel format";
593 jpeg_destroy_decompress(&cinfo);
594 return false;
597 output->resize(row_write_stride * cinfo.output_height);
599 scoped_ptr<unsigned char[]> row_data(new unsigned char[row_read_stride]);
600 unsigned char* rowptr = row_data.get();
601 for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
602 if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
603 return false;
604 converter(rowptr, *w, &(*output)[row * row_write_stride]);
607 #endif
609 jpeg_finish_decompress(&cinfo);
610 jpeg_destroy_decompress(&cinfo);
611 return true;
614 // static
615 SkBitmap* JPEGCodec::Decode(const unsigned char* input, size_t input_size) {
616 int w, h;
617 std::vector<unsigned char> data_vector;
618 if (!Decode(input, input_size, FORMAT_SkBitmap, &data_vector, &w, &h))
619 return NULL;
621 // Skia only handles 32 bit images.
622 int data_length = w * h * 4;
624 SkBitmap* bitmap = new SkBitmap();
625 bitmap->allocN32Pixels(w, h);
626 memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length);
628 return bitmap;
631 } // namespace gfx