]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkpixmap-win32.c
Large changes to the Win32 backend, partially made necessary by the
[~andy/gtk] / gdk / win32 / gdkpixmap-win32.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998-1999 Tor Lillqvist
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "config.h"
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "gdkpixmap.h"
35 #include "gdkinternals.h"
36 #include "gdkprivate-win32.h"
37
38 typedef struct
39 {
40   gchar *color_string;
41   GdkColor color;
42   gint transparent;
43 } _GdkPixmapColor;
44
45 typedef struct
46 {
47   guint ncolors;
48   GdkColormap *colormap;
49   gulong pixels[1];
50 } _GdkPixmapInfo;
51
52 static void
53 gdk_win32_pixmap_destroy (GdkPixmap *pixmap)
54 {
55   GdkDrawablePrivate *private = (GdkDrawablePrivate *) pixmap;
56
57   GDK_NOTE (MISC, g_print ("gdk_win32_pixmap_destroy: %#x\n",
58                            GDK_DRAWABLE_XID (pixmap)));
59
60   if (!DeleteObject (GDK_DRAWABLE_XID (pixmap)))
61     WIN32_GDI_FAILED ("DeleteObject");
62
63   gdk_xid_table_remove (GDK_DRAWABLE_XID (pixmap));
64
65   g_free (GDK_DRAWABLE_WIN32DATA (pixmap));
66 }
67
68 static GdkDrawable *
69 gdk_win32_pixmap_alloc (void)
70 {
71   GdkDrawable *drawable;
72   GdkDrawablePrivate *private;
73   
74   static GdkDrawableClass klass;
75   static gboolean initialized = FALSE;
76
77   if (!initialized)
78     {
79       initialized = TRUE;
80       
81       klass = _gdk_win32_drawable_class;
82       klass.destroy = gdk_win32_pixmap_destroy;
83     }
84
85   drawable = gdk_drawable_alloc ();
86   private = (GdkDrawablePrivate *) drawable;
87
88   private->klass = &klass;
89   private->klass_data = g_new (GdkDrawableWin32Data, 1);
90   private->window_type = GDK_DRAWABLE_PIXMAP;
91
92   return drawable;
93 }
94
95 GdkPixmap*
96 gdk_pixmap_new (GdkWindow *window,
97                 gint       width,
98                 gint       height,
99                 gint       depth)
100 {
101   GdkPixmap *pixmap;
102   GdkDrawablePrivate *private;
103   struct {
104     BITMAPINFOHEADER bmiHeader;
105     union {
106       WORD bmiIndices[256];
107       DWORD bmiMasks[3];
108       RGBQUAD bmiColors[256];
109     } u;
110   } bmi;
111   UINT iUsage;
112   HDC hdc;
113   GdkVisual *visual;
114   guchar *bits;
115   gint i;
116
117   g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
118   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
119 #if 1
120   g_return_val_if_fail ((width != 0) && (height != 0), NULL);
121 #else
122   /* HB: Not The Right Thing to do, but a nice way to debug
123    * the backing store facility without immediate crashes ...
124    */
125   if (width == 0 || height == 0)
126     {
127       g_warning("gdk_pixmap_new: size requested: %ld %ld", width, height);
128       /* testing: where does it crash next? */
129       if (width == 0) width = 1;
130       if (height == 0) height = 1;
131     }
132 #endif
133
134   if (!window)
135     window = gdk_parent_root;
136
137   if (GDK_DRAWABLE_DESTROYED (window))
138     return NULL;
139
140   if (depth == -1)
141     depth = gdk_drawable_get_visual (window)->depth;
142
143   GDK_NOTE (MISC, g_print ("gdk_pixmap_new: %dx%dx%d\n",
144                            width, height, depth));
145
146   pixmap = gdk_win32_pixmap_alloc ();
147   private = (GdkDrawablePrivate *) pixmap;
148
149   visual = gdk_drawable_get_visual (window);
150
151   if ((hdc = GetDC (GDK_DRAWABLE_XID (window))) == NULL)
152     {
153       WIN32_GDI_FAILED ("GetDC");
154       g_free (private);
155       return NULL;
156     }
157
158   bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
159   bmi.bmiHeader.biWidth = width;
160   bmi.bmiHeader.biHeight = -height;
161   bmi.bmiHeader.biPlanes = 1;
162   if (depth == 15)
163     bmi.bmiHeader.biBitCount = 16;
164   else
165     bmi.bmiHeader.biBitCount = depth;
166 #if 1
167   if (depth == 16)
168     bmi.bmiHeader.biCompression = BI_BITFIELDS;
169   else
170 #endif
171     bmi.bmiHeader.biCompression = BI_RGB;
172   bmi.bmiHeader.biSizeImage = 0;
173   bmi.bmiHeader.biXPelsPerMeter =
174     bmi.bmiHeader.biYPelsPerMeter = 0;
175   bmi.bmiHeader.biClrUsed = 0;
176   bmi.bmiHeader.biClrImportant = 0;
177
178   iUsage = DIB_RGB_COLORS;
179   if (depth == 1)
180     {
181       bmi.u.bmiColors[0].rgbBlue =
182         bmi.u.bmiColors[0].rgbGreen =
183         bmi.u.bmiColors[0].rgbRed = 0x00;
184       bmi.u.bmiColors[0].rgbReserved = 0x00;
185
186       bmi.u.bmiColors[1].rgbBlue =
187         bmi.u.bmiColors[1].rgbGreen =
188         bmi.u.bmiColors[1].rgbRed = 0xFF;
189       bmi.u.bmiColors[1].rgbReserved = 0x00;
190       private->colormap = NULL;
191     }
192   else
193     {
194       private->colormap = ((GdkWindowPrivate *) window)->drawable.colormap;
195       if (private->colormap == NULL)
196         private->colormap = gdk_colormap_get_system ();
197
198       if (depth == 8)
199         {
200           iUsage = DIB_PAL_COLORS;
201           for (i = 0; i < 256; i++)
202             bmi.u.bmiIndices[i] = i;
203         }
204       else
205         {
206           if (depth != visual->depth)
207             g_warning ("gdk_pixmap_new: depth %d doesn't match display depth %d",
208                        depth, visual->depth);
209 #if 1
210           if (depth == 16)
211             {
212               bmi.u.bmiMasks[0] = visual->red_mask;
213               bmi.u.bmiMasks[1] = visual->green_mask;
214               bmi.u.bmiMasks[2] = visual->blue_mask;
215             }
216 #endif
217         }
218     }
219   if ((GDK_DRAWABLE_WIN32DATA (pixmap)->xid =
220        CreateDIBSection (hdc, (BITMAPINFO *) &bmi,
221                          iUsage, (PVOID *) &bits, NULL, 0)) == NULL)
222     {
223       WIN32_GDI_FAILED ("CreateDIBSection");
224       ReleaseDC (GDK_DRAWABLE_XID (window), hdc);
225       g_free (pixmap);
226       return NULL;
227     }
228   ReleaseDC (GDK_DRAWABLE_XID (window), hdc);
229
230   GDK_NOTE (MISC, g_print ("... = %#x\n", GDK_DRAWABLE_XID (pixmap)));
231
232   private->width = width;
233   private->height = height;
234
235   gdk_xid_table_insert (&GDK_DRAWABLE_XID (pixmap), pixmap);
236
237   return pixmap;
238 }
239
240 GdkPixmap *
241 gdk_pixmap_create_on_shared_image (GdkImage **image_return,
242                                    GdkWindow *window,
243                                    GdkVisual *visual,
244                                    gint       width,
245                                    gint       height,
246                                    gint       depth)
247 {
248   GdkPixmap *pixmap;
249   GdkDrawablePrivate *private;
250
251   g_return_val_if_fail (window != NULL, NULL);
252
253
254   if (depth == 1)
255     *image_return = gdk_image_bitmap_new (GDK_IMAGE_SHARED_PIXMAP, visual, width, height);
256   else
257     {
258       g_return_val_if_fail (depth == visual->depth, NULL);
259       *image_return = gdk_image_new (GDK_IMAGE_SHARED_PIXMAP, visual, width, height);
260     }
261
262   g_return_val_if_fail (*image_return != NULL, NULL);
263
264   pixmap = gdk_win32_pixmap_alloc ();
265   private = (GdkDrawablePrivate *) pixmap;
266
267   GDK_DRAWABLE_WIN32DATA (pixmap)->xid =
268     ((GdkImagePrivateWin32 *) *image_return)->ximage;
269   private->colormap = ((GdkWindowPrivate *) window)->drawable.colormap;
270   private->width = width;
271   private->height = height;
272
273   gdk_xid_table_insert (&GDK_DRAWABLE_XID (pixmap), pixmap);
274
275   GDK_NOTE (MISC,
276             g_print ("gdk_pixmap_create_on_shared_image: %dx%dx%d = %#x\n",
277                      width, height, depth, GDK_DRAWABLE_XID (pixmap)));
278
279   return pixmap;
280 }
281
282 static unsigned char mirror[256] = {
283   0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
284   0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
285   0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
286   0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
287   0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
288   0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
289   0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
290   0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
291   0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
292   0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
293   0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
294   0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
295   0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
296   0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
297   0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
298   0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
299   0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
300   0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
301   0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
302   0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
303   0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
304   0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
305   0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
306   0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
307   0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
308   0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
309   0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
310   0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
311   0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
312   0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
313   0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
314   0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
315 };
316
317 GdkPixmap *
318 gdk_bitmap_create_from_data (GdkWindow   *window,
319                              const gchar *data,
320                              gint         width,
321                              gint         height)
322 {
323   GdkPixmap *pixmap;
324   GdkDrawablePrivate *private;
325   gint i, j, bpl, aligned_bpl;
326   guchar *bits;
327
328   g_return_val_if_fail (data != NULL, NULL);
329   g_return_val_if_fail ((width != 0) && (height != 0), NULL);
330   g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
331
332   if (!window)
333     window = gdk_parent_root;
334
335   if (GDK_DRAWABLE_DESTROYED (window))
336     return NULL;
337
338   pixmap = gdk_win32_pixmap_alloc ();
339   private = (GdkDrawablePrivate *) pixmap;
340
341   private->width = width;
342   private->height = height;
343
344   bpl = ((width - 1) / 8 + 1);
345   aligned_bpl = ((bpl - 1) / 2 + 1) * 2;
346   bits = g_malloc (aligned_bpl * height);
347   for (i = 0; i < height; i++)
348     for (j = 0; j < bpl; j++)
349       bits[i*aligned_bpl + j] = mirror[(guchar) data[i*bpl + j]];
350
351   GDK_DRAWABLE_WIN32DATA (pixmap)->xid =
352     CreateBitmap (width, height, 1, 1, bits);
353
354   GDK_NOTE (MISC, g_print ("gdk_bitmap_create_from_data: %dx%d = %#x\n",
355                            width, height, GDK_DRAWABLE_XID (pixmap)));
356
357   g_free (bits);
358
359   private->colormap = NULL;
360   gdk_xid_table_insert (&GDK_DRAWABLE_XID (pixmap), pixmap);
361
362   return pixmap;
363 }
364
365 GdkPixmap*
366 gdk_pixmap_create_from_data (GdkWindow   *window,
367                              const gchar *data,
368                              gint         width,
369                              gint         height,
370                              gint         depth,
371                              GdkColor    *fg,
372                              GdkColor    *bg)
373 {
374   /* Oh wow. I struggled with dozens of lines of code trying to get
375    * this right using a monochrome Win32 bitmap created from data, and
376    * a colour DIB section as the result, trying setting pens,
377    * background colors, whatnot and BitBlt:ing.  Nope. Then finally I
378    * realized it's much easier to do it using gdk...:
379    */
380
381   GdkPixmap *result = gdk_pixmap_new (window, width, height, depth);
382   GdkPixmap *source = gdk_bitmap_create_from_data (window, data, width, height);
383   GdkGC *gc = gdk_gc_new (result);
384   gdk_gc_set_foreground (gc, fg);
385   gdk_gc_set_background (gc, bg);
386   gdk_draw_drawable (result, gc, source, 0, 0, 0, 0, width, height);
387   gdk_drawable_unref (source);
388   gdk_gc_unref (gc);
389
390   GDK_NOTE (MISC, g_print ("gdk_pixmap_create_from_data: %dx%dx%d = %#x\n",
391                            width, height, depth,
392                            GDK_DRAWABLE_XID (result)));
393   return result;
394 }
395
396 static gint
397 gdk_pixmap_seek_string (FILE  *infile,
398                         const gchar *str,
399                         gint   skip_comments)
400 {
401   char instr[1024];
402
403   while (1)
404     {
405       if (fscanf (infile, "%1023s", instr) != 1)
406         return FALSE;
407           
408       if (skip_comments == TRUE && strcmp (instr, "/*") == 0)
409         {
410           do
411             {
412               if (fscanf (infile, "%1023s", instr) != 1)
413                 return FALSE;
414             }
415           while (strcmp (instr, "*/") != 0);
416         }
417       else if (strcmp (instr, str) == 0)
418         return TRUE;
419     }
420 }
421
422 static gint
423 gdk_pixmap_seek_char (FILE  *infile,
424                       gchar  c)
425 {
426   gint b, oldb;
427
428   while ((b = getc(infile)) != EOF)
429     {
430       if (c != b && b == '/')
431         {
432           b = getc (infile);
433           if (b == EOF)
434             return FALSE;
435           else if (b == '*')    /* we have a comment */
436             {
437               b = -1;
438               do
439                 {
440                   oldb = b;
441                   b = getc (infile);
442                   if (b == EOF)
443                     return FALSE;
444                 }
445               while (!(oldb == '*' && b == '/'));
446             }
447         }
448       else if (c == b)
449         return TRUE;
450     }
451   return FALSE;
452 }
453
454 static gint
455 gdk_pixmap_read_string (FILE  *infile,
456                         gchar **buffer,
457                         guint *buffer_size)
458 {
459   gint c;
460   guint cnt = 0, bufsiz, ret = FALSE;
461   gchar *buf;
462
463   buf = *buffer;
464   bufsiz = *buffer_size;
465   if (buf == NULL)
466     {
467       bufsiz = 10 * sizeof (gchar);
468       buf = g_new(gchar, bufsiz);
469     }
470
471   do
472     c = getc (infile);
473   while (c != EOF && c != '"');
474
475   if (c != '"')
476     goto out;
477
478   while ((c = getc(infile)) != EOF)
479     {
480       if (cnt == bufsiz)
481         {
482           guint new_size = bufsiz * 2;
483           if (new_size > bufsiz)
484             bufsiz = new_size;
485           else
486             goto out;
487         
488           buf = (gchar *) g_realloc (buf, bufsiz);
489           buf[bufsiz-1] = '\0';
490         }
491
492       if (c != '"')
493         buf[cnt++] = c;
494       else
495         {
496           buf[cnt] = 0;
497           ret = TRUE;
498           break;
499         }
500     }
501
502  out:
503   buf[bufsiz-1] = '\0';         /* ensure null termination for errors */
504   *buffer = buf;
505   *buffer_size = bufsiz;
506   return ret;
507 }
508
509 static gchar*
510 gdk_pixmap_skip_whitespaces (gchar *buffer)
511 {
512   gint32 index = 0;
513
514   while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09))
515     index++;
516
517   return &buffer[index];
518 }
519
520 static gchar*
521 gdk_pixmap_skip_string (gchar *buffer)
522 {
523   gint32 index = 0;
524
525   while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09)
526     index++;
527
528   return &buffer[index];
529 }
530
531 #define MAX_COLOR_LEN 120
532
533 static gchar*
534 gdk_pixmap_extract_color (gchar *buffer)
535 {
536   gint counter, numnames;
537   gchar *ptr = NULL, ch, temp[128];
538   gchar color[MAX_COLOR_LEN], *retcol;
539   gint space;
540
541   counter = 0;
542   while (ptr == NULL)
543     {
544       if (buffer[counter] == 'c')
545         {
546           ch = buffer[counter + 1];
547           if (ch == 0x20 || ch == 0x09)
548             ptr = &buffer[counter + 1];
549         }
550       else if (buffer[counter] == 0)
551         return NULL;
552
553       counter++;
554     }
555
556   ptr = gdk_pixmap_skip_whitespaces (ptr);
557
558   if (ptr[0] == 0)
559     return NULL;
560   else if (ptr[0] == '#')
561     {
562       counter = 1;
563       while (ptr[counter] != 0 &&
564              ((ptr[counter] >= '0' && ptr[counter] <= '9') ||
565               (ptr[counter] >= 'a' && ptr[counter] <= 'f') ||
566               (ptr[counter] >= 'A' && ptr[counter] <= 'F')))
567         counter++;
568
569       retcol = g_new (gchar, counter+1);
570       strncpy (retcol, ptr, counter);
571
572       retcol[counter] = 0;
573
574       return retcol;
575     }
576
577   color[0] = 0;
578   numnames = 0;
579
580   space = MAX_COLOR_LEN - 1;
581   while (space > 0)
582     {
583       sscanf (ptr, "%127s", temp);
584
585       if (((gint)ptr[0] == 0) ||
586           (strcmp ("s", temp) == 0) || (strcmp ("m", temp) == 0) ||
587           (strcmp ("g", temp) == 0) || (strcmp ("g4", temp) == 0))
588         {
589           break;
590         }
591       else
592         {
593           if (numnames > 0)
594             {
595               space -= 1;
596               strcat (color, " ");
597             }
598           strncat (color, temp, space);
599           space -= MIN (space, strlen (temp));
600           ptr = gdk_pixmap_skip_string (ptr);
601           ptr = gdk_pixmap_skip_whitespaces (ptr);
602           numnames++;
603         }
604     }
605
606   retcol = g_strdup (color);
607   return retcol;
608 }
609
610
611 enum buffer_op
612 {
613   op_header,
614   op_cmap,
615   op_body
616 };
617
618
619 static void
620 gdk_xpm_destroy_notify (gpointer data)
621 {
622   _GdkPixmapInfo *info = (_GdkPixmapInfo *)data;
623   GdkColor color;
624   int i;
625
626   for (i=0; i<info->ncolors; i++)
627     {
628       color.pixel = info->pixels[i];
629       gdk_colormap_free_colors (info->colormap, &color, 1);
630     }
631
632   gdk_colormap_unref (info->colormap);
633   g_free (info);
634 }
635
636 static GdkPixmap *
637 _gdk_pixmap_create_from_xpm (GdkWindow   *window,
638                              GdkColormap *colormap,
639                              GdkBitmap  **mask,
640                              GdkColor    *transparent_color,
641                              gchar *    (*get_buf) (enum buffer_op op,
642                                                     gpointer       handle),
643                              gpointer     handle)
644 {
645   GdkPixmap *pixmap = NULL;
646   GdkImage *image = NULL;
647   GdkVisual *visual;
648   GdkGC *gc = NULL;
649   GdkColor tmp_color;
650   gint width, height, num_cols, cpp, n, ns, cnt, xcnt, ycnt, wbytes;
651   gchar *buffer, pixel_str[32];
652   gchar *name_buf;
653   _GdkPixmapColor *color = NULL, *fallbackcolor = NULL;
654   _GdkPixmapColor *colors = NULL;
655   gulong index;
656   GHashTable *color_hash = NULL;
657   _GdkPixmapInfo *color_info = NULL;
658
659   if ((window == NULL) && (colormap == NULL))
660     g_warning ("Creating pixmap from xpm with NULL window and colormap");
661
662   if (window == NULL)
663     window = gdk_parent_root;
664
665   if (colormap == NULL)
666     {
667       colormap = gdk_drawable_get_colormap (window);
668       visual = gdk_drawable_get_visual (window);
669     }
670   else
671     visual = ((GdkColormapPrivate *)colormap)->visual;
672
673   buffer = (*get_buf) (op_header, handle);
674   if (buffer == NULL)
675     return NULL;
676
677   sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
678   if (cpp >= 32)
679     {
680       g_warning ("Pixmap has more than 31 characters per color");
681       return NULL;
682     }
683
684   color_hash = g_hash_table_new (g_str_hash, g_str_equal);
685
686   if (transparent_color == NULL)
687     {
688       gdk_color_white (colormap, &tmp_color);
689       transparent_color = &tmp_color;
690     }
691
692   /* For pseudo-color and grayscale visuals, we have to remember
693    * the colors we allocated, so we can free them later.
694    */
695   if ((visual->type == GDK_VISUAL_PSEUDO_COLOR) ||
696       (visual->type == GDK_VISUAL_GRAYSCALE))
697     {
698       color_info = g_malloc (sizeof (_GdkPixmapInfo) +
699                              sizeof(gulong) * (num_cols - 1));
700       color_info->ncolors = num_cols;
701       color_info->colormap = colormap;
702       gdk_colormap_ref (colormap);
703     }
704
705   name_buf = g_new (gchar, num_cols * (cpp+1));
706   colors = g_new (_GdkPixmapColor, num_cols);
707
708   for (cnt = 0; cnt < num_cols; cnt++)
709     {
710       gchar *color_name;
711
712       buffer = (*get_buf) (op_cmap, handle);
713       if (buffer == NULL)
714         goto error;
715
716       color = &colors[cnt];
717       color->color_string = &name_buf [cnt * (cpp + 1)];
718       strncpy (color->color_string, buffer, cpp);
719       color->color_string[cpp] = 0;
720       buffer += strlen (color->color_string);
721       color->transparent = FALSE;
722
723       color_name = gdk_pixmap_extract_color (buffer);
724
725       if (color_name == NULL ||
726           gdk_color_parse (color_name, &color->color) == FALSE)
727         {
728           color->color = *transparent_color;
729           color->transparent = TRUE;
730         }
731
732       g_free (color_name);
733
734       /* FIXME: The remaining slowness appears to happen in this
735          function. */
736       gdk_color_alloc (colormap, &color->color);
737
738       if (color_info)
739         color_info->pixels[cnt] = color->color.pixel;
740
741       g_hash_table_insert (color_hash, color->color_string, color);
742       if (cnt == 0)
743         fallbackcolor = color;
744     }
745
746   index = 0;
747   image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
748
749   if (mask)
750     {
751       /* The pixmap mask is just a bits pattern.
752        * Color 0 is used for background and 1 for foreground.
753        * We don't care about the colormap, we just need 0 and 1.
754        */
755       GdkColor mask_pattern;
756
757       *mask = gdk_pixmap_new (window, width, height, 1);
758       gc = gdk_gc_new (*mask);
759
760       mask_pattern.pixel = 0;
761       gdk_gc_set_foreground (gc, &mask_pattern);
762       gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
763
764       mask_pattern.pixel = 1;
765       gdk_gc_set_foreground (gc, &mask_pattern);
766     }
767
768   wbytes = width * cpp;
769   for (ycnt = 0; ycnt < height; ycnt++)
770     {
771       buffer = (*get_buf) (op_body, handle);
772
773       /* FIXME: this slows things down a little - it could be
774        * integrated into the strncpy below, perhaps. OTOH, strlen
775        * is fast.
776        */
777       if ((buffer == NULL) || strlen (buffer) < wbytes)
778         continue;
779
780       for (n = 0, cnt = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++)
781         {
782           strncpy (pixel_str, &buffer[n], cpp);
783           pixel_str[cpp] = 0;
784           ns = 0;
785           
786           color = g_hash_table_lookup (color_hash, pixel_str);
787           
788           if (!color) /* screwed up XPM file */
789             color = fallbackcolor;
790           
791           gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
792           
793           if (mask && color->transparent)
794             {
795               if (cnt < xcnt)
796                 gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
797               cnt = xcnt + 1;
798             }
799         }
800
801       if (mask && (cnt < xcnt))
802         gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
803     }
804
805  error:
806
807   if (mask)
808     gdk_gc_unref (gc);
809
810   if (image != NULL)
811     {
812       pixmap = gdk_pixmap_new (window, width, height, visual->depth);
813
814       if (color_info)
815         gdk_drawable_set_data (pixmap, "gdk-xpm", color_info,
816                                gdk_xpm_destroy_notify);
817
818       gc = gdk_gc_new (pixmap);
819       gdk_gc_set_foreground (gc, transparent_color);
820       gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
821       gdk_gc_unref (gc);
822       gdk_image_unref (image);
823     }
824   else if (color_info)
825     gdk_xpm_destroy_notify (color_info);
826
827   if (color_hash != NULL)
828     g_hash_table_destroy (color_hash);
829
830   if (colors != NULL)
831     g_free (colors);
832
833   if (name_buf != NULL)
834     g_free (name_buf);
835
836   return pixmap;
837 }
838
839
840 struct file_handle
841 {
842   FILE *infile;
843   gchar *buffer;
844   guint buffer_size;
845 };
846
847
848 static gchar *
849 file_buffer (enum buffer_op op, gpointer handle)
850 {
851   struct file_handle *h = handle;
852
853   switch (op)
854     {
855     case op_header:
856       if (gdk_pixmap_seek_string (h->infile, "XPM", FALSE) != TRUE)
857         break;
858
859       if (gdk_pixmap_seek_char (h->infile,'{') != TRUE)
860         break;
861       /* Fall through to the next gdk_pixmap_seek_char. */
862
863     case op_cmap:
864       gdk_pixmap_seek_char (h->infile, '"');
865       fseek (h->infile, -1, SEEK_CUR);
866       /* Fall through to the gdk_pixmap_read_string. */
867
868     case op_body:
869       gdk_pixmap_read_string (h->infile, &h->buffer, &h->buffer_size);
870       return h->buffer;
871     }
872   return 0;
873 }
874
875 GdkPixmap*
876 gdk_pixmap_colormap_create_from_xpm (GdkWindow   *window,
877                                      GdkColormap *colormap,
878                                      GdkBitmap  **mask,
879                                      GdkColor    *transparent_color,
880                                      const gchar *filename)
881 {
882   struct file_handle h;
883   GdkPixmap *pixmap = NULL;
884
885   memset (&h, 0, sizeof (h));
886   h.infile = fopen (filename, "rb");
887   if (h.infile != NULL)
888     {
889       pixmap = _gdk_pixmap_create_from_xpm (window, colormap, mask,
890                                             transparent_color,
891                                             file_buffer, &h);
892       fclose (h.infile);
893       g_free (h.buffer);
894     }
895
896   return pixmap;
897 }
898
899 GdkPixmap*
900 gdk_pixmap_create_from_xpm (GdkWindow  *window,
901                             GdkBitmap **mask,
902                             GdkColor   *transparent_color,
903                             const gchar *filename)
904 {
905   return gdk_pixmap_colormap_create_from_xpm (window, NULL, mask,
906                                               transparent_color, filename);
907 }
908
909 struct mem_handle
910 {
911   gchar **data;
912   int offset;
913 };
914
915
916 static gchar *
917 mem_buffer (enum buffer_op op, gpointer handle)
918 {
919   struct mem_handle *h = handle;
920   switch (op)
921     {
922     case op_header:
923     case op_cmap:
924     case op_body:
925       if (h->data[h->offset])
926         return h->data[h->offset ++];
927     }
928   return 0;
929 }
930
931 GdkPixmap*
932 gdk_pixmap_colormap_create_from_xpm_d (GdkWindow  *window,
933                                        GdkColormap *colormap,
934                                        GdkBitmap **mask,
935                                        GdkColor   *transparent_color,
936                                        gchar     **data)
937 {
938   struct mem_handle h;
939   GdkPixmap *pixmap = NULL;
940
941   memset (&h, 0, sizeof (h));
942   h.data = data;
943   pixmap = _gdk_pixmap_create_from_xpm (window, colormap, mask,
944                                         transparent_color,
945                                         mem_buffer, &h);
946   return pixmap;
947 }
948
949 GdkPixmap*
950 gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
951                               GdkBitmap **mask,
952                               GdkColor   *transparent_color,
953                               gchar     **data)
954 {
955   return gdk_pixmap_colormap_create_from_xpm_d (window, NULL, mask,
956                                                 transparent_color, data);
957 }
958
959 GdkPixmap*
960 gdk_pixmap_foreign_new (guint32 anid)
961 {
962   GdkPixmap *pixmap;
963   GdkDrawablePrivate *private;
964   HBITMAP xpixmap;
965   SIZE size;
966   unsigned int x_ret, y_ret, w_ret, h_ret, bw_ret, depth_ret;
967
968   /* check to make sure we were passed something at
969      least a little sane */
970   g_return_val_if_fail((anid != 0), NULL);
971
972   /* set the pixmap to the passed in value */
973   xpixmap = (HBITMAP) anid;
974
975   /* get information about the BITMAP to fill in the structure for
976      the gdk window */
977   GetBitmapDimensionEx (xpixmap, &size);
978   w_ret = size.cx;
979   h_ret = size.cy;
980
981   /* allocate a new gdk pixmap */
982   pixmap = gdk_win32_pixmap_alloc ();
983   private = (GdkDrawablePrivate *) pixmap;
984
985   GDK_DRAWABLE_WIN32DATA (pixmap)->xid = xpixmap;
986   private->colormap = NULL;
987   private->width = w_ret;
988   private->height = h_ret;
989
990   gdk_xid_table_insert(&GDK_DRAWABLE_XID (pixmap), pixmap);
991
992   return pixmap;
993 }