inline pixbufs



Hi,

Changes I just made to my local copy of inline pixbuf code:

 - fixed the unaligned read issue Darin mentioned, or at least made
   some attempt to do so

 - use "GdkPixbuf" instead of random number for the magic (though, 
   I just cast the string bytes to an int, and that might be sort 
   of busted ;-)

The changes aren't tested, I'm building GTK again now to do that.

Changes I don't think we should make:

 - Match Bonobo implementation; the Bonobo implementation is almost
   the same, but lacks the format tag, and I think the format tag is
   important; so I'd like to see Bonobo cut-and-paste the stuff from
   gdk-pixbuf once we finalize it.

 - GdkPixdataType; we already have a data type that stores pixdata,
   it's called GdkPixbuf. The point of new_from_inline() is to take a
   GdkPixbuf to and from a byte stream. I don't understand the
   advantage of an intermediate type; certainly we shouldn't have an
   intermediate type in the public API.
   
   Conceivably it makes the autogenerated inline code files more
   human-editable, but it also adds complexity to them and makes the
   code harder to understand, and I think it's pretty useless to
   human-edit these files anyway.

 - Remove rowstride; we're just dumping a pixbuf to a stream, the
   stream shouldn't lose information that was present in the original
   pixbuf. Also, as Owen points out, we need this to be able to align
   read-only static data on natural boundaries. Clearly rowstride only
   exists in GDK_PIXBUF_INLINE_RAW format, however, and the loader for
   the inlined pixbuf could choose to ignore the rowstride in the
   copy_pixels case.

 - Use g_malloc(); we really want g_try_malloc() for pixel buffers,
   because the buffers are potentially quite huge, and an image viewer
   app (for example) shouldn't simply fall over if you try to load a
   huge image. Granted, this probably won't happen in the inline case,
   if you inline an image much over 48x48 you are likely insane. So
   there it's just consistency with the other gdk_pixbuf_new()
   routines.

Changes that are pending:

 - RLE encoding; this would go in gdk_pixbuf_new_from_inline(), add
   GDK_PIXBUF_INLINE_RLE to the format switch, then write a function
   to load it. To make it useful you'd also need to hack
   make-inline-pixbuf.c to write one of these, the problem is that
   make-inline-pixbuf.c has no real option parsing, which makes it
   annoying to add a --encode=rle option. Because the public API
   contains no way to create an inline pixbuf, there is currently no
   public API change involved. If there were such an inline-creation
   function, the API change would just be adding the
   GDK_PIXBUF_INLINE_RLE enum value.

   Anyway, basically this is trivial except for the annoying
   option-parsing issue.

 - Documentation. I would like to see gtk-reference sorted out (or is
   it already sorted out?) for 2.0, so we can put docs in there. I'm
   not sure just adding more text files is sensible.

My overall thought on the API is that it should be kept super-minimal;
right now the public interface is "make-inline-pixbuf src_image" and
gdk_pixbuf_new_from_inline(), and that's it. I'm pretty sure we don't
want this to become some sort of alternative to PNG, and I wouldn't
even like the Gimp for example to be able to load/save it directly,
it's better to use PNG as the disk format and convert to/from inline
data in the Makefile. The world does not need another format...

Havoc


Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/ChangeLog,v
retrieving revision 1.242
diff -u -u -r1.242 ChangeLog
--- ChangeLog	2000/07/22 23:50:18	1.242
+++ ChangeLog	2000/07/26 16:45:26
@@ -1,3 +1,20 @@
+2000-06-26  Havoc Pennington  <hp@redhat.com>
+
+	* gdk-pixbuf-private.h (GdkPixbufInlineFormat): include an 
+	enum here for the known formats of inlined pixbufs.
+	Also, #define our file magic here.
+
+2000-06-23  Havoc Pennington  <hp@redhat.com>
+
+	* make-inline-pixbuf.c: Small program that creates C variable
+	declarations of inline pixbuf data. This can be read 
+	by gdk_pixbuf_new_from_inline.
+
+	* gdk-pixbuf.h (gdk_pixbuf_new_from_inline): New function to read
+	inline pixbuf data and create a pixbuf from it.	
+
+	* gdk-pixbuf-data.c (gdk_pixbuf_new_from_inline): implement here
+
 2000-07-23  Tor Lillqvist  <tml@iki.fi>
 
 	Win32 build setup:
