static void prepared_callback (GdkPixbufLoader *loader, gpointer data) { AniLoaderContext *context = (AniLoaderContext*)data; #ifdef DEBUG_ANI g_print ("%d pixbuf prepared\n", context->pos); #endif GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); if (!pixbuf) return; if (gdk_pixbuf_get_width (pixbuf) > context->animation->width) context->animation->width = gdk_pixbuf_get_width (pixbuf); if (gdk_pixbuf_get_height (pixbuf) > context->animation->height) context->animation->height = gdk_pixbuf_get_height (pixbuf); if (context->title != NULL) gdk_pixbuf_set_option (pixbuf, "Title", context->title); if (context->author != NULL) gdk_pixbuf_set_option (pixbuf, "Author", context->author); g_object_ref (pixbuf); context->animation->pixbufs[context->pos] = pixbuf; if (context->pos == 0) { if (context->prepared_func) (* context->prepared_func) (pixbuf, GDK_PIXBUF_ANIMATION (context->animation), context->user_data); } else { /* FIXME - this is necessary for nice display of loading animations because GtkImage ignores gdk_pixbuf_animation_iter_on_currently_loading_frame() and always exposes the full frame */ GdkPixbuf *last = context->animation->pixbufs[context->pos - 1]; gint width = MIN (gdk_pixbuf_get_width (last), gdk_pixbuf_get_width (pixbuf)); gint height = MIN (gdk_pixbuf_get_height (last), gdk_pixbuf_get_height (pixbuf)); gdk_pixbuf_copy_area (last, 0, 0, width, height, pixbuf, 0, 0); } context->pos++; }
/** * gdk_cursor_get_image: * @cursor: a #GdkCursor * * Returns a #GdkPixbuf with the image used to display the cursor. * * Note that depending on the capabilities of the windowing system and * on the cursor, GDK may not be able to obtain the image data. In this * case, %NULL is returned. * * Returns: (transfer full): a #GdkPixbuf representing @cursor, or %NULL * * Since: 2.8 */ GdkPixbuf* gdk_cursor_get_image (GdkCursor *cursor) { int w, h; cairo_surface_t *surface; GdkPixbuf *pixbuf; gchar buf[32]; double x_hot, y_hot; double x_scale, y_scale; g_return_val_if_fail (GDK_IS_CURSOR (cursor), NULL); surface = gdk_cursor_get_surface (cursor, &x_hot, &y_hot); if (surface == NULL) return NULL; w = cairo_image_surface_get_width (surface); h = cairo_image_surface_get_height (surface); x_scale = y_scale = 1; #ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE cairo_surface_get_device_scale (surface, &x_scale, &y_scale); #endif pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, w, h); cairo_surface_destroy (surface); if (x_scale != 1) { GdkPixbuf *old; old = pixbuf; pixbuf = gdk_pixbuf_scale_simple (old, w / x_scale, h / y_scale, GDK_INTERP_HYPER); g_object_unref (old); } g_snprintf (buf, 32, "%d", (int)x_hot); gdk_pixbuf_set_option (pixbuf, "x_hot", buf); g_snprintf (buf, 32, "%d", (int)y_hot); gdk_pixbuf_set_option (pixbuf, "y_hot", buf); return pixbuf; }
GdkPixbuf * file_to_pixbuf (const char *path, guint destination_size, GError **error) { GdkPixbuf *pixbuf, *tmp_pixbuf; GFile *file; char *uri; int original_width, original_height; file = g_file_new_for_path (path); uri = g_file_get_uri (file); pixbuf = _gdk_pixbuf_new_from_uri_at_scale (uri, destination_size, error); if (pixbuf == NULL) return NULL; tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf); gdk_pixbuf_copy_options (pixbuf, tmp_pixbuf); gdk_pixbuf_remove_option (tmp_pixbuf, "orientation"); g_object_unref (pixbuf); pixbuf = tmp_pixbuf; original_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf), "gnome-original-width")); original_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf), "gnome-original-height")); if (original_width > 0 && original_height > 0) { char *tmp; tmp = g_strdup_printf ("%d", original_width); gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", tmp); g_free (tmp); tmp = g_strdup_printf ("%d", original_height); gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", tmp); g_free (tmp); } return pixbuf; }
/* This function does all the work. */ static GdkPixbuf * pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle, GError **error) { gint w, h, n_col, cpp, x_hot, y_hot, items; gint cnt, xcnt, ycnt, wbytes, n; gint is_trans = FALSE; const gchar *buffer; gchar *name_buf; gchar pixel_str[32]; GHashTable *color_hash; XPMColor *colors, *color, *fallbackcolor; guchar *pixtmp; GdkPixbuf *pixbuf; fallbackcolor = NULL; buffer = (*get_buf) (op_header, handle); if (!buffer) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("No XPM header found")); return NULL; } items = sscanf (buffer, "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot); if (items != 4 && items != 6) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Invalid XPM header")); return NULL; } if (w <= 0) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("XPM file has image width <= 0")); return NULL; } if (h <= 0) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("XPM file has image height <= 0")); return NULL; } if (cpp <= 0 || cpp >= 32) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("XPM has invalid number of chars per pixel")); return NULL; } if (n_col <= 0 || n_col >= G_MAXINT / (cpp + 1) || n_col >= G_MAXINT / sizeof (XPMColor)) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("XPM file has invalid number of colors")); return NULL; } /* The hash is used for fast lookups of color from chars */ color_hash = g_hash_table_new (g_str_hash, g_str_equal); name_buf = g_try_malloc (n_col * (cpp + 1)); if (!name_buf) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Cannot allocate memory for loading XPM image")); g_hash_table_destroy (color_hash); return NULL; } colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col); if (!colors) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Cannot allocate memory for loading XPM image")); g_hash_table_destroy (color_hash); g_free (name_buf); return NULL; } for (cnt = 0; cnt < n_col; cnt++) { gchar *color_name; buffer = (*get_buf) (op_cmap, handle); if (!buffer) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Cannot read XPM colormap")); g_hash_table_destroy (color_hash); g_free (name_buf); g_free (colors); return NULL; } color = &colors[cnt]; color->color_string = &name_buf[cnt * (cpp + 1)]; strncpy (color->color_string, buffer, cpp); color->color_string[cpp] = 0; buffer += strlen (color->color_string); color->transparent = FALSE; color_name = xpm_extract_color (buffer); if ((color_name == NULL) || (g_ascii_strcasecmp (color_name, "None") == 0) || (parse_color (color_name, color) == FALSE)) { color->transparent = TRUE; color->red = 0; color->green = 0; color->blue = 0; is_trans = TRUE; } g_free (color_name); g_hash_table_insert (color_hash, color->color_string, color); if (cnt == 0) fallbackcolor = color; } pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, is_trans, 8, w, h); if (!pixbuf) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Cannot allocate memory for loading XPM image")); g_hash_table_destroy (color_hash); g_free (colors); g_free (name_buf); return NULL; } wbytes = w * cpp; for (ycnt = 0; ycnt < h; ycnt++) { pixtmp = pixbuf->pixels + ycnt * pixbuf->rowstride; buffer = (*get_buf) (op_body, handle); if ((!buffer) || (strlen (buffer) < wbytes)) continue; for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) { strncpy (pixel_str, &buffer[n], cpp); pixel_str[cpp] = 0; color = g_hash_table_lookup (color_hash, pixel_str); /* Bad XPM...punt */ if (!color) color = fallbackcolor; *pixtmp++ = color->red >> 8; *pixtmp++ = color->green >> 8; *pixtmp++ = color->blue >> 8; if (is_trans && color->transparent) *pixtmp++ = 0; else if (is_trans) *pixtmp++ = 0xFF; } } g_hash_table_destroy (color_hash); g_free (colors); g_free (name_buf); if (items == 6) { gchar hot[10]; g_snprintf (hot, 10, "%d", x_hot); gdk_pixbuf_set_option (pixbuf, "x_hot", hot); g_snprintf (hot, 10, "%d", y_hot); gdk_pixbuf_set_option (pixbuf, "y_hot", hot); } return pixbuf; }
static GdkPixbuf * tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error) { guchar *pixels = NULL; gint width, height, rowstride, bytes; GdkPixbuf *pixbuf; guint16 bits_per_sample = 0; uint16 orientation = 0; uint16 transform = 0; uint16 codec; gchar *icc_profile_base64; const gchar *icc_profile; guint icc_profile_size; uint16 resolution_unit; gchar *density_str; gint retval; /* We're called with the lock held. */ if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width)) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Could not get image width (bad TIFF file)")); return NULL; } if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height)) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Could not get image height (bad TIFF file)")); return NULL; } if (width <= 0 || height <= 0) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Width or height of TIFF image is zero")); return NULL; } rowstride = width * 4; if (rowstride / 4 != width) { /* overflow */ g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Dimensions of TIFF image too large")); return NULL; } bytes = height * rowstride; if (bytes / rowstride != height) { /* overflow */ g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Dimensions of TIFF image too large")); return NULL; } if (context && context->size_func) { gint w = width; gint h = height; (* context->size_func) (&w, &h, context->user_data); /* This is a signal that this function is being called to support gdk_pixbuf_get_file_info, so we can stop parsing the tiff file at this point. It is not an error condition. */ if (w == 0 || h == 0) return NULL; } pixels = g_try_malloc (bytes); if (!pixels) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to open TIFF file")); return NULL; } pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, width, height, rowstride, free_buffer, NULL); if (!pixbuf) { g_free (pixels); g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to open TIFF file")); return NULL; } /* Save the bits per sample as an option since pixbufs are expected to be always 8 bits per sample. */ TIFFGetField (tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); if (bits_per_sample > 0) { gchar str[5]; g_snprintf (str, sizeof (str), "%d", bits_per_sample); gdk_pixbuf_set_option (pixbuf, "bits-per-sample", str); } /* Set the "orientation" key associated with this image. libtiff orientation handling is odd, so further processing is required by higher-level functions based on this tag. If the embedded orientation tag is 1-4, libtiff flips/mirrors the image as required, and no client processing is required - so we report no orientation. Orientations 5-8 require rotations which would swap the width and height of the image. libtiff does not do this. Instead it interprets orientations 5-8 the same as 1-4. See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548. To correct for this, the client must apply the transform normally used for orientation 5 to both orientations 5 and 7, and apply the transform normally used for orientation 7 for both orientations 6 and 8. Then everythings works out OK! */ TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation); switch (orientation) { case 5: case 7: transform = 5; break; case 6: case 8: transform = 7; break; default: transform = 0; break; } if (transform > 0 ) { gchar str[5]; g_snprintf (str, sizeof (str), "%d", transform); gdk_pixbuf_set_option (pixbuf, "orientation", str); } TIFFGetField (tiff, TIFFTAG_COMPRESSION, &codec); if (codec > 0) { gchar str[5]; g_snprintf (str, sizeof (str), "%d", codec); gdk_pixbuf_set_option (pixbuf, "compression", str); } /* Extract embedded ICC profile */ retval = TIFFGetField (tiff, TIFFTAG_ICCPROFILE, &icc_profile_size, &icc_profile); if (retval == 1) { icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, icc_profile_size); gdk_pixbuf_set_option (pixbuf, "icc-profile", icc_profile_base64); g_free (icc_profile_base64); } retval = TIFFGetField (tiff, TIFFTAG_RESOLUTIONUNIT, &resolution_unit); if (retval == 1) { float x_resolution = 0, y_resolution = 0; TIFFGetField (tiff, TIFFTAG_XRESOLUTION, &x_resolution); TIFFGetField (tiff, TIFFTAG_YRESOLUTION, &y_resolution); switch (resolution_unit) { case RESUNIT_INCH: density_str = g_strdup_printf ("%d", (int) round (x_resolution)); gdk_pixbuf_set_option (pixbuf, "x-dpi", density_str); g_free (density_str); density_str = g_strdup_printf ("%d", (int) round (y_resolution)); gdk_pixbuf_set_option (pixbuf, "y-dpi", density_str); g_free (density_str); break; case RESUNIT_CENTIMETER: density_str = g_strdup_printf ("%d", DPCM_TO_DPI (x_resolution)); gdk_pixbuf_set_option (pixbuf, "x-dpi", density_str); g_free (density_str); density_str = g_strdup_printf ("%d", DPCM_TO_DPI (y_resolution)); gdk_pixbuf_set_option (pixbuf, "y-dpi", density_str); g_free (density_str); break; } } if (context && context->prepare_func) (* context->prepare_func) (pixbuf, NULL, context->user_data); if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1)) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Failed to load RGB data from TIFF file")); g_object_unref (pixbuf); return NULL; } /* Flag multi-page documents, because this loader only handles the first page. The client app may wish to warn the user. */ if (TIFFReadDirectory (tiff)) gdk_pixbuf_set_option (pixbuf, "multipage", "yes"); #if G_BYTE_ORDER == G_BIG_ENDIAN /* Turns out that the packing used by TIFFRGBAImage depends on * the host byte order... */ while (pixels < pixbuf->pixels + bytes) { uint32 pixel = *(uint32 *)pixels; int r = TIFFGetR(pixel); int g = TIFFGetG(pixel); int b = TIFFGetB(pixel); int a = TIFFGetA(pixel); *pixels++ = r; *pixels++ = g; *pixels++ = b; *pixels++ = a; } #endif if (context && context->update_func) (* context->update_func) (pixbuf, 0, 0, width, height, context->user_data); return pixbuf; }
/* * context - from image_begin_load * buf - new image data * size - length of new image data * * append image data onto inrecrementally built output image */ static gboolean gdk_pixbuf__jpeg_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error) { JpegProgContext *context = (JpegProgContext *)data; struct jpeg_decompress_struct *cinfo; my_src_ptr src; guint num_left, num_copy; guint last_num_left, last_bytes_left; guint spinguard; gboolean first; const guchar *bufhd; gint width, height; int is_otag; char otag_str[5]; g_return_val_if_fail (context != NULL, FALSE); g_return_val_if_fail (buf != NULL, FALSE); src = (my_src_ptr) context->cinfo.src; cinfo = &context->cinfo; context->jerr.error = error; /* check for fatal error */ if (sigsetjmp (context->jerr.setjmp_buffer, 1)) { return FALSE; } /* skip over data if requested, handle unsigned int sizes cleanly */ /* only can happen if we've already called jpeg_get_header once */ if (context->src_initialized && src->skip_next) { if (src->skip_next > size) { src->skip_next -= size; return TRUE; } else { num_left = size - src->skip_next; bufhd = buf + src->skip_next; src->skip_next = 0; } } else { num_left = size; bufhd = buf; } if (num_left == 0) return TRUE; last_num_left = num_left; last_bytes_left = 0; spinguard = 0; first = TRUE; while (TRUE) { /* handle any data from caller we haven't processed yet */ if (num_left > 0) { if(src->pub.bytes_in_buffer && src->pub.next_input_byte != src->buffer) memmove(src->buffer, src->pub.next_input_byte, src->pub.bytes_in_buffer); num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer, num_left); memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy); src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer += num_copy; bufhd += num_copy; num_left -= num_copy; } /* did anything change from last pass, if not return */ if (first) { last_bytes_left = src->pub.bytes_in_buffer; first = FALSE; } else if (src->pub.bytes_in_buffer == last_bytes_left && num_left == last_num_left) { spinguard++; } else { last_bytes_left = src->pub.bytes_in_buffer; last_num_left = num_left; } /* should not go through twice and not pull bytes out of buf */ if (spinguard > 2) return TRUE; /* try to load jpeg header */ if (!context->got_header) { int rc; jpeg_save_markers (cinfo, EXIF_JPEG_MARKER, 0xffff); rc = jpeg_read_header (cinfo, TRUE); context->src_initialized = TRUE; if (rc == JPEG_SUSPENDED) continue; context->got_header = TRUE; /* check for orientation tag */ is_otag = get_orientation (cinfo); width = cinfo->image_width; height = cinfo->image_height; if (context->size_func) { (* context->size_func) (&width, &height, context->user_data); if (width == 0 || height == 0) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Transformed JPEG has zero width or height.")); return FALSE; } } cinfo->scale_num = 1; for (cinfo->scale_denom = 2; cinfo->scale_denom <= 8; cinfo->scale_denom *= 2) { jpeg_calc_output_dimensions (cinfo); if (cinfo->output_width < width || cinfo->output_height < height) { cinfo->scale_denom /= 2; break; } } jpeg_calc_output_dimensions (cinfo); context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo->output_components == 4 ? TRUE : FALSE, 8, cinfo->output_width, cinfo->output_height); if (context->pixbuf == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for loading JPEG file")); return FALSE; } /* if orientation tag was found set an option to remember its value */ if (is_otag) { g_snprintf (otag_str, sizeof (otag_str), "%d", is_otag); gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str); } /* Use pixbuf buffer to store decompressed data */ context->dptr = context->pixbuf->pixels; /* Notify the client that we are ready to go */ if (context->prepared_func) (* context->prepared_func) (context->pixbuf, NULL, context->user_data); } else if (!context->did_prescan) { int rc; /* start decompression */ cinfo->buffered_image = cinfo->progressive_mode; rc = jpeg_start_decompress (cinfo); cinfo->do_fancy_upsampling = FALSE; cinfo->do_block_smoothing = FALSE; if (rc == JPEG_SUSPENDED) continue; context->did_prescan = TRUE; } else if (!cinfo->buffered_image) { /* we're decompressing unbuffered so * simply get scanline by scanline from jpeg lib */ if (! gdk_pixbuf__jpeg_image_load_lines (context, error)) return FALSE; if (cinfo->output_scanline >= cinfo->output_height) return TRUE; } else { /* we're decompressing buffered (progressive) * so feed jpeg lib scanlines */ /* keep going until we've done all passes */ while (!jpeg_input_complete (cinfo)) { if (!context->in_output) { if (jpeg_start_output (cinfo, cinfo->input_scan_number)) { context->in_output = TRUE; context->dptr = context->pixbuf->pixels; } else break; } /* get scanlines from jpeg lib */ if (! gdk_pixbuf__jpeg_image_load_lines (context, error)) return FALSE; if (cinfo->output_scanline >= cinfo->output_height && jpeg_finish_output (cinfo)) context->in_output = FALSE; else break; } if (jpeg_input_complete (cinfo)) /* did entire image */ return TRUE; else continue; } } }
/* Shared library entry point */ static GdkPixbuf * gdk_pixbuf__jpeg_image_load (FILE *f, GError **error) { gint i; int is_otag; char otag_str[5]; GdkPixbuf * volatile pixbuf = NULL; guchar *dptr; guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, * from the header file: * " Usually rec_outbuf_height will be 1 or 2, * at most 4." */ guchar **lptr; struct jpeg_decompress_struct cinfo; struct error_handler_data jerr; stdio_src_ptr src; /* setup error handler */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = fatal_error_handler; jerr.pub.output_message = output_message_handler; jerr.error = error; if (sigsetjmp (jerr.setjmp_buffer, 1)) { /* Whoops there was a jpeg error */ if (pixbuf) g_object_unref (pixbuf); jpeg_destroy_decompress (&cinfo); /* error should have been set by fatal_error_handler () */ return NULL; } /* load header, setup */ jpeg_create_decompress (&cinfo); cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, sizeof (stdio_source_mgr)); src = (stdio_src_ptr) cinfo.src; src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, JPEG_PROG_BUF_SIZE * sizeof (JOCTET)); src->pub.init_source = stdio_init_source; src->pub.fill_input_buffer = stdio_fill_input_buffer; src->pub.skip_input_data = stdio_skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ src->pub.term_source = stdio_term_source; src->infile = f; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src->pub.next_input_byte = NULL; /* until buffer loaded */ jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff); jpeg_read_header (&cinfo, TRUE); /* check for orientation tag */ is_otag = get_orientation (&cinfo); jpeg_start_decompress (&cinfo); cinfo.do_fancy_upsampling = FALSE; cinfo.do_block_smoothing = FALSE; pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, cinfo.out_color_components == 4 ? TRUE : FALSE, 8, cinfo.output_width, cinfo.output_height); if (!pixbuf) { jpeg_destroy_decompress (&cinfo); /* broken check for *error == NULL for robustness against * crappy JPEG library */ if (error && *error == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to load image, try exiting some applications to free memory")); } return NULL; } /* if orientation tag was found set an option to remember its value */ if (is_otag) { g_snprintf (otag_str, sizeof (otag_str), "%d", is_otag); gdk_pixbuf_set_option (pixbuf, "orientation", otag_str); } dptr = pixbuf->pixels; /* decompress all the lines, a few at a time */ while (cinfo.output_scanline < cinfo.output_height) { lptr = lines; for (i = 0; i < cinfo.rec_outbuf_height; i++) { *lptr++ = dptr; dptr += pixbuf->rowstride; } jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height); switch (cinfo.out_color_space) { case JCS_GRAYSCALE: explode_gray_into_buf (&cinfo, lines); break; case JCS_RGB: /* do nothing */ break; case JCS_CMYK: convert_cmyk_to_rgb (&cinfo, lines); break; default: g_object_unref (pixbuf); if (error && *error == NULL) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Unsupported JPEG color space (%s)"), colorspace_name (cinfo.out_color_space)); } jpeg_destroy_decompress (&cinfo); return NULL; } } jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); return pixbuf; }
static GdkPixbuf * gdip_bitmap_to_pixbuf (GpBitmap *bitmap, GError **error) { GdkPixbuf *pixbuf = NULL; guchar *cursor = NULL; gint rowstride; gboolean has_alpha = FALSE; gint n_channels = 0; gchar *option; guint width = 0, height = 0, x, y; gdip_bitmap_get_size (bitmap, &width, &height); gdip_bitmap_get_has_alpha (bitmap, &has_alpha); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height); if (!pixbuf) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't load bitmap")); return NULL; } rowstride = gdk_pixbuf_get_rowstride (pixbuf); cursor = gdk_pixbuf_get_pixels (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { ARGB pixel; GpStatus status; guchar *b = cursor + (y * rowstride + (x * n_channels)); if (Ok != (status = GdipBitmapGetPixel (bitmap, x, y, &pixel))) { gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); g_object_unref (pixbuf); return NULL; } b[0] = (pixel & 0xff0000) >> 16; b[1] = (pixel & 0x00ff00) >> 8; b[2] = (pixel & 0x0000ff) >> 0; if (has_alpha) b[3] = (pixel & 0xff000000) >> 24; } } if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagOrientation, &option)) { gdk_pixbuf_set_option (pixbuf, "orientation", option); g_free (option); } if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagArtist, &option)) { gdk_pixbuf_set_option (pixbuf, "Author", option); g_free (option); } if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagImageTitle, &option)) { gdk_pixbuf_set_option (pixbuf, "Title", option); g_free (option); } return pixbuf; }
/* Called at the start of the progressive load, once we have image info */ static void png_info_callback (png_structp png_read_ptr, png_infop png_info_ptr) { LoadContext* lc; png_uint_32 width, height; png_textp png_text_ptr; int i, num_texts; int color_type; gboolean have_alpha = FALSE; gchar *icc_profile_base64; const gchar *icc_profile_title; const gchar *icc_profile; png_uint_32 icc_profile_size; guint32 retval; gint compression_type; lc = png_get_progressive_ptr(png_read_ptr); if (lc->fatal_error_occurred) return; if (!setup_png_transformations(lc->png_read_ptr, lc->png_info_ptr, lc->error, &width, &height, &color_type)) { lc->fatal_error_occurred = TRUE; return; } /* If we have alpha, set a flag */ if (color_type & PNG_COLOR_MASK_ALPHA) have_alpha = TRUE; if (lc->size_func) { gint w = width; gint h = height; (* lc->size_func) (&w, &h, lc->notify_user_data); if (w == 0 || h == 0) { lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error_literal (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Transformed PNG has zero width or height.")); } return; } } lc->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, have_alpha, 8, width, height); if (lc->pixbuf == NULL) { /* Failed to allocate memory */ lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to store a %lu by %lu image; try exiting some applications to reduce memory usage"), (gulong) width, (gulong) height); } return; } /* Extract text chunks and attach them as pixbuf options */ if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) { for (i = 0; i < num_texts; i++) { gchar *key, *value; if (png_text_to_pixbuf_option (png_text_ptr[i], &key, &value)) { gdk_pixbuf_set_option (lc->pixbuf, key, value); g_free (key); g_free (value); } } } #if defined(PNG_cHRM_SUPPORTED) /* Extract embedded ICC profile */ retval = png_get_iCCP (png_read_ptr, png_info_ptr, (png_charpp) &icc_profile_title, &compression_type, (png_bytepp) &icc_profile, &icc_profile_size); if (retval != 0) { icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size); gdk_pixbuf_set_option (lc->pixbuf, "icc-profile", icc_profile_base64); g_free (icc_profile_base64); } #endif /* Notify the client that we are ready to go */ if (lc->prepare_func) (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data); return; }
/* Shared library entry point */ static GdkPixbuf * gdk_pixbuf__png_image_load (FILE *f, GError **error) { GdkPixbuf * volatile pixbuf = NULL; png_structp png_ptr; png_infop info_ptr; png_textp text_ptr; gint i, ctype; png_uint_32 w, h; png_bytepp volatile rows = NULL; gint num_texts; gchar *key; gchar *value; gchar *icc_profile_base64; const gchar *icc_profile_title; const gchar *icc_profile; png_uint_32 icc_profile_size; guint32 retval; gint compression_type; #ifdef PNG_USER_MEM_SUPPORTED png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING, error, png_simple_error_callback, png_simple_warning_callback, NULL, png_malloc_callback, png_free_callback); #else png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, error, png_simple_error_callback, png_simple_warning_callback); #endif if (!png_ptr) return NULL; info_ptr = png_create_info_struct (png_ptr); if (!info_ptr) { png_destroy_read_struct (&png_ptr, NULL, NULL); return NULL; } if (setjmp (png_jmpbuf(png_ptr))) { g_free (rows); if (pixbuf) g_object_unref (pixbuf); png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return NULL; } png_init_io (png_ptr, f); png_read_info (png_ptr, info_ptr); if (!setup_png_transformations(png_ptr, info_ptr, error, &w, &h, &ctype)) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return NULL; } pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, ctype & PNG_COLOR_MASK_ALPHA, 8, w, h); if (!pixbuf) { if (error && *error == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to load PNG file")); } png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return NULL; } rows = g_new (png_bytep, h); for (i = 0; i < h; i++) rows[i] = pixbuf->pixels + i * pixbuf->rowstride; png_read_image (png_ptr, rows); png_read_end (png_ptr, info_ptr); if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) { for (i = 0; i < num_texts; i++) { png_text_to_pixbuf_option (text_ptr[i], &key, &value); gdk_pixbuf_set_option (pixbuf, key, value); g_free (key); g_free (value); } } #if defined(PNG_cHRM_SUPPORTED) /* Extract embedded ICC profile */ retval = png_get_iCCP (png_ptr, info_ptr, (png_charpp) &icc_profile_title, &compression_type, (png_bytepp) &icc_profile, (png_uint_32*) &icc_profile_size); if (retval != 0) { icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size); gdk_pixbuf_set_option (pixbuf, "icc-profile", icc_profile_base64); g_free (icc_profile_base64); } #endif g_free (rows); png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return pixbuf; }
static gboolean set_image_text(GObject* image, const char* key, const char* val) { return gdk_pixbuf_set_option(GDK_PIXBUF(image), key, val); }
void generate_thumbnails_with_gdk_pixbuf(ThumbnailTask* task) { /* FIXME: only formats supported by GdkPixbuf should be handled this way. */ GFile* gf = fm_path_to_gfile(task->fi->path); GFileInputStream* ins; GdkPixbuf* normal_pix = NULL; GdkPixbuf* large_pix = NULL; DEBUG("generate thumbnail for %s", task->fi->path->name); if( ins = g_file_read(gf, generator_cancellable, NULL) ) { GdkPixbuf* ori_pix; gssize len; ori_pix = gdk_pixbuf_new_from_stream(G_INPUT_STREAM(ins), generator_cancellable, NULL); if(ori_pix) /* if the original image is successfully loaded */ { const char* orientation_str = gdk_pixbuf_get_option(ori_pix, "orientation"); int width = gdk_pixbuf_get_width(ori_pix); int height = gdk_pixbuf_get_height(ori_pix); gboolean need_save; if(task->flags & GENERATE_NORMAL) { /* don't create thumbnails for images which are too small */ if(width <=128 && height <= 128) { normal_pix = (GdkPixbuf*)g_object_ref(ori_pix); need_save = FALSE; } else { normal_pix = scale_pix(ori_pix, 128); need_save = TRUE; } if(orientation_str) { GdkPixbuf* rotated; gdk_pixbuf_set_option(normal_pix, "orientation", orientation_str); rotated = gdk_pixbuf_apply_embedded_orientation(normal_pix); g_object_unref(normal_pix); normal_pix = rotated; } if(need_save) save_thumbnail_to_disk(task, normal_pix, task->normal_path); } if(task->flags & GENERATE_LARGE) { /* don't create thumbnails for images which are too small */ if(width <=256 && height <= 256) { large_pix = (GdkPixbuf*)g_object_ref(ori_pix); need_save = FALSE; } else { large_pix = scale_pix(ori_pix, 256); need_save = TRUE; } if(orientation_str) { GdkPixbuf* rotated; gdk_pixbuf_set_option(large_pix, "orientation", orientation_str); rotated = gdk_pixbuf_apply_embedded_orientation(large_pix); g_object_unref(large_pix); large_pix = rotated; } if(need_save) save_thumbnail_to_disk(task, large_pix, task->large_path); } g_object_unref(ori_pix); } g_input_stream_close(G_INPUT_STREAM(ins), NULL, NULL); } G_LOCK(queue); thumbnail_task_finish(task, normal_pix, large_pix); cur_generating = NULL; G_UNLOCK(queue); if(normal_pix) g_object_unref(normal_pix); if(large_pix) g_object_unref(large_pix); g_object_unref(gf); }