]> Pileus Git - ~andy/gtk/blob - gdk/gdkpixmap.c
Fix to pixmap mask handling. (gtk-fortier-971122-0.patch)
[~andy/gtk] / gdk / gdkpixmap.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "../config.h"
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 /* Needed for SEEK_END in SunOS */
23 #include <unistd.h>
24 #include <X11/Xlib.h>
25
26 #include "gdk.h"
27 #include "gdkprivate.h"
28
29 typedef struct
30 {
31   gchar *color_string;
32   GdkColor color;
33   gint transparent;
34 } _GdkPixmapColor;
35
36 GdkPixmap*
37 gdk_pixmap_new (GdkWindow *window,
38                 gint       width,
39                 gint       height,
40                 gint       depth)
41 {
42   GdkPixmap *pixmap;
43   GdkWindowPrivate *private;
44   GdkWindowPrivate *window_private;
45
46   if (!window)
47     window = (GdkWindow*) &gdk_root_parent;
48
49   if (depth == -1)
50     gdk_window_get_geometry (window, NULL, NULL, NULL, NULL, &depth);
51
52   private = g_new (GdkWindowPrivate, 1);
53   pixmap = (GdkPixmap*) private;
54
55   window_private = (GdkWindowPrivate*) window;
56
57   private->xdisplay = window_private->xdisplay;
58   private->window_type = GDK_WINDOW_PIXMAP;
59   private->xwindow = XCreatePixmap (private->xdisplay, window_private->xwindow,
60                                     width, height, depth);
61   private->parent = NULL;
62   private->x = 0;
63   private->y = 0;
64   private->width = width;
65   private->height = height;
66   private->resize_count = 0;
67   private->ref_count = 1;
68   private->destroyed = 0;
69
70   gdk_xid_table_insert (&private->xwindow, pixmap);
71
72   return pixmap;
73 }
74
75 GdkPixmap *
76 gdk_bitmap_create_from_data (GdkWindow *window,
77                              gchar     *data,
78                              gint       width,
79                              gint       height)
80 {
81   GdkPixmap *pixmap;
82   GdkWindowPrivate *private;
83   GdkWindowPrivate *window_private;
84
85   g_return_val_if_fail (data != NULL, NULL);
86
87   if (!window)
88     window = (GdkWindow*) &gdk_root_parent;
89
90   private = g_new (GdkWindowPrivate, 1);
91   pixmap = (GdkPixmap*) private;
92
93   window_private = (GdkWindowPrivate*) window;
94
95   private->parent = NULL;
96   private->xdisplay = window_private->xdisplay;
97   private->window_type = GDK_WINDOW_PIXMAP;
98   private->x = 0;
99   private->y = 0;
100   private->width = width;
101   private->height = height;
102   private->resize_count = 0;
103   private->ref_count = 1;
104   private->destroyed = FALSE;
105
106   private->xwindow = XCreateBitmapFromData (private->xdisplay,
107                                             window_private->xwindow,
108                                             data, width, height);
109
110   gdk_xid_table_insert (&private->xwindow, pixmap);
111
112   return pixmap;
113 }
114
115 GdkPixmap*
116 gdk_pixmap_create_from_data (GdkWindow *window,
117                              gchar     *data,
118                              gint       width,
119                              gint       height,
120                              gint       depth,
121                              GdkColor  *fg,
122                              GdkColor  *bg)
123 {
124   GdkPixmap *pixmap;
125   GdkWindowPrivate *private;
126   GdkWindowPrivate *window_private;
127
128   g_return_val_if_fail (data != NULL, NULL);
129   g_return_val_if_fail (fg != NULL, NULL);
130   g_return_val_if_fail (bg != NULL, NULL);
131
132   if (!window)
133     window = (GdkWindow*) &gdk_root_parent;
134
135   if (depth == -1)
136     gdk_window_get_geometry (window, NULL, NULL, NULL, NULL, &depth);
137
138   private = g_new (GdkWindowPrivate, 1);
139   pixmap = (GdkPixmap*) private;
140
141   window_private = (GdkWindowPrivate*) window;
142
143   private->parent = NULL;
144   private->xdisplay = window_private->xdisplay;
145   private->window_type = GDK_WINDOW_PIXMAP;
146   private->x = 0;
147   private->y = 0;
148   private->width = width;
149   private->height = height;
150   private->resize_count = 0;
151   private->ref_count = 1;
152   private->destroyed = FALSE;
153
154   private->xwindow = XCreatePixmapFromBitmapData (private->xdisplay,
155                                                   window_private->xwindow,
156                                                   data, width, height,
157                                                   fg->pixel, bg->pixel, depth);
158
159   gdk_xid_table_insert (&private->xwindow, pixmap);
160
161   return pixmap;
162 }
163
164 gint
165 gdk_pixmap_seek_string (FILE  *infile,
166                         const gchar *str,
167                         gint   skip_comments)
168 {
169   char instr[1024];
170
171   while (!feof (infile))
172     {
173       fscanf (infile, "%s", instr);
174       if (skip_comments == TRUE && strcmp (instr, "/*") == 0)
175         {
176           fscanf (infile, "%s", instr);
177           while (!feof (infile) && strcmp (instr, "*/") != 0)
178             fscanf (infile, "%s", instr);
179           fscanf(infile, "%s", instr);
180         }
181       if (strcmp (instr, str)==0)
182         return TRUE;
183     }
184
185   return FALSE;
186 }
187
188 gint
189 gdk_pixmap_seek_char (FILE  *infile,
190                       gchar  c)
191 {
192   gchar b, oldb;
193
194   while (!feof (infile))
195     {
196       fscanf(infile, "%c", &b);
197       if (c != b && b == '/')
198         {
199           fscanf (infile, "%c", &b);
200           if (b == '*')
201             {
202               oldb = b;
203               while (!feof (infile) && !(oldb == '*' && b == '/'))
204                 {
205                   oldb = b;
206                   fscanf (infile, "%c", &b);
207                 }
208               fscanf (infile, "%c", &b);
209             }
210         }
211       if (c == b)
212         return TRUE;
213     }
214
215   return FALSE;
216 }
217
218 gint
219 gdk_pixmap_read_string (FILE  *infile,
220                         gchar **buffer,
221                         int *buffer_size)
222 {
223   gchar c;
224   gint cnt = 0;
225
226   if ((*buffer) == NULL)
227     {
228       (*buffer_size) = 10 * sizeof (gchar);
229       (*buffer) = (gchar *) malloc (*buffer_size);
230     }
231
232   do
233     fscanf (infile, "%c", &c);
234   while (!feof (infile) && c != '"');
235
236   if (c != '"')
237     return FALSE;
238
239   while (!feof (infile))
240     {
241       fscanf (infile, "%c", &c);
242
243       if (cnt == (*buffer_size))
244         {
245           (*buffer_size) *= 2;
246           (*buffer) = (gchar *) realloc ((*buffer), *buffer_size);        
247         }
248
249       if (c != '"')
250         (*buffer)[cnt++] = c;
251       else
252         {
253           (*buffer)[cnt++] = 0;
254           return TRUE;
255         }
256     }
257
258   return FALSE;
259 }
260
261 gchar*
262 gdk_pixmap_skip_whitespaces (gchar *buffer)
263 {
264   gint32 index = 0;
265
266   while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09))
267     index++;
268
269   return &buffer[index];
270 }
271
272 gchar*
273 gdk_pixmap_skip_string (gchar *buffer)
274 {
275   gint32 index = 0;
276
277   while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09)
278     index++;
279
280   return &buffer[index];
281 }
282
283 gchar*
284 gdk_pixmap_extract_color (gchar *buffer)
285 {
286   gint counter, finished = FALSE, numnames;
287   gchar *ptr = NULL, ch, temp[128];
288   gchar color[128], *retcol;
289
290   counter = 0;
291   while (ptr == NULL)
292     {
293       if (buffer[counter] == 'c')
294         {
295           ch = buffer[counter + 1];
296           if (ch == 0x20 || ch == 0x09)
297             ptr = &buffer[counter + 1];
298         }
299       else if (buffer[counter] == 0)
300         return NULL;
301
302       counter++;
303     }
304
305   if (ptr == NULL)
306     return NULL;
307
308   ptr = gdk_pixmap_skip_whitespaces (ptr);
309
310   if (ptr[0] == 0)
311     return NULL;
312   else if (ptr[0] == '#')
313     {
314       retcol = g_new(gchar, strlen (ptr) + 1);
315       strcpy (retcol, ptr);
316       return retcol;
317     }
318
319   color[0] = 0;
320   numnames = 0;
321
322   while (finished == FALSE)
323     {
324       sscanf (ptr, "%s", temp);
325
326       if ((gint)ptr[0] == 0 || strcmp ("s", temp) == 0 || strcmp ("m", temp) == 0 ||
327           strcmp ("g", temp) == 0 || strcmp ("g4", temp) == 0)
328        finished = TRUE;
329       else
330         {
331           if (numnames > 0)
332             strcat (color, " ");
333           strcat (color, temp);
334           ptr = gdk_pixmap_skip_string (ptr);
335           ptr = gdk_pixmap_skip_whitespaces (ptr);
336           numnames++;
337         }
338     }
339
340   retcol = g_new(gchar, strlen (color) + 1);
341   strcpy (retcol, color);
342   return retcol;
343 }
344
345
346 GdkPixmap*
347 gdk_pixmap_create_from_xpm (GdkWindow  *window,
348                             GdkBitmap **mask,
349                             GdkColor   *transparent_color,
350                             const gchar *filename)
351 {
352   FILE *infile = NULL;
353   GdkPixmap *pixmap = NULL;
354   GdkImage *image = NULL;
355   GdkColormap *colormap;
356   GdkVisual *visual;
357   GdkGC *gc;
358   GdkColor tmp_color;
359   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt;
360   gchar *buffer = NULL, *color_name = NULL, pixel_str[32];
361   guint buffer_size = 0;
362   _GdkPixmapColor *colors = NULL, *color = NULL;
363   gulong index;
364
365   if (!window)
366     window = (GdkWindow*) &gdk_root_parent;
367
368   infile = fopen (filename, "rb");
369   if (infile != NULL)
370     {
371       if (gdk_pixmap_seek_string (infile, "XPM", FALSE) == TRUE)
372         {
373           if (gdk_pixmap_seek_char (infile,'{') == TRUE)
374             {
375               gdk_pixmap_seek_char (infile, '"');
376               fseek (infile, -1, SEEK_CUR);
377               gdk_pixmap_read_string (infile, &buffer, &buffer_size);
378
379               sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
380
381               colors = g_new(_GdkPixmapColor, num_cols);
382
383               colormap = gdk_window_get_colormap (window);
384               visual = gdk_window_get_visual (window);
385
386               if (transparent_color == NULL) 
387                 {
388                   gdk_color_white (colormap, &tmp_color);
389                   transparent_color = &tmp_color;
390                 }
391
392               for (cnt = 0; cnt < num_cols; cnt++)
393                 {
394                   gdk_pixmap_seek_char (infile, '"');
395                   fseek (infile, -1, SEEK_CUR);
396                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
397
398                   colors[cnt].color_string = g_new(gchar, cpp + 1);
399                   for (n = 0; n < cpp; n++)
400                     colors[cnt].color_string[n] = buffer[n];
401                   colors[cnt].color_string[n] = 0;
402                   colors[cnt].transparent = FALSE;
403
404                   if (color_name != NULL)
405                     g_free (color_name);
406
407                   color_name = gdk_pixmap_extract_color (&buffer[cpp]);
408
409                   if (color_name != NULL)
410                     {
411                       if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
412                         {
413                           colors[cnt].color = *transparent_color;
414                           colors[cnt].transparent = TRUE;
415                         }
416                     }
417                   else
418                     {
419                       colors[cnt].color = *transparent_color;
420                       colors[cnt].transparent = TRUE;
421                     }
422
423                   gdk_color_alloc (colormap, &colors[cnt].color);
424                 }
425
426               index = 0;
427               image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
428
429               gc = NULL;
430               if (mask)
431                 {
432                   /* The pixmap mask is just a bits pattern.
433                    * Color 0 is used for background and 1 for foreground.
434                    * We don't care about the colormap, we just need 0 and 1.
435                    */
436                   GdkColor mask_pattern;
437                   
438                   *mask = gdk_pixmap_new (window, width, height, 1);
439                   gc = gdk_gc_new (*mask);
440                   
441                   mask_pattern.pixel = 0;
442                   gdk_gc_set_foreground (gc, &mask_pattern);
443                   gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
444                   
445                   mask_pattern.pixel = 1;
446                   gdk_gc_set_foreground (gc, &mask_pattern);
447                 }
448
449               for (ycnt = 0; ycnt < height; ycnt++)
450                 {
451                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
452
453                   for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
454                     {
455                       strncpy (pixel_str, &buffer[n], cpp);
456                       pixel_str[cpp] = 0;
457                       color = NULL;
458                       ns = 0;
459
460                       while (color == NULL)
461                         {
462                           if (strcmp (pixel_str, colors[ns].color_string) == 0)
463                             color = &colors[ns];
464                           else
465                             ns++;
466                         }
467
468                       gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
469
470                       if (mask && color->transparent)
471                         {
472                           if (cnt < xcnt)
473                             gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
474                           cnt = xcnt + 1;
475                         }
476                     }
477
478                   if (mask && (cnt < xcnt))
479                     gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
480                 }
481
482               if (mask)
483                 gdk_gc_destroy (gc);
484
485               pixmap = gdk_pixmap_new (window, width, height, visual->depth);
486
487               gc = gdk_gc_new (pixmap);
488               gdk_gc_set_foreground (gc, transparent_color);
489               gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
490               gdk_gc_destroy (gc);
491               gdk_image_destroy (image);
492             }
493         }
494
495       fclose (infile);
496       free (buffer);
497
498       if (colors != NULL)
499         {
500           for (cnt = 0; cnt < num_cols; cnt++)
501             g_free (colors[cnt].color_string);
502           g_free (colors);
503         }
504     }
505
506   return pixmap;
507 }
508
509 GdkPixmap*
510 gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
511                               GdkBitmap **mask,
512                               GdkColor   *transparent_color,
513                               gchar     **data)
514 {
515   GdkPixmap *pixmap = NULL;
516   GdkImage *image = NULL;
517   GdkColormap *colormap;
518   GdkVisual *visual;
519   GdkGC *gc;
520   GdkColor tmp_color;
521   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt, i;
522   gchar *buffer, *color_name = NULL, pixel_str[32];
523   _GdkPixmapColor *colors = NULL, *color = NULL;
524   gulong index;
525
526   if (!window)
527     window = (GdkWindow*) &gdk_root_parent;
528
529   i = 0;
530   buffer = data[i++];
531   sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
532
533   colors = g_new(_GdkPixmapColor, num_cols);
534
535   colormap = gdk_window_get_colormap (window);
536   visual = gdk_window_get_visual (window);
537
538   if (transparent_color == NULL) 
539     {
540       gdk_color_white (colormap, &tmp_color);
541       transparent_color = &tmp_color;
542     }
543
544   for (cnt = 0; cnt < num_cols; cnt++)
545     {
546       buffer = data[i++];
547
548       colors[cnt].color_string = g_new(gchar, cpp + 1);
549       for (n = 0; n < cpp; n++)
550         colors[cnt].color_string[n] = buffer[n];
551       colors[cnt].color_string[n] = 0;
552       colors[cnt].transparent = FALSE;
553
554       if (color_name != NULL)
555         g_free (color_name);
556
557       color_name = gdk_pixmap_extract_color (&buffer[cpp]);
558
559       if (color_name != NULL)
560         {
561           if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
562             {
563               colors[cnt].color = *transparent_color;
564               colors[cnt].transparent = TRUE;
565             }
566         }
567       else
568         {
569           colors[cnt].color = *transparent_color;
570           colors[cnt].transparent = TRUE;
571         }
572
573       gdk_color_alloc (colormap, &colors[cnt].color);
574     }
575
576   index = 0;
577   image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
578
579   gc = NULL;
580   if (mask)
581     {
582       /* The pixmap mask is just a bits pattern.
583        * Color 0 is used for background and 1 for foreground.
584        * We don't care about the colormap, we just need 0 and 1.
585        */
586       GdkColor mask_pattern;
587
588       *mask = gdk_pixmap_new (window, width, height, 1);
589       gc = gdk_gc_new (*mask);
590
591       mask_pattern.pixel = 0;
592       gdk_gc_set_foreground (gc, &mask_pattern);
593       gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
594
595       mask_pattern.pixel = 1;
596       gdk_gc_set_foreground (gc, &mask_pattern);
597     }
598
599   for (ycnt = 0; ycnt < height; ycnt++)
600     {
601       buffer = data[i++];
602
603       for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
604         {
605           strncpy (pixel_str, &buffer[n], cpp);
606           pixel_str[cpp] = 0;
607           color = NULL;
608           ns = 0;
609
610           while (color == NULL)
611             {
612               if (strcmp (pixel_str, colors[ns].color_string) == 0)
613                 color = &colors[ns];
614               else
615                 ns++;
616             }
617
618           gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
619
620           if (mask && color->transparent)
621             {
622               if (cnt < xcnt)
623                 gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
624               cnt = xcnt + 1;
625             }
626         }
627
628       if (mask && (cnt < xcnt))
629         gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
630     }
631
632   if (mask)
633     gdk_gc_destroy (gc);
634
635   pixmap = gdk_pixmap_new (window, width, height, visual->depth);
636
637   gc = gdk_gc_new (pixmap);
638   gdk_gc_set_foreground (gc, transparent_color);
639   gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
640   gdk_gc_destroy (gc);
641   gdk_image_destroy (image);
642
643   if (colors != NULL)
644     {
645       for (cnt = 0; cnt < num_cols; cnt++)
646         g_free (colors[cnt].color_string);
647       g_free (colors);
648     }
649
650   return pixmap;
651 }
652
653 void
654 gdk_pixmap_destroy (GdkPixmap *pixmap)
655 {
656   GdkWindowPrivate *private;
657
658   g_return_if_fail (pixmap != NULL);
659
660   private = (GdkPixmapPrivate*) pixmap;
661   if (private->ref_count <= 0)
662     {
663       XFreePixmap (private->xdisplay, private->xwindow);
664       gdk_xid_table_remove (private->xwindow);
665       g_free (pixmap);
666     }
667   else
668     {
669       private->ref_count -= 1;
670     }
671 }