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