]> Pileus Git - ~andy/gtk/commitdiff
Support saving ICOs and CURs.
authorMatthias Clasen <matthiasc@src.gnome.org>
Thu, 3 Jul 2003 23:37:34 +0000 (23:37 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Thu, 3 Jul 2003 23:37:34 +0000 (23:37 +0000)
gdk-pixbuf/ChangeLog
gdk-pixbuf/gdk-pixbuf-io.c
gdk-pixbuf/io-ico.c

index ecd6c68ffbc22cb9937c9eeafc9a4fc7c3478477..334a2dd71588bd369844a8bb41e7b1f5580dc175 100644 (file)
@@ -1,3 +1,11 @@
+2003-07-04  Matthias Clasen  <maclas@gmx.de>
+
+       * gdk-pixbuf-io.c: Document ICO save parameters.
+
+       * io-ico.c: Support saving of ICOs and CURs. Currently, only single-image ICOs are produced,
+       but the code for multi-image ICOs is already in place. The saver understands the options
+       "depth" (can be "32", "24" or "16") and "x_hot"/"y_hot" for hotspot coordinates of CURs.
+
 2003-06-28  Matthias Clasen  <maclas@gmx.de>
 
        * io-ico.c (DecodeHeader): Stop discriminating against 32bpp ICOs a): Use the byte 
index f3f6f4b320179ceff5496649ab2f482f53d2ecea..afb376eeed4326a59d4827090bc0a55d44781552 100644 (file)
@@ -891,8 +891,8 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
  * @error: return location for error, or %NULL
  * @Varargs: list of key-value save options
  *
- * Saves pixbuf to a file in @type, which is currently "jpeg" or
- * "png".  If @error is set, %FALSE will be returned. Possible errors include 
+ * Saves pixbuf to a file in @type, which is currently "jpeg", "png" or
+ * "ico".  If @error is set, %FALSE will be returned. Possible errors include 
  * those in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
  *
  * The variable argument list should be %NULL-terminated; if not empty,
@@ -908,6 +908,9 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
  * Text chunks can be attached to PNG images by specifying parameters of
  * the form "tEXt::key", where key is an ASCII string of length 1-79.
  * The values are UTF-8 encoded strings. 
+ * ICO images can be saved in depth 16, 24, or 32, by using the "depth"
+ * parameter. When the ICO saver is given "x_hot" and "y_hot" parameters,
+ * it produces a CUR instead of an ICO.
  *
  * Return value: whether an error was set
  **/
index 5adf43f2157baa6c2ca9e2a3e17e1691f93e133a..482cff29a99a59d931adc14c4135b8cff7294d6d 100644 (file)
@@ -36,6 +36,7 @@ Known bugs:
 
 #include <config.h>
 #include <stdio.h>
+#include <stdlib.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -837,12 +838,327 @@ gdk_pixbuf__ico_image_load_increment(gpointer data,
        return TRUE;
 }
 
