/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * pikadrawablepreview.c * * 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" /** * SECTION: pikadrawablepreview * @title: PikaDrawablePreview * @short_description: A widget providing a preview of a #PikaDrawable. * * A widget providing a preview of a #PikaDrawable. **/ #define SELECTION_BORDER 8 enum { PROP_0, PROP_DRAWABLE }; typedef struct { gint x; gint y; gboolean update; } PreviewSettings; struct _PikaDrawablePreviewPrivate { PikaDrawable *drawable; }; #define GET_PRIVATE(obj) (((PikaDrawablePreview *) (obj))->priv) static void pika_drawable_preview_constructed (GObject *object); static void pika_drawable_preview_dispose (GObject *object); static void pika_drawable_preview_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_drawable_preview_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_drawable_preview_style_updated (GtkWidget *widget); static void pika_drawable_preview_draw_original (PikaPreview *preview); static void pika_drawable_preview_draw_thumb (PikaPreview *preview, PikaPreviewArea *area, gint width, gint height); static void pika_drawable_preview_draw_buffer (PikaPreview *preview, const guchar *buffer, gint rowstride); static void pika_drawable_preview_set_drawable (PikaDrawablePreview *preview, PikaDrawable *drawable); G_DEFINE_TYPE_WITH_PRIVATE (PikaDrawablePreview, pika_drawable_preview, PIKA_TYPE_SCROLLED_PREVIEW) #define parent_class pika_drawable_preview_parent_class static gint pika_drawable_preview_counter = 0; static void pika_drawable_preview_class_init (PikaDrawablePreviewClass *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_drawable_preview_constructed; object_class->dispose = pika_drawable_preview_dispose; object_class->get_property = pika_drawable_preview_get_property; object_class->set_property = pika_drawable_preview_set_property; widget_class->style_updated = pika_drawable_preview_style_updated; preview_class->draw = pika_drawable_preview_draw_original; preview_class->draw_thumb = pika_drawable_preview_draw_thumb; preview_class->draw_buffer = pika_drawable_preview_draw_buffer; /** * PikaDrawablePreview:drawable-id: * * The drawable the #PikaDrawablePreview 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)); } static void pika_drawable_preview_init (PikaDrawablePreview *preview) { preview->priv = pika_drawable_preview_get_instance_private (preview); g_object_set (pika_preview_get_area (PIKA_PREVIEW (preview)), "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); } static void pika_drawable_preview_constructed (GObject *object) { gchar *data_name; PreviewSettings settings; GBytes *settings_bytes = NULL; G_OBJECT_CLASS (parent_class)->constructed (object); data_name = g_strdup_printf ("%s-drawable-preview-%d", g_get_prgname (), ++pika_drawable_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); pika_scrolled_preview_set_position (PIKA_SCROLLED_PREVIEW (object), settings.x, settings.y); } g_bytes_unref (settings_bytes); g_object_set_data_full (object, "pika-drawable-preview-data-name", data_name, (GDestroyNotify) g_free); } static void pika_drawable_preview_dispose (GObject *object) { PikaDrawablePreviewPrivate *priv = GET_PRIVATE (object); const gchar *data_name; data_name = g_object_get_data (G_OBJECT (object), "pika-drawable-preview-data-name"); if (data_name) { PikaPreview *preview = PIKA_PREVIEW (object); GBytes *bytes; PreviewSettings settings; pika_preview_get_position (preview, &settings.x, &settings.y); 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_clear_object (&priv->drawable); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_drawable_preview_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaDrawablePreview *preview = PIKA_DRAWABLE_PREVIEW (object); switch (property_id) { case PROP_DRAWABLE: g_value_set_object (value, pika_drawable_preview_get_drawable (preview)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_drawable_preview_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaDrawablePreview *preview = PIKA_DRAWABLE_PREVIEW (object); switch (property_id) { case PROP_DRAWABLE: pika_drawable_preview_set_drawable (preview, g_value_dup_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_drawable_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) { gint xmin, ymin; gint xmax, ymax; gint size; pika_preview_get_bounds (preview, &xmin, &ymin, &xmax, &ymax); gtk_widget_style_get (widget, "size", &size, NULL); gtk_widget_set_size_request (area, MIN (xmax - xmin, size), MIN (ymax - ymin, size)); } } static void pika_drawable_preview_draw_original (PikaPreview *preview) { PikaDrawablePreviewPrivate *priv = GET_PRIVATE (preview); GBytes *buffer; gint width, height; gint tn_width, tn_height; gint xoff, yoff; gint xmin, ymin; gint xmax, ymax; gint bpp; PikaImageType type; if (priv->drawable == NULL) return; pika_preview_get_size (preview, &width, &height); pika_preview_get_offsets (preview, &xoff, &yoff); pika_preview_get_bounds (preview, &xmin, &ymin, &xmax, &ymax); xoff = CLAMP (xoff, 0, xmax - xmin - width); yoff = CLAMP (yoff, 0, ymax - ymin - height); pika_preview_set_offsets (preview, xoff, yoff); buffer = pika_drawable_get_sub_thumbnail_data (priv->drawable, xoff + xmin, yoff + ymin, width, height, width, height, &tn_width, &tn_height, &bpp); switch (bpp) { case 1: type = PIKA_GRAY_IMAGE; break; case 2: type = PIKA_GRAYA_IMAGE; break; case 3: type = PIKA_RGB_IMAGE; break; case 4: type = PIKA_RGBA_IMAGE; break; default: g_bytes_unref (buffer); return; } pika_preview_area_draw (PIKA_PREVIEW_AREA (pika_preview_get_area (preview)), 0, 0, tn_width, tn_height, type, g_bytes_get_data (buffer, NULL), width * bpp); g_bytes_unref (buffer); } static void pika_drawable_preview_draw_thumb (PikaPreview *preview, PikaPreviewArea *area, gint width, gint height) { PikaDrawablePreviewPrivate *priv = GET_PRIVATE (preview); if (priv->drawable) _pika_drawable_preview_area_draw_thumb (area, priv->drawable, width, height); } void _pika_drawable_preview_area_draw_thumb (PikaPreviewArea *area, PikaDrawable *drawable, gint width, gint height) { GBytes *buffer; gint x1, y1, x2, y2; gint bpp; gint size = 100; gint nav_width, nav_height; gint tn_width, tn_height; g_return_if_fail (PIKA_IS_PREVIEW_AREA (area)); g_return_if_fail (pika_item_is_valid (PIKA_ITEM (drawable))); g_return_if_fail (pika_item_is_drawable (PIKA_ITEM (drawable))); if (_pika_drawable_preview_get_bounds (drawable, &x1, &y1, &x2, &y2)) { width = x2 - x1; height = y2 - y1; } else { width = pika_drawable_get_width (drawable); height = pika_drawable_get_height (drawable); } if (width > height) { nav_width = MIN (width, size); nav_height = (height * nav_width) / width; } else { nav_height = MIN (height, size); nav_width = (width * nav_height) / height; } if (_pika_drawable_preview_get_bounds (drawable, &x1, &y1, &x2, &y2)) { buffer = pika_drawable_get_sub_thumbnail_data (drawable, x1, y1, x2 - x1, y2 - y1, nav_width, nav_height, &tn_width, &tn_height, &bpp); } else { buffer = pika_drawable_get_thumbnail_data (drawable, nav_width, nav_height, &tn_width, &tn_height, &bpp); } if (buffer) { PikaImageType type; gtk_widget_set_size_request (GTK_WIDGET (area), tn_width, tn_height); gtk_widget_show (GTK_WIDGET (area)); gtk_widget_realize (GTK_WIDGET (area)); switch (bpp) { case 1: type = PIKA_GRAY_IMAGE; break; case 2: type = PIKA_GRAYA_IMAGE; break; case 3: type = PIKA_RGB_IMAGE; break; case 4: type = PIKA_RGBA_IMAGE; break; default: g_bytes_unref (buffer); return; } pika_preview_area_draw (area, 0, 0, tn_width, tn_height, type, g_bytes_get_data (buffer, NULL), bpp * tn_width); g_bytes_unref (buffer); } } static void pika_drawable_preview_draw_area (PikaDrawablePreview *preview, gint x, gint y, gint width, gint height, const guchar *buf, gint rowstride) { PikaDrawablePreviewPrivate *priv = GET_PRIVATE (preview); PikaPreview *pika_preview = PIKA_PREVIEW (preview); GtkWidget *area = pika_preview_get_area (pika_preview); PikaImage *image; gint xmin, ymin; gint xoff, yoff; pika_preview_get_bounds (pika_preview, &xmin, &ymin, NULL, NULL); pika_preview_get_offsets (pika_preview, &xoff, &yoff); image = pika_item_get_image (PIKA_ITEM (priv->drawable)); if (pika_selection_is_empty (image)) { pika_preview_area_draw (PIKA_PREVIEW_AREA (area), x - xoff - xmin, y - yoff - ymin, width, height, pika_drawable_type (priv->drawable), buf, rowstride); } else { gint offset_x, offset_y; gint mask_x, mask_y; gint mask_width, mask_height; gint draw_x, draw_y; gint draw_width, draw_height; pika_drawable_get_offsets (priv->drawable, &offset_x, &offset_y); if (pika_drawable_mask_intersect (priv->drawable, &mask_x, &mask_y, &mask_width, &mask_height) && pika_rectangle_intersect (mask_x, mask_y, mask_width, mask_height, x, y, width, height, &draw_x, &draw_y, &draw_width, &draw_height)) { PikaImageType type; PikaSelection *selection; GBytes *src; GBytes *sel; gint d_w, d_h, d_bpp; gint s_w, s_h, s_bpp; selection = pika_image_get_selection (image); src = pika_drawable_get_sub_thumbnail_data (priv->drawable, draw_x, draw_y, draw_width, draw_height, draw_width, draw_height, &d_w, &d_h, &d_bpp); sel = pika_drawable_get_sub_thumbnail_data (PIKA_DRAWABLE (selection), draw_x + offset_x, draw_y + offset_y, draw_width, draw_height, draw_width, draw_height, &s_w, &s_h, &s_bpp); switch (d_bpp) { case 1: type = PIKA_GRAY_IMAGE; break; case 2: type = PIKA_GRAYA_IMAGE; break; case 3: type = PIKA_RGB_IMAGE; break; case 4: type = PIKA_RGBA_IMAGE; break; default: g_bytes_unref (sel); g_bytes_unref (src); return; } pika_preview_area_mask (PIKA_PREVIEW_AREA (area), draw_x - xoff - xmin, draw_y - yoff - ymin, draw_width, draw_height, type, g_bytes_get_data (src, NULL), draw_width * d_bpp, (buf + (draw_x - x) * d_bpp + (draw_y - y) * d_w * d_bpp), rowstride, g_bytes_get_data (sel, NULL), s_w); g_bytes_unref (sel); g_bytes_unref (src); } } } static void pika_drawable_preview_draw_buffer (PikaPreview *preview, const guchar *buffer, gint rowstride) { gint x, y; gint width, height; pika_preview_get_position (preview, &x, &y); pika_preview_get_size (preview, &width, &height); pika_drawable_preview_draw_area (PIKA_DRAWABLE_PREVIEW (preview), x, y, width, height, buffer, rowstride); } static void pika_drawable_preview_set_drawable (PikaDrawablePreview *drawable_preview, PikaDrawable *drawable) { PikaPreview *preview = PIKA_PREVIEW (drawable_preview); PikaDrawablePreviewPrivate *priv = GET_PRIVATE (preview); gint x1, y1, x2, y2; g_return_if_fail (priv->drawable == NULL); priv->drawable = drawable; _pika_drawable_preview_get_bounds (drawable, &x1, &y1, &x2, &y2); pika_preview_set_bounds (preview, x1, y1, x2, y2); if (pika_drawable_is_indexed (drawable)) { PikaImage *image = pika_item_get_image (PIKA_ITEM (drawable)); GtkWidget *area = pika_preview_get_area (preview); guchar *cmap; gint num_colors; cmap = pika_image_get_colormap (image, NULL, &num_colors); pika_preview_area_set_colormap (PIKA_PREVIEW_AREA (area), cmap, num_colors); g_free (cmap); } } #define MAX3(a, b, c) (MAX (MAX ((a), (b)), (c))) #define MIN3(a, b, c) (MIN (MIN ((a), (b)), (c))) gboolean _pika_drawable_preview_get_bounds (PikaDrawable *drawable, gint *xmin, gint *ymin, gint *xmax, gint *ymax) { gint width; gint height; gint offset_x; gint offset_y; gint x1, y1; gint x2, y2; gboolean retval; g_return_val_if_fail (pika_item_is_valid (PIKA_ITEM (drawable)), FALSE); g_return_val_if_fail (pika_item_is_drawable (PIKA_ITEM (drawable)), FALSE); width = pika_drawable_get_width (drawable); height = pika_drawable_get_height (drawable); retval = pika_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2); pika_drawable_get_offsets (drawable, &offset_x, &offset_y); *xmin = MAX3 (x1 - SELECTION_BORDER, 0, - offset_x); *ymin = MAX3 (y1 - SELECTION_BORDER, 0, - offset_y); *xmax = MIN3 (x2 + SELECTION_BORDER, width, width + offset_x); *ymax = MIN3 (y2 + SELECTION_BORDER, height, height + offset_y); return retval; } /** * pika_drawable_preview_new_from_drawable: * @drawable: (transfer none): a drawable * * Creates a new #PikaDrawablePreview widget for @drawable. * * Returns: A pointer to the new #PikaDrawablePreview widget. * * Since: 2.10 **/ GtkWidget * pika_drawable_preview_new_from_drawable (PikaDrawable *drawable) { g_return_val_if_fail (pika_item_is_valid (PIKA_ITEM (drawable)), NULL); g_return_val_if_fail (pika_item_is_drawable (PIKA_ITEM (drawable)), NULL); return g_object_new (PIKA_TYPE_DRAWABLE_PREVIEW, "drawable", drawable, NULL); } /** * pika_drawable_preview_get_drawable: * @preview: a #PikaDrawablePreview widget * * Returns: (transfer none): the drawable that has been passed to * pika_drawable_preview_new_from_drawable(). * * Since: 2.10 **/ PikaDrawable * pika_drawable_preview_get_drawable (PikaDrawablePreview *preview) { g_return_val_if_fail (PIKA_IS_DRAWABLE_PREVIEW (preview), NULL); return GET_PRIVATE (preview)->drawable; }