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