+/* saving ICOs */ 
+
+static gint
+write8 (FILE     *f,
+       guint8   *data,
+       gint      count)
+{
+  gint bytes;
+  gint written;
+
+  written = 0;
+  while (count > 0)
+    {
+      bytes = fwrite ((char*) data, sizeof (char), count, f);
+      if (bytes <= 0)
+        break;
+      count -= bytes;
+      data += bytes;
+      written += bytes;
+    }
+
+  return written;
+}
+
+static gint
+write16 (FILE     *f,
+        guint16  *data,
+        gint      count)
+{
+  gint i;
+
+  for (i = 0; i < count; i++)
+         data[i] = GUINT16_TO_LE (data[i]);
+
+  return write8 (f, (guint8*) data, count * 2);
+}
+
+static gint
+write32 (FILE     *f,
+        guint32  *data,
+        gint      count)
+{
+  gint i;
+
+  for (i = 0; i < count; i++)
+         data[i] = GUINT32_TO_LE (data[i]);
+  
+  return write8 (f, (guint8*) data, count * 4);
+}
+
+typedef struct _IconEntry IconEntry;
+struct _IconEntry {
+       gint width;
+       gint height;
+       gint depth;
+       gint hot_x;
+       gint hot_y;
+
+       guint8 n_colors;
+       guint32 *colors;
+       guint xor_rowstride;
+       guint8 *xor;
+       guint and_rowstride;
+       guint8 *and;
+};
+
+static gboolean
+fill_entry (IconEntry *icon, 
+           GdkPixbuf *pixbuf, 
+           gint       hot_x, 
+           gint       hot_y, 
+           GError   **error) 
+ {
+       guchar *p, *pixels, *and, *xor;
+       gint n_channels, v, x, y;
+
+       if (icon->width > 255 || icon->height > 255) {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_BAD_OPTION,
+                            _("Image too large to be saved as ICO"));
+               return FALSE;
+       } 
+       
+       if (hot_x > -1 && hot_y > -1) {
+               icon->hot_x = hot_x;
+               icon->hot_x = hot_y;
+               if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
+                       g_set_error (error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_BAD_OPTION,
+                                    _("Cursor hotspot outside image"));
+                       return FALSE;
+               }
+       }
+       else {
+               icon->hot_x = -1;
+               icon->hot_y = -1;
+       }
+       
+       switch (icon->depth) {
+       case 32:
+               icon->xor_rowstride = icon->width * 4;
+               break;
+       case 24:
+               icon->xor_rowstride = icon->width * 3;
+               break;
+       case 16:
+               icon->xor_rowstride = icon->width * 2;
+               break;
+       default:
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_BAD_OPTION,
+                            _("Unsupported depth for ICO file: %d"), icon->depth);
+               return FALSE;
+       }
+
+       if ((icon->xor_rowstride % 4) != 0)             
+               icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
+       icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
+
+       icon->and_rowstride = icon->width / 8;
+       if ((icon->and_rowstride % 4) != 0)             
+               icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
+       icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
+
+       pixels = gdk_pixbuf_get_pixels (pixbuf);
+       n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+       for (y = 0; y < icon->height; y++) {
+               p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
+               and = icon->and + icon->and_rowstride * y;
+               xor = icon->xor + icon->xor_rowstride * y;
+               for (x = 0; x < icon->width; x++) {
+                       switch (icon->depth) {
+                       case 32:
+                               xor[0] = p[2];
+                               xor[1] = p[1];
+                               xor[2] = p[0];
+                               xor[3] = 0xff;
+                               if (n_channels == 4) {
+                                       xor[3] = p[3];
+                                       if (p[3] < 0x80)
+                                               *and |= 1 << (7 - x % 8);
+                               }
+                               xor += 4;
+                               break;
+                       case 24:
+                               xor[0] = p[2];
+                               xor[1] = p[1];
+                               xor[2] = p[0];
+                               if (n_channels == 4 && p[3] < 0x80)
+                                       *and |= 1 << (7 - x % 8);
+                               xor += 3;
+                               break;
+                       case 16:
+                               v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
+                               xor[0] = v & 0xff;
+                               xor[1] = v >> 8;
+                               if (n_channels == 4 && p[3] < 0x80)
+                                       *and |= 1 << (7 - x % 8);
+                               xor += 2;
+                               break;
+                       }
+                       
+                       p += n_channels;
+                       if (x % 8 == 7) 
+                               and++;
+               }
+       }
+
+       return TRUE;
+}
+
+static void
+free_entry (IconEntry *icon)
+{
+       g_free (icon->colors);
+       g_free (icon->and);
+       g_free (icon->xor);
+       g_free (icon);
+}
+
+static void
+write_icon (FILE *f, GSList *entries)
+{
+       IconEntry *icon;
+       GSList *entry;
+       guint8 bytes[4];
+       guint16 words[4];
+       guint32 dwords[6];
+       gint type;
+       gint n_entries;
+       gint offset;
+       gint size;
+
+       if (((IconEntry *)entries->data)->hot_x > -1)
+               type = 2;
+       else 
+               type = 1;
+       n_entries = g_slist_length (entries);
+
+       /* header */
+       words[0] = 0;
+       words[1] = type;
+       words[2] = n_entries;
+       write16 (f, words, 3);
+       
+       offset = 6 + 16 * n_entries;
+
+       for (entry = entries; entry; entry = entry->next) {
+               icon = (IconEntry *)entry->data;
+               size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
+               
+               /* directory entry */
+               bytes[0] = icon->width;
+               bytes[1] = icon->height;
+               bytes[2] = icon->n_colors;
+               bytes[3] = 0;
+               write8 (f, bytes, 4);
+               if (type == 1) {
+                       words[0] = 1;
+                       words[1] = icon->depth;
+               }
+               else {
+                       words[0] = icon->hot_x;
+                       words[1] = icon->hot_y;
+               }
+               write16 (f, words, 2);
+               dwords[0] = size;
+               dwords[1] = offset;
+               write32 (f, dwords, 2);
+
+               offset += size;
+       }
+
+       for (entry = entries; entry; entry = entry->next) {
+               icon = (IconEntry *)entry->data;
+
+               /* bitmap header */
+               dwords[0] = 40;
+               dwords[1] = icon->width;
+               dwords[2] = icon->height * 2;
+               write32 (f, dwords, 3);
+               words[0] = 1;
+               words[1] = icon->depth;
+               write16 (f, words, 2);
+               dwords[0] = 0;
+               dwords[1] = 0;
+               dwords[2] = 0;
+               dwords[3] = 0;
+               dwords[4] = 0;
+               dwords[5] = 0;
+               write32 (f, dwords, 6);
+
+               /* image data */
+               write8 (f, icon->xor, icon->xor_rowstride * icon->height);
+               write8 (f, icon->and, icon->and_rowstride * icon->height);
+       }
+}
+
+static gboolean
+gdk_pixbuf__ico_image_save (FILE          *f, 
+                            GdkPixbuf     *pixbuf, 
+                            gchar        **keys,
+                            gchar        **values,
+                            GError       **error)
+{
+       gint hot_x, hot_y;
+       IconEntry *icon;
+       GSList *entries = NULL;
+
+       /* support only single-image ICOs for now */
+       icon = g_new0 (IconEntry, 1);
+       icon->width = gdk_pixbuf_get_width (pixbuf);
+       icon->height = gdk_pixbuf_get_height (pixbuf);
+       icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
+       hot_x = -1;
+       hot_y = -1;
+       
+       /* parse options */
+       if (keys && *keys) {
+               gchar **kiter;
+               gchar **viter;
+               
+               for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
+                       char *endptr;
+                       if (strcmp (*kiter, "depth") == 0) {
+                               sscanf (*viter, "%d", &icon->depth);
+                       }
+                       else if (strcmp (*kiter, "x_hot") == 0) {
+                               hot_x = strtol (*viter, &endptr, 10);
+                       }
+                       else if (strcmp (*kiter, "y_hot") == 0) {
+                               hot_y = strtol (*viter, &endptr, 10);
+                       }
+
+               }
+       }
+
+       if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
+               free_entry (icon);
+               return FALSE;
+       }
+
+       entries = g_slist_append (entries, icon); 
+       write_icon (f, entries);
+
+       g_slist_foreach (entries, (GFunc)free_entry, NULL);
+       g_slist_free (entries);
+
+       return TRUE;
+}
+
 void
 MODULE_ENTRY (ico, fill_vtable) (GdkPixbufModule *module)
 {
        module->begin_load = gdk_pixbuf__ico_image_begin_load;
        module->stop_load = gdk_pixbuf__ico_image_stop_load;
        module->load_increment = gdk_pixbuf__ico_image_load_increment;
+        module->save = gdk_pixbuf__ico_image_save;
 }
 
 void
@@ -868,7 +1184,7 @@ MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
        info->description = N_("The ICO image format");
        info->mime_types = mime_types;
        info->extensions = extensions;
-       info->flags = 0;
+       info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
 }