@@ -58,7 +75,7 @@
 
 	* Makefile.am (libgdk_pixbuf_la_LDFLAGS): Use GTK+ version
 	soname scheme for gdk-pixbuf.
-
+	
 2000-06-21  Havoc Pennington  <hp@pobox.com>
 
 	* gdk-pixbuf.c: Convert GdkPixbuf to GObject, leaving it opaque
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/Makefile.am,v
retrieving revision 1.73
diff -u -u -r1.73 Makefile.am
--- Makefile.am	2000/07/22 23:50:18	1.73
+++ Makefile.am	2000/07/26 16:45:26
@@ -138,6 +138,12 @@
 
 test_gdk_pixbuf_LDADD = $(LDADDS)
 
+bin_PROGRAMS=make-inline-pixbuf
+
+make_inline_pixbuf_SOURCES=make-inline-pixbuf.c
+
+make_inline_pixbuf_LDADD = $(LDADDS) -lgmodule
+
 GDK_PIXBUF_LIBS = $(GLIB_LIBS)
 
 #
Index: gdk-pixbuf-data.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf-data.c,v
retrieving revision 1.11
diff -u -u -r1.11 gdk-pixbuf-data.c
--- gdk-pixbuf-data.c	2000/07/11 04:46:11	1.11
+++ gdk-pixbuf-data.c	2000/07/26 16:45:26
@@ -23,6 +23,8 @@
 #include <config.h>
 #include "gdk-pixbuf.h"
 #include "gdk-pixbuf-private.h"
+#include <stdlib.h>
+#include <string.h>
 
 
 
@@ -75,3 +77,121 @@
 
 	return pixbuf;
 }
+
+static guint32
+read_int (const guchar **p)
+{
+        guint32 num;
+
+        /* Note most significant bytes are first in the byte stream */
+        num =
+          (*p)[3]         |
+          ((*p)[2] << 8)  |
+          ((*p)[1] << 16) |
+          ((*p)[0] << 24);
+
+        *p += 4;
+
+        return num;
+}
+
+static gboolean
+read_bool (const guchar **p)
+{
+        gboolean val = **p != 0;
+        
+        ++(*p);
+        
+        return val;
+}
+
+static void
+free_buffer (guchar *pixels, gpointer data)
+{
+	free (pixels);
+}
+
+static GdkPixbuf*
+read_raw_inline (const guchar *data, gboolean copy_pixels)
+{
+        GdkPixbuf *pixbuf;
+        const guchar *p = data;
+        
+        pixbuf = GDK_PIXBUF (g_type_create_instance (GDK_TYPE_PIXBUF));
+
+        pixbuf->rowstride = read_int (&p);
+        pixbuf->width = read_int (&p);
+        pixbuf->height = read_int (&p);
+        pixbuf->has_alpha = read_bool (&p);
+        pixbuf->colorspace = read_int (&p);
+        pixbuf->n_channels = read_int (&p);
+        pixbuf->bits_per_sample = read_int (&p);
+  
+        if (copy_pixels) {
+                pixbuf->pixels = malloc (pixbuf->height * pixbuf->rowstride);
+
+                if (pixbuf->pixels == NULL) {                        
+                        g_object_unref (G_OBJECT (pixbuf));
+                        return NULL;
+                }
+
+                pixbuf->destroy_fn = free_buffer;
+                pixbuf->destroy_fn_data = NULL;
+
+                memcpy (pixbuf->pixels, p, pixbuf->height * pixbuf->rowstride);
+        } else {
+                pixbuf->pixels = (guchar *) p;
+        }
+
+        return pixbuf;
+}
+
+/**
+ * gdk_pixbuf_new_from_inline:
+ * @data: An inlined GdkPixbuf
+ * @copy_pixels: whether to copy the pixels out of the inline data, or to use them in-place
+ *
+ * Create a #GdkPixbuf from a custom format invented to store pixbuf
+ * data in C program code. This library comes with a program called "image-to-inline"
+ * that can write out a variable definition containing an inlined pixbuf.
+ * This is useful if you want to ship a program with images, but
+ * don't want to depend on any external files.
+ * 
+ * The inline data format contains the pixels in #GdkPixbuf's native format.
+ * Since the inline pixbuf is static data, you don't really need to copy it.
+ * However it's typically in read-only memory, so if you plan to modify
+ * it you must copy it.
+ * 
+ * Return value: A newly-created #GdkPixbuf structure with a reference count of
+ * 1.
+ **/
+GdkPixbuf*
+gdk_pixbuf_new_from_inline   (const guchar *inline_pixbuf,
+                              gboolean      copy_pixels)
+{
+        const guchar *p;
+        GdkPixbuf *pixbuf;
+        GdkPixbufInlineFormat format;
+        
+        p = inline_pixbuf;
+
+        if (read_int (&p) != GDK_PIXBUF_INLINE_MAGIC_NUMBER) {
+                g_warning ("Bad inline data; wrong magic number");
+                return NULL;
+        }
+
+        format = read_int (&p);
+
+        switch (format)
+        {
+        case GDK_PIXBUF_INLINE_RAW:
+                pixbuf = read_raw_inline (p, copy_pixels);
+                break;
+
+        default:
+                return NULL;
+        }
+
+        return pixbuf;
+}
+
Index: gdk-pixbuf-private.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf-private.h,v
retrieving revision 1.3
diff -u -u -r1.3 gdk-pixbuf-private.h
--- gdk-pixbuf-private.h	2000/06/22 15:36:11	1.3
+++ gdk-pixbuf-private.h	2000/07/26 16:45:26
@@ -117,4 +117,15 @@
 
 
 
