Rice GLES2 (from mupen64plus-ae) plugin. Compile but doesn't works well on the OpenPa...
[mupen64plus-pandora.git] / source / gles2rice / src / liblinux / BMGImage.c
CommitLineData
292f9317 1/*
2// source code for the BMGImage functions
3//
4// Copyright (C) 2001 Michael S. Heiman
5//
6// You may use the software for any purpose you see fit. You may modify
7// it, incorporate it in a commercial application, use it for school,
8// even turn it in as homework. You must keep the Copyright in the
9// header and source files. This software is not in the "Public Domain".
10// You may use this software at your own risk. I have made a reasonable
11// effort to verify that this software works in the manner I expect it to;
12// however,...
13//
14// THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" AND
15// WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING
16// WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A
17// PARTICULAR PURPOSE. IN NO EVENT SHALL MICHAEL S. HEIMAN BE LIABLE TO
18// YOU OR ANYONE ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR
19// CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING
20// WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE,
21// OR THE CLAIMS OF THIRD PARTIES, WHETHER OR NOT MICHAEL S. HEIMAN HAS
22// BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
23// ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
24// POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
25*/
26
27#include <memory.h>
28#include <setjmp.h>
29#include <stdlib.h>
30
31#include "BMGUtils.h"
32
33/* initializes a BMGImage to default values */
34void InitBMGImage( struct BMGImageStruct *img )
35{
36 img->width = img->height = 0;
37 img->bits_per_pixel = 0;
38 img->palette_size = 0;
39 img->bytes_per_palette_entry = 0;
40 img->bits = NULL;
41 img->palette = NULL;
42 img->opt_for_bmp = 0;
43 img->scan_width = 0;
44 img->transparency_index = -1;
45}
46
47/* frees memory allocated to a BMGImage */
48void FreeBMGImage( struct BMGImageStruct *img )
49{
50 if ( img->bits != NULL )
51 {
52 free( img->bits );
53 img->bits = NULL;
54 }
55 if ( img->palette != NULL )
56 {
57 free( img->palette );
58 img->palette = NULL;
59 }
60 img->bits_per_pixel = 0;
61 img->palette_size = 0;
62 img->bytes_per_palette_entry = 0;
63 img->width = img->height = 0;
64 img->opt_for_bmp = 0;
65 img->scan_width = 0;
66 img->transparency_index = -1;
67}
68
69/* allocates memory for the bits & palette. Assigned values to scan_line
70 & bits_per_palette_entry as well. Assumes opt_for_bmp has been set before
71 this function is called. Assumes that all images with bits_per_pixel <= 8
72 require a palette.
73 */
74BMGError AllocateBMGImage( struct BMGImageStruct *img )
75{
76 unsigned int mempal;
77
78 SetLastBMGError( BMG_OK );
79
80 /* make sure that all REQUIRED parameters are valid */
81 if ( img->width * img->height <= 0 )
82 {
83 SetLastBMGError(errInvalidSize);
84 return errInvalidSize;
85 }
86
87 switch( img->bits_per_pixel )
88 {
89 case 1:
90 case 4:
91 case 8:
92 case 16:
93 case 24:
94 case 32:
95 break;
96 default:
97 SetLastBMGError( errInvalidPixelFormat );
98 return errInvalidPixelFormat;
99 }
100
101 /* delete old memory */
102 if ( img->bits != NULL )
103 {
104 free( img->bits );
105 img->bits = NULL;
106 }
107 if ( img->palette != NULL )
108 {
109 free( img->palette );
110 img->palette = NULL;
111 }
112
113 /* allocate memory for the palette */
114 if ( img->bits_per_pixel <= 8 )
115 {
116 if ( img->opt_for_bmp > 0 )
117 img->bytes_per_palette_entry = 4U;
118 else
119 {
120 /* we only support 3-byte and 4-byte palettes */
121 if ( img->bytes_per_palette_entry <= 3U )
122 img->bytes_per_palette_entry = 3U;
123 else
124 img->bytes_per_palette_entry = 4U;
125 }
126 /*
127 use bits_per_pixel to determine palette_size if none was
128 specified
129 */
130 if ( img->palette_size == 0 )
131 img->palette_size = (unsigned short)(1 << img->bits_per_pixel);
132
133 mempal = img->bytes_per_palette_entry * img->palette_size;
134 img->palette = (unsigned char *)calloc( mempal, sizeof(unsigned char) );
135 if ( img->palette == NULL )
136 {
137 SetLastBMGError(errMemoryAllocation);
138 return errMemoryAllocation;
139 }
140 }
141 else
142 {
143 img->bytes_per_palette_entry = 0;
144 img->palette_size = 0;
145 }
146
147 /*
148 set the scan width. Bitmaps optimized for windows have scan widths that
149 are evenly divisible by 4.
150 */
151 img->scan_width = ( img->bits_per_pixel * img->width + 7 ) / 8;
152 if ( img->opt_for_bmp && img->scan_width % 4 )
153 img->scan_width += 4 - img->scan_width % 4;
154
155 /* allocate memory for the bits */
156 mempal = img->scan_width * img->height;
157 if ( mempal > 0 )
158 {
159 img->bits = (unsigned char *)calloc( mempal, sizeof( unsigned char) );
160 if ( img->bits == NULL )
161 {
162 if ( img->palette != NULL )
163 {
164 free( img->palette );
165 img->palette = NULL;
166 }
167 SetLastBMGError(errMemoryAllocation);
168 return errMemoryAllocation;
169 }
170 }
171 else
172 {
173 SetLastBMGError(errInvalidSize);
174 return errInvalidSize;
175 }
176
177 return BMG_OK;
178}
179
180/*******************************************************************************
181 A utility function for compressing paletted images. Will automatically
182 convert 8-bit paletted images to 1-bit or 4-bit paletted images based
183 upon palette_size. Assumes that indices in img->bits are valid. That is,
184 0 <= img->bits[i] <= 1 for all i if 1-bit compression is desired, and
185 0 <= img->bits[i] <= 15 for all i if 4-bit compression is desired Returns
186 BMG_OK if successful, or an error code otherwise.
187*******************************************************************************/
188BMGError CompressBMGImage( struct BMGImageStruct *img )
189{
190 unsigned char new_bits_per_pixel;
191 unsigned int new_scan_width;
192 unsigned char *new_bits = NULL;
193 unsigned int new_bit_size;
194 unsigned char *new_row, *old_row, *p, *q;
195 unsigned char *end;
196 unsigned short scale;
197
198 SetLastBMGError( BMG_OK );
199
200 /* if we cannot compress it then do no harm and return "true" */
201 if ( img->palette == NULL ||
202 img->palette_size > 16 ||
203 img->bits_per_pixel != 8 )
204 {
205 return BMG_OK;
206 }
207
208 /* calculate new dimensions */
209 new_bits_per_pixel = img->palette_size <= 2 ? 1U : 4U;
210 new_scan_width = ( new_bits_per_pixel * img->width + 7 ) / 8;
211 if ( img->opt_for_bmp > 0 && new_scan_width % 4 )
212 new_scan_width += 4 - new_scan_width % 4;
213 new_bit_size = new_scan_width * img->height;
214
215 /* allocate & test memory */
216 new_bits = (unsigned char *)calloc( new_bit_size, sizeof(unsigned char) );
217 if ( new_bits == NULL )
218 {
219 SetLastBMGError( errMemoryAllocation );
220 return errMemoryAllocation;
221 }
222
223 old_row = img->bits;
224 for ( new_row = new_bits; new_row < new_bits + new_bit_size;
225 new_row += new_scan_width, old_row += img->scan_width )
226 {
227 scale = 8 / new_bits_per_pixel;
228 end = new_row + img->width / scale;
229 p = old_row;
230 if ( new_bits_per_pixel == 1 )
231 {
232 for ( q = new_row; q < end; q++, p += scale )
233 {
234 *q = (unsigned char)( (p[0] << 7) | (p[1] << 6) |
235 (p[2] << 5) | (p[3] << 4) |
236 (p[4] << 3) | (p[5] << 2) |
237 (p[6] << 1) | p[7] );
238 }
239 scale = img->width % scale;
240 if ( scale-- > 0 )
241 {
242 *q = (unsigned char)(p[0] << 7);
243 if ( scale-- )
244 {
245 *q |= (unsigned char)(p[1] << 6);
246 if ( scale-- )
247 {
248 *q |= (unsigned char)(p[2] << 5);
249 if ( scale-- )
250 {
251 *q |= (unsigned char)(p[3] << 4);
252 if ( scale-- )
253 {
254 *q |= (unsigned char)(p[4] << 3);
255 if ( scale-- )
256 {
257 *q |= (unsigned char)(p[5] << 2);
258 if ( scale-- )
259 *q |= (unsigned char)(p[6] << 1);
260 }
261 }
262 }
263 }
264 }
265 }
266 }
267 else /* new_bits_per_pixel == 4 */
268 {
269 for ( q = new_row; q < end; q++, p += scale )
270 {
271 *q = (unsigned char)( (p[0] << 4) | (p[1] & 0x0F) );
272 }
273 if ( img->width % scale )
274 *q = (unsigned char)(p[0] << 4);
275 }
276 }
277
278 /* replace old values with new values */
279 free( img->bits );
280 img->bits = new_bits;
281 img->scan_width = new_scan_width;
282 img->bits_per_pixel = new_bits_per_pixel;
283
284 return BMG_OK;
285}
286
287/* this function simply frees memory that was allocated by any function
288 in the BMGLib. This was required because acces violations occurred
289 when I tried to delete memory created by CreateRGBAArray in the demo
290 applications */
291void FreeBMGMemory( unsigned char *mem )
292{
293 if ( mem != NULL )
294 free( mem );
295}
296
297/* converts a BGR to a gray scale
298// color[0] = blue, color[1] = green, color[2] = red */
299static unsigned char CreateGrayScale( unsigned char *color )
300{
301 return (unsigned char)( 0.299f * color[2] + 0.587f * color[1]
302 + 0.114f * color[0] + 0.5f );
303}
304
305/*
306// converts a color image to a gray scale image. If img is a 16 or
307// 24-BPP image then it is converted to a 256 color grayscale bitmap.
308// If img is a 1, 4, or 8 BPP image, then it will have the same number
309// of grayscales as it has palette entries. If it is a 32-BPP bitmap then
310// it will remain a 32-BPP bitmap to preserve the alpha channel.
311//
312// This function returns BMG_OK if successfull, or an error state
313// otherwise.
314*/
315BMGError ConvertToGrayScale( struct BMGImageStruct *img )
316{
317 unsigned char *p, *q, *r, *end, gray;
318
319 SetLastBMGError( BMG_OK );
320
321 /* if this is a paletted image then we simply need to convert the
322 // palette entries */
323 switch ( img->bits_per_pixel )
324 {
325 default:
326 end = img->palette + img->palette_size * img->bytes_per_palette_entry;
327 for ( p = img->palette; p < end; p += img->bytes_per_palette_entry )
328 {
329 gray = CreateGrayScale( p );
330 memset( (void *)p, gray, 3 );
331 }
332 break;
333 /* 16 BPP image are converted to 24 BPP images */
334 case 16:
335 {
336 BMGError tmp = Convert16to24( img );
337 if ( tmp != BMG_OK )
338 {
339 SetLastBMGError( tmp );
340 return tmp;
341 }
342 }
343 case 24:
344 {
345 unsigned char *new_bits;
346 unsigned char *s, *s_end;
347 unsigned short i;
348
349 /* calculate the new scan width */
350 unsigned int new_scan_width = img->width;
351 if ( new_scan_width % 4 && img->opt_for_bmp )
352 new_scan_width += 4 - new_scan_width % 4;
353
354 /* allocate memory for the new pixel values */
355 new_bits = (unsigned char *)calloc( new_scan_width * img->height,
356 sizeof(unsigned char) );
357 if ( new_bits == NULL )
358 {
359 SetLastBMGError( errMemoryAllocation );
360 return errMemoryAllocation;
361 }
362
363 /* allocate memory for a 256 gray scale palette */
364 img->bytes_per_palette_entry = img->opt_for_bmp == 1 ? 4 : 3;
365 img->palette_size = 256;
366 img->palette =
367 (unsigned char *)calloc(img->bytes_per_palette_entry *
368 img->palette_size,
369 sizeof(unsigned char) );
370 if ( img->palette == NULL )
371 {
372 free( new_bits );
373 img->bytes_per_palette_entry = 0;
374 img->palette_size = 0;
375 SetLastBMGError( errMemoryAllocation );
376 return errMemoryAllocation;
377 }
378
379 /* assign values to the gray scale palette */
380 for ( i = 0; i < 256; i++ )
381 {
382 p = img->palette + i * img->bytes_per_palette_entry;
383 memset( (void *)p, i, 3 );
384 if ( img->bytes_per_palette_entry == 4 )
385 p[3] = 0;
386 }
387
388 /* cycle through the pixels and convert them to gray scale values */
389 q = new_bits;
390 end = img->bits + img->scan_width * img->height;
391
392 for ( p = img->bits; p < end; p += img->scan_width, q += new_scan_width )
393 {
394 s_end = p + 3 * img->width;
395 r = q;
396 for ( s = p; s < s_end; s += 3, r++ )
397 *r = CreateGrayScale( s );
398 }
399
400 free( img->bits );
401 img->bits = new_bits;
402 img->scan_width = new_scan_width;
403 img->bits_per_pixel = 8;
404
405 break;
406 }
407 case 32:
408 end = img->bits + img->scan_width * img->height;
409 for ( p = img->bits; p < end; p += img->scan_width )
410 {
411 r = p + img->scan_width;
412 for ( q = p; q < r; q += 4 )
413 {
414 gray = CreateGrayScale( q );
415 memset( (void *)q, gray, 3 );
416 }
417 }
418 break;
419 }
420
421 return BMG_OK;
422}
423
424/*
425// converts a color image to a pseudo-gray scale image. This is a implementation
426// is based upon the code published by Rich Franzen
427// <http://rocq.home.att.net/pseudoGrey.html>. I have "simplified" the 2 functions
428// he published into a single function. This implementation creates 1786 gray
429// scales from a 24-bit image. 16-BPP images are converted to 24-BPP images. 24
430// and 32-BPP images will keep the same bitdepth. Paletted images and 16-BPP images
431// are not supported.
432//
433// This function returns BMK_OK if successfull,
434// errInvalidPixelFormat otherwise
435*/
436BMGError ConvertToPseudoGrayScale( struct BMGImageStruct *img )
437{
438 unsigned char *p, *p_end;
439 unsigned char *q, *q_end;
440 unsigned char gray;
441 unsigned int bytes_per_pixel;
442
443 SetLastBMGError( errMemoryAllocation );
444
445 if ( img->bits_per_pixel <= 16 )
446 {
447 SetLastBMGError( errInvalidPixelFormat );
448 return errInvalidPixelFormat;
449 }
450
451 bytes_per_pixel = img->bits_per_pixel / 8;
452 p_end = img->bits + img->scan_width * img->height;
453
454 for ( p = img->bits; p < p_end; p += img->scan_width )
455 {
456 q_end = p + bytes_per_pixel * img->width;
457 for ( q = p; q < q_end; q += bytes_per_pixel )
458 {
459 /* Rich's code has 1 function that converts an RGB triplet to a float
460 // bounded by 0 and 1. He has a second function that converts a
461 // float to a pseudo gray value. Pseudo gray values are RGB triplets
462 // whose red, green and blue values differ by no more than 1. I have
463 // combined these two functions into a single function that simply
464 // looks for pseudo gray RGB triplets. If an RGB triplet meets this
465 // criteria, I leave it unchanged; otherwise, I use the common intensity
466 // conversion to create a grayscale value */
467 unsigned char cmin, cmax;
468
469 cmin = q[0];
470 if ( q[1] < cmin )
471 cmin = q[1];
472 if ( q[2] < cmin )
473 cmin = q[2];
474
475 cmax = q[0];
476 if ( q[1] > cmax )
477 cmax = q[1];
478 if ( q[2] > cmax )
479 cmax = q[2];
480
481 if ( cmax - cmin > 2 )
482 {
483 gray = CreateGrayScale( q );
484 memset( (void *)q, gray, 3 );
485 }
486 }
487 }
488
489 return BMG_OK;
490}
491
492#ifdef _WIN32
493/*******************************************************************************
494// extracts the dimensional information, pixel array, and color table from an
495// HBITMAP.
496// hBitmap can be a handle to a DIB or a DDB. This function assumes that DDBs
497// will not have a palette. If you create a DDB on a 256-color graphics card,
498// then the DDB will have a palette and this function will fail.
499//
500// returns BMK_OK if successfull, and error state otherwise.
501********************************************************************************/
502BMGError GetDataFromBitmap( HBITMAP hBitmap,
503 struct BMGImageStruct *img,
504 int remove_alpha )
505{
506 unsigned int DIBScanWidth;
507 DIBSECTION DS;
508 HWND hWnd = GetForegroundWindow();
509 HDC hDC = NULL;
510 HDC hMemDC = NULL;
511 unsigned char red, green, blue;
512 int FreelpBits = 0;
513 unsigned int numBytes;
514 size_t soDIBSECTION = sizeof(DIBSECTION);
515 size_t soBITMAP = sizeof(BITMAP);
516
517 unsigned char *p, *q, *lpBits, alpha;
518
519 jmp_buf err_jmp;
520 int error;
521 BMGError bmgerr;
522
523 /* error handler */
524 error = setjmp( err_jmp );
525 if ( error != 0 )
526 {
527 if ( hMemDC != NULL )
528 DeleteDC( hMemDC );
529 if ( hDC != NULL )
530 ReleaseDC( hWnd, hDC );
531 if ( FreelpBits )
532 free( lpBits );
533 FreeBMGImage( img );
534 SetLastBMGError( (BMGError)error );
535 return (BMGError)error;
536 }
537
538 SetLastBMGError( BMG_OK );
539 /* check for valid bitmap*/
540 if ( !hBitmap )
541 longjmp( err_jmp, (int)errInvalidBitmapHandle );
542
543 /* Extract DIBSECTION info from the HBITMAP. numBytes will equal
544 // soDIBSECTION (84) if hBitmap is a handle to a DIBSECTION (DIB).
545 // numBytes will equal soBITMAP (24) if hBitmap is a handle to a
546 // BITMAP (DDB). */
547 numBytes = GetObject( hBitmap, sizeof(DIBSECTION), &DS );
548 if ( numBytes == 0 )
549 longjmp( err_jmp, (int)errWindowsAPI );
550
551 img->opt_for_bmp = 1;
552 if ( numBytes == soDIBSECTION )
553 {
554 img->width = DS.dsBmih.biWidth;
555 img->height = DS.dsBmih.biHeight;
556 img->bits_per_pixel = (unsigned char)DS.dsBmih.biBitCount;
557 if ( img->bits_per_pixel <= 8 && DS.dsBmih.biClrUsed > 0 )
558 img->palette_size = (unsigned short)DS.dsBmih.biClrUsed;
559 lpBits = (unsigned char *)DS.dsBm.bmBits;
560 }
561 /* this may be a DDB which must be handled differently */
562 else if ( numBytes == soBITMAP )
563 {
564 BITMAP bm;
565 BITMAPINFO bmi;
566
567 if ( GetObject( hBitmap, sizeof(BITMAP), &bm ) == 0 )
568 longjmp( err_jmp, (int)errWindowsAPI );
569
570 /* DDB with a palette */
571 if ( bm.bmBitsPixel <= 8 )
572 longjmp( err_jmp, (int)errInvalidPixelFormat );
573
574 img->width = bm.bmWidth;
575 img->height = bm.bmHeight;
576 img->bits_per_pixel = (unsigned char)bm.bmBitsPixel;
577 bmi = InternalCreateBMI( bm.bmWidth, bm.bmHeight, bm.bmBitsPixel,
578 BI_RGB );
579
580 lpBits = (unsigned char *)calloc( bm.bmHeight * bm.bmWidthBytes,
581 sizeof(unsigned char) );
582 if ( lpBits == 0 )
583 longjmp( err_jmp, (int)errMemoryAllocation );
584 FreelpBits = 1;
585 hDC = GetDC( hWnd );
586 if ( GetDIBits(hDC, hBitmap, 0, bm.bmHeight, (void *)lpBits, &bmi,
587 DIB_RGB_COLORS ) == 0 )
588 longjmp( err_jmp, (int)errWindowsAPI );
589 ReleaseDC( hWnd, hDC );
590 hDC = NULL;
591 }
592 else /* I have no idea what this is */
593 longjmp( err_jmp, (int)errInvalidBitmapHandle );
594
595 /* allocate memory */
596 bmgerr = AllocateBMGImage( img );
597 if ( bmgerr != BMG_OK )
598 longjmp( err_jmp, (int)bmgerr );
599
600 /* dimensions */
601 DIBScanWidth = ( img->width * img->bits_per_pixel + 7 )/8;
602 if ( DIBScanWidth % 4 )
603 DIBScanWidth += 4 - DIBScanWidth % 4;
604
605 p = img->bits;
606 for ( q = lpBits; q < lpBits + DIBScanWidth * img->height;
607 p += img->scan_width, q += DIBScanWidth )
608 {
609 memcpy( (void *)p, (void *)q, DIBScanWidth );
610 }
611
612 /* "un-blend" the image if requested. NOTE: unblending only works with
613 // bland backgrounds */
614 if ( remove_alpha > 0 &&
615 img->bits_per_pixel == 32 &&
616 numBytes == soDIBSECTION )
617 {
618 unsigned char *color = GetBackgroundColor();
619 red = color[2];
620 green = color[1];
621 blue = color[0];
622
623 for ( p = img->bits; p < img->bits + img->scan_width * img->height;
624 p += 4 )
625 {
626 alpha = p[3];
627 p[2] = InverseAlphaComp( p[2], alpha, blue);
628 p[1] = InverseAlphaComp( p[1], alpha, green);
629 p[0] = InverseAlphaComp( p[0], alpha, red);
630 }
631 }
632
633 /* 32-bit DDBs must have the alpha channel set to 0xFF before they are
634 // saved to a file. This may not be true for all devices that generate
635 // 32-bit DDBs. I have only created 32-bit DDBs using an Intense3D Wildcat
636 // 4110 card. The alpha channel was always 0. */
637 if (img->bits_per_pixel == 32 && numBytes == soBITMAP )
638 {
639 for ( p = img->bits + 3; p < img->bits + img->scan_width * img->height;
640 p += 4 )
641 {
642 *p = 0xFF;
643 }
644 }
645
646 /* create palette if necessary */
647 if ( img->bits_per_pixel <= 8 )
648 {
649 hDC = GetDC( hWnd );
650 hMemDC = CreateCompatibleDC( hDC );
651 SelectObject( hMemDC, hBitmap );
652 if ( !GetDIBColorTable( hMemDC, 0, img->palette_size,
653 (RGBQUAD *)img->palette ) )
654 {
655 longjmp( err_jmp, (int)errWindowsAPI );
656 }
657 DeleteDC( hMemDC );
658 ReleaseDC( hWnd, hDC );
659 }
660
661 if ( FreelpBits )
662 free( lpBits );
663
664 return BMG_OK;
665}
666
667/*******************************************************************************
668// this function creates a bitmap from raw data. Returns an HBITMAP if it
669// succeeds, otherwise NULL */
670HBITMAP CreateBitmapFromData( struct BMGImageStruct img,
671 int alpha_blend )
672{
673 HBITMAP hBitmap = NULL;
674 HDC hMemDC = NULL;
675 HWND hWnd = GetForegroundWindow();
676 HDC hDC = NULL;
677 RGBQUAD *pColor = NULL;
678 BITMAPINFO bmi;
679 unsigned char *rbits;
680 unsigned char *bits;
681 unsigned char *lpBits;
682 unsigned char alpha;
683 unsigned int DIBScanWidth;
684 int i;
685
686 jmp_buf err_jmp;
687 int error;
688
689 /* error handler */
690 error = setjmp( err_jmp );
691 if ( error != 0 )
692 {
693 if ( hMemDC != NULL )
694 DeleteDC( hMemDC );
695 if ( hDC != NULL )
696 ReleaseDC( hWnd, hDC );
697 if ( pColor != NULL && img.bytes_per_palette_entry == 3U )
698 free( pColor );
699 SetLastBMGError( (BMGError)error );
700 return 0;
701 }
702
703 SetLastBMGError( BMG_OK );
704
705 /* create the DIB section that will hold this bitmap */
706 bmi = InternalCreateBMI( (unsigned int)img.width, (unsigned int)img.height,
707 (unsigned short)img.bits_per_pixel, BI_RGB );
708 bmi.bmiHeader.biClrUsed = bmi.bmiHeader.biClrImportant =
709 img.palette_size;
710 hDC = GetDC( hWnd );
711 hBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
712 (void **)&lpBits, NULL, 0 );
713
714 if ( !hBitmap || !lpBits )
715 longjmp( err_jmp, (int)errWindowsAPI );
716
717 /* create a palette if needed */
718 if ( img.palette != NULL )
719 {
720 /* copy pixel data to pColor */
721 if ( img.bytes_per_palette_entry == 4U )
722 pColor = (RGBQUAD *)img.palette;
723 else /* bytes_per_palette_entry === 3 */
724 {
725 pColor = (RGBQUAD *)calloc(img.palette_size, sizeof(RGBQUAD) );
726 if ( pColor == NULL )
727 longjmp( err_jmp, (int)errMemoryAllocation );
728
729 bits = img.palette;
730 for ( i = 0; i < (int)bmi.bmiHeader.biClrUsed; i++, bits += 3 )
731 {
732 pColor[i].rgbRed = bits[0];
733 pColor[i].rgbGreen = bits[1];
734 pColor[i].rgbBlue = bits[2];
735 }
736 }
737
738 if ( img.transparency_index > -1 )
739 {
740 unsigned char *color = GetBackgroundColor();
741 rbits = img.palette + img.bytes_per_palette_entry *
742 img.transparency_index;
743 rbits[0] = color[2];
744 rbits[1] = color[1];
745 rbits[2] = color[0];
746 }
747 /* save color table in bitmap */
748 hMemDC = CreateCompatibleDC( hDC );
749 SelectObject( hMemDC, hBitmap );
750 if ( !SetDIBColorTable( hMemDC, 0, img.palette_size, pColor ) )
751 longjmp( err_jmp, (int)errWindowsAPI );
752
753 DeleteDC( hMemDC );
754 hMemDC = NULL;
755 if ( img.bytes_per_palette_entry == 3U )
756 free( pColor );
757 pColor = NULL;
758 }
759
760 /* calculate the scan line width */
761 DIBScanWidth = img.scan_width;
762 if ( DIBScanWidth % 4 )
763 DIBScanWidth += 4 - DIBScanWidth % 4;
764
765 if ( img.opt_for_bmp == 0 )
766 {
767 /* store bits into hBitmap */
768 rbits = img.bits;
769 for ( bits = lpBits;
770 bits < lpBits + img.height * DIBScanWidth;
771 bits += DIBScanWidth, rbits += img.scan_width )
772 {
773 memcpy( (void *)bits, (void *)rbits, img.scan_width );
774 }
775 }
776 else
777 memcpy( (void *)lpBits, (void *)img.bits, img.scan_width * img.height );
778
779 /* blend the image with the window background if alpha pixels
780 // are present */
781 if ( img.bits_per_pixel == 32 )
782 {
783 /* blend with a bland background */
784 if ( alpha_blend == 1 )
785 {
786 unsigned char *color = GetBackgroundColor();
787 unsigned char red = color[2];
788 unsigned char green = color[1];
789 unsigned char blue = color[0];
790
791 for ( rbits = lpBits;
792 rbits < lpBits + img.height*DIBScanWidth;
793 rbits += DIBScanWidth )
794 {
795 for ( bits = rbits; bits < rbits + DIBScanWidth; bits += 4 )
796 {
797 alpha = bits[3];
798 bits[2] = AlphaComp( bits[2], alpha, blue );
799 bits[1] = AlphaComp( bits[1], alpha, green );
800 bits[0] = AlphaComp( bits[0], alpha, red );
801 }
802 }
803 }
804 /* blend with a background image */
805 else if ( alpha_blend == 2 )
806 {
807 unsigned char *bg_bits;
808 unsigned char *bg_bits_2;
809 unsigned int bg_bytes_per_pixel;
810 struct BMGImageStruct *bg = GetBackgroundImage();
811
812 /* make sure we can blend with a background image
813 // I assume that the background image is invalid if it does not
814 // have a valid width */
815 if ( bg->width <= 0 || bg->height <= 0 )
816 longjmp( err_jmp, (int)errUndefinedBGImage );
817
818 /* I cannot blend a foreground image with a background image that
819 // is smaller than it */
820 if ( bg->width < img.width || bg->height < img.height )
821 longjmp( err_jmp, (int)errBGImageTooSmall );
822
823 /* the background image was forced to be a 24 or 32-BPP image;
824 // therefore, we can safely divide by 8 to determined the
825 // bytes per pixel*/
826 bg_bytes_per_pixel = bg->bits_per_pixel / 8;
827
828 /* I will assume that the upper left corner of the input image
829 // must be aligned with the upper left corner of the background
830 // image. This allows me to have background images that are bigger
831 // than the input image. */
832 bg_bits = bg->bits;
833 for ( rbits = lpBits;
834 rbits < lpBits + img.height*DIBScanWidth;
835 rbits += DIBScanWidth, bg_bits += bg->scan_width )
836 {
837 bg_bits_2 = bg_bits;
838 for ( bits = rbits; bits < rbits + DIBScanWidth;
839 bits += 4, bg_bits_2 += bg_bytes_per_pixel )
840 {
841 alpha = bits[3];
842 bits[2] = AlphaComp( bits[2], alpha, bg_bits_2[2] );
843 bits[1] = AlphaComp( bits[1], alpha, bg_bits_2[1] );
844 bits[0] = AlphaComp( bits[0], alpha, bg_bits_2[0] );
845 }
846 }
847
848 }
849 }
850
851 ReleaseDC( hWnd, hDC );
852
853 return hBitmap;
854}
855#endif // _WIN32
856/******************************************************************************
857// ConvertPaletteToRGB converts paletted and 16-BPP images that do not have
858// transparent pixels to 24-BPP images. Paletted images with transparent pixels
859// are converted to 32-BPP images. 24-BPP and 32-BPP images are simply copied
860// to the output structure
861//
862// INPUTS:
863// img_in
864// OUTPUTS:
865// img_out
866//
867// returns BMG_OK if no errors occur, an error code otherwise
868******************************************************************************/
869BMGError ConvertPaletteToRGB( struct BMGImageStruct img_in,
870 struct BMGImageStruct *img_out )
871{
872 jmp_buf err_jmp;
873 int error;
874
875 /* error handler */
876 error = setjmp( err_jmp );
877 if ( error != 0 )
878 {
879 FreeBMGImage( img_out );
880 SetLastBMGError( (BMGError)error );
881 return (BMGError)error;
882 }
883
884 SetLastBMGError( BMG_OK );
885
886 if ( img_in.height == 0 || img_in.width == 0 )
887 longjmp( err_jmp, (int)errInvalidSize );
888
889 InitBMGImage( img_out );
890
891 // copy 16, 24, and 32-BPP images into the output image
892 if ( img_in.bits_per_pixel > 8 )
893 {
894 BMGError out;
895 img_out->bits_per_pixel = img_in.bits_per_pixel;
896 out = CopyBMGImage( img_in, img_out );
897 if ( out != BMG_OK )
898 longjmp( err_jmp, (int)out );
899
900 // 16-BPP are converted to 24-BPP images
901 if ( img_out->bits_per_pixel == 16 )
902 {
903 out = Convert16to24( img_out );
904 if ( out != BMG_OK )
905 longjmp( err_jmp, (int)out );
906 }
907 }
908 else // convert paletted images to 24-BPP BGR or 32-BPP BGRA images
909 {
910 BMGError out;
911 unsigned char *buf;
912 unsigned int scan_width;
913 int dealloc;
914 unsigned char *q0, *q1, *p0, *p1;
915 unsigned int bpp;
916
917 // allocate memory for the 24-BPP output image
918 img_out->width = img_in.width;
919 img_out->height = img_in.height;
920 img_out->opt_for_bmp = img_in.opt_for_bmp;
921 img_out->bits_per_pixel = img_in.transparency_index > -1 ? 32 : 24;
922
923 out = AllocateBMGImage( img_out );
924 if ( out != BMG_OK )
925 longjmp( err_jmp, (int)out );
926
927 // 1-BPP and 4-BPP images are packed, so we need to unpack them
928 if ( img_in.bits_per_pixel < 8 )
929 {
930 dealloc = 1;
931 scan_width = img_in.width;
932 buf = (unsigned char *)malloc(scan_width * img_in.height);
933 if ( buf == NULL )
934 longjmp( err_jmp, (int)errMemoryAllocation );
935
936 if ( img_in.bits_per_pixel == 1 )
937 Convert1to8( img_in, buf );
938 else
939 Convert4to8( img_in, buf );
940 }
941 else // simply point to the bits array if we have a 8-BPP image
942 {
943 dealloc = 0;
944 buf = img_in.bits;
945 scan_width = img_in.scan_width;
946 }
947
948 // convert palette indices to BGR pixels
949 bpp = img_out->bits_per_pixel / 8;
950 q0 = img_out->bits;
951 for ( p0 = buf; p0 < buf + scan_width * img_in.height;
952 p0 += scan_width, q0 += img_out->scan_width )
953 {
954 q1 = q0;
955 for ( p1 = p0; p1 < p0 + img_in.width; p1++, q1 += bpp )
956 {
957 memcpy( (void *)q1,
958 (void *)(img_in.palette + *p1 * img_in.bytes_per_palette_entry), 3 );
959 if ( bpp == 4 )
960 {
961 q1[3] = *p1 == img_in.transparency_index ? 0 : 0xFF;
962 }
963 }
964 }
965
966 if ( dealloc == 1 )
967 free( buf );
968 }
969
970 return BMG_OK;
971}
972
973/******************************************************************************
974// CopyBMG copies the contents of img_in into img_out.
975//
976// CopyBMG returns BMG_OK if successful, otherwise, it returns an error code
977******************************************************************************/
978BMGError CopyBMGImage( struct BMGImageStruct img_in,
979 struct BMGImageStruct *img_out )
980{
981 BMGError out = BMG_OK;
982 SetLastBMGError( out );
983
984 FreeBMGImage( img_out );
985
986 img_out->height = img_in.height;
987 img_out->width = img_in.width;
988 img_out->bits_per_pixel = img_in.bits_per_pixel;
989 img_out->palette_size = img_in.palette_size;
990 img_out->opt_for_bmp = img_in.opt_for_bmp;
991
992 if ( img_in.width > 0 && img_in.height > 0 )
993 {
994 out = AllocateBMGImage( img_out );
995 if ( out == BMG_OK )
996 {
997 memcpy( (void *)img_out->bits, (void *)img_in.bits,
998 img_in.scan_width * img_in.height );
999 if ( img_in.palette_size > 0 )
1000 memcpy( (void *)img_out->palette, (void *)img_in.palette,
1001 img_in.palette_size * img_in.bytes_per_palette_entry );
1002 }
1003 }
1004
1005 return out;
1006}
1007
1008/* sets the background color for alpha blending
1009 color points to an array of 4 unsigned chars
1010 color[0] = blue, color[1] = green, color[2] = red, color[3] = unused */
1011void SetBMGBackgroundColor( unsigned char *color )
1012{
1013 memcpy( (void *)GetBackgroundColor(), (void *)color,
1014 4*sizeof(unsigned char) );
1015}
1016
1017#ifdef _WIN32
1018/* defines the background bitmap that is used for alpha blending & transparent
1019 pixels */
1020BMGError SetBMGBackgroundBitmap( HBITMAP hBitmap )
1021{
1022 BMGError out;
1023 struct BMGImageStruct tmp;
1024 InitBMGImage( &tmp );
1025
1026 /* first we extract the data from the HBITMAP */
1027 out = GetDataFromBitmap( hBitmap, &tmp, 0 );
1028 if ( out == BMG_OK )
1029 {
1030 /* clean up the old background image */
1031 FreeBMGImage( GetBackgroundImage() );
1032
1033 /* next, we convert paletted & 16-BPP images to 24 or 32-BPP images.
1034 // this will simplify the alpha blending. */
1035 out = ConvertPaletteToRGB( tmp, GetBackgroundImage() );
1036 }
1037
1038 return out;
1039}
1040#endif // _WIN32
1041
1042/* defines the background image that is used for alpha blending & transparent
1043 pixels */
1044BMGError SetBMGBackgroundImage( struct BMGImageStruct img )
1045{
1046 /* clean up the old background image */
1047 FreeBMGImage( GetBackgroundImage() );
1048
1049 /* convert paletted and 16-BPP images to 24-BPP or 32-BPP images. This
1050 // will simplify the alpha blending logic*/
1051 return ConvertPaletteToRGB( img, GetBackgroundImage() );
1052}
1053