]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkpixmap-x11.c
Merged changes from gtk-1-0. Check ChangeLog for details.
[~andy/gtk] / gdk / x11 / gdkpixmap-x11.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           (*buffer_size) *= 2;
260           (*buffer) = (gchar *) g_realloc ((*buffer), *buffer_size);    }
261
262       if (c != '"')
263         (*buffer)[cnt++] = c;
264       else
265         {
266           (*buffer)[cnt++] = 0;
267           return TRUE;
268         }
269     }
270
271   return FALSE;
272 }
273
274 gchar*
275 gdk_pixmap_skip_whitespaces (gchar *buffer)
276 {
277   gint32 index = 0;
278
279   while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09))
280     index++;
281
282   return &buffer[index];
283 }
284
285 gchar*
286 gdk_pixmap_skip_string (gchar *buffer)
287 {
288   gint32 index = 0;
289
290   while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09)
291     index++;
292
293   return &buffer[index];
294 }
295
296 gchar*
297 gdk_pixmap_extract_color (gchar *buffer)
298 {
299   gint counter, finished = FALSE, numnames;
300   gchar *ptr = NULL, ch, temp[128];
301   gchar color[128], *retcol;
302
303   counter = 0;
304   while (ptr == NULL)
305     {
306       if (buffer[counter] == 'c')
307         {
308           ch = buffer[counter + 1];
309           if (ch == 0x20 || ch == 0x09)
310             ptr = &buffer[counter + 1];
311         }
312       else if (buffer[counter] == 0)
313         return NULL;
314
315       counter++;
316     }
317
318   if (ptr == NULL)
319     return NULL;
320
321   ptr = gdk_pixmap_skip_whitespaces (ptr);
322
323   if (ptr[0] == 0)
324     return NULL;
325   else if (ptr[0] == '#')
326     {
327       retcol = g_strdup (ptr);
328       return retcol;
329     }
330
331   color[0] = 0;
332   numnames = 0;
333
334   while (finished == FALSE)
335     {
336       sscanf (ptr, "%127s", temp);
337
338       if ((gint)ptr[0] == 0 || strcmp ("s", temp) == 0 || strcmp ("m", temp) == 0 ||
339           strcmp ("g", temp) == 0 || strcmp ("g4", temp) == 0)
340        finished = TRUE;
341       else
342         {
343           if (numnames > 0)
344             strcat (color, " ");
345           strcat (color, temp);
346           ptr = gdk_pixmap_skip_string (ptr);
347           ptr = gdk_pixmap_skip_whitespaces (ptr);
348           numnames++;
349         }
350     }
351
352   retcol = g_strdup (color);
353   return retcol;
354 }
355
356
357 GdkPixmap*
358 gdk_pixmap_colormap_create_from_xpm (GdkWindow   *window,
359                                      GdkColormap *colormap,
360                                      GdkBitmap  **mask,
361                                      GdkColor    *transparent_color,
362                                      const gchar *filename)
363 {
364   FILE *infile = NULL;
365   GdkPixmap *pixmap = NULL;
366   GdkImage *image = NULL;
367   GdkVisual *visual;
368   GdkGC *gc;
369   GdkColor tmp_color;
370   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt;
371   gchar *buffer = NULL, pixel_str[32];
372   guint buffer_size = 0;
373   _GdkPixmapColor *colors = NULL, *color = NULL;
374   gulong index;
375
376   if ((window == NULL) && (colormap == NULL))
377     g_warning ("Creating pixmap from xpm with NULL window and colormap");
378
379   if (window == NULL)
380     window = (GdkWindow *)&gdk_root_parent;
381
382   if (colormap == NULL)
383     {
384       colormap = gdk_window_get_colormap (window);
385       visual = gdk_window_get_visual (window);
386     }
387   else
388     visual = ((GdkColormapPrivate *)colormap)->visual;
389
390   infile = fopen (filename, "rb");
391   if (infile != NULL)
392     {
393       if (gdk_pixmap_seek_string (infile, "XPM", FALSE) == TRUE)
394         {
395           if (gdk_pixmap_seek_char (infile,'{') == TRUE)
396             {
397               gdk_pixmap_seek_char (infile, '"');
398               fseek (infile, -1, SEEK_CUR);
399               gdk_pixmap_read_string (infile, &buffer, &buffer_size);
400
401               sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
402
403               colors = g_new(_GdkPixmapColor, num_cols);
404
405               if (transparent_color == NULL) 
406                 {
407                   gdk_color_white (colormap, &tmp_color);
408                   transparent_color = &tmp_color;
409                 }
410
411               for (cnt = 0; cnt < num_cols; cnt++)
412                 {
413                   gchar *color_name;
414
415                   gdk_pixmap_seek_char (infile, '"');
416                   fseek (infile, -1, SEEK_CUR);
417                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
418
419                   colors[cnt].color_string = g_new(gchar, cpp + 1);
420                   for (n = 0; n < cpp; n++)
421                     colors[cnt].color_string[n] = buffer[n];
422                   colors[cnt].color_string[n] = 0;
423                   colors[cnt].transparent = FALSE;
424
425                   color_name = gdk_pixmap_extract_color (&buffer[cpp]);
426
427                   if (color_name != NULL)
428                     {
429                       if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
430                         {
431                           colors[cnt].color = *transparent_color;
432                           colors[cnt].transparent = TRUE;
433                         }
434                     }
435                   else
436                     {
437                       colors[cnt].color = *transparent_color;
438                       colors[cnt].transparent = TRUE;
439                     }
440
441                   g_free (color_name);
442
443                   gdk_color_alloc (colormap, &colors[cnt].color);
444                 }
445
446               index = 0;
447               image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
448
449               gc = NULL;
450               if (mask)
451                 {
452                   /* The pixmap mask is just a bits pattern.
453                    * Color 0 is used for background and 1 for foreground.
454                    * We don't care about the colormap, we just need 0 and 1.
455                    */
456                   GdkColor mask_pattern;
457                   
458                   *mask = gdk_pixmap_new (window, width, height, 1);
459                   gc = gdk_gc_new (*mask);
460                   
461                   mask_pattern.pixel = 0;
462                   gdk_gc_set_foreground (gc, &mask_pattern);
463                   gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
464                   
465                   mask_pattern.pixel = 1;
466                   gdk_gc_set_foreground (gc, &mask_pattern);
467                 }
468
469               for (ycnt = 0; ycnt < height; ycnt++)
470                 {
471                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
472
473                   for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
474                     {
475                       strncpy (pixel_str, &buffer[n], cpp);
476                       pixel_str[cpp] = 0;
477                       color = NULL;
478                       ns = 0;
479
480                       while ((color == NULL) && (ns < num_cols))
481                         {
482                           if (strcmp (pixel_str, colors[ns].color_string) == 0)
483                             color = &colors[ns];
484                           else
485                             ns++;
486                         }
487
488                       if (!color) /* screwed up XPM file */
489                         color = &colors[0];
490
491                       gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
492
493                       if (mask && color->transparent)
494                         {
495                           if (cnt < xcnt)
496                             gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
497                           cnt = xcnt + 1;
498                         }
499                     }
500
501                   if (mask && (cnt < xcnt))
502                     gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
503                 }
504
505               if (mask)
506                 gdk_gc_destroy (gc);
507
508               pixmap = gdk_pixmap_new (window, width, height, visual->depth);
509
510               gc = gdk_gc_new (pixmap);
511               gdk_gc_set_foreground (gc, transparent_color);
512               gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
513               gdk_gc_destroy (gc);
514               gdk_image_destroy (image);
515             }
516         }
517
518       fclose (infile);
519       free (buffer);
520
521       if (colors != NULL)
522         {
523           for (cnt = 0; cnt < num_cols; cnt++)
524             g_free (colors[cnt].color_string);
525           g_free (colors);
526         }
527     }
528
529   return pixmap;
530 }
531
532 GdkPixmap*
533 gdk_pixmap_create_from_xpm (GdkWindow  *window,
534                             GdkBitmap **mask,
535                             GdkColor   *transparent_color,
536                             const gchar *filename)
537 {
538   return gdk_pixmap_colormap_create_from_xpm (window, NULL, mask, 
539                                        transparent_color, filename);
540 }
541
542
543 GdkPixmap*
544 gdk_pixmap_colormap_create_from_xpm_d (GdkWindow  *window,
545                                        GdkColormap *colormap,
546                                        GdkBitmap **mask,
547                                        GdkColor   *transparent_color,
548                                        gchar     **data)
549 {
550   GdkPixmap *pixmap = NULL;
551   GdkImage *image = NULL;
552   GdkVisual *visual;
553   GdkGC *gc;
554   GdkColor tmp_color;
555   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt, i;
556   gchar *buffer, pixel_str[32];
557   _GdkPixmapColor *colors = NULL, *color = NULL;
558   gulong index;
559
560   if ((window == NULL) && (colormap == NULL))
561     g_warning ("Creating pixmap from xpm with NULL window and colormap");
562
563   if (window == NULL)
564     window = (GdkWindow *)&gdk_root_parent;
565
566   if (colormap == NULL)
567     {
568       colormap = gdk_window_get_colormap (window);
569       visual = gdk_window_get_visual (window);
570     }
571   else
572     visual = ((GdkColormapPrivate *)colormap)->visual;
573
574   i = 0;
575   buffer = data[i++];
576   sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
577
578   colors = g_new(_GdkPixmapColor, num_cols);
579
580   if (transparent_color == NULL) 
581     {
582       gdk_color_white (colormap, &tmp_color);
583       transparent_color = &tmp_color;
584     }
585
586   for (cnt = 0; cnt < num_cols; cnt++)
587     {
588       gchar *color_name;
589
590       buffer = data[i++];
591
592       colors[cnt].color_string = g_new(gchar, cpp + 1);
593       for (n = 0; n < cpp; n++)
594         colors[cnt].color_string[n] = buffer[n];
595       colors[cnt].color_string[n] = 0;
596       colors[cnt].transparent = FALSE;
597
598       color_name = gdk_pixmap_extract_color (&buffer[cpp]);
599
600       if (color_name != NULL)
601         {
602           if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
603             {
604               colors[cnt].color = *transparent_color;
605               colors[cnt].transparent = TRUE;
606             }
607         }
608       else
609         {
610           colors[cnt].color = *transparent_color;
611           colors[cnt].transparent = TRUE;
612         }
613
614       g_free (color_name);
615
616       gdk_color_alloc (colormap, &colors[cnt].color);
617     }
618
619   index = 0;
620   image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
621
622   gc = NULL;
623   if (mask)
624     {
625       /* The pixmap mask is just a bits pattern.
626        * Color 0 is used for background and 1 for foreground.
627        * We don't care about the colormap, we just need 0 and 1.
628        */
629       GdkColor mask_pattern;
630
631       *mask = gdk_pixmap_new (window, width, height, 1);
632       gc = gdk_gc_new (*mask);
633
634       mask_pattern.pixel = 0;
635       gdk_gc_set_foreground (gc, &mask_pattern);
636       gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
637
638       mask_pattern.pixel = 1;
639       gdk_gc_set_foreground (gc, &mask_pattern);
640     }
641
642   for (ycnt = 0; ycnt < height; ycnt++)
643     {
644       buffer = data[i++];
645
646       for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
647         {
648           strncpy (pixel_str, &buffer[n], cpp);
649           pixel_str[cpp] = 0;
650           color = NULL;
651           ns = 0;
652
653           while ((color == NULL) && (ns < num_cols))
654             {
655               if (strcmp (pixel_str, colors[ns].color_string) == 0)
656                 color = &colors[ns];
657               else
658                 ns++;
659             }
660
661           if (!color) /* screwed up XPM file */
662             color = &colors[0];
663
664           gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
665
666           if (mask && color->transparent)
667             {
668               if (cnt < xcnt)
669                 gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
670               cnt = xcnt + 1;
671             }
672         }
673
674       if (mask && (cnt < xcnt))
675         gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
676     }
677
678   if (mask)
679     gdk_gc_destroy (gc);
680
681   pixmap = gdk_pixmap_new (window, width, height, visual->depth);
682
683   gc = gdk_gc_new (pixmap);
684   gdk_gc_set_foreground (gc, transparent_color);
685   gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
686   gdk_gc_destroy (gc);
687   gdk_image_destroy (image);
688
689   if (colors != NULL)
690     {
691       for (cnt = 0; cnt < num_cols; cnt++)
692         g_free (colors[cnt].color_string);
693       g_free (colors);
694     }
695
696   return pixmap;
697 }
698
699 GdkPixmap*
700 gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
701                               GdkBitmap **mask,
702                               GdkColor   *transparent_color,
703                               gchar     **data)
704 {
705   return gdk_pixmap_colormap_create_from_xpm_d (window, NULL, mask,
706                                                 transparent_color, data);
707 }
708
709 GdkPixmap*
710 gdk_pixmap_ref (GdkPixmap *pixmap)
711 {
712   GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
713   g_return_val_if_fail (pixmap != NULL, NULL);
714
715   private->ref_count += 1;
716   return pixmap;
717 }
718
719 void
720 gdk_pixmap_unref (GdkPixmap *pixmap)
721 {
722   GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
723   g_return_if_fail(pixmap != NULL);
724
725   private->ref_count -= 1;
726   if (private->ref_count == 0)
727     {
728       XFreePixmap (private->xdisplay, private->xwindow);
729       gdk_xid_table_remove (private->xwindow);
730       g_free (private);
731     }
732 }
733
734 GdkBitmap *
735 gdk_bitmap_ref (GdkBitmap *bitmap)
736 {
737   return (GdkBitmap *)gdk_pixmap_ref ((GdkPixmap *)bitmap);
738 }
739
740 void
741 gdk_bitmap_unref (GdkBitmap *bitmap)
742 {
743   gdk_pixmap_unref ((GdkPixmap *)bitmap);
744 }