diff options
Diffstat (limited to 'eel/eel-debug-drawing.c')
-rw-r--r-- | eel/eel-debug-drawing.c | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/eel/eel-debug-drawing.c b/eel/eel-debug-drawing.c new file mode 100644 index 00000000..aee400d5 --- /dev/null +++ b/eel/eel-debug-drawing.c @@ -0,0 +1,566 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + eel-debug-drawing.c: Eel drawing debugging aids. + + Copyright (C) 2000 Eazel, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Ramiro Estrugo <[email protected]> +*/ + +#include <config.h> +#include "eel-debug-drawing.h" + +#include "eel-art-gtk-extensions.h" +#include "eel-debug.h" +#include "eel-gdk-extensions.h" +#include "eel-gdk-extensions.h" +#include "eel-gdk-pixbuf-extensions.h" +#include "eel-gtk-extensions.h" +#include "eel-gtk-extensions.h" +#include "eel-gtk-macros.h" + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +/* + * PixbufViewer is a very simple "private" widget that displays + * a GdkPixbuf. It is used by eel_debug_show_pixbuf() to + * display a pixbuf in an in process pixbuf debugging window. + * + * We cant use EelImage for this because part of the reason + * for pixbuf debugging is to debug EelImage itself. + */ + +#define DEBUG_TYPE_PIXBUF_VIEWER debug_pixbuf_viewer_get_type() +#define DEBUG_PIXBUF_VIEWER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEBUG_TYPE_PIXBUF_VIEWER, DebugPixbufViewer)) +#define DEBUG_IS_PIXBUF_VIEWER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEBUG_TYPE_PIXBUF_VIEWER)) + +typedef struct DebugPixbufViewer DebugPixbufViewer; +typedef struct DebugPixbufViewerClass DebugPixbufViewerClass; + +static GType debug_pixbuf_viewer_get_type (void); +static void debug_pixbuf_viewer_set_pixbuf (DebugPixbufViewer *viewer, + GdkPixbuf *pixbuf); + +struct DebugPixbufViewer +{ + GtkWidget widget; + GdkPixbuf *pixbuf; +}; + +struct DebugPixbufViewerClass +{ + GtkWidgetClass parent_class; +}; + +/* GtkObjectClass methods */ +static void debug_pixbuf_viewer_class_init (DebugPixbufViewerClass *pixbuf_viewer_class); +static void debug_pixbuf_viewer_init (DebugPixbufViewer *pixbuf_viewer); +static void debug_pixbuf_viewer_finalize (GObject *object); + +/* GtkWidgetClass methods */ +static void debug_pixbuf_viewer_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static int debug_pixbuf_viewer_expose_event (GtkWidget *widget, + GdkEventExpose *event); + +EEL_CLASS_BOILERPLATE (DebugPixbufViewer, debug_pixbuf_viewer, GTK_TYPE_WIDGET) + +static void +debug_pixbuf_viewer_class_init (DebugPixbufViewerClass *pixbuf_viewer_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (pixbuf_viewer_class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (pixbuf_viewer_class); + + object_class->finalize = debug_pixbuf_viewer_finalize; + widget_class->size_request = debug_pixbuf_viewer_size_request; + widget_class->expose_event = debug_pixbuf_viewer_expose_event; +} + +static void +debug_pixbuf_viewer_init (DebugPixbufViewer *viewer) +{ + gtk_widget_set_can_focus (GTK_WIDGET (viewer), FALSE); + gtk_widget_set_has_window (GTK_WIDGET (viewer), FALSE); +} + +static void +debug_pixbuf_viewer_finalize (GObject *object) +{ + DebugPixbufViewer *viewer; + + viewer = DEBUG_PIXBUF_VIEWER (object); + eel_gdk_pixbuf_unref_if_not_null (viewer->pixbuf); + viewer->pixbuf = NULL; + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +debug_pixbuf_viewer_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + DebugPixbufViewer *viewer; + EelDimensions dimensions; + + g_assert (DEBUG_IS_PIXBUF_VIEWER (widget)); + g_assert (requisition != NULL); + + viewer = DEBUG_PIXBUF_VIEWER (widget); + + if (viewer->pixbuf != NULL) + { + dimensions = eel_gdk_pixbuf_get_dimensions (viewer->pixbuf); + } + else + { + dimensions = eel_dimensions_empty; + } + + requisition->width = MAX (2, dimensions.width); + requisition->height = MAX (2, dimensions.height); +} + +static int +debug_pixbuf_viewer_expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + DebugPixbufViewer *viewer; + EelIRect clipped_dirty_area; + EelIRect dirty_area; + EelIRect bounds; + GtkAllocation allocation; + + g_assert (DEBUG_IS_PIXBUF_VIEWER (widget)); + g_assert (event != NULL); + g_assert (event->window == gtk_widget_get_window (widget)); + g_assert (gtk_widget_get_realized (widget)); + + viewer = DEBUG_PIXBUF_VIEWER (widget); + + if (viewer->pixbuf == NULL) + { + return TRUE; + } + + gtk_widget_get_allocation (widget, &allocation); + bounds.x0 = allocation.x + (allocation.width - gdk_pixbuf_get_width (viewer->pixbuf)) / 2; + bounds.y0 = allocation.y + (allocation.height - gdk_pixbuf_get_height (viewer->pixbuf)) / 2; + bounds.x1 = bounds.x0 + gdk_pixbuf_get_width (viewer->pixbuf); + bounds.y1 = bounds.y0 + gdk_pixbuf_get_height (viewer->pixbuf); + + /* Clip the dirty area to the screen; bail if no work to do */ + dirty_area = eel_gdk_rectangle_to_eel_irect (event->area); + clipped_dirty_area = eel_gdk_window_clip_dirty_area_to_screen (event->window, + dirty_area); + if (!eel_irect_is_empty (&clipped_dirty_area)) + { + EelIRect clipped_bounds; + + eel_irect_intersect (&clipped_bounds, &bounds, &clipped_dirty_area); + + if (!eel_irect_is_empty (&clipped_bounds)) + { + g_assert (clipped_bounds.x0 >= bounds.x0); + g_assert (clipped_bounds.y0 >= bounds.y0); + + eel_gdk_pixbuf_draw_to_drawable (viewer->pixbuf, + event->window, + gtk_widget_get_style (widget)->white_gc, + clipped_bounds.x0 - bounds.x0, + clipped_bounds.y0 - bounds.y0, + clipped_bounds, + GDK_RGB_DITHER_NONE, + GDK_PIXBUF_ALPHA_BILEVEL, + EEL_STANDARD_ALPHA_THRESHHOLD); + } + } + + bounds.x0 -= 1; + bounds.y0 -= 1; + bounds.x1 += 1; + bounds.y1 += 1; + + return TRUE; +} + +static void +debug_pixbuf_viewer_set_pixbuf (DebugPixbufViewer *viewer, GdkPixbuf *pixbuf) +{ + g_assert (DEBUG_IS_PIXBUF_VIEWER (viewer)); + + if (pixbuf != viewer->pixbuf) + { + eel_gdk_pixbuf_unref_if_not_null (viewer->pixbuf); + eel_gdk_pixbuf_ref_if_not_null (pixbuf); + viewer->pixbuf = pixbuf; + gtk_widget_queue_resize (GTK_WIDGET (viewer)); + } +} + +/** + * eel_debug_draw_rectangle_and_cross: + * @rectangle: Rectangle bounding the rectangle. + * @color: Color to use for the rectangle and cross. + * + * Draw a rectangle and cross. Useful for debugging exposure events. + */ +void +eel_debug_draw_rectangle_and_cross (GdkDrawable *drawable, + EelIRect rectangle, + guint32 color, + gboolean draw_cross) +{ + GdkGC *gc; + GdkColor color_gdk = { 0 }; + + int width; + int height; + + g_return_if_fail (drawable != NULL); + g_return_if_fail (!eel_irect_is_empty (&rectangle)); + + width = rectangle.x1 - rectangle.x0; + height = rectangle.y1 - rectangle.y0; + + gc = gdk_gc_new (drawable); + gdk_gc_set_function (gc, GDK_COPY); + + color_gdk.red = ((color >> 16) & 0xff) << 8; + color_gdk.green = ((color >> 8) & 0xff) << 8; + color_gdk.blue = ((color ) & 0xff) << 8; + gdk_colormap_alloc_color ( + gdk_drawable_get_colormap (drawable), + &color_gdk, FALSE, FALSE); + gdk_gc_set_rgb_fg_color (gc, &color_gdk); + + gdk_draw_rectangle (drawable, + gc, + FALSE, + rectangle.x0, + rectangle.y0, + width - 1, + height - 1); + + if (draw_cross) + { + gdk_draw_line (drawable, + gc, + rectangle.x0, + rectangle.y0, + rectangle.x0 + width - 1, + rectangle.y0 + height - 1); + + gdk_draw_line (drawable, + gc, + rectangle.x0 + width - 1, + rectangle.y0, + rectangle.x0, + rectangle.y0 + height - 1); + } + + g_object_unref (gc); +} + +/** + * eel_debug_show_pixbuf_in_external_viewer: + * @pixbuf: Pixbuf to show. + * @viewer_name: Viewer name. + * + * Show the given pixbuf in an external out of process viewer. + * This is very useful for debugging pixbuf stuff. + * + * Perhaps this function should be #ifdef DEBUG or something like that. + */ +void +eel_debug_show_pixbuf_in_external_viewer (const GdkPixbuf *pixbuf, + const char *viewer_name) +{ + char *command; + char *file_name; + gboolean save_result; + int ignore; + int fd; + + g_return_if_fail (pixbuf != NULL); + g_return_if_fail (viewer_name != NULL); + + file_name = g_strdup ("/tmp/eel-debug-png-file-XXXXXX"); + + fd = g_mkstemp (file_name); + if (fd == -1) + { + g_free (file_name); + file_name = g_strdup_printf ("/tmp/isis-debug-png-file-%d", getpid ()); + } + else + { + close (fd); + } + + save_result = eel_gdk_pixbuf_save_to_file (pixbuf, file_name); + + if (save_result == FALSE) + { + g_warning ("Failed to save '%s'", file_name); + g_free (file_name); + return; + } + + command = g_strdup_printf ("%s %s", viewer_name, file_name); + + ignore = system (command); + g_free (command); + remove (file_name); + g_free (file_name); +} + +static GtkWidget *debug_window = NULL; +static GtkWidget *debug_image = NULL; + +static void +debug_delete_event (GtkWidget *widget, GdkEvent *event, gpointer callback_data) +{ + gtk_widget_hide (widget); +} + +static void +destroy_debug_window (void) +{ + if (debug_window != NULL) + { + gtk_widget_destroy (debug_window); + debug_window = NULL; + } +} + +/** + * eel_debug_show_pixbuf_in: + * @pixbuf: Pixbuf to show. + * + * Show the given pixbuf in an in process window. + */ +void +eel_debug_show_pixbuf (GdkPixbuf *pixbuf) +{ + if (debug_window == NULL) + { + GtkWidget *vbox; + + debug_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (debug_window), vbox); + gtk_window_set_title (GTK_WINDOW (debug_window), "Pixbuf debugging"); + gtk_window_set_resizable (GTK_WINDOW (debug_window), TRUE); + gtk_container_set_border_width (GTK_CONTAINER (debug_window), 10); + g_signal_connect (debug_window, "delete_event", G_CALLBACK (debug_delete_event), NULL); + + debug_image = gtk_widget_new (debug_pixbuf_viewer_get_type (), NULL); + + gtk_box_pack_start (GTK_BOX (vbox), debug_image, TRUE, TRUE, 0); + + eel_gtk_widget_set_background_color (debug_window, "white"); + + eel_debug_call_at_shutdown (destroy_debug_window); + + gtk_widget_show (debug_image); + gtk_widget_show (vbox); + } + + gtk_widget_show (debug_window); + debug_pixbuf_viewer_set_pixbuf (DEBUG_PIXBUF_VIEWER (debug_image), pixbuf); + + gdk_window_clear_area_e (gtk_widget_get_window (debug_window), 0, 0, -1, -1); +} + +void +eel_debug_pixbuf_draw_point (GdkPixbuf *pixbuf, + int x, + int y, + guint32 color, + int opacity) +{ + EelDimensions dimensions; + guchar *pixels; + gboolean has_alpha; + guint pixel_offset; + guint rowstride; + guchar red; + guchar green; + guchar blue; + guchar alpha; + guchar *offset; + + g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf)); + g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT); + g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE); + + dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf); + + g_return_if_fail (x >= 0 && x < dimensions.width); + g_return_if_fail (y >= 0 && y < dimensions.height); + + pixels = gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + pixel_offset = has_alpha ? 4 : 3; + + red = EEL_RGBA_COLOR_GET_R (color); + green = EEL_RGBA_COLOR_GET_G (color); + blue = EEL_RGBA_COLOR_GET_B (color); + alpha = (guchar) opacity; + + offset = pixels + y * rowstride + x * pixel_offset; + + *(offset + 0) = red; + *(offset + 1) = green; + *(offset + 2) = blue; + + if (has_alpha) + { + *(offset + 3) = alpha; + } +} + +void +eel_debug_pixbuf_draw_rectangle (GdkPixbuf *pixbuf, + gboolean filled, + int x0, + int y0, + int x1, + int y1, + guint32 color, + int opacity) +{ + EelDimensions dimensions; + int x; + int y; + + g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf)); + g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT); + g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE); + + dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf); + + if (x0 == -1) + { + x0 = 0; + } + + if (y0 == -1) + { + y0 = 0; + } + + if (x1 == -1) + { + x1 = dimensions.width - 1; + } + + if (y1 == -1) + { + y1 = dimensions.height - 1; + } + + g_return_if_fail (x1 > x0); + g_return_if_fail (y1 > y0); + g_return_if_fail (x0 >= 0 && x0 < dimensions.width); + g_return_if_fail (y0 >= 0 && y0 < dimensions.height); + g_return_if_fail (x1 >= 0 && x1 < dimensions.width); + g_return_if_fail (y1 >= 0 && y1 < dimensions.height); + + if (filled) + { + for (y = y0; y <= y1; y++) + { + for (x = x0; x <= x1; x++) + { + eel_debug_pixbuf_draw_point (pixbuf, x, y, color, opacity); + } + } + } + else + { + /* Top / Bottom */ + for (x = x0; x <= x1; x++) + { + eel_debug_pixbuf_draw_point (pixbuf, x, y0, color, opacity); + eel_debug_pixbuf_draw_point (pixbuf, x, y1, color, opacity); + } + + /* Left / Right */ + for (y = y0; y <= y1; y++) + { + eel_debug_pixbuf_draw_point (pixbuf, x0, y, color, opacity); + eel_debug_pixbuf_draw_point (pixbuf, x1, y, color, opacity); + } + } +} + +void +eel_debug_pixbuf_draw_rectangle_inset (GdkPixbuf *pixbuf, + gboolean filled, + int x0, + int y0, + int x1, + int y1, + guint32 color, + int opacity, + int inset) +{ + EelDimensions dimensions; + + g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf)); + g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT); + g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE); + + dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf); + + if (x0 == -1) + { + x0 = 0; + } + + if (y0 == -1) + { + y0 = 0; + } + + if (x1 == -1) + { + x1 = dimensions.width - 1; + } + + if (y1 == -1) + { + y1 = dimensions.height - 1; + } + + x0 += inset; + y0 += inset; + x1 -= inset; + y1 -= inset; + + g_return_if_fail (x1 > x0); + g_return_if_fail (y1 > y0); + + eel_debug_pixbuf_draw_rectangle (pixbuf, filled, x0, y0, x1, y1, color, opacity); +} |