+/* Is this Evil? I'm not sure. */
+#define GDK_PIXBUF_INLINE_MAGIC_NUMBER (* (guint32 *) "GdkPixbufMagic")
+
+typedef enum
+{
+  GDK_PIXBUF_INLINE_RAW = 0,
+  GDK_PIXBUF_INLINE_RLE = 1
+} GdkPixbufInlineFormat;
+
+
+
 #endif
Index: gdk-pixbuf.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf.h,v
retrieving revision 1.43
diff -u -u -r1.43 gdk-pixbuf.h
--- gdk-pixbuf.h	2000/07/19 16:11:39	1.43
+++ gdk-pixbuf.h	2000/07/26 16:45:26
@@ -103,6 +103,10 @@
 
 GdkPixbuf *gdk_pixbuf_new_from_xpm_data (const char **data);
 
+/* Read an inline pixbuf */
+GdkPixbuf *gdk_pixbuf_new_from_inline   (const guchar *inline_pixbuf,
+                                         gboolean      copy_pixels);
+
 /* Adding an alpha channel */
 GdkPixbuf *gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf, gboolean substitute_color,
 				 guchar r, guchar g, guchar b);
Index: make-inline-pixbuf.c
===================================================================
RCS file: make-inline-pixbuf.c
diff -N make-inline-pixbuf.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ make-inline-pixbuf.c	Wed Jul 26 12:45:26 2000
@@ -0,0 +1,212 @@
+/* Program to convert an image file to inline C data
+ *
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * Developed by Havoc Pennington <hp@redhat.com>
+ *
+ * This library 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 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "gdk-pixbuf-private.h"
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+void
+output_int (FILE *outfile, guint32 num, const char *comment)
+{
+  guchar bytes[4];
+
+  g_assert (sizeof (bytes) == sizeof (num));
+
+  /* Note, most significant bytes first */
+  bytes[0] = num >> 24;
+  bytes[1] = num >> 16;
+  bytes[2] = num >> 8;
+  bytes[3] = num;
+
+  fprintf(outfile, "  /* %s (decimal: %u) */\n  0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x,\n",
+          comment, num,
+          bytes[0], bytes[1], bytes[2], bytes[3]);  
+}
+
+void
+output_bool (FILE *outfile, gboolean val, const char *comment)
+{
+  fprintf(outfile, "  /* %s (%s) */\n  0x%.2x,\n",
+          comment,
+          val ? "TRUE" : "FALSE",
+          val ? 1 : 0);
+}
+
+void
+output_pixbuf (FILE *outfile, gboolean ext_symbols,
+               const char *varname,
+               GdkPixbuf *pixbuf)
+{
+  const char *modifier;
+  const guchar *p;
+  const guchar *end;
+  gboolean has_alpha;
+  int column;
+  
+  modifier = "static ";
+  if (ext_symbols)
+    modifier = "";
+  
+  fprintf (outfile, "%sconst guchar ", modifier);
+  fputs (varname, outfile);
+  fputs (" [] =\n", outfile);
+  fputs ("{\n", outfile);
+
+  p = gdk_pixbuf_get_pixels (pixbuf);
+  end = p + gdk_pixbuf_get_rowstride (pixbuf) * gdk_pixbuf_get_height (pixbuf);
+  has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+
+  /* Sync the order of writing with the order of reading in
+   * gdk-pixbuf-data.c
+   */
+  output_int (outfile, GDK_PIXBUF_INLINE_MAGIC_NUMBER, "File magic");
+  output_int (outfile, GDK_PIXBUF_INLINE_RAW, "Format of following stuff");
+  output_int (outfile, gdk_pixbuf_get_rowstride (pixbuf), "Rowstride");
+  output_int (outfile, gdk_pixbuf_get_width (pixbuf), "Width");
+  output_int (outfile, gdk_pixbuf_get_height (pixbuf), "Height");
+
+  output_bool (outfile, has_alpha, "Has an alpha channel");
+
+  output_int (outfile, gdk_pixbuf_get_colorspace (pixbuf), "Colorspace (0 == RGB, no other options implemented)");
+
+  output_int (outfile, gdk_pixbuf_get_n_channels (pixbuf), "Number of channels");
+
+  output_int (outfile, gdk_pixbuf_get_bits_per_sample (pixbuf), "Bits per sample");
+
+  fputs ("  /* Image data */\n", outfile);
+  
+  /* Copy the data in the pixbuf */
+  column = 0;
+  while (p != end)
+    {
+      guchar r, g, b, a;
+      
+      r = *p;
+      ++p;
+      g = *p;
+      ++p;
+      b = *p;
+      ++p;
+      if (has_alpha)
+        {
+          a = *p;
+          ++p;
+        }
+      else
+        a = 0;
+
+      
+      if (has_alpha)
+        fprintf(outfile, "  0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x", r, g, b, a);
+      else
+        fprintf(outfile, "  0x%.2x, 0x%.2x, 0x%.2x", r, g, b);
+
+      if (p != end)
+        fputs (",", outfile);
+      else
+        fputs ("\n", outfile);
+      
+      ++column;
+      
+      if (column > 2)
+        {
+          fputs ("\n", outfile);
+          column = 0;
+        }
+    }
+
+  fputs ("};\n\n", outfile);
+}
+
+void
+usage (void)
+{
+  fprintf (stderr, "Usage: image-to-inline [--extern-symbols] OUTFILE varname1 imagefile1 varname2 imagefile2 ...\n");
+  exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+  gboolean ext_symbols = FALSE;
+  FILE *outfile;
+  int i;
+  
+  gdk_pixbuf_init ();
+  
+  if (argc < 4)
+    usage ();
+
+  i = 1;
+  if (strcmp (argv[i], "--extern-symbols") == 0)
+    {
+      ext_symbols = TRUE;
+      ++i;
+      if (argc < 5)
+        usage ();
+    }
+
+  outfile = fopen (argv[i], "w");
+  if (outfile == NULL)
+    {
+      fprintf (stderr, "Failed to open output file `%s': %s\n",
+               argv[i], strerror (errno));
+      exit (1);
+    }
+
+  ++i;
+
+  fputs ("/* This file was automatically generated by the image-to-inline program.\n"
+         " * It contains inline RGB image data.\n"
+         " */\n\n", outfile);
+  
+  while (i < argc)
+    {
+      GdkPixbuf *pixbuf;
+
+      if ((i + 1) == argc)
+        usage ();
+      
+      pixbuf = gdk_pixbuf_new_from_file (argv[i+1]);
+
+      if (pixbuf == NULL)
+        {
+          fprintf (stderr, "Failed to open image file `%s': %s\n",
+                   argv[i+1], strerror (errno));
+
+          exit (1);
+        }
+
+      output_pixbuf (outfile, ext_symbols, argv[i], pixbuf);
+      
+      gdk_pixbuf_unref (pixbuf);
+      
+      i += 2;
+    }
+  
+  fclose (outfile);
+
+  return 0;
+}




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]