From 85ab8496645e958bbc36b64cd2827e0c47c761eb Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Tue, 28 Jan 2014 19:34:17 -0500 Subject: [PATCH] lbxgui: Use Cairo for drawing. The GDK stuff is deprecated so let's convert all the GDK drawing bits to Cairo. Unlike GDK, Cairo does not include an XBM loader so we have to implement that from scratch. Keep the GDK pixbuf bits around for now. --- src/gui/lbxgui.c | 121 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 30 deletions(-) diff --git a/src/gui/lbxgui.c b/src/gui/lbxgui.c index bb9e5e5..14da378 100644 --- a/src/gui/lbxgui.c +++ b/src/gui/lbxgui.c @@ -1,7 +1,7 @@ /* * 2ooM: The Master of Orion II Reverse Engineering Project * Graphical tool for inspecting LBX archives. - * Copyright (C) 2010, 2014 Nick Bowler + * Copyright © 2010, 2014 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,8 +17,10 @@ * along with this program. If not, see . */ #include +#include #include #include +#include #include #include @@ -33,7 +35,9 @@ static GtkTreeStore *archives; static GtkBuilder *builder; static GtkWidget *canvas; -static GdkGC *bg_gc; + +static cairo_pattern_t *bg_pattern; +static bool bg_stipple; static struct lbx_image *image; @@ -135,17 +139,32 @@ static gboolean timeout(gpointer data) gboolean canvas_expose(GtkWidget *canvas, GdkEventExpose *event, gpointer data) { + cairo_t *cr; + if (!framebuf) return FALSE; - gdk_draw_rectangle(canvas->window, bg_gc, TRUE, - event->area.x, event->area.y, - event->area.width, event->area.height); + cr = gdk_cairo_create(canvas->window); + + if (bg_stipple) { + cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); + gdk_cairo_rectangle(cr, &event->area); + cairo_fill(cr); + + cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); + gdk_cairo_rectangle(cr, &event->area); + cairo_mask(cr, bg_pattern); + } else { + cairo_set_source_rgb(cr, 0, 0, 0); + gdk_cairo_rectangle(cr, &event->area); + cairo_fill(cr); + } + + gdk_cairo_set_source_pixbuf(cr, framebuf, 0, 0); + gdk_cairo_rectangle(cr, &event->area); + cairo_fill(cr); - gdk_draw_pixbuf(canvas->window, NULL, framebuf, - event->area.x, event->area.y, event->area.x, event->area.y, - event->area.width, event->area.height, - GDK_RGB_DITHER_NORMAL, 0, 0); + cairo_destroy(cr); return TRUE; } @@ -280,19 +299,7 @@ void set_image(GtkComboBox *combo) void set_background(GtkCheckMenuItem *item, gpointer data) { - GdkColor black = { .red = 0, .green = 0, .blue = 0 }; - GdkColor dark = { .red = 0x6666, .green = 0x6666, .blue = 0x6666 }; - GdkColor light = { .red = 0x9999, .green = 0x9999, .blue = 0x9999 }; - - if (gtk_check_menu_item_get_active(item)) { - /* Enable stippled background. */ - gdk_gc_set_rgb_fg_color(bg_gc, &dark); - gdk_gc_set_rgb_bg_color(bg_gc, &light); - gdk_gc_set_fill(bg_gc, GDK_OPAQUE_STIPPLED); - } else { - gdk_gc_set_rgb_fg_color(bg_gc, &black); - gdk_gc_set_fill(bg_gc, GDK_SOLID); - } + bg_stipple = gtk_check_menu_item_get_active(item); redraw_image(); } @@ -444,18 +451,72 @@ static void init_interface(void) init_combobox(builder, "overchooser"); } -static void init_background(GdkDrawable *drawable) +/* + * Reverse the bits in each byte of a 32-bit word. + */ +static guint32 reverse4b(guint32 v) { - GtkWidget *check; - GdkBitmap *bitmap; + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); + v = ((v >> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4); + return v; +} + +static cairo_surface_t * +xbm_to_cairo(const void *xbmdata, int width, int height) +{ + const unsigned char *data = xbmdata; + unsigned char *sdata; + cairo_surface_t *s; + int stride; + + s = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height); + if (cairo_surface_status(s) != CAIRO_STATUS_SUCCESS) + return s; + + sdata = cairo_image_surface_get_data(s); + stride = cairo_image_surface_get_stride(s); + assert(stride % 4 == 0); + width = (width + 7) / 8; + + cairo_surface_flush(s); + for (int y = 0; y < height; y++) { + /* + * Cairo is nuts. The mapping of bits to pixels in the A1 + * format actually depends on the host *byte* ordering, so + * we actually need these two separate cases. + */ + if (G_BYTE_ORDER == G_LITTLE_ENDIAN) { + /* Easy, XBM and Cairo match in this case */ + memcpy(sdata, data, width); + } else { + /* No such luck, we have to convert manually */ + for (int x = 0; x < width; x += 4) { + guint32 word = 0; + + memcpy(&word, data+x, MIN(4, width-x)); + word = reverse4b(word); + memcpy(sdata+x, &word, 4); + } + } - bg_gc = gdk_gc_new(canvas->window); + sdata += stride; + data += width; + } + cairo_surface_mark_dirty(s); + + return s; +} - bitmap = gdk_bitmap_create_from_data(drawable, (gchar *)bg_bits, - bg_width, bg_height); +static void init_background(GdkDrawable *drawable) +{ + cairo_surface_t *bg; + GtkWidget *check; - gdk_gc_set_stipple(bg_gc, bitmap); - gdk_gc_set_ts_origin(bg_gc, 0, 0); + bg = xbm_to_cairo(bg_bits, bg_width, bg_height); + bg_pattern = cairo_pattern_create_for_surface(bg); + cairo_pattern_set_extend(bg_pattern, CAIRO_EXTEND_REPEAT); + cairo_surface_destroy(bg); check = GTK_WIDGET(gtk_builder_get_object(builder, "menu-background")); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(check), TRUE); -- 2.43.2