2 // source code for the BMGImage functions
4 // Copyright (C) 2001 Michael S. Heiman
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;
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.
33 /* initializes a BMGImage to default values */
34 void InitBMGImage( struct BMGImageStruct *img )
36 img->width = img->height = 0;
37 img->bits_per_pixel = 0;
38 img->palette_size = 0;
39 img->bytes_per_palette_entry = 0;
44 img->transparency_index = -1;
47 /* frees memory allocated to a BMGImage */
48 void FreeBMGImage( struct BMGImageStruct *img )
50 if ( img->bits != NULL )
55 if ( img->palette != NULL )
60 img->bits_per_pixel = 0;
61 img->palette_size = 0;
62 img->bytes_per_palette_entry = 0;
63 img->width = img->height = 0;
66 img->transparency_index = -1;
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
74 BMGError AllocateBMGImage( struct BMGImageStruct *img )
78 SetLastBMGError( BMG_OK );
80 /* make sure that all REQUIRED parameters are valid */
81 if ( img->width * img->height <= 0 )
83 SetLastBMGError(errInvalidSize);
84 return errInvalidSize;
87 switch( img->bits_per_pixel )
97 SetLastBMGError( errInvalidPixelFormat );
98 return errInvalidPixelFormat;
101 /* delete old memory */
102 if ( img->bits != NULL )
107 if ( img->palette != NULL )
109 free( img->palette );
113 /* allocate memory for the palette */
114 if ( img->bits_per_pixel <= 8 )
116 if ( img->opt_for_bmp > 0 )
117 img->bytes_per_palette_entry = 4U;
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;
124 img->bytes_per_palette_entry = 4U;
127 use bits_per_pixel to determine palette_size if none was
130 if ( img->palette_size == 0 )
131 img->palette_size = (unsigned short)(1 << img->bits_per_pixel);
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 )
137 SetLastBMGError(errMemoryAllocation);
138 return errMemoryAllocation;
143 img->bytes_per_palette_entry = 0;
144 img->palette_size = 0;
148 set the scan width. Bitmaps optimized for windows have scan widths that
149 are evenly divisible by 4.
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;
155 /* allocate memory for the bits */
156 mempal = img->scan_width * img->height;
159 img->bits = (unsigned char *)calloc( mempal, sizeof( unsigned char) );
160 if ( img->bits == NULL )
162 if ( img->palette != NULL )
164 free( img->palette );
167 SetLastBMGError(errMemoryAllocation);
168 return errMemoryAllocation;
173 SetLastBMGError(errInvalidSize);
174 return errInvalidSize;
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 *******************************************************************************/
188 BMGError CompressBMGImage( struct BMGImageStruct *img )
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;
196 unsigned short scale;
198 SetLastBMGError( BMG_OK );
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 )
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;
215 /* allocate & test memory */
216 new_bits = (unsigned char *)calloc( new_bit_size, sizeof(unsigned char) );
217 if ( new_bits == NULL )
219 SetLastBMGError( errMemoryAllocation );
220 return errMemoryAllocation;
224 for ( new_row = new_bits; new_row < new_bits + new_bit_size;
225 new_row += new_scan_width, old_row += img->scan_width )
227 scale = 8 / new_bits_per_pixel;
228 end = new_row + img->width / scale;
230 if ( new_bits_per_pixel == 1 )
232 for ( q = new_row; q < end; q++, p += scale )
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] );
239 scale = img->width % scale;
242 *q = (unsigned char)(p[0] << 7);
245 *q |= (unsigned char)(p[1] << 6);
248 *q |= (unsigned char)(p[2] << 5);
251 *q |= (unsigned char)(p[3] << 4);
254 *q |= (unsigned char)(p[4] << 3);
257 *q |= (unsigned char)(p[5] << 2);
259 *q |= (unsigned char)(p[6] << 1);
267 else /* new_bits_per_pixel == 4 */
269 for ( q = new_row; q < end; q++, p += scale )
271 *q = (unsigned char)( (p[0] << 4) | (p[1] & 0x0F) );
273 if ( img->width % scale )
274 *q = (unsigned char)(p[0] << 4);
278 /* replace old values with new values */
280 img->bits = new_bits;
281 img->scan_width = new_scan_width;
282 img->bits_per_pixel = new_bits_per_pixel;
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
291 void FreeBMGMemory( unsigned char *mem )
297 /* converts a BGR to a gray scale
298 // color[0] = blue, color[1] = green, color[2] = red */
299 static unsigned char CreateGrayScale( unsigned char *color )
301 return (unsigned char)( 0.299f * color[2] + 0.587f * color[1]
302 + 0.114f * color[0] + 0.5f );
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.
312 // This function returns BMG_OK if successfull, or an error state
315 BMGError ConvertToGrayScale( struct BMGImageStruct *img )
317 unsigned char *p, *q, *r, *end, gray;
319 SetLastBMGError( BMG_OK );
321 /* if this is a paletted image then we simply need to convert the
322 // palette entries */
323 switch ( img->bits_per_pixel )
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 )
329 gray = CreateGrayScale( p );
330 memset( (void *)p, gray, 3 );
333 /* 16 BPP image are converted to 24 BPP images */
336 BMGError tmp = Convert16to24( img );
339 SetLastBMGError( tmp );
345 unsigned char *new_bits;
346 unsigned char *s, *s_end;
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;
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 )
359 SetLastBMGError( errMemoryAllocation );
360 return errMemoryAllocation;
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;
367 (unsigned char *)calloc(img->bytes_per_palette_entry *
369 sizeof(unsigned char) );
370 if ( img->palette == NULL )
373 img->bytes_per_palette_entry = 0;
374 img->palette_size = 0;
375 SetLastBMGError( errMemoryAllocation );
376 return errMemoryAllocation;
379 /* assign values to the gray scale palette */
380 for ( i = 0; i < 256; i++ )
382 p = img->palette + i * img->bytes_per_palette_entry;
383 memset( (void *)p, i, 3 );
384 if ( img->bytes_per_palette_entry == 4 )
388 /* cycle through the pixels and convert them to gray scale values */
390 end = img->bits + img->scan_width * img->height;
392 for ( p = img->bits; p < end; p += img->scan_width, q += new_scan_width )
394 s_end = p + 3 * img->width;
396 for ( s = p; s < s_end; s += 3, r++ )
397 *r = CreateGrayScale( s );
401 img->bits = new_bits;
402 img->scan_width = new_scan_width;
403 img->bits_per_pixel = 8;
408 end = img->bits + img->scan_width * img->height;
409 for ( p = img->bits; p < end; p += img->scan_width )
411 r = p + img->scan_width;
412 for ( q = p; q < r; q += 4 )
414 gray = CreateGrayScale( q );
415 memset( (void *)q, gray, 3 );
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.
433 // This function returns BMK_OK if successfull,
434 // errInvalidPixelFormat otherwise
436 BMGError ConvertToPseudoGrayScale( struct BMGImageStruct *img )
438 unsigned char *p, *p_end;
439 unsigned char *q, *q_end;
441 unsigned int bytes_per_pixel;
443 SetLastBMGError( errMemoryAllocation );
445 if ( img->bits_per_pixel <= 16 )
447 SetLastBMGError( errInvalidPixelFormat );
448 return errInvalidPixelFormat;
451 bytes_per_pixel = img->bits_per_pixel / 8;
452 p_end = img->bits + img->scan_width * img->height;
454 for ( p = img->bits; p < p_end; p += img->scan_width )
456 q_end = p + bytes_per_pixel * img->width;
457 for ( q = p; q < q_end; q += bytes_per_pixel )
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;
481 if ( cmax - cmin > 2 )
483 gray = CreateGrayScale( q );
484 memset( (void *)q, gray, 3 );
493 /*******************************************************************************
494 // extracts the dimensional information, pixel array, and color table from an
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.
500 // returns BMK_OK if successfull, and error state otherwise.
501 ********************************************************************************/
502 BMGError GetDataFromBitmap( HBITMAP hBitmap,
503 struct BMGImageStruct *img,
506 unsigned int DIBScanWidth;
508 HWND hWnd = GetForegroundWindow();
511 unsigned char red, green, blue;
513 unsigned int numBytes;
514 size_t soDIBSECTION = sizeof(DIBSECTION);
515 size_t soBITMAP = sizeof(BITMAP);
517 unsigned char *p, *q, *lpBits, alpha;
524 error = setjmp( err_jmp );
527 if ( hMemDC != NULL )
530 ReleaseDC( hWnd, hDC );
534 SetLastBMGError( (BMGError)error );
535 return (BMGError)error;
538 SetLastBMGError( BMG_OK );
539 /* check for valid bitmap*/
541 longjmp( err_jmp, (int)errInvalidBitmapHandle );
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
547 numBytes = GetObject( hBitmap, sizeof(DIBSECTION), &DS );
549 longjmp( err_jmp, (int)errWindowsAPI );
551 img->opt_for_bmp = 1;
552 if ( numBytes == soDIBSECTION )
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;
561 /* this may be a DDB which must be handled differently */
562 else if ( numBytes == soBITMAP )
567 if ( GetObject( hBitmap, sizeof(BITMAP), &bm ) == 0 )
568 longjmp( err_jmp, (int)errWindowsAPI );
570 /* DDB with a palette */
571 if ( bm.bmBitsPixel <= 8 )
572 longjmp( err_jmp, (int)errInvalidPixelFormat );
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,
580 lpBits = (unsigned char *)calloc( bm.bmHeight * bm.bmWidthBytes,
581 sizeof(unsigned char) );
583 longjmp( err_jmp, (int)errMemoryAllocation );
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 );
592 else /* I have no idea what this is */
593 longjmp( err_jmp, (int)errInvalidBitmapHandle );
595 /* allocate memory */
596 bmgerr = AllocateBMGImage( img );
597 if ( bmgerr != BMG_OK )
598 longjmp( err_jmp, (int)bmgerr );
601 DIBScanWidth = ( img->width * img->bits_per_pixel + 7 )/8;
602 if ( DIBScanWidth % 4 )
603 DIBScanWidth += 4 - DIBScanWidth % 4;
606 for ( q = lpBits; q < lpBits + DIBScanWidth * img->height;
607 p += img->scan_width, q += DIBScanWidth )
609 memcpy( (void *)p, (void *)q, DIBScanWidth );
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 )
618 unsigned char *color = GetBackgroundColor();
623 for ( p = img->bits; p < img->bits + img->scan_width * img->height;
627 p[2] = InverseAlphaComp( p[2], alpha, blue);
628 p[1] = InverseAlphaComp( p[1], alpha, green);
629 p[0] = InverseAlphaComp( p[0], alpha, red);
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 )
639 for ( p = img->bits + 3; p < img->bits + img->scan_width * img->height;
646 /* create palette if necessary */
647 if ( img->bits_per_pixel <= 8 )
650 hMemDC = CreateCompatibleDC( hDC );
651 SelectObject( hMemDC, hBitmap );
652 if ( !GetDIBColorTable( hMemDC, 0, img->palette_size,
653 (RGBQUAD *)img->palette ) )
655 longjmp( err_jmp, (int)errWindowsAPI );
658 ReleaseDC( hWnd, hDC );
667 /*******************************************************************************
668 // this function creates a bitmap from raw data. Returns an HBITMAP if it
669 // succeeds, otherwise NULL */
670 HBITMAP CreateBitmapFromData( struct BMGImageStruct img,
673 HBITMAP hBitmap = NULL;
675 HWND hWnd = GetForegroundWindow();
677 RGBQUAD *pColor = NULL;
679 unsigned char *rbits;
681 unsigned char *lpBits;
683 unsigned int DIBScanWidth;
690 error = setjmp( err_jmp );
693 if ( hMemDC != NULL )
696 ReleaseDC( hWnd, hDC );
697 if ( pColor != NULL && img.bytes_per_palette_entry == 3U )
699 SetLastBMGError( (BMGError)error );
703 SetLastBMGError( BMG_OK );
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 =
711 hBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
712 (void **)&lpBits, NULL, 0 );
714 if ( !hBitmap || !lpBits )
715 longjmp( err_jmp, (int)errWindowsAPI );
717 /* create a palette if needed */
718 if ( img.palette != NULL )
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 */
725 pColor = (RGBQUAD *)calloc(img.palette_size, sizeof(RGBQUAD) );
726 if ( pColor == NULL )
727 longjmp( err_jmp, (int)errMemoryAllocation );
730 for ( i = 0; i < (int)bmi.bmiHeader.biClrUsed; i++, bits += 3 )
732 pColor[i].rgbRed = bits[0];
733 pColor[i].rgbGreen = bits[1];
734 pColor[i].rgbBlue = bits[2];
738 if ( img.transparency_index > -1 )
740 unsigned char *color = GetBackgroundColor();
741 rbits = img.palette + img.bytes_per_palette_entry *
742 img.transparency_index;
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 );
755 if ( img.bytes_per_palette_entry == 3U )
760 /* calculate the scan line width */
761 DIBScanWidth = img.scan_width;
762 if ( DIBScanWidth % 4 )
763 DIBScanWidth += 4 - DIBScanWidth % 4;
765 if ( img.opt_for_bmp == 0 )
767 /* store bits into hBitmap */
770 bits < lpBits + img.height * DIBScanWidth;
771 bits += DIBScanWidth, rbits += img.scan_width )
773 memcpy( (void *)bits, (void *)rbits, img.scan_width );
777 memcpy( (void *)lpBits, (void *)img.bits, img.scan_width * img.height );
779 /* blend the image with the window background if alpha pixels
781 if ( img.bits_per_pixel == 32 )
783 /* blend with a bland background */
784 if ( alpha_blend == 1 )
786 unsigned char *color = GetBackgroundColor();
787 unsigned char red = color[2];
788 unsigned char green = color[1];
789 unsigned char blue = color[0];
791 for ( rbits = lpBits;
792 rbits < lpBits + img.height*DIBScanWidth;
793 rbits += DIBScanWidth )
795 for ( bits = rbits; bits < rbits + DIBScanWidth; bits += 4 )
798 bits[2] = AlphaComp( bits[2], alpha, blue );
799 bits[1] = AlphaComp( bits[1], alpha, green );
800 bits[0] = AlphaComp( bits[0], alpha, red );
804 /* blend with a background image */
805 else if ( alpha_blend == 2 )
807 unsigned char *bg_bits;
808 unsigned char *bg_bits_2;
809 unsigned int bg_bytes_per_pixel;
810 struct BMGImageStruct *bg = GetBackgroundImage();
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 );
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 );
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
826 bg_bytes_per_pixel = bg->bits_per_pixel / 8;
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. */
833 for ( rbits = lpBits;
834 rbits < lpBits + img.height*DIBScanWidth;
835 rbits += DIBScanWidth, bg_bits += bg->scan_width )
838 for ( bits = rbits; bits < rbits + DIBScanWidth;
839 bits += 4, bg_bits_2 += bg_bytes_per_pixel )
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] );
851 ReleaseDC( hWnd, hDC );
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
867 // returns BMG_OK if no errors occur, an error code otherwise
868 ******************************************************************************/
869 BMGError ConvertPaletteToRGB( struct BMGImageStruct img_in,
870 struct BMGImageStruct *img_out )
876 error = setjmp( err_jmp );
879 FreeBMGImage( img_out );
880 SetLastBMGError( (BMGError)error );
881 return (BMGError)error;
884 SetLastBMGError( BMG_OK );
886 if ( img_in.height == 0 || img_in.width == 0 )
887 longjmp( err_jmp, (int)errInvalidSize );
889 InitBMGImage( img_out );
891 // copy 16, 24, and 32-BPP images into the output image
892 if ( img_in.bits_per_pixel > 8 )
895 img_out->bits_per_pixel = img_in.bits_per_pixel;
896 out = CopyBMGImage( img_in, img_out );
898 longjmp( err_jmp, (int)out );
900 // 16-BPP are converted to 24-BPP images
901 if ( img_out->bits_per_pixel == 16 )
903 out = Convert16to24( img_out );
905 longjmp( err_jmp, (int)out );
908 else // convert paletted images to 24-BPP BGR or 32-BPP BGRA images
912 unsigned int scan_width;
914 unsigned char *q0, *q1, *p0, *p1;
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;
923 out = AllocateBMGImage( img_out );
925 longjmp( err_jmp, (int)out );
927 // 1-BPP and 4-BPP images are packed, so we need to unpack them
928 if ( img_in.bits_per_pixel < 8 )
931 scan_width = img_in.width;
932 buf = (unsigned char *)malloc(scan_width * img_in.height);
934 longjmp( err_jmp, (int)errMemoryAllocation );
936 if ( img_in.bits_per_pixel == 1 )
937 Convert1to8( img_in, buf );
939 Convert4to8( img_in, buf );
941 else // simply point to the bits array if we have a 8-BPP image
945 scan_width = img_in.scan_width;
948 // convert palette indices to BGR pixels
949 bpp = img_out->bits_per_pixel / 8;
951 for ( p0 = buf; p0 < buf + scan_width * img_in.height;
952 p0 += scan_width, q0 += img_out->scan_width )
955 for ( p1 = p0; p1 < p0 + img_in.width; p1++, q1 += bpp )
958 (void *)(img_in.palette + *p1 * img_in.bytes_per_palette_entry), 3 );
961 q1[3] = *p1 == img_in.transparency_index ? 0 : 0xFF;
973 /******************************************************************************
974 // CopyBMG copies the contents of img_in into img_out.
976 // CopyBMG returns BMG_OK if successful, otherwise, it returns an error code
977 ******************************************************************************/
978 BMGError CopyBMGImage( struct BMGImageStruct img_in,
979 struct BMGImageStruct *img_out )
981 BMGError out = BMG_OK;
982 SetLastBMGError( out );
984 FreeBMGImage( img_out );
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;
992 if ( img_in.width > 0 && img_in.height > 0 )
994 out = AllocateBMGImage( img_out );
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 );
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 */
1011 void SetBMGBackgroundColor( unsigned char *color )
1013 memcpy( (void *)GetBackgroundColor(), (void *)color,
1014 4*sizeof(unsigned char) );
1018 /* defines the background bitmap that is used for alpha blending & transparent
1020 BMGError SetBMGBackgroundBitmap( HBITMAP hBitmap )
1023 struct BMGImageStruct tmp;
1024 InitBMGImage( &tmp );
1026 /* first we extract the data from the HBITMAP */
1027 out = GetDataFromBitmap( hBitmap, &tmp, 0 );
1028 if ( out == BMG_OK )
1030 /* clean up the old background image */
1031 FreeBMGImage( GetBackgroundImage() );
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() );
1042 /* defines the background image that is used for alpha blending & transparent
1044 BMGError SetBMGBackgroundImage( struct BMGImageStruct img )
1046 /* clean up the old background image */
1047 FreeBMGImage( GetBackgroundImage() );
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() );