| 1 | /* Modified for clg by esj */ |
| 2 | |
| 3 | /* Copyright 2005 Google Inc. All Rights Reserved. |
| 4 | * Author: yangzh@google.com (Zhonghao Yang) |
| 5 | * |
| 6 | * Google's own patch to add jpeg I/O support for cairo. |
| 7 | * |
| 8 | */ |
| 9 | |
| 10 | /* cairo - a vector graphics library with display and print output |
| 11 | * |
| 12 | * Copyright © 2003 University of Southern California |
| 13 | * |
| 14 | * This library is free software; you can redistribute it and/or |
| 15 | * modify it either under the terms of the GNU Lesser General Public |
| 16 | * License version 2.1 as published by the Free Software Foundation |
| 17 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
| 18 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
| 19 | * notice, a recipient may use your version of this file under either |
| 20 | * the MPL or the LGPL. |
| 21 | * |
| 22 | * You should have received a copy of the LGPL along with this library |
| 23 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
| 24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 25 | * You should have received a copy of the MPL along with this library |
| 26 | * in the file COPYING-MPL-1.1 |
| 27 | * |
| 28 | * The contents of this file are subject to the Mozilla Public License |
| 29 | * Version 1.1 (the "License"); you may not use this file except in |
| 30 | * compliance with the License. You may obtain a copy of the License at |
| 31 | * http://www.mozilla.org/MPL/ |
| 32 | * |
| 33 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
| 34 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
| 35 | * the specific language governing rights and limitations. |
| 36 | * |
| 37 | * The Original Code is the cairo graphics library. |
| 38 | * |
| 39 | * The Initial Developer of the Original Code is University of Southern |
| 40 | * California. |
| 41 | * |
| 42 | * Contributor(s): |
| 43 | * Carl D. Worth <cworth@cworth.org> |
| 44 | * Kristian Høgsberg <krh@redhat.com> |
| 45 | */ |
| 46 | |
| 47 | #include <stdio.h> |
| 48 | #include <stdint.h> |
| 49 | #include <string.h> |
| 50 | #include <stdlib.h> |
| 51 | #include <setjmp.h> |
| 52 | #include <errno.h> |
| 53 | #include <jpeglib.h> |
| 54 | #include <cairo.h> |
| 55 | #include "cairo-lut-private.h" |
| 56 | #include "cairo-prebuilt-lut.h" |
| 57 | |
| 58 | #define BUF_SIZE_JPEG 4096 |
| 59 | |
| 60 | struct clg_error_mgr { |
| 61 | struct jpeg_error_mgr pub; |
| 62 | jmp_buf setjmp_buffer; |
| 63 | }; |
| 64 | |
| 65 | typedef struct clg_error_mgr *error_ptr; |
| 66 | |
| 67 | static void |
| 68 | _error_exit (j_common_ptr cinfo) |
| 69 | { |
| 70 | error_ptr err = (error_ptr) cinfo->err; |
| 71 | |
| 72 | (*cinfo->err->output_message) (cinfo); |
| 73 | longjmp(err->setjmp_buffer, 1); |
| 74 | } |
| 75 | |
| 76 | |
| 77 | typedef unsigned int (*cairo_jpeg_read_func_t) |
| 78 | (void *closure, |
| 79 | unsigned char *data, |
| 80 | unsigned int length); |
| 81 | |
| 82 | struct read_closure { |
| 83 | /** |
| 84 | * Model underlying system's read behavior: |
| 85 | * data can be read from stdio, stream, or anything. |
| 86 | */ |
| 87 | cairo_jpeg_read_func_t jpeg_func; |
| 88 | |
| 89 | /** |
| 90 | * Auxiliary data: |
| 91 | * For stdio, this should be struct file_closure * |
| 92 | */ |
| 93 | void *user_data; |
| 94 | /* |
| 95 | * Buffer for reading from |
| 96 | */ |
| 97 | unsigned char block[BUF_SIZE_JPEG]; |
| 98 | |
| 99 | cairo_status_t status; |
| 100 | }; |
| 101 | |
| 102 | /* |
| 103 | * Read function for stdio. |
| 104 | * Check length of read and return error if necessary. |
| 105 | */ |
| 106 | static |
| 107 | unsigned int stdio_read_func (void *closure, |
| 108 | unsigned char *data, |
| 109 | unsigned int length) |
| 110 | { |
| 111 | return fread((void *)data, 1, length, (FILE *)closure); |
| 112 | } |
| 113 | |
| 114 | /* |
| 115 | * Initialize source. |
| 116 | * called by jpeg_read_header before any data is actually read. |
| 117 | */ |
| 118 | static void init_source_custom (j_decompress_ptr cinfo) |
| 119 | { |
| 120 | cinfo->src->bytes_in_buffer = 0; |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | * Fill the input buffer. |
| 125 | * called whenever buffer is emptied. |
| 126 | */ |
| 127 | static boolean fill_input_buffer_custom (j_decompress_ptr cinfo) |
| 128 | { |
| 129 | struct read_closure *closure = (struct read_closure*) (cinfo->client_data); |
| 130 | unsigned int len; |
| 131 | |
| 132 | len = (*closure->jpeg_func) (closure->user_data, |
| 133 | closure->block, BUF_SIZE_JPEG); |
| 134 | |
| 135 | if (len == 0) { |
| 136 | /* really hit EOF in the beginning: insert a fake EOI marker. */ |
| 137 | static const unsigned char jpeg_eof[] = { 0xFF, JPEG_EOI }; |
| 138 | cinfo->src->bytes_in_buffer = 2; |
| 139 | cinfo->src->next_input_byte = jpeg_eof; |
| 140 | } else { |
| 141 | cinfo->src->bytes_in_buffer = len; |
| 142 | cinfo->src->next_input_byte = closure->block; |
| 143 | } |
| 144 | return TRUE; |
| 145 | } |
| 146 | |
| 147 | |
| 148 | /* |
| 149 | * Skip data. |
| 150 | * used to skip over a potentially large amount of uninteresting |
| 151 | * data (such as APPn marker). |
| 152 | * Writers of suspendable-input applications must note that skip_input_data |
| 153 | * is not granted the right to give a suspension return. If the skip extends |
| 154 | * beyond the data currently in the buffer, the buffer can be marked empty so |
| 155 | * that the next read will cause a fill_input_buffer call that can suspend. |
| 156 | * Arranging for additional bytes to be discarded before reloading the input |
| 157 | * buffer is the application writer's problem. |
| 158 | */ |
| 159 | static void skip_input_data_custom (j_decompress_ptr cinfo, long num_bytes) |
| 160 | { |
| 161 | while (num_bytes > (long) (cinfo->src->bytes_in_buffer)) { |
| 162 | num_bytes -= (long) (cinfo->src->bytes_in_buffer); |
| 163 | fill_input_buffer_custom (cinfo); |
| 164 | } |
| 165 | |
| 166 | cinfo->src->next_input_byte += (size_t) num_bytes; |
| 167 | cinfo->src->bytes_in_buffer -= (size_t) num_bytes; |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | * Terminate source. |
| 172 | * called by jpeg_finish_decompress after all data has been read. |
| 173 | * Often a no-op. |
| 174 | */ |
| 175 | static void term_source_custom (j_decompress_ptr cinfo) |
| 176 | { |
| 177 | } |
| 178 | |
| 179 | /* |
| 180 | * read jpeg from any source. |
| 181 | */ |
| 182 | static cairo_surface_t * |
| 183 | read_jpeg (struct read_closure *closure) |
| 184 | { |
| 185 | cairo_surface_t *surface = NULL; |
| 186 | |
| 187 | struct jpeg_decompress_struct cinfo; |
| 188 | struct clg_error_mgr jerr; |
| 189 | struct jpeg_source_mgr src; |
| 190 | |
| 191 | int channels; |
| 192 | uint8_t *data; |
| 193 | int stride; |
| 194 | |
| 195 | JSAMPROW row = NULL; |
| 196 | JSAMPROW rowptr[1]; |
| 197 | |
| 198 | register JSAMPROW src_pixel; |
| 199 | register uint32_t *dst_pixel; |
| 200 | unsigned int i, j; |
| 201 | |
| 202 | closure->status = CAIRO_STATUS_SUCCESS; |
| 203 | |
| 204 | /* Step 1: Allocate and initialize a JPEG decompression object */ |
| 205 | /* Step 2: Specify the source of the compressed data (eg, a file) */ |
| 206 | memset (&cinfo, 0, sizeof (cinfo)); |
| 207 | memset (&jerr, 0, sizeof (jerr)); |
| 208 | |
| 209 | cinfo.err = jpeg_std_error (&jerr.pub); |
| 210 | jerr.pub.error_exit = _error_exit; |
| 211 | if (setjmp(jerr.setjmp_buffer)) |
| 212 | goto ERROR; |
| 213 | |
| 214 | jpeg_create_decompress (&cinfo); |
| 215 | |
| 216 | cinfo.client_data = closure; |
| 217 | |
| 218 | src.init_source = init_source_custom; |
| 219 | src.fill_input_buffer = fill_input_buffer_custom; |
| 220 | src.skip_input_data = skip_input_data_custom; |
| 221 | src.resync_to_restart = jpeg_resync_to_restart; /* JPEG library default */ |
| 222 | src.term_source = term_source_custom; |
| 223 | src.bytes_in_buffer = 0; /* forces fill_input_buffer on first read. */ |
| 224 | src.next_input_byte = NULL; /* until buffer loaded. */ |
| 225 | cinfo.src = &src; |
| 226 | |
| 227 | /* save the APP14 marker to check for Adobe Photoshop CMYK */ |
| 228 | /* files with inverted components. */ |
| 229 | jpeg_save_markers (&cinfo, JPEG_APP0 + 14, 256); |
| 230 | |
| 231 | /* Step 3: Call jpeg_read_header() to obtain image info. */ |
| 232 | if (jpeg_read_header (&cinfo, TRUE) != JPEG_HEADER_OK) |
| 233 | goto ERROR; |
| 234 | |
| 235 | /* if (cinfo.image_height > INT_MAX || cinfo.image_width > INT_MAX) */ |
| 236 | /* goto BAIL1; */ |
| 237 | |
| 238 | /* NOTE(yangzh): do not support these two yet. */ |
| 239 | if (cinfo.jpeg_color_space == JCS_CMYK || cinfo.jpeg_color_space == JCS_YCCK) |
| 240 | goto ERROR; |
| 241 | |
| 242 | cinfo.out_color_space = JCS_RGB; |
| 243 | |
| 244 | /* the fastest, but less accurate integer method for DCT. */ |
| 245 | /* not recommende if high quality is a concern. we do not have this issue. */ |
| 246 | cinfo.dct_method = JDCT_IFAST; |
| 247 | |
| 248 | /* Step 4: Set target cairo image. |
| 249 | * for CAIRO_FORMAT_ARGB32, every pixel will take 4 bytes. |
| 250 | */ |
| 251 | surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, |
| 252 | cinfo.image_width, |
| 253 | cinfo.image_height); |
| 254 | if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) { |
| 255 | closure->status = cairo_surface_status (surface); |
| 256 | goto ERROR; |
| 257 | } |
| 258 | |
| 259 | /* Step 5: jpeg_start_decompress(...); */ |
| 260 | jpeg_start_decompress (&cinfo); |
| 261 | |
| 262 | /* Step 6: while (scan lines remain to be read) */ |
| 263 | /* jpeg_read_scanlines(...); */ |
| 264 | channels = 3; |
| 265 | row = (JSAMPROW) malloc (cinfo.output_width * channels * sizeof (JSAMPLE)); |
| 266 | if (row == NULL) { |
| 267 | closure->status = CAIRO_STATUS_NO_MEMORY; |
| 268 | goto ERROR; |
| 269 | } |
| 270 | |
| 271 | data = cairo_image_surface_get_data (surface); |
| 272 | stride = cairo_image_surface_get_stride (surface); |
| 273 | |
| 274 | rowptr[0] = row; |
| 275 | for (i = 0; i < cinfo.output_height; i++) { |
| 276 | jpeg_read_scanlines (&cinfo, rowptr, 1); |
| 277 | src_pixel = row; |
| 278 | dst_pixel = (uint32_t*)(data + i * stride); |
| 279 | for (j = 0; j < cinfo.output_width; j++, src_pixel += channels, dst_pixel++) { |
| 280 | /* Store ARGB in native endian. */ |
| 281 | *dst_pixel = (0xFF << 24) /* always 0xFF in alpha channel. */ |
| 282 | + (src_pixel[0] << 16) |
| 283 | + (src_pixel[1] << 8) |
| 284 | + (src_pixel[2]); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /* Step 7: jpeg_finish_decompress(...); */ |
| 289 | jpeg_finish_decompress (&cinfo); |
| 290 | goto EXIT; |
| 291 | |
| 292 | ERROR: |
| 293 | if (surface) { |
| 294 | cairo_surface_destroy (surface); |
| 295 | surface = NULL; |
| 296 | } |
| 297 | if (closure->status == CAIRO_STATUS_SUCCESS) |
| 298 | closure->status = CAIRO_STATUS_READ_ERROR; |
| 299 | |
| 300 | EXIT: |
| 301 | /* Release other buffers used in this function. */ |
| 302 | if (row) |
| 303 | free (row); |
| 304 | |
| 305 | /* Step 8: Release the JPEG decompression object. */ |
| 306 | jpeg_destroy_decompress (&cinfo); |
| 307 | |
| 308 | return surface; |
| 309 | } |
| 310 | |
| 311 | cairo_surface_t * |
| 312 | cairo_image_surface_create_from_jpeg (const char *filename, |
| 313 | cairo_status_t *status) |
| 314 | { |
| 315 | struct read_closure jpeg_closure; |
| 316 | FILE *fp; |
| 317 | cairo_surface_t *surface; |
| 318 | |
| 319 | fp = fopen (filename, "rb"); |
| 320 | if (fp == NULL) { |
| 321 | switch (errno) { |
| 322 | case ENOMEM: |
| 323 | *status = CAIRO_STATUS_NO_MEMORY; |
| 324 | return NULL; |
| 325 | case ENOENT: |
| 326 | *status = CAIRO_STATUS_FILE_NOT_FOUND; |
| 327 | return NULL; |
| 328 | default: |
| 329 | *status = CAIRO_STATUS_READ_ERROR; |
| 330 | return NULL; |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | jpeg_closure.jpeg_func = stdio_read_func; |
| 335 | jpeg_closure.user_data = (void*) fp; |
| 336 | |
| 337 | surface = read_jpeg (&jpeg_closure); |
| 338 | fclose (fp); |
| 339 | |
| 340 | *status = jpeg_closure.status; |
| 341 | return surface; |
| 342 | } |
| 343 | |
| 344 | cairo_surface_t * |
| 345 | cairo_image_surface_create_from_jpeg_stream (cairo_jpeg_read_func_t read_func, |
| 346 | void *closure, |
| 347 | cairo_status_t *status) |
| 348 | { |
| 349 | struct read_closure jpeg_closure; |
| 350 | jpeg_closure.jpeg_func = read_func; |
| 351 | jpeg_closure.user_data = closure; |
| 352 | |
| 353 | cairo_surface_t *surface = read_jpeg (&jpeg_closure); |
| 354 | |
| 355 | *status = jpeg_closure.status; |
| 356 | return surface; |
| 357 | } |
| 358 | |
| 359 | |
| 360 | typedef struct _cairo_jpeg_parameter { |
| 361 | int quality; |
| 362 | cairo_bool_t interlace; |
| 363 | } cairo_jpeg_parameter_t; |
| 364 | |
| 365 | void cairo_get_default_jpeg_parameter (cairo_jpeg_parameter_t *param); |
| 366 | |
| 367 | typedef unsigned int (*cairo_jpeg_write_func_t) |
| 368 | (void *closure, |
| 369 | const unsigned char *data, |
| 370 | unsigned int length); |
| 371 | |
| 372 | struct write_closure { |
| 373 | cairo_jpeg_write_func_t jpeg_func; |
| 374 | |
| 375 | /** |
| 376 | * Auxiliary data: |
| 377 | * For stdio, this should be struct file_closure * |
| 378 | */ |
| 379 | void *user_data; |
| 380 | /* |
| 381 | * Buffer for writing to. |
| 382 | */ |
| 383 | unsigned char block[BUF_SIZE_JPEG]; |
| 384 | }; |
| 385 | |
| 386 | /* |
| 387 | * write func for stdio. |
| 388 | * since closure_data->byte_written contains enough diagnostic information, |
| 389 | * we always return SUCCESS here. |
| 390 | */ |
| 391 | static |
| 392 | cairo_status_t stdio_write_func (void *closure, |
| 393 | const unsigned char *data, |
| 394 | unsigned int length) |
| 395 | { |
| 396 | int bytes_written; |
| 397 | do { |
| 398 | bytes_written = fwrite(data, 1, length, (FILE *)closure); |
| 399 | data += bytes_written; |
| 400 | length -= bytes_written; |
| 401 | } while (bytes_written > 0 && length > 0); |
| 402 | |
| 403 | return CAIRO_STATUS_SUCCESS; |
| 404 | } |
| 405 | |
| 406 | /* |
| 407 | * Initialize destination. |
| 408 | * called by jpeg_start_compress before any data is actually written. |
| 409 | */ |
| 410 | static void init_destination_jpeg_custom (j_compress_ptr cinfo) |
| 411 | { |
| 412 | struct write_closure *closure = (struct write_closure*) (cinfo->client_data); |
| 413 | cinfo->dest->next_output_byte = closure->block; |
| 414 | cinfo->dest->free_in_buffer = BUF_SIZE_JPEG; |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | * Empty buffer. |
| 419 | * called whenever buffer fills up. |
| 420 | * In typical applications, this should write the entire output buffer |
| 421 | * (ignoring the current state of next_output_byte & free_in_buffer), |
| 422 | * reset the pointer & count to the start of the buffer, and return TRUE |
| 423 | * indicating that the buffer has been dumped. |
| 424 | * |
| 425 | * In applications that need to be able to suspend compression due to output |
| 426 | * overrun, a FALSE return indicates that the buffer cannot be emptied now. |
| 427 | * In this situation, the compressor will return to its caller (possibly with |
| 428 | * an indication that it has not accepted all the supplied scanlines). The |
| 429 | * application should resume compression after it has made more room in the |
| 430 | * output buffer. Note that there are substantial restrictions on the use of |
| 431 | * suspension --- see the documentation. |
| 432 | * |
| 433 | * When suspending, the compressor will back up to a convenient restart point |
| 434 | * (typically the start of the current MCU). next_output_byte & free_in_buffer |
| 435 | * indicate where the restart point will be if the current call returns FALSE. |
| 436 | * Data beyond this point will be regenerated after resumption, so do not |
| 437 | * write it out when emptying the buffer externally. |
| 438 | */ |
| 439 | static boolean empty_output_buffer_jpeg_custom (j_compress_ptr cinfo) |
| 440 | { |
| 441 | struct write_closure *closure = (struct write_closure*) (cinfo->client_data); |
| 442 | int len; |
| 443 | len = (*closure->jpeg_func) (closure->user_data, |
| 444 | closure->block, BUF_SIZE_JPEG); |
| 445 | if (len != BUF_SIZE_JPEG) { |
| 446 | /* seems not all remaining bytes in buffer has been dumped yet. */ |
| 447 | /* by return FALSE, we reply on JPEG's error handling machnism? */ |
| 448 | return FALSE; |
| 449 | } |
| 450 | cinfo->dest->next_output_byte = closure->block; |
| 451 | cinfo->dest->free_in_buffer = BUF_SIZE_JPEG; |
| 452 | return TRUE; |
| 453 | } |
| 454 | |
| 455 | /* |
| 456 | * Terminate destination. |
| 457 | * called by jpeg_finish_compress afer all data has been written. |
| 458 | * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding |
| 459 | * application must deal with any cleanup that should happen even |
| 460 | * for error exit. |
| 461 | */ |
| 462 | static void term_destination_jpeg_custom (j_compress_ptr cinfo) |
| 463 | { |
| 464 | struct write_closure *closure = (struct write_closure*) (cinfo->client_data); |
| 465 | unsigned int len; |
| 466 | len = BUF_SIZE_JPEG - cinfo->dest->free_in_buffer; |
| 467 | if (len > 0) |
| 468 | (*closure->jpeg_func) (closure->user_data, closure->block, len); |
| 469 | } |
| 470 | |
| 471 | static cairo_status_t |
| 472 | write_jpeg (cairo_surface_t *surface, void* closure, |
| 473 | const cairo_jpeg_parameter_t *parameter) |
| 474 | { |
| 475 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
| 476 | |
| 477 | struct jpeg_compress_struct cinfo; |
| 478 | struct clg_error_mgr jerr; |
| 479 | struct jpeg_destination_mgr dest; |
| 480 | cairo_jpeg_parameter_t *param, default_parameters; |
| 481 | |
| 482 | JSAMPROW row = NULL; |
| 483 | JSAMPROW rowptr[1]; |
| 484 | |
| 485 | register uint32_t *src_pixel; |
| 486 | register uint8_t *src_pixel_gray; |
| 487 | register JSAMPROW dst_pixel; |
| 488 | unsigned int i, j; |
| 489 | int convert_alpha; |
| 490 | |
| 491 | param = (cairo_jpeg_parameter_t *)parameter; |
| 492 | if (param == NULL) { |
| 493 | param = &default_parameters; |
| 494 | cairo_get_default_jpeg_parameter(param); |
| 495 | } |
| 496 | |
| 497 | |
| 498 | /* Step 1: Allocate and initialize a JPEG compression object. */ |
| 499 | memset (&cinfo, 0, sizeof (cinfo)); |
| 500 | memset (&jerr, 0, sizeof (jerr)); |
| 501 | |
| 502 | cinfo.err = jpeg_std_error (&jerr.pub); |
| 503 | jerr.pub.error_exit = _error_exit; |
| 504 | if (setjmp(jerr.setjmp_buffer)) |
| 505 | goto BAIL; |
| 506 | |
| 507 | jpeg_create_compress (&cinfo); |
| 508 | |
| 509 | /* Step 2: Specify the destination for the compressed data. */ |
| 510 | cinfo.client_data = closure; |
| 511 | |
| 512 | dest.init_destination = init_destination_jpeg_custom; |
| 513 | dest.empty_output_buffer = empty_output_buffer_jpeg_custom; |
| 514 | dest.term_destination = term_destination_jpeg_custom; |
| 515 | dest.next_output_byte = NULL; |
| 516 | dest.free_in_buffer = 0; |
| 517 | cinfo.dest = &dest; |
| 518 | |
| 519 | /* Step 3: Set parameters for compression, image size & colorspace, etc. */ |
| 520 | cinfo.image_width = cairo_image_surface_get_width (surface); |
| 521 | cinfo.image_height = cairo_image_surface_get_height (surface); |
| 522 | convert_alpha = 0; |
| 523 | |
| 524 | switch (cairo_image_surface_get_format (surface)) { |
| 525 | case CAIRO_FORMAT_RGB24: |
| 526 | cinfo.input_components = 3; /* # of color components per pixel */ |
| 527 | cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ |
| 528 | break; |
| 529 | case CAIRO_FORMAT_ARGB32: |
| 530 | cinfo.input_components = 3; /* # of color components per pixel */ |
| 531 | cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ |
| 532 | convert_alpha = 1; |
| 533 | break; |
| 534 | case CAIRO_FORMAT_A8: /* Put A channel into a grayscale JPEG image. */ |
| 535 | case CAIRO_FORMAT_A1: /* Put A channel into a "binary" JPEG image. */ |
| 536 | cinfo.input_components = 1; |
| 537 | cinfo.in_color_space = JCS_GRAYSCALE; |
| 538 | default: |
| 539 | status = CAIRO_STATUS_NULL_POINTER; |
| 540 | goto BAIL; |
| 541 | } |
| 542 | |
| 543 | jpeg_set_defaults (&cinfo); /* set compression parameters all default values. */ |
| 544 | |
| 545 | /* the fastest, but less accurate integer method for DCT. */ |
| 546 | /* not recommende if high quality is a concern. we do not have this issue. */ |
| 547 | cinfo.dct_method = JDCT_IFAST; |
| 548 | |
| 549 | if (param->quality >= 0) |
| 550 | jpeg_set_quality(&cinfo, param->quality, TRUE); |
| 551 | |
| 552 | /* If user requests interlace, translate that to progressive JPEG */ |
| 553 | if (param->interlace) |
| 554 | jpeg_simple_progression(&cinfo); |
| 555 | |
| 556 | row = (JSAMPROW) malloc (cinfo.image_width * cinfo.input_components |
| 557 | * sizeof (JSAMPLE)); |
| 558 | if (row == NULL) { |
| 559 | status = CAIRO_STATUS_NO_MEMORY; |
| 560 | goto BAIL; |
| 561 | } |
| 562 | |
| 563 | rowptr[0] = row; |
| 564 | |
| 565 | /* Step 4: jpeg_start_compress(...); */ |
| 566 | jpeg_start_compress (&cinfo, TRUE); |
| 567 | |
| 568 | /* Step 5: while (scan lines remain to be written) */ |
| 569 | /* jpeg_write_scanlines(...); */ |
| 570 | uint8_t *data = cairo_image_surface_get_data (surface); |
| 571 | int stride = cairo_image_surface_get_stride (surface); |
| 572 | |
| 573 | if (cinfo.input_components == 3) { /* truecolor JPEG. */ |
| 574 | for (i = 0; i < cinfo.image_height; i++) { |
| 575 | src_pixel = (uint32_t *) (data + i * stride); |
| 576 | dst_pixel = row; |
| 577 | /* |
| 578 | * If the pixels have an alpha channel, convert |
| 579 | * the pixels to normal RGB |
| 580 | */ |
| 581 | if (convert_alpha) { |
| 582 | for (j = 0; j < cinfo.image_width; j++, dst_pixel += 3) { |
| 583 | uint8_t r, g, b; |
| 584 | cairo_to_rgb(*src_pixel++, &r, &g, &b); |
| 585 | dst_pixel[0] = r; |
| 586 | dst_pixel[1] = g; |
| 587 | dst_pixel[2] = b; |
| 588 | } |
| 589 | } else { |
| 590 | for (j = 0; j < cinfo.image_width; j++, dst_pixel += 3) { |
| 591 | unsigned int pix; |
| 592 | pix = *src_pixel++; |
| 593 | dst_pixel[0] = _get_red(pix); |
| 594 | dst_pixel[1] = _get_green(pix); |
| 595 | dst_pixel[2] = _get_blue(pix); |
| 596 | } |
| 597 | } |
| 598 | jpeg_write_scanlines (&cinfo, rowptr, 1); |
| 599 | } |
| 600 | } else { /* write to a grayscale JPEG. */ |
| 601 | src_pixel_gray = (uint8_t *) data; |
| 602 | for (i = 0; i < cinfo.image_height; i++) { |
| 603 | memcpy (row, src_pixel_gray, cinfo.image_width); |
| 604 | src_pixel_gray += stride; |
| 605 | jpeg_write_scanlines (&cinfo, rowptr, 1); |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | /* Step 6: jpeg_finish_compress(...); */ |
| 610 | jpeg_finish_compress (&cinfo); |
| 611 | |
| 612 | BAIL: |
| 613 | if (row) |
| 614 | free (row); |
| 615 | |
| 616 | /* Step 7: Release the JPEG compression object. */ |
| 617 | jpeg_destroy_compress (&cinfo); |
| 618 | |
| 619 | return status; |
| 620 | } |
| 621 | |
| 622 | void |
| 623 | cairo_get_default_jpeg_parameter (cairo_jpeg_parameter_t *param) |
| 624 | { |
| 625 | param->quality = 75; |
| 626 | param->interlace = TRUE; |
| 627 | } |
| 628 | |
| 629 | cairo_status_t |
| 630 | cairo_surface_write_to_jpeg (cairo_surface_t *surface, |
| 631 | const char *filename, |
| 632 | const cairo_jpeg_parameter_t *parameter) |
| 633 | { |
| 634 | FILE *fp; |
| 635 | struct write_closure jpeg_closure; |
| 636 | cairo_status_t status; |
| 637 | |
| 638 | fp = fopen (filename, "wb"); |
| 639 | if (fp == NULL) |
| 640 | return CAIRO_STATUS_WRITE_ERROR; |
| 641 | |
| 642 | jpeg_closure.jpeg_func = stdio_write_func; |
| 643 | jpeg_closure.user_data = fp; |
| 644 | |
| 645 | status = write_jpeg (surface, &jpeg_closure, parameter); |
| 646 | if (fclose(fp) < 0) |
| 647 | status = CAIRO_STATUS_WRITE_ERROR; |
| 648 | |
| 649 | return status; |
| 650 | } |
| 651 | |
| 652 | cairo_status_t |
| 653 | cairo_surface_write_to_jpeg_stream (cairo_surface_t *surface, |
| 654 | cairo_jpeg_write_func_t write_func, |
| 655 | void *closure, |
| 656 | const cairo_jpeg_parameter_t *parameter) |
| 657 | { |
| 658 | struct write_closure jpeg_closure; |
| 659 | jpeg_closure.jpeg_func = write_func; |
| 660 | jpeg_closure.user_data = closure; |
| 661 | |
| 662 | return write_jpeg (surface, &jpeg_closure, parameter); |
| 663 | } |