/* 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; }
/* This function does all the work. */ static GdkPixbuf * pixbuf_create_from_xpm (gpointer handle, xfwmColorSymbol *color_sym) { gchar pixel_str[32]; const gchar *buffer; gchar *name_buf; gint w, h, n_col, cpp, items; gint cnt, xcnt, ycnt, wbytes, n; GHashTable *color_hash; XPMColor *colors, *color, *fallbackcolor; guchar *pixtmp; GdkPixbuf *pixbuf; fallbackcolor = NULL; buffer = file_buffer (op_header, handle); if (!buffer) { g_warning ("Cannot read Pixmap header"); return NULL; } items = sscanf (buffer, "%d %d %d %d", &w, &h, &n_col, &cpp); if (items != 4) { g_warning ("Pixmap definition contains invalid number attributes (expecting at least 4, got %i)", items); return NULL; } if ((w <= 0) || (h <= 0) || (cpp <= 0) || (cpp >= 32) || (n_col <= 0) || (n_col >= G_MAXINT / (cpp + 1)) || (n_col >= G_MAXINT / (gint) sizeof (XPMColor))) { g_warning ("Pixmap definition contains invalid attributes"); 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_hash_table_destroy (color_hash); g_warning ("Cannot allocate buffer"); return NULL; } colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col); if (!colors) { g_hash_table_destroy (color_hash); g_free (name_buf); g_warning ("Cannot allocate colors for Pixmap"); return NULL; } for (cnt = 0; cnt < n_col; cnt++) { gchar *color_name; buffer = file_buffer (op_cmap, handle); if (!buffer) { g_hash_table_destroy (color_hash); g_free (name_buf); g_free (colors); g_warning ("Cannot load colormap attributes"); 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, color_sym); 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; } 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, TRUE, 8, w, h); if (!pixbuf) { g_hash_table_destroy (color_hash); g_free (colors); g_free (name_buf); g_warning ("Cannot allocate Pixbuf"); return NULL; } wbytes = w * cpp; for (ycnt = 0; ycnt < h; ycnt++) { pixtmp = gdk_pixbuf_get_pixels (pixbuf) + ycnt * gdk_pixbuf_get_rowstride(pixbuf); buffer = file_buffer (op_body, handle); if ((!buffer) || (wbytes > (gint) strlen (buffer))) { continue; } for (n = 0, cnt = 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 (color->transparent) { *pixtmp++ = 0; } else { *pixtmp++ = 0xFF; } } } g_hash_table_destroy (color_hash); g_free (colors); g_free (name_buf); return pixbuf; }