/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * pikazoompreview.c * Copyright (C) 2005 David Odin * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include #include #include "libpikawidgets/pikawidgets.h" #include "pikauitypes.h" #include "pika.h" #include "pikapdb-private.h" #include "pikadrawablepreview.h" #include "pikazoompreview.h" /** * SECTION: pikazoompreview * @title: PikaZoomPreview * @short_description: A drawable preview with zooming capabilities. * * A drawable preview with zooming capabilities. **/ enum { PROP_0, PROP_DRAWABLE, PROP_MODEL }; typedef struct { gboolean update; } PreviewSettings; struct _PikaZoomPreviewPrivate { PikaDrawable *drawable; PikaZoomModel *model; GdkRectangle extents; }; #define GET_PRIVATE(obj) (((PikaZoomPreview *) (obj))->priv) static void pika_zoom_preview_constructed (GObject *object); static void pika_zoom_preview_finalize (GObject *object); static void pika_zoom_preview_dispose (GObject *object); static void pika_zoom_preview_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_zoom_preview_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_zoom_preview_set_adjustments (PikaZoomPreview *preview, gdouble old_factor, gdouble new_factor); static void pika_zoom_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation, PikaZoomPreview *preview); static void pika_zoom_preview_style_updated (GtkWidget *widget); static gboolean pika_zoom_preview_scroll_event (GtkWidget *widget, GdkEventScroll *event, PikaZoomPreview *preview); static void pika_zoom_preview_draw (PikaPreview *preview); static void pika_zoom_preview_draw_buffer (PikaPreview *preview, const guchar *buffer, gint rowstride); static void pika_zoom_preview_draw_thumb (PikaPreview *preview, PikaPreviewArea *area, gint width, gint height); static void pika_zoom_preview_set_cursor (PikaPreview *preview); static void pika_zoom_preview_transform (PikaPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y); static void pika_zoom_preview_untransform (PikaPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y); static void pika_zoom_preview_set_drawable (PikaZoomPreview *preview, PikaDrawable *drawable); static void pika_zoom_preview_set_model (PikaZoomPreview *preview, PikaZoomModel *model); static void pika_zoom_preview_get_source_area (PikaPreview *preview, gint *x, gint *y, gint *w, gint *h); G_DEFINE_TYPE_WITH_PRIVATE (PikaZoomPreview, pika_zoom_preview, PIKA_TYPE_SCROLLED_PREVIEW) #define parent_class pika_zoom_preview_parent_class static gint pika_zoom_preview_counter = 0; static void pika_zoom_preview_class_init (PikaZoomPreviewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); PikaPreviewClass *preview_class = PIKA_PREVIEW_CLASS (klass); object_class->constructed = pika_zoom_preview_constructed; object_class->finalize = pika_zoom_preview_finalize; object_class->dispose = pika_zoom_preview_dispose; object_class->get_property = pika_zoom_preview_get_property; object_class->set_property = pika_zoom_preview_set_property; widget_class->style_updated = pika_zoom_preview_style_updated; preview_class->draw = pika_zoom_preview_draw; preview_class->draw_buffer = pika_zoom_preview_draw_buffer; preview_class->draw_thumb = pika_zoom_preview_draw_thumb; preview_class->set_cursor = pika_zoom_preview_set_cursor; preview_class->transform = pika_zoom_preview_transform; preview_class->untransform = pika_zoom_preview_untransform; /** * PikaZoomPreview:drawable-id: * * The drawable the #PikaZoomPreview is attached to. * * Since: 2.10 */ g_object_class_install_property (object_class, PROP_DRAWABLE, g_param_spec_object ("drawable", "Drawable", "The drawable this preview is attached to", PIKA_TYPE_DRAWABLE, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * PikaZoomPreview:model: * * The #PikaZoomModel used by this #PikaZoomPreview. * * Since: 2.4 */ g_object_class_install_property (object_class, PROP_MODEL, g_param_spec_object ("model", "Model", "The zoom preview's PikaZoomModel", PIKA_TYPE_ZOOM_MODEL, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void pika_zoom_preview_init (PikaZoomPreview *preview) { GtkWidget *area = pika_preview_get_area (PIKA_PREVIEW (preview)); preview->priv = pika_zoom_preview_get_instance_private (preview); g_signal_connect (area, "size-allocate", G_CALLBACK (pika_zoom_preview_size_allocate), preview); g_signal_connect (area, "scroll-event", G_CALLBACK (pika_zoom_preview_scroll_event), preview); g_object_set (area, "check-size", pika_check_size (), "check-type", pika_check_type (), "check-custom-color1", pika_check_custom_color1 (), "check-custom-color2", pika_check_custom_color2 (), NULL); pika_scrolled_preview_set_policy (PIKA_SCROLLED_PREVIEW (preview), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); } static void pika_zoom_preview_constructed (GObject *object) { PikaZoomPreviewPrivate *priv = GET_PRIVATE (object); gchar *data_name; PreviewSettings settings; GBytes *settings_bytes = NULL; G_OBJECT_CLASS (parent_class)->constructed (object); data_name = g_strdup_printf ("%s-zoom-preview-%d", g_get_prgname (), pika_zoom_preview_counter++); if (pika_pdb_get_data (data_name, &settings_bytes) && g_bytes_get_size (settings_bytes) == sizeof (PreviewSettings)) { settings = *((PreviewSettings *) g_bytes_get_data (settings_bytes, NULL)); pika_preview_set_update (PIKA_PREVIEW (object), settings.update); } g_bytes_unref (settings_bytes); g_object_set_data_full (object, "pika-zoom-preview-data-name", data_name, (GDestroyNotify) g_free); if (! priv->model) { PikaZoomModel *model = pika_zoom_model_new (); pika_zoom_model_set_range (model, 1.0, 256.0); pika_zoom_preview_set_model (PIKA_ZOOM_PREVIEW (object), model); g_object_unref (model); } pika_zoom_preview_set_adjustments (PIKA_ZOOM_PREVIEW (object), 1.0, 1.0); } static void pika_zoom_preview_finalize (GObject *object) { PikaZoomPreviewPrivate *priv = GET_PRIVATE (object); g_clear_object (&priv->model); g_clear_object (&priv->drawable); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_zoom_preview_dispose (GObject *object) { const gchar *data_name = g_object_get_data (G_OBJECT (object), "pika-zoom-preview-data-name"); if (data_name) { PikaPreview *preview = PIKA_PREVIEW (object); GBytes *bytes; PreviewSettings settings; settings.update = pika_preview_get_update (preview); bytes = g_bytes_new_static (&settings, sizeof (PreviewSettings)); pika_pdb_set_data (data_name, bytes); g_bytes_unref (bytes); } G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_zoom_preview_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaZoomPreview *preview = PIKA_ZOOM_PREVIEW (object); switch (property_id) { case PROP_DRAWABLE: g_value_set_object (value, pika_zoom_preview_get_drawable (preview)); break; case PROP_MODEL: g_value_set_object (value, pika_zoom_preview_get_model (preview)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_zoom_preview_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaZoomPreview *preview = PIKA_ZOOM_PREVIEW (object); switch (property_id) { case PROP_DRAWABLE: pika_zoom_preview_set_drawable (preview, g_value_dup_object (value)); break; case PROP_MODEL: pika_zoom_preview_set_model (preview, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_zoom_preview_set_adjustments (PikaZoomPreview *preview, gdouble old_factor, gdouble new_factor) { PikaScrolledPreview *scrolled_preview = PIKA_SCROLLED_PREVIEW (preview); GtkAdjustment *hadj; GtkAdjustment *vadj; gint width; gint height; gdouble ratio; pika_scrolled_preview_freeze (scrolled_preview); pika_preview_get_size (PIKA_PREVIEW (preview), &width, &height); ratio = new_factor / old_factor; pika_scrolled_preview_get_adjustments (scrolled_preview, &hadj, &vadj); gtk_adjustment_configure (hadj, (gtk_adjustment_get_value (vadj) + width / 2.0) * ratio - width / 2.0, 0, width * new_factor, new_factor, MAX (width / 2.0, new_factor), width); gtk_adjustment_configure (vadj, (gtk_adjustment_get_value (vadj) + height / 2.0) * ratio - height / 2.0, 0, height * new_factor, new_factor, MAX (height / 2.0, new_factor), height); pika_scrolled_preview_thaw (scrolled_preview); } static void pika_zoom_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation, PikaZoomPreview *preview) { gdouble zoom; #if 0 gint width = PIKA_PREVIEW (preview)->xmax - PIKA_PREVIEW (preview)->xmin; gint height = PIKA_PREVIEW (preview)->ymax - PIKA_PREVIEW (preview)->ymin; PIKA_PREVIEW (preview)->width = MIN (width, allocation->width); PIKA_PREVIEW (preview)->height = MIN (height, allocation->height); #endif zoom = pika_zoom_model_get_factor (preview->priv->model); pika_zoom_preview_set_adjustments (preview, zoom, zoom); } static void pika_zoom_preview_style_updated (GtkWidget *widget) { PikaPreview *preview = PIKA_PREVIEW (widget); GtkWidget *area = pika_preview_get_area (preview); GTK_WIDGET_CLASS (parent_class)->style_updated (widget); if (area) { PikaZoomPreviewPrivate *priv = GET_PRIVATE (preview); gint size; gint width; gint height; gint preview_width; gint preview_height; gint x1, y1; gint x2, y2; gtk_widget_style_get (widget, "size", &size, NULL); if (_pika_drawable_preview_get_bounds (priv->drawable, &x1, &y1, &x2, &y2)) { width = x2 - x1; height = y2 - y1; } else { width = pika_drawable_get_width (priv->drawable); height = pika_drawable_get_height (priv->drawable); } if (width > height) { preview_width = MIN (width, size); preview_height = (height * preview_width) / width; } else { preview_height = MIN (height, size); preview_width = (width * preview_height) / height; } pika_preview_set_size (preview, preview_width, preview_height); } } static gboolean pika_zoom_preview_scroll_event (GtkWidget *widget, GdkEventScroll *event, PikaZoomPreview *preview) { if (event->state & GDK_CONTROL_MASK) { PikaZoomPreviewPrivate *priv = GET_PRIVATE (preview); gdouble delta; pika_scrolled_preview_freeze (PIKA_SCROLLED_PREVIEW (preview)); switch (event->direction) { case GDK_SCROLL_UP: pika_zoom_model_zoom (priv->model, PIKA_ZOOM_IN, 0.0); break; case GDK_SCROLL_DOWN: pika_zoom_model_zoom (priv->model, PIKA_ZOOM_OUT, 0.0); break; case GDK_SCROLL_SMOOTH: gdk_event_get_scroll_deltas ((GdkEvent *) event, NULL, &delta); pika_zoom_model_zoom (priv->model, PIKA_ZOOM_SMOOTH, delta); break; default: break; } pika_scrolled_preview_thaw (PIKA_SCROLLED_PREVIEW (preview)); } return FALSE; } static void pika_zoom_preview_draw (PikaPreview *preview) { PikaZoomPreviewPrivate *priv = PIKA_ZOOM_PREVIEW (preview)->priv; guchar *data; gint width; gint height; gint bpp; if (! priv->model) return; if (priv->drawable == NULL) return; data = pika_zoom_preview_get_source (PIKA_ZOOM_PREVIEW (preview), &width, &height, &bpp); if (data) { GtkWidget *area = pika_preview_get_area (preview); pika_preview_area_draw (PIKA_PREVIEW_AREA (area), 0, 0, width, height, pika_drawable_type (priv->drawable), data, width * bpp); g_free (data); } } static void pika_zoom_preview_draw_buffer (PikaPreview *preview, const guchar *buffer, gint rowstride) { PikaZoomPreviewPrivate *priv = PIKA_ZOOM_PREVIEW (preview)->priv; GtkWidget *area = pika_preview_get_area (preview); PikaImage *image; gint width; gint height; pika_preview_get_size (preview, &width, &height); image = pika_item_get_image (PIKA_ITEM (priv->drawable)); if (pika_selection_is_empty (image)) { pika_preview_area_draw (PIKA_PREVIEW_AREA (area), 0, 0, width, height, pika_drawable_type (priv->drawable), buffer, rowstride); } else { GBytes *sel; GBytes *src; PikaSelection *selection; gint w, h; gint bpp; gint src_x; gint src_y; gint src_width; gint src_height; gint offsx = 0; gint offsy = 0; selection = pika_image_get_selection (image); pika_zoom_preview_get_source_area (preview, &src_x, &src_y, &src_width, &src_height); src = pika_drawable_get_sub_thumbnail_data (priv->drawable, src_x, src_y, src_width, src_height, width, height, &w, &h, &bpp); pika_drawable_get_offsets (priv->drawable, &offsx, &offsy); sel = pika_drawable_get_sub_thumbnail_data (PIKA_DRAWABLE (selection), src_x + offsx, src_y + offsy, src_width, src_height, width, height, &w, &h, &bpp); pika_preview_area_mask (PIKA_PREVIEW_AREA (area), 0, 0, width, height, pika_drawable_type (priv->drawable), g_bytes_get_data (src, NULL), width * pika_drawable_get_bpp (priv->drawable), buffer, rowstride, g_bytes_get_data (sel, NULL), width); g_bytes_unref (sel); g_bytes_unref (src); } } static void pika_zoom_preview_draw_thumb (PikaPreview *preview, PikaPreviewArea *area, gint width, gint height) { PikaZoomPreviewPrivate *priv = PIKA_ZOOM_PREVIEW (preview)->priv; if (priv->drawable != NULL) _pika_drawable_preview_area_draw_thumb (area, priv->drawable, width, height); } static void pika_zoom_preview_set_cursor (PikaPreview *preview) { GtkWidget *area = pika_preview_get_area (preview); if (! gtk_widget_get_realized (area)) return; if (pika_zoom_preview_get_factor (PIKA_ZOOM_PREVIEW (preview)) > 1.0) { GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (preview)); GdkCursor *cursor; cursor = gdk_cursor_new_for_display (display, GDK_HAND1); gdk_window_set_cursor (gtk_widget_get_window (area), cursor); g_object_unref (cursor); } else { gdk_window_set_cursor (gtk_widget_get_window (area), pika_preview_get_default_cursor (preview)); } } static void pika_zoom_preview_transform (PikaPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y) { PikaZoomPreviewPrivate *priv = PIKA_ZOOM_PREVIEW (preview)->priv; gint width; gint height; gint xoff; gint yoff; gdouble zoom; pika_preview_get_size (preview, &width, &height); pika_preview_get_offsets (preview, &xoff, &yoff); zoom = pika_zoom_preview_get_factor (PIKA_ZOOM_PREVIEW (preview)); *dest_x = ((gdouble) (src_x - priv->extents.x) * width / priv->extents.width * zoom) - xoff; *dest_y = ((gdouble) (src_y - priv->extents.y) * height / priv->extents.height * zoom) - yoff; } static void pika_zoom_preview_untransform (PikaPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y) { PikaZoomPreviewPrivate *priv = PIKA_ZOOM_PREVIEW (preview)->priv; gint width; gint height; gint xoff; gint yoff; gdouble zoom; pika_preview_get_size (preview, &width, &height); pika_preview_get_offsets (preview, &xoff, &yoff); zoom = pika_zoom_preview_get_factor (PIKA_ZOOM_PREVIEW (preview)); *dest_x = (priv->extents.x + ((gdouble) (src_x + xoff) * priv->extents.width / width / zoom)); *dest_y = (priv->extents.y + ((gdouble) (src_y + yoff) * priv->extents.height / height / zoom)); } static void pika_zoom_preview_set_drawable (PikaZoomPreview *preview, PikaDrawable *drawable) { PikaZoomPreviewPrivate *priv = preview->priv; gint x, y; gint width, height; gint max_width, max_height; g_return_if_fail (preview->priv->drawable == NULL); priv->drawable = drawable; if (pika_drawable_mask_intersect (drawable, &x, &y, &width, &height)) { priv->extents.x = x; priv->extents.y = y; } else { width = pika_drawable_get_width (drawable); height = pika_drawable_get_height (drawable); priv->extents.x = 0; priv->extents.y = 0; } priv->extents.width = width; priv->extents.height = height; if (width > height) { max_width = MIN (width, 512); max_height = (height * max_width) / width; } else { max_height = MIN (height, 512); max_width = (width * max_height) / height; } pika_preview_set_bounds (PIKA_PREVIEW (preview), 0, 0, max_width, max_height); g_object_set (pika_preview_get_frame (PIKA_PREVIEW (preview)), "ratio", (gdouble) width / (gdouble) height, NULL); } static void pika_zoom_preview_set_model (PikaZoomPreview *preview, PikaZoomModel *model) { PikaZoomPreviewPrivate *priv = GET_PRIVATE (preview); GtkWidget *button_bar; GtkWidget *button; GtkWidget *box; g_return_if_fail (priv->model == NULL); if (! model) return; priv->model = g_object_ref (model); g_signal_connect_swapped (priv->model, "zoomed", G_CALLBACK (pika_zoom_preview_set_adjustments), preview); box = pika_preview_get_controls (PIKA_PREVIEW (preview)); g_return_if_fail (GTK_IS_BOX (box)); button_bar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); gtk_box_pack_end (GTK_BOX (box), button_bar, FALSE, FALSE, 0); gtk_widget_show (button_bar); /* zoom out */ button = pika_zoom_button_new (priv->model, PIKA_ZOOM_OUT, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_box_pack_start (GTK_BOX (button_bar), button, FALSE, FALSE, 0); gtk_widget_show (button); /* zoom in */ button = pika_zoom_button_new (priv->model, PIKA_ZOOM_IN, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_box_pack_start (GTK_BOX (button_bar), button, FALSE, FALSE, 0); gtk_widget_show (button); } static void pika_zoom_preview_get_source_area (PikaPreview *preview, gint *x, gint *y, gint *w, gint *h) { PikaZoomPreviewPrivate *priv = GET_PRIVATE (preview); gdouble zoom = pika_zoom_model_get_factor (priv->model); pika_zoom_preview_untransform (preview, 0, 0, x, y); *w = priv->extents.width / zoom; *h = priv->extents.height / zoom; } /** * pika_zoom_preview_new_from_drawable: * @drawable: (transfer none): a drawable * * Creates a new #PikaZoomPreview widget for @drawable. * * Since: 3.0 * * Returns: a new #PikaZoomPreview. **/ GtkWidget * pika_zoom_preview_new_from_drawable (PikaDrawable *drawable) { g_return_val_if_fail (PIKA_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (pika_item_is_valid (PIKA_ITEM (drawable)), NULL); return g_object_new (PIKA_TYPE_ZOOM_PREVIEW, "drawable", drawable, NULL); } /** * pika_zoom_preview_new_with_model_from_drawable: * @drawable: a #PikaDrawable * @model: a #PikaZoomModel * * Creates a new #PikaZoomPreview widget for @drawable using the * given @model. * * This variant of pika_zoom_preview_new_from_drawable() allows you * to create a preview using an existing zoom model. This may be * useful if for example you want to have two zoom previews that keep * their zoom factor in sync. * * Since: 2.10 * * Returns: a new #PikaZoomPreview. **/ GtkWidget * pika_zoom_preview_new_with_model_from_drawable (PikaDrawable *drawable, PikaZoomModel *model) { g_return_val_if_fail (PIKA_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (pika_item_is_valid (PIKA_ITEM (drawable)), NULL); g_return_val_if_fail (PIKA_IS_ZOOM_MODEL (model), NULL); return g_object_new (PIKA_TYPE_ZOOM_PREVIEW, "drawable", drawable, "model", model, NULL); } /** * pika_zoom_preview_get_drawable: * @preview: a #PikaZoomPreview widget * * Returns the drawable the #PikaZoomPreview is attached to. * * Returns: (transfer none): the drawable that was passed to * pika_zoom_preview_new_from_drawable(). * * Since: 3.0 **/ PikaDrawable * pika_zoom_preview_get_drawable (PikaZoomPreview *preview) { g_return_val_if_fail (PIKA_IS_ZOOM_PREVIEW (preview), NULL); return GET_PRIVATE (preview)->drawable; } /** * pika_zoom_preview_get_model: * @preview: a #PikaZoomPreview widget * * Returns the #PikaZoomModel the preview is using. * * Returns: (transfer none): a pointer to the #PikaZoomModel owned * by the @preview * * Since: 2.4 **/ PikaZoomModel * pika_zoom_preview_get_model (PikaZoomPreview *preview) { g_return_val_if_fail (PIKA_IS_ZOOM_PREVIEW (preview), NULL); return GET_PRIVATE (preview)->model; } /** * pika_zoom_preview_get_factor: * @preview: a #PikaZoomPreview widget * * Returns the zoom factor the preview is currently using. * * Returns: the current zoom factor * * Since: 2.4 **/ gdouble pika_zoom_preview_get_factor (PikaZoomPreview *preview) { PikaZoomPreviewPrivate *priv; g_return_val_if_fail (PIKA_IS_ZOOM_PREVIEW (preview), 1.0); priv = GET_PRIVATE (preview); return priv->model ? pika_zoom_model_get_factor (priv->model) : 1.0; } /** * pika_zoom_preview_get_source: * @preview: a #PikaZoomPreview widget * @width: (out): a pointer to an int where the current width of the zoom widget * will be put. * @height: (out): a pointer to an int where the current width of the zoom widget * will be put. * @bpp: (out): return location for the number of bytes per pixel * * Returns the scaled image data of the part of the drawable the * #PikaZoomPreview is currently showing, as a newly allocated array of guchar. * This function also allow to get the current width, height and bpp of the * #PikaZoomPreview. * * Returns: (transfer full) (array): newly allocated data that should be * released using g_free() when it is not any longer needed * * Since: 2.4 */ guchar * pika_zoom_preview_get_source (PikaZoomPreview *preview, gint *width, gint *height, gint *bpp) { PikaDrawable *drawable; g_return_val_if_fail (PIKA_IS_ZOOM_PREVIEW (preview), NULL); g_return_val_if_fail (width != NULL && height != NULL && bpp != NULL, NULL); drawable = pika_zoom_preview_get_drawable (preview); if (drawable) { PikaPreview *pika_preview = PIKA_PREVIEW (preview); gint src_x; gint src_y; gint src_width; gint src_height; GBytes *src_bytes; gsize src_bytes_size; pika_preview_get_size (pika_preview, width, height); pika_zoom_preview_get_source_area (pika_preview, &src_x, &src_y, &src_width, &src_height); src_bytes = pika_drawable_get_sub_thumbnail_data (drawable, src_x, src_y, src_width, src_height, *width, *height, width, height, bpp); return g_bytes_unref_to_data (src_bytes, &src_bytes_size); } else { *width = 0; *height = 0; *bpp = 0; return NULL; } }