]> Pileus Git - ~andy/gtk/blob - gdk/gdkpixbuf-drawable.c
New function to create a pixbuf pointing to a subregion of another pixbuf.
[~andy/gtk] / gdk / gdkpixbuf-drawable.c
1 /* GdkPixbuf library - convert X drawable information to RGB
2  *
3  * Copyright (C) 1999 Michael Zucchi
4  *
5  * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au>
6  *          Cody Russell <bratsche@dfw.net>
7  *          Federico Mena-Quintero <federico@gimp.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <config.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include "gdk.h"                /* For gdk_screen_width/gdk_screen_height */
29 #include "gdkcolor.h"
30 #include "gdkimage.h"
31 #include "gdkvisual.h"
32 #include "gdkwindow.h"
33 #include "gdkpixbuf.h"
34 #include "gdk-pixbuf-private.h"
35
36 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
37 #define LITTLE
38 #endif
39 #define d(x)
40
41 \f
42
43 static guint32 mask_table[] = {
44   0x00000000, 0x00000001, 0x00000003, 0x00000007,
45   0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
46   0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
47   0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
48   0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
49   0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
50   0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
51   0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
52   0xffffffff
53 };
54
55 \f
56
57 /*
58  * convert 1 bits-pixel data
59  * no alpha
60  */
61 static void
62 rgb1 (GdkImage    *image,
63       guchar      *pixels,
64       int          rowstride,
65       GdkColormap *colormap)
66 {
67   int xx, yy;
68   int width, height;
69   int bpl;
70   guint8 *s;
71   register guint8 data;
72   guint8 *o;
73   guint8 *srow = image->mem, *orow = pixels;
74
75   d (printf ("1 bits/pixel\n"));
76
77   /* convert upto 8 pixels/time */
78   /* its probably not worth trying to make this run very fast, who uses
79    * 1 bit displays anymore?
80    */
81   width = image->width;
82   height = image->height;
83   bpl = image->bpl;
84
85   for (yy = 0; yy < height; yy++)
86     {
87       s = srow;
88       o = orow;
89       
90       for (xx = 0; xx < width; xx ++)
91         {
92           data = srow[xx >> 3] >> (7 - (xx & 7)) & 1;
93           *o++ = colormap->colors[data].red;
94           *o++ = colormap->colors[data].green;
95           *o++ = colormap->colors[data].blue;
96         }
97       srow += bpl;
98       orow += rowstride;
99     }
100 }
101
102 /*
103  * convert 1 bits/pixel data
104  * with alpha
105  */
106 static void
107 rgb1a (GdkImage    *image,
108        guchar      *pixels,
109        int          rowstride,
110        GdkColormap *colormap)
111 {
112   int xx, yy;
113   int width, height;
114   int bpl;
115   guint8 *s;
116   register guint8 data;
117   guint8 *o;
118   guint8 *srow = image->mem, *orow = pixels;
119   guint32 remap[2];
120
121   d (printf ("1 bits/pixel\n"));
122
123   /* convert upto 8 pixels/time */
124   /* its probably not worth trying to make this run very fast, who uses
125    * 1 bit displays anymore? */
126   width = image->width;
127   height = image->height;
128   bpl = image->bpl;
129
130   for (xx = 0; xx < 2; xx++)
131     {
132 #ifdef LITTLE
133       remap[xx] = 0xff000000
134         | colormap->colors[xx].blue << 16
135         | colormap->colors[xx].green << 8
136         | colormap->colors[xx].red;
137 #else
138       remap[xx] = 0xff
139         | colormap->colors[xx].red << 24
140         | colormap->colors[xx].green << 16
141         | colormap->colors[xx].blue << 8;
142 #endif
143     }
144
145   for (yy = 0; yy < height; yy++)
146     {
147       s = srow;
148       o = orow;
149       
150       for (xx = 0; xx < width; xx ++)
151         {
152           data = srow[xx >> 3] >> (7 - (xx & 7)) & 1;
153           *o++ = remap[data];
154         }
155       srow += bpl;
156       orow += rowstride;
157     }
158 }
159
160 /*
161  * convert 8 bits/pixel data
162  * no alpha
163  */
164 static void
165 rgb8 (GdkImage    *image,
166       guchar      *pixels,
167       int          rowstride,
168       GdkColormap *colormap)
169 {
170   int xx, yy;
171   int width, height;
172   int bpl;
173   guint32 mask;
174   register guint32 data;
175   guint8 *srow = image->mem, *orow = pixels;
176   register guint8 *s;
177   register guint8 *o;
178
179   width = image->width;
180   height = image->height;
181   bpl = image->bpl;
182
183   d (printf ("8 bit, no alpha output\n"));
184
185   mask = mask_table[image->depth];
186
187   for (yy = 0; yy < height; yy++)
188     {
189       s = srow;
190       o = orow;
191       for (xx = 0; xx < width; xx++) {
192         data = *s++ & mask;
193         *o++ = colormap->colors[data].red;
194         *o++ = colormap->colors[data].green;
195         *o++ = colormap->colors[data].blue;
196       }
197       srow += bpl;
198       orow += rowstride;
199     }
200 }
201
202 /*
203  * convert 8 bits/pixel data
204  * with alpha
205  */
206 static void
207 rgb8a (GdkImage    *image,
208        guchar      *pixels,
209        int          rowstride,
210        GdkColormap *colormap)
211 {
212   int xx, yy;
213   int width, height;
214   int bpl;
215   guint32 mask;
216   register guint32 data;
217   guint32 remap[256];
218   register guint8 *s;   /* read 2 pixels at once */
219   register guint32 *o;
220   guint8 *srow = image->mem, *orow = pixels;
221
222   width = image->width;
223   height = image->height;
224   bpl = image->bpl;
225
226   d (printf ("8 bit, with alpha output\n"));
227
228   mask = mask_table[image->depth];
229
230   for (xx = 0; xx < colormap->size; xx++)
231     {
232 #ifdef LITTLE
233       remap[xx] = 0xff000000
234         | colormap->colors[xx].blue << 16
235         | colormap->colors[xx].green << 8
236         | colormap->colors[xx].red;
237 #else
238       remap[xx] = 0xff
239         | colormap->colors[xx].red << 24
240         | colormap->colors[xx].green << 16
241         | colormap->colors[xx].blue << 8;
242 #endif
243     }
244
245   for (yy = 0; yy < height; yy++)
246     {
247       s = srow;
248       o = (guint32 *) orow;
249       for (xx = 0; xx < width; xx ++)
250         {
251           data = *s++ & mask;
252           *o++ = remap[data];
253         }
254       srow += bpl;
255       orow += rowstride;
256     }
257 }
258
259 /*
260  * convert 16 bits/pixel data
261  * no alpha
262  * data in lsb format
263  */
264 static void
265 rgb565lsb (GdkImage    *image,
266            guchar      *pixels,
267            int          rowstride,
268            GdkColormap *colormap)
269 {
270   int xx, yy;
271   int width, height;
272   int bpl;
273
274 #ifdef LITTLE
275   register guint32 *s;  /* read 2 pixels at once */
276 #else
277   register guint8 *s;   /* read 2 pixels at once */
278 #endif
279   register guint16 *o;
280   guint8 *srow = image->mem, *orow = pixels;
281
282   width = image->width;
283   height = image->height;
284   bpl = image->bpl;
285
286   for (yy = 0; yy < height; yy++)
287     {
288 #ifdef LITTLE
289       s = (guint32 *) srow;
290 #else
291       s = srow;
292 #endif
293       o = (guint16 *) orow;
294       for (xx = 1; xx < width; xx += 2)
295         {
296           register guint32 data;
297 #ifdef LITTLE
298           data = *s++;
299           *o++ = (data & 0xf800) >> 8 | (data & 0xe000) >> 13
300             | (data & 0x7e0) << 5 | (data & 0x600) >> 1;
301           *o++ = (data & 0x1f) << 3 | (data & 0x1c) >> 2
302             | (data & 0xf8000000) >> 16 | (data & 0xe0000000) >> 21;
303           *o++ = (data & 0x7e00000) >> 19 | (data & 0x6000000) >> 25
304             | (data & 0x1f0000) >> 5 | (data & 0x1c0000) >> 10;
305 #else
306           /* swap endianness first */
307           data = s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
308           s += 4;
309           *o++ = (data & 0xf800) | (data & 0xe000) >> 5
310             | (data & 0x7e0) >> 3 | (data & 0x600) >> 9;
311           *o++ = (data & 0x1f) << 11 | (data & 0x1c) << 6
312             | (data & 0xf8000000) >> 24 | (data & 0xe0000000) >> 29;
313           *o++ = (data & 0x7e00000) >> 11 | (data & 0x6000000) >> 17
314             | (data & 0x1f0000) >> 13 | (data & 0x1c0000) >> 18;
315 #endif
316         }
317       /* check for last remaining pixel */
318       if (width & 1)
319         {
320           register guint16 data;
321 #ifdef LITTLE
322           data = *((short *) s);
323 #else
324           data = *((short *) s);
325           data = ((data >> 8) & 0xff) | ((data & 0xff) << 8);
326 #endif
327           ((char *) o)[0] = ((data >> 8) & 0xf8) | ((data >> 13) & 0x7);
328           ((char *) o)[1] = ((data >> 3) & 0xfc) | ((data >> 9) & 0x3);
329           ((char *) o)[2] = ((data << 3) & 0xf8) | ((data >> 2) & 0x7);
330         }
331       srow += bpl;
332       orow += rowstride;
333     }
334 }
335
336 /*
337  * convert 16 bits/pixel data
338  * no alpha
339  * data in msb format
340  */
341 static void
342 rgb565msb (GdkImage    *image,
343            guchar      *pixels,
344            int          rowstride,
345            GdkColormap *colormap)
346 {
347   int xx, yy;
348   int width, height;
349   int bpl;
350
351 #ifdef LITTLE
352   register guint8 *s;   /* need to swap data order */
353 #else
354   register guint32 *s;  /* read 2 pixels at once */
355 #endif
356   register guint16 *o;
357   guint8 *srow = image->mem, *orow = pixels;
358
359   width = image->width;
360   height = image->height;
361   bpl = image->bpl;
362
363   for (yy = 0; yy < height; yy++)
364     {
365 #ifdef LITTLE
366       s = srow;
367 #else
368       s = (guint32 *) srow;
369 #endif
370       o = (guint16 *) orow;
371       for (xx = 1; xx < width; xx += 2)
372         {
373           register guint32 data;
374 #ifdef LITTLE
375           /* swap endianness first */
376           data = s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
377           s += 4;
378           *o++ = (data & 0xf800) >> 8 | (data & 0xe000) >> 13
379             | (data & 0x7e0) << 5 | (data & 0x600) >> 1;
380           *o++ = (data & 0x1f) << 3 | (data & 0x1c) >> 2
381             | (data & 0xf8000000) >> 16 | (data & 0xe0000000) >> 21;
382           *o++ = (data & 0x7e00000) >> 19 | (data & 0x6000000) >> 25
383             | (data & 0x1f0000) >> 5 | (data & 0x1c0000) >> 10;
384 #else
385           data = *s++;
386           *o++ = (data & 0xf800) | (data & 0xe000) >> 5
387             | (data & 0x7e0) >> 3 | (data & 0x600) >> 9;
388           *o++ = (data & 0x1f) << 11 | (data & 0x1c) << 6
389             | (data & 0xf8000000) >> 24 | (data & 0xe0000000) >> 29;
390           *o++ = (data & 0x7e00000) >> 11 | (data & 0x6000000) >> 17
391             | (data & 0x1f0000) >> 13 | (data & 0x1c0000) >> 18;
392 #endif
393         }
394       /* check for last remaining pixel */
395       if (width & 1)
396         {
397           register guint16 data;
398 #ifdef LITTLE
399           data = *((short *) s);
400           data = ((data >> 8) & 0xff) | ((data & 0xff) << 8);
401 #else
402           data = *((short *) s);
403 #endif
404           ((char *) o)[0] = ((data >> 8) & 0xf8) | ((data >> 13) & 0x7);
405           ((char *) o)[1] = ((data >> 3) & 0xfc) | ((data >> 9) & 0x3);
406           ((char *) o)[2] = ((data << 3) & 0xf8) | ((data >> 2) & 0x7);
407         }
408       srow += bpl;
409       orow += rowstride;
410     }
411 }
412
413 /*
414  * convert 16 bits/pixel data
415  * with alpha
416  * data in lsb format
417  */
418 static void
419 rgb565alsb (GdkImage    *image,
420             guchar      *pixels,
421             int          rowstride,
422             GdkColormap *colormap)
423 {
424   int xx, yy;
425   int width, height;
426   int bpl;
427
428 #ifdef LITTLE
429   register guint16 *s;  /* read 1 pixels at once */
430 #else
431   register guint8 *s;
432 #endif
433   register guint32 *o;
434
435   guint8 *srow = image->mem, *orow = pixels;
436
437   width = image->width;
438   height = image->height;
439   bpl = image->bpl;
440
441   for (yy = 0; yy < height; yy++)
442     {
443 #ifdef LITTLE
444       s = (guint16 *) srow;
445 #else
446       s = (guint8 *) srow;
447 #endif
448       o = (guint32 *) orow;
449       for (xx = 0; xx < width; xx ++)
450         {
451           register guint32 data;
452           /*  rrrrrggg gggbbbbb -> rrrrrRRR ggggggGG bbbbbBBB aaaaaaaa */
453           /*  little endian: aaaaaaaa bbbbbBBB ggggggGG rrrrrRRR */
454 #ifdef LITTLE
455           data = *s++;
456           *o++ = (data & 0xf800) >> 8 | (data & 0xe000) >> 13
457             | (data & 0x7e0) << 5 | (data & 0x600) >> 1
458             | (data & 0x1f) << 19 | (data & 0x1c) << 14
459             | 0xff000000;
460 #else
461           /* swap endianness first */
462           data = s[0] | s[1] << 8;
463           s += 2;
464           *o++ = (data & 0xf800) << 16 | (data & 0xe000) << 11
465             | (data & 0x7e0) << 13 | (data & 0x600) << 7
466             | (data & 0x1f) << 11 | (data & 0x1c) << 6
467             | 0xff;
468 #endif
469         }
470       srow += bpl;
471       orow += rowstride;
472     }
473 }
474
475 /*
476  * convert 16 bits/pixel data
477  * with alpha
478  * data in msb format
479  */
480 static void
481 rgb565amsb (GdkImage    *image,
482             guchar      *pixels,
483             int          rowstride,
484             GdkColormap *colormap)
485 {
486   int xx, yy;
487   int width, height;
488   int bpl;
489
490 #ifdef LITTLE
491   register guint8 *s;
492 #else
493   register guint16 *s;  /* read 1 pixels at once */
494 #endif
495   register guint32 *o;
496
497   guint8 *srow = image->mem, *orow = pixels;
498
499   width = image->width;
500   height = image->height;
501   bpl = image->bpl;
502
503   for (yy = 0; yy < height; yy++)
504     {
505       s = srow;
506       o = (guint32 *) orow;
507       for (xx = 0; xx < width; xx ++)
508         {
509           register guint32 data;
510           /*  rrrrrggg gggbbbbb -> rrrrrRRR gggggg00 bbbbbBBB aaaaaaaa */
511           /*  little endian: aaaaaaaa bbbbbBBB gggggg00 rrrrrRRR */
512 #ifdef LITTLE
513           /* swap endianness first */
514           data = s[0] | s[1] << 8;
515           s += 2;
516           *o++ = (data & 0xf800) >> 8 | (data & 0xe000) >> 13
517             | (data & 0x7e0) << 5 | (data & 0x600) >> 1
518             | (data & 0x1f) << 19 | (data & 0x1c) << 14
519             | 0xff000000;
520 #else
521           data = *s++;
522           *o++ = (data & 0xf800) << 16 | (data & 0xe000) << 11
523             | (data & 0x7e0) << 13 | (data & 0x600) << 7
524             | (data & 0x1f) << 11 | (data & 0x1c) << 6
525             | 0xff;
526 #endif
527         }
528       srow += bpl;
529       orow += rowstride;
530     }
531 }
532
533 /*
534  * convert 15 bits/pixel data
535  * no alpha
536  * data in lsb format
537  */
538 static void
539 rgb555lsb (GdkImage     *image,
540            guchar       *pixels,
541            int           rowstride,
542            GdkColormap  *colormap)
543 {
544   int xx, yy;
545   int width, height;
546   int bpl;
547
548 #ifdef LITTLE
549   register guint32 *s;  /* read 2 pixels at once */
550 #else
551   register guint8 *s;   /* read 2 pixels at once */
552 #endif
553   register guint16 *o;
554   guint8 *srow = image->mem, *orow = pixels;
555
556   width = image->width;
557   height = image->height;
558   bpl = image->bpl;
559
560   for (yy = 0; yy < height; yy++)
561     {
562 #ifdef LITTLE
563       s = (guint32 *) srow;
564 #else
565       s = srow;
566 #endif
567       o = (guint16 *) orow;
568       for (xx = 1; xx < width; xx += 2)
569         {
570           register guint32 data;
571 #ifdef LITTLE
572           data = *s++;
573           *o++ = (data & 0x7c00) >> 7 | (data & 0x7000) >> 12
574             | (data & 0x3e0) << 6 | (data & 0x380) << 1;
575           *o++ = (data & 0x1f) << 3 | (data & 0x1c) >> 2
576             | (data & 0x7c000000) >> 15 | (data & 0x70000000) >> 20;
577           *o++ = (data & 0x3e00000) >> 18 | (data & 0x3800000) >> 23
578             | (data & 0x1f0000) >> 5 | (data & 0x1c0000) >> 10;
579 #else
580           /* swap endianness first */
581           data = s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
582           s += 4;
583           *o++ = (data & 0x7c00) << 1 | (data & 0x7000) >> 4
584             | (data & 0x3e0) >> 2 | (data & 0x380) >> 7;
585           *o++ = (data & 0x1f) << 11 | (data & 0x1c) << 6
586             | (data & 0x7c000000) >> 23 | (data & 0x70000000) >> 28;
587           *o++ = (data & 0x3e00000) >> 10 | (data & 0x3800000) >> 15
588             | (data & 0x1f0000) >> 13 | (data & 0x1c0000) >> 18;
589 #endif
590         }
591       /* check for last remaining pixel */
592       if (width & 1)
593         {
594           register guint16 data;
595 #ifdef LITTLE
596           data = *((short *) s);
597 #else
598           data = *((short *) s);
599           data = ((data >> 8) & 0xff) | ((data & 0xff) << 8);
600 #endif
601           ((char *) o)[0] = (data & 0x7c00) >> 7 | (data & 0x7000) >> 12;
602           ((char *) o)[1] = (data & 0x3e0) >> 2 | (data & 0x380) >> 7;
603           ((char *) o)[2] = (data & 0x1f) << 3 | (data & 0x1c) >> 2;
604         }
605       srow += bpl;
606       orow += rowstride;
607     }
608 }
609
610 /*
611  * convert 15 bits/pixel data
612  * no alpha
613  * data in msb format
614  */
615 static void
616 rgb555msb (GdkImage    *image,
617            guchar      *pixels,
618            int          rowstride,
619            GdkColormap *colormap)
620 {
621   int xx, yy;
622   int width, height;
623   int bpl;
624
625 #ifdef LITTLE
626   register guint8 *s;   /* read 2 pixels at once */
627 #else
628   register guint32 *s;  /* read 2 pixels at once */
629 #endif
630   register guint16 *o;
631   guint8 *srow = image->mem, *orow = pixels;
632
633   width = image->width;
634   height = image->height;
635   bpl = image->bpl;
636
637   for (yy = 0; yy < height; yy++)
638     {
639       s = srow;
640       o = (guint16 *) orow;
641       for (xx = 1; xx < width; xx += 2)
642         {
643           register guint32 data;
644 #ifdef LITTLE
645           /* swap endianness first */
646           data = s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
647           s += 4;
648           *o++ = (data & 0x7c00) >> 7 | (data & 0x7000) >> 12
649             | (data & 0x3e0) << 6 | (data & 0x380) << 1;
650           *o++ = (data & 0x1f) << 3 | (data & 0x1c) >> 2
651             | (data & 0x7c000000) >> 15 | (data & 0x70000000) >> 20;
652           *o++ = (data & 0x3e00000) >> 18 | (data & 0x3800000) >> 23
653             | (data & 0x1f0000) >> 5 | (data & 0x1c0000) >> 10;
654 #else
655           data = *s++;
656           *o++ = (data & 0x7c00) << 1 | (data & 0x7000) >> 4
657             | (data & 0x3e0) >> 2 | (data & 0x380) >> 7;
658           *o++ = (data & 0x1f) << 11 | (data & 0x1c) << 6
659             | (data & 0x7c000000) >> 23 | (data & 0x70000000) >> 28;
660           *o++ = (data & 0x3e00000) >> 10 | (data & 0x3800000) >> 15
661             | (data & 0x1f0000) >> 13 | (data & 0x1c0000) >> 18;
662 #endif
663         }
664       /* check for last remaining pixel */
665       if (width & 1)
666         {
667           register guint16 data;
668 #ifdef LITTLE
669           data = *((short *) s);
670           data = ((data >> 8) & 0xff) | ((data & 0xff) << 8);
671 #else
672           data = *((short *) s);
673 #endif
674           ((char *) o)[0] = (data & 0x7c00) >> 7 | (data & 0x7000) >> 12;
675           ((char *) o)[1] = (data & 0x3e0) >> 2 | (data & 0x380) >> 7;
676           ((char *) o)[2] = (data & 0x1f) << 3 | (data & 0x1c) >> 2;
677         }
678       srow += bpl;
679       orow += rowstride;
680     }
681 }
682
683 /*
684  * convert 15 bits/pixel data
685  * with alpha
686  * data in lsb format
687  */
688 static void
689 rgb555alsb (GdkImage    *image,
690             guchar      *pixels,
691             int          rowstride,
692             GdkColormap *colormap)
693 {
694   int xx, yy;
695   int width, height;
696   int bpl;
697
698 #ifdef LITTLE
699   register guint16 *s;  /* read 1 pixels at once */
700 #else
701   register guint8 *s;
702 #endif
703   register guint32 *o;
704
705   guint8 *srow = image->mem, *orow = pixels;
706
707   width = image->width;
708   height = image->height;
709   bpl = image->bpl;
710
711   for (yy = 0; yy < height; yy++)
712     {
713 #ifdef LITTLE
714       s = (guint16 *) srow;
715 #else
716       s = srow;
717 #endif
718       o = (guint32 *) orow;
719       for (xx = 0; xx < width; xx++)
720         {
721           register guint32 data;
722           /*  rrrrrggg gggbbbbb -> rrrrrRRR gggggGGG bbbbbBBB aaaaaaaa */
723           /*  little endian: aaaaaaaa bbbbbBBB gggggGGG rrrrrRRR */
724 #ifdef LITTLE
725           data = *s++;
726           *o++ = (data & 0x7c00) >> 7 | (data & 0x7000) >> 12
727             | (data & 0x3e0) << 6 | (data & 0x380) << 1
728             | (data & 0x1f) << 19 | (data & 0x1c) << 14
729             | 0xff000000;
730 #else
731           /* swap endianness first */
732           data = s[0] | s[1] << 8;
733           s += 2;
734           *o++ = (data & 0x7c00) << 17 | (data & 0x7000) << 12
735             | (data & 0x3e0) << 14 | (data & 0x380) << 9
736             | (data & 0x1f) << 11 | (data & 0x1c) << 6
737             | 0xff;
738 #endif
739         }
740       srow += bpl;
741       orow += rowstride;
742     }
743 }
744
745 /*
746  * convert 15 bits/pixel data
747  * with alpha
748  * data in msb format
749  */
750 static void
751 rgb555amsb (GdkImage    *image,
752             guchar      *pixels,
753             int          rowstride,
754             GdkColormap *colormap)
755 {
756   int xx, yy;
757   int width, height;
758   int bpl;
759
760 #ifdef LITTLE
761   register guint16 *s;  /* read 1 pixels at once */
762 #else
763   register guint8 *s;
764 #endif
765   register guint32 *o;
766
767   guint8 *srow = image->mem, *orow = pixels;
768
769   width = image->width;
770   height = image->height;
771   bpl = image->bpl;
772
773   for (yy = 0; yy < height; yy++)
774     {
775 #ifdef LITTLE
776       s = (guint16 *) srow;
777 #else
778       s = srow;
779 #endif
780       o = (guint32 *) orow;
781       for (xx = 0; xx < width; xx++)
782         {
783           register guint32 data;
784           /*  rrrrrggg gggbbbbb -> rrrrrRRR gggggGGG bbbbbBBB aaaaaaaa */
785           /*  little endian: aaaaaaaa bbbbbBBB gggggGGG rrrrrRRR */
786 #ifdef LITTLE
787           /* swap endianness first */
788           data = s[0] | s[1] << 8;
789           s += 2;
790           *o++ = (data & 0x7c00) >> 7 | (data & 0x7000) >> 12
791             | (data & 0x3e0) << 6 | (data & 0x380) << 1
792             | (data & 0x1f) << 19 | (data & 0x1c) << 14
793             | 0xff000000;
794 #else
795           data = *s++;
796           *o++ = (data & 0x7c00) << 17 | (data & 0x7000) << 12
797             | (data & 0x3e0) << 14 | (data & 0x380) << 9
798             | (data & 0x1f) << 11 | (data & 0x1c) << 6
799             | 0xff;
800 #endif
801         }
802       srow += bpl;
803       orow += rowstride;
804     }
805 }
806
807
808 static void
809 rgb888alsb (GdkImage    *image,
810             guchar      *pixels,
811             int          rowstride,
812             GdkColormap *colormap)
813 {
814   int xx, yy;
815   int width, height;
816   int bpl;
817
818   guint8 *s;    /* for byte order swapping */
819   guint8 *o;
820   guint8 *srow = image->mem, *orow = pixels;
821
822   width = image->width;
823   height = image->height;
824   bpl = image->bpl;
825
826   d (printf ("32 bits/pixel with alpha\n"));
827
828   /* lsb data */
829   for (yy = 0; yy < height; yy++)
830     {
831       s = srow;
832       o = orow;
833       for (xx = 0; xx < width; xx++)
834         {
835           *o++ = s[2];
836           *o++ = s[1];
837           *o++ = s[0];
838           *o++ = 0xff;
839           s += 4;
840         }
841       srow += bpl;
842       orow += rowstride;
843     }
844 }
845
846 static void
847 rgb888lsb (GdkImage    *image,
848            guchar      *pixels,
849            int          rowstride,
850            GdkColormap *colormap)
851 {
852   int xx, yy;
853   int width, height;
854   int bpl;
855
856   guint8 *srow = image->mem, *orow = pixels;
857   guint8 *o, *s;
858
859   width = image->width;
860   height = image->height;
861   bpl = image->bpl;
862
863   d (printf ("32 bit, lsb, no alpha\n"));
864
865   for (yy = 0; yy < height; yy++)
866     {
867       s = srow;
868       o = orow;
869       for (xx = 0; xx < width; xx++)
870         {
871           *o++ = s[2];
872           *o++ = s[1];
873           *o++ = s[0];
874           s += 4;
875         }
876       srow += bpl;
877       orow += rowstride;
878     }
879 }
880
881 static void
882 rgb888amsb (GdkImage    *image,
883             guchar      *pixels,
884             int          rowstride,
885             GdkColormap *colormap)
886 {
887   int xx, yy;
888   int width, height;
889   int bpl;
890
891   guint8 *srow = image->mem, *orow = pixels;
892 #ifdef LITTLE
893   guint32 *o;
894   guint32 *s;
895 #else
896   guint8 *s;    /* for byte order swapping */
897   guint8 *o;
898 #endif
899
900   d (printf ("32 bit, msb, with alpha\n"));
901
902   width = image->width;
903   height = image->height;
904   bpl = image->bpl;
905
906   /* msb data */
907   for (yy = 0; yy < height; yy++)
908     {
909 #ifdef LITTLE
910       s = (guint32 *) srow;
911       o = (guint32 *) orow;
912 #else
913       s = srow;
914       o = orow;
915 #endif
916       for (xx = 0; xx < width; xx++)
917         {
918 #ifdef LITTLE
919           *o++ = s[1];
920           *o++ = s[2];
921           *o++ = s[3];
922           *o++ = 0xff;
923           s += 4;
924 #else
925           *o++ = (*s << 8) | 0xff; /* untested */
926           s++;
927 #endif
928         }
929       srow += bpl;
930       orow += rowstride;
931     }
932 }
933
934 static void
935 rgb888msb (GdkImage    *image,
936            guchar      *pixels,
937            int          rowstride,
938            GdkColormap *colormap)
939 {
940   int xx, yy;
941   int width, height;
942   int bpl;
943
944   guint8 *srow = image->mem, *orow = pixels;
945   guint8 *s;
946   guint8 *o;
947
948   d (printf ("32 bit, msb, no alpha\n"));
949
950   width = image->width;
951   height = image->height;
952   bpl = image->bpl;
953
954   for (yy = 0; yy < height; yy++)
955     {
956       s = srow;
957       o = orow;
958       for (xx = 0; xx < width; xx++)
959         {
960           *o++ = s[1];
961           *o++ = s[2];
962           *o++ = s[3];
963           s += 4;
964         }
965       srow += bpl;
966       orow += rowstride;
967     }
968 }
969
970 /*
971  * This should work correctly with any display/any endianness, but will probably
972  * run quite slow
973  */
974 static void
975 convert_real_slow (GdkImage    *image,
976                    guchar      *pixels,
977                    int          rowstride,
978                    GdkColormap *cmap,
979                    int          alpha)
980 {
981   int xx, yy;
982   int width, height;
983   int bpl;
984   guint8 *srow = image->mem, *orow = pixels;
985   guint8 *s;
986   guint8 *o;
987   guint32 pixel;
988   GdkVisual *v;
989   guint8 component;
990   int i;
991
992   width = image->width;
993   height = image->height;
994   bpl = image->bpl;
995   v = gdk_colormap_get_visual(cmap);
996
997   d(printf("rgb  mask/shift/prec = %x:%x:%x %d:%d:%d  %d:%d:%d\n",
998            v->red_mask, v->green_mask, v->blue_mask,
999            v->red_shift, v->green_shift, v->blue_shift,
1000            v->red_prec, v->green_prec, v->blue_prec));
1001
1002   for (yy = 0; yy < height; yy++)
1003     {
1004       s = srow;
1005       o = orow;
1006       for (xx = 0; xx < width; xx++)
1007         {
1008           pixel = gdk_image_get_pixel(image, xx, yy);
1009           switch (v->type)
1010             {
1011                                 /* I assume this is right for static & greyscale's too? */
1012             case GDK_VISUAL_STATIC_GRAY:
1013             case GDK_VISUAL_GRAYSCALE:
1014             case GDK_VISUAL_STATIC_COLOR:
1015             case GDK_VISUAL_PSEUDO_COLOR:
1016               *o++ = cmap->colors[pixel].red;
1017               *o++ = cmap->colors[pixel].green;
1018               *o++ = cmap->colors[pixel].blue;
1019               break;
1020             case GDK_VISUAL_TRUE_COLOR:
1021                                 /* This is odd because it must sometimes shift left (otherwise
1022                                  * I'd just shift >> (*_shift - 8 + *_prec + <0-7>). This logic
1023                                  * should work for all bit sizes/shifts/etc.
1024                                  */
1025               component = 0;
1026               for (i = 24; i < 32; i += v->red_prec)
1027                 component |= ((pixel & v->red_mask) << (32 - v->red_shift - v->red_prec)) >> i;
1028               *o++ = component;
1029               component = 0;
1030               for (i = 24; i < 32; i += v->green_prec)
1031                 component |= ((pixel & v->green_mask) << (32 - v->green_shift - v->green_prec)) >> i;
1032               *o++ = component;
1033               component = 0;
1034               for (i = 24; i < 32; i += v->blue_prec)
1035                 component |= ((pixel & v->blue_mask) << (32 - v->blue_shift - v->blue_prec)) >> i;
1036               *o++ = component;
1037               break;
1038             case GDK_VISUAL_DIRECT_COLOR:
1039               *o++ = cmap->colors[((pixel & v->red_mask) << (32 - v->red_shift - v->red_prec)) >> 24].red;
1040               *o++ = cmap->colors[((pixel & v->green_mask) << (32 - v->green_shift - v->green_prec)) >> 24].green;
1041               *o++ = cmap->colors[((pixel & v->blue_mask) << (32 - v->blue_shift - v->blue_prec)) >> 24].blue;
1042               break;
1043             }
1044           if (alpha)
1045             *o++ = 0xff;
1046         }
1047       srow += bpl;
1048       orow += rowstride;
1049     }
1050 }
1051
1052 typedef void (* cfunc) (GdkImage *image, guchar *pixels, int rowstride, GdkColormap *cmap);
1053
1054 static cfunc convert_map[] = {
1055   rgb1,rgb1,rgb1a,rgb1a,
1056   rgb8,rgb8,rgb8a,rgb8a,
1057   rgb555lsb,rgb555msb,rgb555alsb,rgb555amsb,
1058   rgb565lsb,rgb565msb,rgb565alsb,rgb565amsb,
1059   rgb888lsb,rgb888msb,rgb888alsb,rgb888amsb
1060 };
1061
1062 /*
1063  * perform actual conversion
1064  *
1065  *  If we can, try and use the optimised code versions, but as a default
1066  * fallback, and always for direct colour, use the generic/slow but complete
1067  * conversion function.
1068  */
1069 static void
1070 rgbconvert (GdkImage    *image,
1071             guchar      *pixels,
1072             int          rowstride,
1073             int          alpha,
1074             GdkColormap *cmap)
1075 {
1076   int index = (image->byte_order == GDK_MSB_FIRST) | (alpha != 0) << 1;
1077   int bank=5;           /* default fallback converter */
1078   GdkVisual *v = gdk_colormap_get_visual(cmap);
1079
1080   d(printf("masks = %x:%x:%x\n", v->red_mask, v->green_mask, v->blue_mask));
1081   d(printf("image depth = %d, bpp = %d\n", image->depth, image->bpp));
1082
1083   switch (v->type)
1084     {
1085                                 /* I assume this is right for static & greyscale's too? */
1086     case GDK_VISUAL_STATIC_GRAY:
1087     case GDK_VISUAL_GRAYSCALE:
1088     case GDK_VISUAL_STATIC_COLOR:
1089     case GDK_VISUAL_PSEUDO_COLOR:
1090       switch (image->bpp)
1091         {
1092         case 1:
1093           bank = 0;
1094           break;
1095         case 8:
1096           bank = 1;
1097           break;
1098         }
1099       break;
1100     case GDK_VISUAL_TRUE_COLOR:
1101       switch (image->depth)
1102         {
1103         case 15:
1104           if (v->red_mask == 0x7c00 && v->green_mask == 0x3e0 && v->blue_mask == 0x1f
1105               && image->bpp == 16)
1106             bank = 2;
1107           break;
1108         case 16:
1109           if (v->red_mask == 0xf800 && v->green_mask == 0x7e0 && v->blue_mask == 0x1f
1110               && image->bpp == 16)
1111             bank = 3;
1112           break;
1113         case 24:
1114         case 32:
1115           if (v->red_mask == 0xff0000 && v->green_mask == 0xff00 && v->blue_mask == 0xff
1116               && image->bpp == 32)
1117             bank = 4;
1118           break;
1119         }
1120       break;
1121     case GDK_VISUAL_DIRECT_COLOR:
1122       /* always use the slow version */
1123       break;
1124     }
1125
1126   d(printf("converting using conversion function in bank %d\n", bank));
1127
1128   if (bank==5)
1129     {
1130       convert_real_slow(image, pixels, rowstride, cmap, alpha);
1131     }
1132   else
1133     {
1134       index |= bank << 2;
1135       (* convert_map[index]) (image, pixels, rowstride, cmap);
1136     }
1137 }
1138
1139
1140 /* Exported functions */
1141
1142 /**
1143  * gdk_pixbuf_get_from_drawable:
1144  * @dest: Destination pixbuf, or NULL if a new pixbuf should be created.
1145  * @src: Source drawable.
1146  * @cmap: A colormap if @src is a pixmap.  If it is a window, this argument will
1147  * be ignored.
1148  * @src_x: Source X coordinate within drawable.
1149  * @src_y: Source Y coordinate within drawable.
1150  * @dest_x: Destination X coordinate in pixbuf, or 0 if @dest is NULL.
1151  * @dest_y: Destination Y coordinate in pixbuf, or 0 if @dest is NULL.
1152  * @width: Width in pixels of region to get.
1153  * @height: Height in pixels of region to get.
1154  *
1155  * Transfers image data from a Gdk drawable and converts it to an RGB(A)
1156  * representation inside a GdkPixbuf.
1157  *
1158  * If the drawable @src is a pixmap, then a suitable colormap must be specified,
1159  * since pixmaps are just blocks of pixel data without an associated colormap.
1160  * If the drawable is a window, the @cmap argument will be ignored and the
1161  * window's own colormap will be used instead.
1162  *
1163  * If the specified destination pixbuf @dest is #NULL, then this function will
1164  * create an RGB pixbuf with 8 bits per channel and no alpha, with the same size
1165  * specified by the @width and @height arguments.  In this case, the @dest_x and
1166  * @dest_y arguments must be specified as 0, otherwise the function will return
1167  * #NULL.  If the specified destination pixbuf is not NULL and it contains alpha
1168  * information, then the filled pixels will be set to full opacity.
1169  *
1170  * If the specified drawable is a pixmap, then the requested source rectangle
1171  * must be completely contained within the pixmap, otherwise the function will
1172  * return #NULL.
1173  *
1174  * If the specified drawable is a window, then it must be viewable, i.e. all of
1175  * its ancestors up to the root window must be mapped.  Also, the specified
1176  * source rectangle must be completely contained within the window and within
1177  * the screen.  If regions of the window are obscured by noninferior windows, the
1178  * contents of those regions are undefined.  The contents of regions obscured by
1179  * inferior windows of a different depth than that of the source window will also
1180  * be undefined.
1181  *
1182  * Return value: The same pixbuf as @dest if it was non-NULL, or a newly-created
1183  * pixbuf with a reference count of 1 if no destination pixbuf was specified; in
1184  * the latter case, NULL will be returned if not enough memory could be
1185  * allocated for the pixbuf to be created.
1186  **/
1187 GdkPixbuf *
1188 gdk_pixbuf_get_from_drawable (GdkPixbuf   *dest,
1189                               GdkDrawable *src,
1190                               GdkColormap *cmap,
1191                               int src_x,  int src_y,
1192                               int dest_x, int dest_y,
1193                               int width,  int height)
1194 {
1195   int src_width, src_height;
1196   GdkImage *image;
1197   int rowstride, bpp, alpha;
1198   
1199   /* General sanity checks */
1200
1201   g_return_val_if_fail (src != NULL, NULL);
1202
1203   if (GDK_IS_WINDOW (src))
1204     /* FIXME: this is not perfect, since is_viewable() only tests
1205      * recursively up the Gdk parent window tree, but stops at
1206      * foreign windows or Gdk toplevels.  I.e. if a window manager
1207      * unmapped one of its own windows, this won't work.
1208      */
1209     g_return_val_if_fail (gdk_window_is_viewable (src), NULL);
1210
1211   if (!dest)
1212     g_return_val_if_fail (dest_x == 0 && dest_y == 0, NULL);
1213   else
1214     {
1215       g_return_val_if_fail (dest->colorspace == GDK_COLORSPACE_RGB, NULL);
1216       g_return_val_if_fail (dest->n_channels == 3 || dest->n_channels == 4, NULL);
1217       g_return_val_if_fail (dest->bits_per_sample == 8, NULL);
1218     }
1219
1220   if (cmap == NULL)
1221     cmap = gdk_drawable_get_colormap (src);
1222
1223   if (cmap == NULL)
1224     {
1225       g_warning ("%s: Source drawable has no colormap; either pass "
1226                  "in a colormap, or set the colormap on the drawable "
1227                  "with gdk_drawable_set_colormap()", G_STRLOC);
1228       return NULL;
1229     }
1230   
1231   /* Coordinate sanity checks */
1232
1233   gdk_drawable_get_size (src, &src_width, &src_height);
1234
1235   g_return_val_if_fail (src_x >= 0 && src_y >= 0, NULL);
1236   g_return_val_if_fail (src_x + width <= src_width && src_y + height <= src_height, NULL);
1237
1238   if (dest)
1239     {
1240       g_return_val_if_fail (dest_x >= 0 && dest_y >= 0, NULL);
1241       g_return_val_if_fail (dest_x + width <= dest->width, NULL);
1242       g_return_val_if_fail (dest_y + height <= dest->height, NULL);
1243     }
1244
1245   if (GDK_IS_WINDOW (src))
1246     {
1247       int ret;
1248       int src_xorigin, src_yorigin;
1249       int screen_width, screen_height;
1250       int screen_srcx, screen_srcy;
1251
1252       ret = gdk_window_get_origin (src, &src_xorigin, &src_yorigin);
1253       g_return_val_if_fail (ret != FALSE, NULL);
1254
1255       screen_width = gdk_screen_width ();
1256       screen_height = gdk_screen_height ();
1257
1258       screen_srcx = src_xorigin + src_x;
1259       screen_srcy = src_yorigin + src_y;
1260
1261       g_return_val_if_fail (screen_srcx >= 0 && screen_srcy >= 0, NULL);
1262       g_return_val_if_fail (screen_srcx + width <= screen_width, NULL);
1263       g_return_val_if_fail (screen_srcy + height <= screen_height, NULL);
1264     }
1265
1266   /* Get Image in ZPixmap format (packed bits). */
1267   image = gdk_image_get (src, src_x, src_y, width, height);
1268   g_return_val_if_fail (image != NULL, NULL);
1269
1270   /* Create the pixbuf if needed */
1271   if (!dest)
1272     {
1273       dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height);
1274       if (!dest)
1275         {
1276           gdk_image_destroy(image);
1277           return NULL;
1278         }
1279     }
1280
1281   alpha = dest->has_alpha;
1282   rowstride = dest->rowstride;
1283   bpp = alpha ? 4 : 3;
1284
1285   /* we offset into the image data based on the position we are retrieving from */
1286   rgbconvert (image, dest->pixels +
1287               (dest_y * rowstride) + (dest_x * bpp),
1288               rowstride,
1289               alpha,
1290               cmap);
1291
1292   gdk_image_destroy(image);
1293
1294   return dest;
1295 }