]> Pileus Git - ~andy/gtk/blob - gdk/gdkpixmap.c
configure.in acheader.h gdk/gdkwindow.c Check for Shape extension both on
[~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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "../config.h"
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 /* Needed for SEEK_END in SunOS */
24 #include <unistd.h>
25 #include <X11/Xlib.h>
26
27 #include "gdk.h"
28 #include "gdkprivate.h"
29
30 typedef struct
31 {
32   gchar *color_string;
33   GdkColor color;
34   gint transparent;
35 } _GdkPixmapColor;
36
37 GdkPixmap*
38 gdk_pixmap_new (GdkWindow *window,
39                 gint       width,
40                 gint       height,
41                 gint       depth)
42 {
43   GdkPixmap *pixmap;
44   GdkWindowPrivate *private;
45   GdkWindowPrivate *window_private;
46
47   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
48   g_return_val_if_fail ((width != 0) && (height != 0), NULL);
49
50   if (!window)
51     window = (GdkWindow*) &gdk_root_parent;
52
53   window_private = (GdkWindowPrivate*) window;
54   if (window_private->destroyed)
55     return NULL;
56
57   if (depth == -1)
58     depth = gdk_window_get_visual (window)->depth;
59
60   private = g_new (GdkWindowPrivate, 1);
61   pixmap = (GdkPixmap*) private;
62
63   private->xdisplay = window_private->xdisplay;
64   private->window_type = GDK_WINDOW_PIXMAP;
65   private->xwindow = XCreatePixmap (private->xdisplay, window_private->xwindow,
66                                     width, height, depth);
67   private->colormap = NULL;
68   private->parent = NULL;
69   private->x = 0;
70   private->y = 0;
71   private->width = width;
72   private->height = height;
73   private->resize_count = 0;
74   private->ref_count = 1;
75   private->destroyed = 0;
76
77   gdk_xid_table_insert (&private->xwindow, pixmap);
78
79   return pixmap;
80 }
81
82 GdkPixmap *
83 gdk_bitmap_create_from_data (GdkWindow *window,
84                              gchar     *data,
85                              gint       width,
86                              gint       height)
87 {
88   GdkPixmap *pixmap;
89   GdkWindowPrivate *private;
90   GdkWindowPrivate *window_private;
91
92   g_return_val_if_fail (data != NULL, NULL);
93   g_return_val_if_fail ((width != 0) && (height != 0), NULL);
94
95   if (!window)
96     window = (GdkWindow*) &gdk_root_parent;
97
98   window_private = (GdkWindowPrivate*) window;
99   if (window_private->destroyed)
100     return NULL;
101
102   private = g_new (GdkWindowPrivate, 1);
103   pixmap = (GdkPixmap*) private;
104
105   private->parent = NULL;
106   private->xdisplay = window_private->xdisplay;
107   private->window_type = GDK_WINDOW_PIXMAP;
108   private->x = 0;
109   private->y = 0;
110   private->width = width;
111   private->height = height;
112   private->resize_count = 0;
113   private->ref_count = 1;
114   private->destroyed = FALSE;
115
116   private->xwindow = XCreateBitmapFromData (private->xdisplay,
117                                             window_private->xwindow,
118                                             data, width, height);
119
120   gdk_xid_table_insert (&private->xwindow, pixmap);
121
122   return pixmap;
123 }
124
125 GdkPixmap*
126 gdk_pixmap_create_from_data (GdkWindow *window,
127                              gchar     *data,
128                              gint       width,
129                              gint       height,
130                              gint       depth,
131                              GdkColor  *fg,
132                              GdkColor  *bg)
133 {
134   GdkPixmap *pixmap;
135   GdkWindowPrivate *private;
136   GdkWindowPrivate *window_private;
137
138   g_return_val_if_fail (data != NULL, NULL);
139   g_return_val_if_fail (fg != NULL, NULL);
140   g_return_val_if_fail (bg != NULL, NULL);
141   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
142   g_return_val_if_fail ((width != 0) && (height != 0), NULL);
143
144   if (!window)
145     window = (GdkWindow*) &gdk_root_parent;
146
147   window_private = (GdkWindowPrivate*) window;
148   if (window_private->destroyed)
149     return NULL;
150
151   if (depth == -1)
152     depth = gdk_window_get_visual (window)->depth;
153
154   private = g_new (GdkWindowPrivate, 1);
155   pixmap = (GdkPixmap*) private;
156
157   private->parent = NULL;
158   private->xdisplay = window_private->xdisplay;
159   private->window_type = GDK_WINDOW_PIXMAP;
160   private->x = 0;
161   private->y = 0;
162   private->width = width;
163   private->height = height;
164   private->resize_count = 0;
165   private->ref_count = 1;
166   private->destroyed = FALSE;
167
168   private->xwindow = XCreatePixmapFromBitmapData (private->xdisplay,
169                                                   window_private->xwindow,
170                                                   data, width, height,
171                                                   fg->pixel, bg->pixel, depth);
172
173   gdk_xid_table_insert (&private->xwindow, pixmap);
174
175   return pixmap;
176 }
177
178 gint
179 gdk_pixmap_seek_string (FILE  *infile,
180                         const gchar *str,
181                         gint   skip_comments)
182 {
183   char instr[1024];
184
185   while (!feof (infile))
186     {
187       fscanf (infile, "%1023s", instr);
188       if (skip_comments == TRUE && strcmp (instr, "/*") == 0)
189         {
190           fscanf (infile, "%1023s", instr);
191           while (!feof (infile) && strcmp (instr, "*/") != 0)
192             fscanf (infile, "%1023s", instr);
193           fscanf(infile, "%1023s", instr);
194         }
195       if (strcmp (instr, str)==0)
196         return TRUE;
197     }
198
199   return FALSE;
200 }
201
202 gint
203 gdk_pixmap_seek_char (FILE  *infile,
204                       gchar  c)
205 {
206   gint b, oldb;
207
208   while ((b = getc(infile)) != EOF)
209     {
210       if (c != b && b == '/')
211         {
212           b = getc (infile);
213           if (b == EOF)
214             return FALSE;
215           else if (b == '*')    /* we have a comment */
216             {
217               b = -1;
218               do
219                 {
220                   oldb = b;
221                   b = getc (infile);
222                   if (b == EOF)
223                     return FALSE;
224                 }
225               while (!(oldb == '*' && b == '/'));
226             }
227         }
228       else if (c == b)
229         return TRUE;
230     }
231   return FALSE;
232 }
233
234 gint
235 gdk_pixmap_read_string (FILE  *infile,
236                         gchar **buffer,
237                         guint *buffer_size)
238 {
239   gint c;
240   guint cnt = 0;
241
242   if ((*buffer) == NULL)
243     {
244       (*buffer_size) = 10 * sizeof (gchar);
245       (*buffer) = g_new(gchar, *buffer_size);
246     }
247
248   do
249     c = getc (infile);
250   while (c != EOF && c != '"');
251
252   if (c != '"')
253     return FALSE;
254
255   while ((c = getc(infile)) != EOF)
256     {
257       if (cnt == (*buffer_size))
258         {
259           guint new_size = (*buffer_size) * 2;
260           if (new_size > (*buffer_size))
261             *buffer_size = new_size;
262           else
263             return FALSE;
264           
265           (*buffer) = (gchar *) g_realloc ((*buffer), *buffer_size);    
266         }
267
268       if (c != '"')
269         (*buffer)[cnt++] = c;
270       else
271         {
272           (*buffer)[cnt++] = 0;
273           return TRUE;
274         }
275     }
276
277   return FALSE;
278 }
279
280 gchar*
281 gdk_pixmap_skip_whitespaces (gchar *buffer)
282 {
283   gint32 index = 0;
284
285   while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09))
286     index++;
287
288   return &buffer[index];
289 }
290
291 gchar*
292 gdk_pixmap_skip_string (gchar *buffer)
293 {
294   gint32 index = 0;
295
296   while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09)
297     index++;
298
299   return &buffer[index];
300 }
301
302 gchar*
303 gdk_pixmap_extract_color (gchar *buffer)
304 {
305   gint counter, finished = FALSE, numnames;
306   gchar *ptr = NULL, ch, temp[128];
307   gchar color[128], *retcol;
308
309   counter = 0;
310   while (ptr == NULL)
311     {
312       if (buffer[counter] == 'c')
313         {
314           ch = buffer[counter + 1];
315           if (ch == 0x20 || ch == 0x09)
316             ptr = &buffer[counter + 1];
317         }
318       else if (buffer[counter] == 0)
319         return NULL;
320
321       counter++;
322     }
323
324   if (ptr == NULL)
325     return NULL;
326
327   ptr = gdk_pixmap_skip_whitespaces (ptr);
328
329   if (ptr[0] == 0)
330     return NULL;
331   else if (ptr[0] == '#')
332     {
333       retcol = g_strdup (ptr);
334       return retcol;
335     }
336
337   color[0] = 0;
338   numnames = 0;
339
340   while (finished == FALSE)
341     {
342       sscanf (ptr, "%127s", temp);
343
344       if ((gint)ptr[0] == 0 || strcmp ("s", temp) == 0 || strcmp ("m", temp) == 0 ||
345           strcmp ("g", temp) == 0 || strcmp ("g4", temp) == 0)
346        finished = TRUE;
347       else
348         {
349           if (numnames > 0)
350             strcat (color, " ");
351           strcat (color, temp);
352           ptr = gdk_pixmap_skip_string (ptr);
353           ptr = gdk_pixmap_skip_whitespaces (ptr);
354           numnames++;
355         }
356     }
357
358   retcol = g_strdup (color);
359   return retcol;
360 }
361
362
363 GdkPixmap*
364 gdk_pixmap_colormap_create_from_xpm (GdkWindow   *window,
365                                      GdkColormap *colormap,
366                                      GdkBitmap  **mask,
367                                      GdkColor    *transparent_color,
368                                      const gchar *filename)
369 {
370   FILE *infile = NULL;
371   GdkPixmap *pixmap = NULL;
372   GdkImage *image = NULL;
373   GdkVisual *visual;
374   GdkGC *gc;
375   GdkColor tmp_color;
376   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt;
377   gchar *buffer = NULL, pixel_str[32];
378   guint buffer_size = 0;
379   _GdkPixmapColor *colors = NULL, *color = NULL;
380   gulong index;
381
382   if ((window == NULL) && (colormap == NULL))
383     g_warning ("Creating pixmap from xpm with NULL window and colormap");
384
385   if (window == NULL)
386     window = (GdkWindow *)&gdk_root_parent;
387
388   if (colormap == NULL)
389     {
390       colormap = gdk_window_get_colormap (window);
391       visual = gdk_window_get_visual (window);
392     }
393   else
394     visual = ((GdkColormapPrivate *)colormap)->visual;
395
396   infile = fopen (filename, "rb");
397   if (infile != NULL)
398     {
399       if (gdk_pixmap_seek_string (infile, "XPM", FALSE) == TRUE)
400         {
401           if (gdk_pixmap_seek_char (infile,'{') == TRUE)
402             {
403               gdk_pixmap_seek_char (infile, '"');
404               fseek (infile, -1, SEEK_CUR);
405               gdk_pixmap_read_string (infile, &buffer, &buffer_size);
406
407               sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
408               if (cpp >= 32)
409                 {
410                   g_warning ("Pixmap has more than 31 characters per color\n");
411                   return NULL;
412                 }
413
414               colors = g_new(_GdkPixmapColor, num_cols);
415
416               if (transparent_color == NULL) 
417                 {
418                   gdk_color_white (colormap, &tmp_color);
419                   transparent_color = &tmp_color;
420                 }
421
422               for (cnt = 0; cnt < num_cols; cnt++)
423                 {
424                   gchar *color_name;
425
426                   gdk_pixmap_seek_char (infile, '"');
427                   fseek (infile, -1, SEEK_CUR);
428                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
429
430                   colors[cnt].color_string = g_new(gchar, cpp + 1);
431                   for (n = 0; n < cpp; n++)
432                     colors[cnt].color_string[n] = buffer[n];
433                   colors[cnt].color_string[n] = 0;
434                   colors[cnt].transparent = FALSE;
435
436                   color_name = gdk_pixmap_extract_color (&buffer[cpp]);
437
438                   if (color_name != NULL)
439                     {
440                       if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
441                         {
442                           colors[cnt].color = *transparent_color;
443                           colors[cnt].transparent = TRUE;
444                         }
445                     }
446                   else
447                     {
448                       colors[cnt].color = *transparent_color;
449                       colors[cnt].transparent = TRUE;
450                     }
451
452                   g_free (color_name);
453
454                   gdk_color_alloc (colormap, &colors[cnt].color);
455                 }
456
457               index = 0;
458               image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
459
460               gc = NULL;
461               if (mask)
462                 {
463                   /* The pixmap mask is just a bits pattern.
464                    * Color 0 is used for background and 1 for foreground.
465                    * We don't care about the colormap, we just need 0 and 1.
466                    */
467                   GdkColor mask_pattern;
468                   
469                   *mask = gdk_pixmap_new (window, width, height, 1);
470                   gc = gdk_gc_new (*mask);
471                   
472                   mask_pattern.pixel = 0;
473                   gdk_gc_set_foreground (gc, &mask_pattern);
474                   gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
475                   
476                   mask_pattern.pixel = 1;
477                   gdk_gc_set_foreground (gc, &mask_pattern);
478                 }
479
480               for (ycnt = 0; ycnt < height; ycnt++)
481                 {
482                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
483
484                   for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
485                     {
486                       strncpy (pixel_str, &buffer[n], cpp);
487                       pixel_str[cpp] = 0;
488                       color = NULL;
489                       ns = 0;
490
491                       while ((color == NULL) && (ns < num_cols))
492                         {
493                           if (strcmp (pixel_str, colors[ns].color_string) == 0)
494                             color = &colors[ns];
495                           else
496                             ns++;
497                         }
498
499                       if (!color) /* screwed up XPM file */
500                         color = &colors[0];
501
502                       gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
503
504                       if (mask && color->transparent)
505                         {
506                           if (cnt < xcnt)
507                             gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
508                           cnt = xcnt + 1;
509                         }
510                     }
511
512                   if (mask && (cnt < xcnt))
513                     gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
514                 }
515
516               if (mask)
517                 gdk_gc_destroy (gc);
518
519               pixmap = gdk_pixmap_new (window, width, height, visual->depth);
520
521               gc = gdk_gc_new (pixmap);
522               gdk_gc_set_foreground (gc, transparent_color);
523               gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
524               gdk_gc_destroy (gc);
525               gdk_image_destroy (image);
526             }
527         }
528
529       fclose (infile);
530       free (buffer);
531
532       if (colors != NULL)
533         {
534           for (cnt = 0; cnt < num_cols; cnt++)
535             g_free (colors[cnt].color_string);
536           g_free (colors);
537         }
538     }
539
540   return pixmap;
541 }
542
543 GdkPixmap*
544 gdk_pixmap_create_from_xpm (GdkWindow  *window,
545                             GdkBitmap **mask,
546                             GdkColor   *transparent_color,
547                             const gchar *filename)
548 {
549   return gdk_pixmap_colormap_create_from_xpm (window, NULL, mask, 
550                                        transparent_color, filename);
551 }
552
553
554 GdkPixmap*
555 gdk_pixmap_colormap_create_from_xpm_d (GdkWindow  *window,
556                                        GdkColormap *colormap,
557                                        GdkBitmap **mask,
558                                        GdkColor   *transparent_color,
559                                        gchar     **data)
560 {
561   GdkPixmap *pixmap = NULL;
562   GdkImage *image = NULL;
563   GdkVisual *visual;
564   GdkGC *gc;
565   GdkColor tmp_color;
566   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt, i;
567   gchar *buffer, pixel_str[32];
568   _GdkPixmapColor *colors = NULL, *color = NULL;
569   gulong index;
570
571   if ((window == NULL) && (colormap == NULL))
572     g_warning ("Creating pixmap from xpm with NULL window and colormap");
573
574   if (window == NULL)
575     window = (GdkWindow *)&gdk_root_parent;
576
577   if (colormap == NULL)
578     {
579       colormap = gdk_window_get_colormap (window);
580       visual = gdk_window_get_visual (window);
581     }
582   else
583     visual = ((GdkColormapPrivate *)colormap)->visual;
584
585   i = 0;
586   buffer = data[i++];
587   sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
588   if (cpp >= 32)
589     {
590       g_warning ("Pixmap has more than 31 characters per color\n");
591       return NULL;
592     }
593
594   colors = g_new(_GdkPixmapColor, num_cols);
595
596   if (transparent_color == NULL) 
597     {
598       gdk_color_white (colormap, &tmp_color);
599       transparent_color = &tmp_color;
600     }
601
602   for (cnt = 0; cnt < num_cols; cnt++)
603     {
604       gchar *color_name;
605
606       buffer = data[i++];
607
608       colors[cnt].color_string = g_new(gchar, cpp + 1);
609       for (n = 0; n < cpp; n++)
610         colors[cnt].color_string[n] = buffer[n];
611       colors[cnt].color_string[n] = 0;
612       colors[cnt].transparent = FALSE;
613
614       color_name = gdk_pixmap_extract_color (&buffer[cpp]);
615
616       if (color_name != NULL)
617         {
618           if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
619             {
620               colors[cnt].color = *transparent_color;
621               colors[cnt].transparent = TRUE;
622             }
623         }
624       else
625         {
626           colors[cnt].color = *transparent_color;
627           colors[cnt].transparent = TRUE;
628         }
629
630       g_free (color_name);
631
632       gdk_color_alloc (colormap, &colors[cnt].color);
633     }
634
635   index = 0;
636   image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
637
638   gc = NULL;
639   if (mask)
640     {
641       /* The pixmap mask is just a bits pattern.
642        * Color 0 is used for background and 1 for foreground.
643        * We don't care about the colormap, we just need 0 and 1.
644        */
645       GdkColor mask_pattern;
646
647       *mask = gdk_pixmap_new (window, width, height, 1);
648       gc = gdk_gc_new (*mask);
649
650       mask_pattern.pixel = 0;
651       gdk_gc_set_foreground (gc, &mask_pattern);
652       gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
653
654       mask_pattern.pixel = 1;
655       gdk_gc_set_foreground (gc, &mask_pattern);
656     }
657
658   for (ycnt = 0; ycnt < height; ycnt++)
659     {
660       buffer = data[i++];
661
662       for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
663         {
664           strncpy (pixel_str, &buffer[n], cpp);
665           pixel_str[cpp] = 0;
666           color = NULL;
667           ns = 0;
668
669           while ((color == NULL) && (ns < num_cols))
670             {
671               if (strcmp (pixel_str, colors[ns].color_string) == 0)
672                 color = &colors[ns];
673               else
674                 ns++;
675             }
676
677           if (!color) /* screwed up XPM file */
678             color = &colors[0];
679
680           gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
681
682           if (mask && color->transparent)
683             {
684               if (cnt < xcnt)
685                 gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
686               cnt = xcnt + 1;
687             }
688         }
689
690       if (mask && (cnt < xcnt))
691         gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
692     }
693
694   if (mask)
695     gdk_gc_destroy (gc);
696
697   pixmap = gdk_pixmap_new (window, width, height, visual->depth);
698
699   gc = gdk_gc_new (pixmap);
700   gdk_gc_set_foreground (gc, transparent_color);
701   gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
702   gdk_gc_destroy (gc);
703   gdk_image_destroy (image);
704
705   if (colors != NULL)
706     {
707       for (cnt = 0; cnt < num_cols; cnt++)
708         g_free (colors[cnt].color_string);
709       g_free (colors);
710     }
711
712   return pixmap;
713 }
714
715 GdkPixmap*
716 gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
717                               GdkBitmap **mask,
718                               GdkColor   *transparent_color,
719                               gchar     **data)
720 {
721   return gdk_pixmap_colormap_create_from_xpm_d (window, NULL, mask,
722                                                 transparent_color, data);
723 }
724
725 GdkPixmap*
726 gdk_pixmap_ref (GdkPixmap *pixmap)
727 {
728   GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
729   g_return_val_if_fail (pixmap != NULL, NULL);
730
731   private->ref_count += 1;
732   return pixmap;
733 }
734
735 void
736 gdk_pixmap_unref (GdkPixmap *pixmap)
737 {
738   GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
739   g_return_if_fail(pixmap != NULL);
740
741   private->ref_count -= 1;
742   if (private->ref_count == 0)
743     {
744       XFreePixmap (private->xdisplay, private->xwindow);
745       gdk_xid_table_remove (private->xwindow);
746       g_free (private);
747     }
748 }
749
750 GdkBitmap *
751 gdk_bitmap_ref (GdkBitmap *bitmap)
752 {
753   return (GdkBitmap *)gdk_pixmap_ref ((GdkPixmap *)bitmap);
754 }
755
756 void
757 gdk_bitmap_unref (GdkBitmap *bitmap)
758 {
759   gdk_pixmap_unref ((GdkPixmap *)bitmap);
760 }