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