/****************************************************************************** // CopyBMG copies the contents of img_in into img_out. // // CopyBMG returns BMG_OK if successful, otherwise, it returns an error code ******************************************************************************/ BMGError CopyBMGImage( struct BMGImageStruct img_in, struct BMGImageStruct *img_out ) { BMGError out = BMG_OK; SetLastBMGError( out ); FreeBMGImage( img_out ); img_out->height = img_in.height; img_out->width = img_in.width; img_out->bits_per_pixel = img_in.bits_per_pixel; img_out->palette_size = img_in.palette_size; img_out->opt_for_bmp = img_in.opt_for_bmp; if ( img_in.width > 0 && img_in.height > 0 ) { out = AllocateBMGImage( img_out ); if ( out == BMG_OK ) { memcpy( (void *)img_out->bits, (void *)img_in.bits, img_in.scan_width * img_in.height ); if ( img_in.palette_size > 0 ) memcpy( (void *)img_out->palette, (void *)img_in.palette, img_in.palette_size * img_in.bytes_per_palette_entry ); } } return out; }
/* defines the background image that is used for alpha blending & transparent pixels */ BMGError SetBMGBackgroundImage( struct BMGImageStruct img ) { /* clean up the old background image */ FreeBMGImage( GetBackgroundImage() ); /* convert paletted and 16-BPP images to 24-BPP or 32-BPP images. This // will simplify the alpha blending logic*/ return ConvertPaletteToRGB( img, GetBackgroundImage() ); }
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { switch( reason ) { case DLL_PROCESS_ATTACH: InitBackground(); break; case DLL_PROCESS_DETACH: FreeBMGImage( GetBackgroundImage() ); break; } return 1; }
/* saves the contents of an HBITMAP to a file. returns 1 if successfull, // 0 otherwise */ BMGError SaveBitmapToPNGFile( HBITMAP hBitmap, /* bitmap to be saved */ const char *filename) /* name of output file */ { struct BMGImageStruct img; char msg[256], ext[4], *period; BMGError out = BMG_OK; InitBMGImage( &img ); /* determine the file type by using the extension */ strcpy( msg, filename ); period = strrchr( msg, '.' ); if ( period != NULL ) { period++; strcpy( ext, period ); ext[0] = toupper( ext[0] ); ext[1] = toupper( ext[1] ); ext[2] = toupper( ext[2] ); ext[3] = 0; } else { strcat( msg, ".PNG" ); strcpy( ext, "PNG" ); } if ( strcmp( ext, "PNG" ) == 0 ) { /* extract data from the bitmap. We assume that 32 bit images have been // blended with the background (unless this is a DDB - see GetDataFromBitmap // for more details) */ out = GetDataFromBitmap( hBitmap, &img, 1 ); if ( out == BMG_OK ) { out = WritePNG( msg, img ); } FreeBMGImage( &img ); } else { out = errInvalidFileExtension; } SetLastBMGError( out ); return out; }
/* Creates an HBITMAP to an image file. returns an HBITMAP if successfull, // NULL otherwise */ HBITMAP CreateBitmapFromPNGFile( const char *filename, int blend ) { char ext[4], msg[256]; char *period; BMGError out = BMG_OK; struct BMGImageStruct img; HBITMAP hBitmap = NULL; InitBMGImage( &img ); img.opt_for_bmp = 1; strcpy( msg, filename ); period = strrchr( msg, '.' ); if ( period != NULL ) { period++; strncpy( ext, period, 3 ); ext[0] = toupper( ext[0] ); ext[1] = toupper( ext[1] ); ext[2] = toupper( ext[2] ); ext[3] = 0; } else { strcat( msg, ".PNG" ); strcpy( ext, "PNG" ); } if ( strcmp( ext, "PNG" ) == 0 ) { out = ReadPNG( msg, &img ); if ( out == BMG_OK ) { hBitmap = CreateBitmapFromData( img, blend ); } FreeBMGImage( &img ); } else { out = errInvalidFileExtension; } SetLastBMGError( out ); return hBitmap; }
/* defines the background bitmap that is used for alpha blending & transparent pixels */ BMGError SetBMGBackgroundBitmap( HBITMAP hBitmap ) { BMGError out; struct BMGImageStruct tmp; InitBMGImage( &tmp ); /* first we extract the data from the HBITMAP */ out = GetDataFromBitmap( hBitmap, &tmp, 0 ); if ( out == BMG_OK ) { /* clean up the old background image */ FreeBMGImage( GetBackgroundImage() ); /* next, we convert paletted & 16-BPP images to 24 or 32-BPP images. // this will simplify the alpha blending. */ out = ConvertPaletteToRGB( tmp, GetBackgroundImage() ); } return out; }
/* ReadBMP - reads the image data from a BMP files and stores it in a BMGImageStruct. Inputs: filename - the name of the file to be opened Outputs: img - the BMGImageStruct containing the image data Returns: BMGError - if the file could not be read or a resource error occurred BMG_OK - if the file was read and the data was stored in img Limitations: will not read BMP files using BI_RLE8, BI_RLE4, or BI_BITFIELDS */ BMGError ReadBMP( const char *filename, struct BMGImageStruct *img ) { FILE *file = NULL; int error; BMGError tmp; unsigned char *p, *q; /*, *q_end; */ /* unsigned int cnt; */ int i; /* int EOBMP; */ BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; /* unsigned int mask[3]; */ unsigned int DIBScanWidth; unsigned int bit_size, rawbit_size; unsigned char *rawbits = NULL; SetLastBMGError( BMG_OK ); if ( img == NULL ) { error = (int) errInvalidBMGImage; goto err_jmp; } file = fopen( filename, "rb" ); if ( file == NULL ) { error = (int) errFileOpen; goto err_jmp; } /* read the file header */ if ( fread( (void *)&bmfh, sizeof(BITMAPFILEHEADER), 1, file ) != 1 ) { error = (int) errFileRead; goto err_jmp; } /* confirm that this is a BMP file */ if ( bmfh.bfType != BMP_ID ) { error = (int) errUnsupportedFileFormat; goto err_jmp; } /* read the bitmap info header */ if ( fread( (void *)&bmih, sizeof(BITMAPINFOHEADER), 1, file ) != 1 ) { error = (int) errFileRead; goto err_jmp; } /* abort if this is an unsupported format */ if ( bmih.biCompression != BI_RGB ) { printf("planes: %i bits: %i type: %i ", bmih.biPlanes, bmih.biBitCount, bmih.biCompression); error = (int) errUnsupportedFileFormat; goto err_jmp; } img->bits_per_pixel = (unsigned char)bmih.biBitCount; img->width = bmih.biWidth; img->height = bmih.biHeight; if ( img->bits_per_pixel <= 8 ) { img->palette_size = (unsigned short)bmih.biClrUsed; img->bytes_per_palette_entry = 4U; } tmp = AllocateBMGImage( img ); if ( tmp != BMG_OK ) { error = (int) tmp; goto err_jmp; } /* read palette if necessary */ if ( img->bits_per_pixel <= 8 ) { if ( fread( (void *)img->palette, sizeof(RGBQUAD), img->palette_size, file ) != (unsigned int)img->palette_size ) { error = (int) errFileRead; goto err_jmp; } } /* dimensions */ DIBScanWidth = ( img->bits_per_pixel * img->width + 7 ) / 8; if ( DIBScanWidth %4 ) DIBScanWidth += 4 - DIBScanWidth % 4; bit_size = img->scan_width * img->height; /* allocate memory for the raw bits */ if ( bmih.biCompression != BI_RGB ) rawbit_size = bmfh.bfSize - bmfh.bfOffBits; else rawbit_size = DIBScanWidth * img->height; rawbits = (unsigned char *)calloc( rawbit_size, 1 ); if ( rawbits == NULL ) { error = (int) errMemoryAllocation; goto err_jmp; } if ( fread( (void *)rawbits, sizeof(unsigned char), rawbit_size, file ) != rawbit_size ) { error = (int) errFileRead; goto err_jmp; } if ( bmih.biCompression == BI_RGB ) { p = rawbits; for ( q = img->bits; q < img->bits + bit_size; q += img->scan_width, p += DIBScanWidth ) { memcpy( (void *)q, (void *)p, img->scan_width ); } } /* swap rows if necessary */ if ( bmih.biHeight < 0 ) { for ( i = 0; i < (int)(img->height) / 2; i++ ) { p = img->bits + i * img->scan_width; q = img->bits + ((img->height) - i - 1 ) * img->scan_width; memcpy( (void *)rawbits, (void *)p, img->scan_width ); memcpy( (void *)p, (void *)q, img->scan_width ); memcpy( (void *)q, (void *)rawbits, img->scan_width ); } } fclose( file ); free( rawbits ); return BMG_OK; /* error handler */ err_jmp: if ( file != NULL ) fclose( file ); if ( rawbits != NULL ) free( rawbits ); FreeBMGImage( img ); SetLastBMGError( (BMGError)error ); return (BMGError)error; }
/****************************************************************************** // ConvertPaletteToRGB converts paletted and 16-BPP images that do not have // transparent pixels to 24-BPP images. Paletted images with transparent pixels // are converted to 32-BPP images. 24-BPP and 32-BPP images are simply copied // to the output structure // // INPUTS: // img_in // OUTPUTS: // img_out // // returns BMG_OK if no errors occur, an error code otherwise ******************************************************************************/ BMGError ConvertPaletteToRGB( struct BMGImageStruct img_in, struct BMGImageStruct *img_out ) { jmp_buf err_jmp; int error; /* error handler */ error = setjmp( err_jmp ); if ( error != 0 ) { FreeBMGImage( img_out ); SetLastBMGError( (BMGError)error ); return (BMGError)error; } SetLastBMGError( BMG_OK ); if ( img_in.height == 0 || img_in.width == 0 ) longjmp( err_jmp, (int)errInvalidSize ); InitBMGImage( img_out ); // copy 16, 24, and 32-BPP images into the output image if ( img_in.bits_per_pixel > 8 ) { BMGError out; img_out->bits_per_pixel = img_in.bits_per_pixel; out = CopyBMGImage( img_in, img_out ); if ( out != BMG_OK ) longjmp( err_jmp, (int)out ); // 16-BPP are converted to 24-BPP images if ( img_out->bits_per_pixel == 16 ) { out = Convert16to24( img_out ); if ( out != BMG_OK ) longjmp( err_jmp, (int)out ); } } else // convert paletted images to 24-BPP BGR or 32-BPP BGRA images { BMGError out; unsigned char *buf; unsigned int scan_width; int dealloc; unsigned char *q0, *q1, *p0, *p1; unsigned int bpp; // allocate memory for the 24-BPP output image img_out->width = img_in.width; img_out->height = img_in.height; img_out->opt_for_bmp = img_in.opt_for_bmp; img_out->bits_per_pixel = img_in.transparency_index > -1 ? 32 : 24; out = AllocateBMGImage( img_out ); if ( out != BMG_OK ) longjmp( err_jmp, (int)out ); // 1-BPP and 4-BPP images are packed, so we need to unpack them if ( img_in.bits_per_pixel < 8 ) { dealloc = 1; scan_width = img_in.width; buf = (unsigned char *)malloc(scan_width * img_in.height); if ( buf == NULL ) longjmp( err_jmp, (int)errMemoryAllocation ); if ( img_in.bits_per_pixel == 1 ) Convert1to8( img_in, buf ); else Convert4to8( img_in, buf ); } else // simply point to the bits array if we have a 8-BPP image { dealloc = 0; buf = img_in.bits; scan_width = img_in.scan_width; } // convert palette indices to BGR pixels bpp = img_out->bits_per_pixel / 8; q0 = img_out->bits; for ( p0 = buf; p0 < buf + scan_width * img_in.height; p0 += scan_width, q0 += img_out->scan_width ) { q1 = q0; for ( p1 = p0; p1 < p0 + img_in.width; p1++, q1 += bpp ) { memcpy( (void *)q1, (void *)(img_in.palette + *p1 * img_in.bytes_per_palette_entry), 3 ); if ( bpp == 4 ) { q1[3] = *p1 == img_in.transparency_index ? 0 : 0xFF; } } } if ( dealloc == 1 ) free( buf ); } return BMG_OK; }
/******************************************************************************* // extracts the dimensional information, pixel array, and color table from an // HBITMAP. // hBitmap can be a handle to a DIB or a DDB. This function assumes that DDBs // will not have a palette. If you create a DDB on a 256-color graphics card, // then the DDB will have a palette and this function will fail. // // returns BMK_OK if successfull, and error state otherwise. ********************************************************************************/ BMGError GetDataFromBitmap( HBITMAP hBitmap, struct BMGImageStruct *img, int remove_alpha ) { unsigned int DIBScanWidth; DIBSECTION DS; HWND hWnd = GetForegroundWindow(); HDC hDC = NULL; HDC hMemDC = NULL; unsigned char red, green, blue; int FreelpBits = 0; unsigned int numBytes; size_t soDIBSECTION = sizeof(DIBSECTION); size_t soBITMAP = sizeof(BITMAP); unsigned char *p, *q, *lpBits, alpha; jmp_buf err_jmp; int error; BMGError bmgerr; /* error handler */ error = setjmp( err_jmp ); if ( error != 0 ) { if ( hMemDC != NULL ) DeleteDC( hMemDC ); if ( hDC != NULL ) ReleaseDC( hWnd, hDC ); if ( FreelpBits ) free( lpBits ); FreeBMGImage( img ); SetLastBMGError( (BMGError)error ); return (BMGError)error; } SetLastBMGError( BMG_OK ); /* check for valid bitmap*/ if ( !hBitmap ) longjmp( err_jmp, (int)errInvalidBitmapHandle ); /* Extract DIBSECTION info from the HBITMAP. numBytes will equal // soDIBSECTION (84) if hBitmap is a handle to a DIBSECTION (DIB). // numBytes will equal soBITMAP (24) if hBitmap is a handle to a // BITMAP (DDB). */ numBytes = GetObject( hBitmap, sizeof(DIBSECTION), &DS ); if ( numBytes == 0 ) longjmp( err_jmp, (int)errWindowsAPI ); img->opt_for_bmp = 1; if ( numBytes == soDIBSECTION ) { img->width = DS.dsBmih.biWidth; img->height = DS.dsBmih.biHeight; img->bits_per_pixel = (unsigned char)DS.dsBmih.biBitCount; if ( img->bits_per_pixel <= 8 && DS.dsBmih.biClrUsed > 0 ) img->palette_size = (unsigned short)DS.dsBmih.biClrUsed; lpBits = (unsigned char *)DS.dsBm.bmBits; } /* this may be a DDB which must be handled differently */ else if ( numBytes == soBITMAP ) { BITMAP bm; BITMAPINFO bmi; if ( GetObject( hBitmap, sizeof(BITMAP), &bm ) == 0 ) longjmp( err_jmp, (int)errWindowsAPI ); /* DDB with a palette */ if ( bm.bmBitsPixel <= 8 ) longjmp( err_jmp, (int)errInvalidPixelFormat ); img->width = bm.bmWidth; img->height = bm.bmHeight; img->bits_per_pixel = (unsigned char)bm.bmBitsPixel; bmi = InternalCreateBMI( bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, BI_RGB ); lpBits = (unsigned char *)calloc( bm.bmHeight * bm.bmWidthBytes, sizeof(unsigned char) ); if ( lpBits == 0 ) longjmp( err_jmp, (int)errMemoryAllocation ); FreelpBits = 1; hDC = GetDC( hWnd ); if ( GetDIBits(hDC, hBitmap, 0, bm.bmHeight, (void *)lpBits, &bmi, DIB_RGB_COLORS ) == 0 ) longjmp( err_jmp, (int)errWindowsAPI ); ReleaseDC( hWnd, hDC ); hDC = NULL; } else /* I have no idea what this is */ longjmp( err_jmp, (int)errInvalidBitmapHandle ); /* allocate memory */ bmgerr = AllocateBMGImage( img ); if ( bmgerr != BMG_OK ) longjmp( err_jmp, (int)bmgerr ); /* dimensions */ DIBScanWidth = ( img->width * img->bits_per_pixel + 7 )/8; if ( DIBScanWidth % 4 ) DIBScanWidth += 4 - DIBScanWidth % 4; p = img->bits; for ( q = lpBits; q < lpBits + DIBScanWidth * img->height; p += img->scan_width, q += DIBScanWidth ) { memcpy( (void *)p, (void *)q, DIBScanWidth ); } /* "un-blend" the image if requested. NOTE: unblending only works with // bland backgrounds */ if ( remove_alpha > 0 && img->bits_per_pixel == 32 && numBytes == soDIBSECTION ) { unsigned char *color = GetBackgroundColor(); red = color[2]; green = color[1]; blue = color[0]; for ( p = img->bits; p < img->bits + img->scan_width * img->height; p += 4 ) { alpha = p[3]; p[2] = InverseAlphaComp( p[2], alpha, blue); p[1] = InverseAlphaComp( p[1], alpha, green); p[0] = InverseAlphaComp( p[0], alpha, red); } } /* 32-bit DDBs must have the alpha channel set to 0xFF before they are // saved to a file. This may not be true for all devices that generate // 32-bit DDBs. I have only created 32-bit DDBs using an Intense3D Wildcat // 4110 card. The alpha channel was always 0. */ if (img->bits_per_pixel == 32 && numBytes == soBITMAP ) { for ( p = img->bits + 3; p < img->bits + img->scan_width * img->height; p += 4 ) { *p = 0xFF; } } /* create palette if necessary */ if ( img->bits_per_pixel <= 8 ) { hDC = GetDC( hWnd ); hMemDC = CreateCompatibleDC( hDC ); SelectObject( hMemDC, hBitmap ); if ( !GetDIBColorTable( hMemDC, 0, img->palette_size, (RGBQUAD *)img->palette ) ) { longjmp( err_jmp, (int)errWindowsAPI ); } DeleteDC( hMemDC ); ReleaseDC( hWnd, hDC ); } if ( FreelpBits ) free( lpBits ); return BMG_OK; }
/* ReadPNG - Reads the contents of a PNG file and stores the contents into BMGImageStruct Inputs: filename - the name of the file to be opened Outputs: img - the BMGImageStruct containing the image data Returns: BMGError - if the file could not be read or a resource error occurred BMG_OK - if the file was read and the data was stored in img Limitations: None. Comments: 2-bit images are converted to 4-bit images. 16-bit images are converted to 8-bit images. gray scale images with alpha components are converted to 32-bit images */ BMGError ReadPNG( const char *filename, struct BMGImageStruct * volatile img ) { jmp_buf err_jmp; int error; FILE * volatile file = NULL; int bit_depth, color_type, interlace_type, compression_type, filter_type, row_bytes; unsigned char signature[8]; png_structp volatile png_ptr = NULL; png_infop volatile info_ptr = NULL; unsigned long Width, Height; unsigned char *bits; unsigned char** volatile rows = NULL; BMGError tmp; /* error handler */ error = setjmp( err_jmp ); if ( error != 0 ) { if (info_ptr != NULL) png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, NULL); else if (png_ptr != NULL) png_destroy_read_struct((png_structp *) &png_ptr, NULL, NULL); if ( rows ) { if ( rows[0] ) free( rows[0] ); free( rows ); } if (img) FreeBMGImage( img ); if (file) fclose( file ); return (BMGError)error; } if ( img == NULL ) longjmp ( err_jmp, (int)errInvalidBMGImage ); file = fopen( filename, "rb" ); if ( !file || fread( signature, 1, 8, file ) != 8) longjmp ( err_jmp, (int)errFileOpen ); /* check the signature */ if (png_sig_cmp( signature, 0, 8 ) != 0) longjmp( err_jmp, (int)errUnsupportedFileFormat ); /* create a pointer to the png read structure */ png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if ( !png_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* create a pointer to the png info structure */ info_ptr = png_create_info_struct( png_ptr ); if ( !info_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* bamboozle the PNG longjmp buffer */ /*generic PNG error handler*/ error = setjmp( png_jmpbuf( png_ptr ) ); if ( error > 0 ) longjmp( err_jmp, error ); /* attach file buffer to the png read pointer */ png_init_io( png_ptr, file ); /*let the read functions know that we have already read the 1st 8 bytes */ png_set_sig_bytes( png_ptr, 8 ); /* read all PNG data up to the image data */ png_read_info( png_ptr, info_ptr ); /* extract the data we need to form the HBITMAP from the PNG header */ png_get_IHDR(png_ptr, info_ptr, &Width, &Height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type ); img->width = (unsigned int) Width; img->height = (unsigned int) Height; img->bits_per_pixel = (unsigned char)32; img->scan_width = Width * 4; /* strip if color channel is larger than 8 bits */ if (bit_depth > 8) { png_set_strip_16(png_ptr); bit_depth = 8; } /* These are not really required per Rice format spec, * but is done just in case someone uses them. * convert palette color to rgb color */ if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); color_type = PNG_COLOR_TYPE_RGB; } /* expand 1,2,4 bit gray scale to 8 bit gray scale */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); /* convert gray scale or gray scale + alpha to rgb color */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); color_type = PNG_COLOR_TYPE_RGB; } /* add alpha channel if any */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); color_type = PNG_COLOR_TYPE_RGB_ALPHA; } /* convert rgb to rgba */ if (color_type == PNG_COLOR_TYPE_RGB) { png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); color_type = PNG_COLOR_TYPE_RGB_ALPHA; } /* punt invalid formats */ if (color_type != PNG_COLOR_TYPE_RGB_ALPHA) { longjmp(err_jmp, (int)errIncorrectFormat); } /* convert rgba to bgra */ png_set_bgr(png_ptr); img->palette_size = (unsigned short)0; img->bytes_per_palette_entry = 4U; tmp = AllocateBMGImage( img ); if ( tmp != BMG_OK ) longjmp( err_jmp, (int)tmp ); /* Update info structure */ png_read_update_info( png_ptr, info_ptr ); /* create buffer to read data to */ rows = (unsigned char **)malloc(Height*sizeof(unsigned char *)); if ( !rows ) longjmp( err_jmp, (int)errMemoryAllocation ); row_bytes = png_get_rowbytes(png_ptr, info_ptr); rows[0] = (unsigned char *)malloc( Height*row_bytes*sizeof(char)); if ( !rows[0] ) longjmp( err_jmp, (int)errMemoryAllocation ); for (int i = 1; i < (int)Height; i++ ) rows[i] = rows[i - 1] + row_bytes; /* read the entire image into rows */ png_read_image( png_ptr, rows ); bits = img->bits + (Height - 1) * img->scan_width; for (int i = 0; i < (int)Height; i++ ) { memcpy(bits, rows[i], 4*Width); bits -= img->scan_width; } free( rows[0] ); free( rows ); png_read_end( png_ptr, info_ptr ); png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, NULL); fclose( file ); return BMG_OK; }
/* ReadPNGInfo - Reads the contents of a PNG header and stores the contents into BMGImageStruct Inputs: filename - the name of the file to be opened Outputs: img - the BMGImageStruct containing the image data Returns: BMGError - if the file could not be read or a resource error occurred BMG_OK - if the file was read and the data was stored in img */ BMGError ReadPNGInfo( const char *filename, struct BMGImageStruct * volatile img ) { jmp_buf err_jmp; int error; FILE * volatile file = NULL; int BitDepth; int ColorType; int InterlaceType; unsigned char signature[8]; png_structp volatile png_ptr = NULL; png_infop volatile info_ptr = NULL; png_uint_32 Width, Height; /* error handler */ error = setjmp( err_jmp ); if (error != 0) { if (info_ptr != NULL) png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, NULL); else if (png_ptr != NULL) png_destroy_read_struct((png_structp *) &png_ptr, NULL, NULL); if (img) FreeBMGImage(img); if (file) fclose(file); return (BMGError) error; } if ( img == NULL ) longjmp ( err_jmp, (int)errInvalidBMGImage ); file = fopen( filename, "rb" ); if ( !file || fread( signature, 1, 8, file ) != 8) longjmp ( err_jmp, (int)errFileOpen ); /* check the signature */ if ( png_sig_cmp( signature, 0, 8 ) != 0 ) longjmp( err_jmp, (int)errUnsupportedFileFormat ); /* create a pointer to the png read structure */ png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if ( !png_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* bamboozle the PNG longjmp buffer */ /*generic PNG error handler*/ error = setjmp( png_jmpbuf( png_ptr ) ); if ( error > 0 ) longjmp( err_jmp, error ); /* set function pointers in the PNG library, for read callbacks */ png_init_io( png_ptr, file ); /*let the read functions know that we have already read the 1st 8 bytes */ png_set_sig_bytes( png_ptr, 8 ); /* read all PNG data up to the image data */ png_read_info( png_ptr, info_ptr ); /* extract the data we need to form the HBITMAP from the PNG header */ png_get_IHDR( png_ptr, info_ptr, &Width, &Height, &BitDepth, &ColorType, &InterlaceType, NULL, NULL); img->width = (unsigned int) Width; img->height = (unsigned int) Height; img->bits_per_pixel = (unsigned char)32; img->scan_width = Width * 4; img->palette_size = (unsigned short)0; img->bytes_per_palette_entry = 4U; img->bits = NULL; png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, NULL); fclose( file ); return BMG_OK; }
/* ReadPNG - Reads the contents of a PNG file and stores the contents into BMGImageStruct Inputs: filename - the name of the file to be opened Outputs: img - the BMGImageStruct containing the image data Returns: BMGError - if the file could not be read or a resource error occurred BMG_OK - if the file was read and the data was stored in img Limitations: None. Comments: 2-bit images are converted to 4-bit images. 16-bit images are converted to 8-bit images. gray scale images with alpha components are converted to 32-bit images */ BMGError ReadPNG( const char *filename, struct BMGImageStruct * volatile img ) { jmp_buf err_jmp; int error; FILE * volatile file = NULL; int BitDepth; int ColorType; int InterlaceType; unsigned char signature[8]; png_structp volatile png_ptr = NULL; png_infop volatile info_ptr = NULL; png_infop volatile end_info = NULL; png_color_16 *ImageBackground = NULL; png_bytep trns = NULL; int NumTrans = 0; int i, k; png_color_16p TransColors = NULL; png_uint_32 Width, Height; unsigned char *bits; unsigned char** volatile rows = NULL; BMGError tmp; /* error handler */ error = setjmp( err_jmp ); if (error != 0) { if (end_info != NULL) png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, (png_infop *) &end_info); else if (info_ptr != NULL) png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, NULL); else if (png_ptr != NULL) png_destroy_read_struct((png_structp *) &png_ptr, NULL, NULL); if (rows) { if (rows[0]) free(rows[0]); free(rows); } if (img) FreeBMGImage(img); if (file) fclose(file); SetLastBMGError((BMGError) error); return (BMGError) error; } if ( img == NULL ) longjmp ( err_jmp, (int)errInvalidBMGImage ); file = fopen( filename, "rb" ); if ( !file || fread( signature, 1, 8, file ) != 8) longjmp ( err_jmp, (int)errFileOpen ); /* check the signature */ if ( png_sig_cmp( signature, 0, 8 ) != 0 ) longjmp( err_jmp, (int)errUnsupportedFileFormat ); /* create a pointer to the png read structure */ png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if ( !png_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* create a pointer to the png info structure */ info_ptr = png_create_info_struct( png_ptr ); if ( !info_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* create a pointer to the png end-info structure */ end_info = png_create_info_struct(png_ptr); if (!end_info) longjmp( err_jmp, (int)errMemoryAllocation ); /* bamboozle the PNG longjmp buffer */ /*generic PNG error handler*/ /* error will always == 1 which == errLib */ // error = png_setjmp(png_ptr); error = setjmp( png_jmpbuf( png_ptr ) ); if ( error > 0 ) longjmp( err_jmp, error ); /* set function pointers in the PNG library, for read callbacks */ png_set_read_fn(png_ptr, (png_voidp) file, user_read_data); /*let the read functions know that we have already read the 1st 8 bytes */ png_set_sig_bytes( png_ptr, 8 ); /* read all PNG data up to the image data */ png_read_info( png_ptr, info_ptr ); /* extract the data we need to form the HBITMAP from the PNG header */ png_get_IHDR( png_ptr, info_ptr, &Width, &Height, &BitDepth, &ColorType, &InterlaceType, NULL, NULL); img->width = (unsigned int) Width; img->height = (unsigned int) Height; img->bits_per_pixel = (unsigned char)32; img->scan_width = Width * 4; /* convert 16-bit images to 8-bit images */ if (BitDepth == 16) png_set_strip_16(png_ptr); /* These are not really required per Rice format spec, * but is done just in case someone uses them. */ /* convert palette color to rgb color */ if (ColorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); ColorType = PNG_COLOR_TYPE_RGB; } /* expand 1,2,4 bit gray scale to 8 bit gray scale */ if (ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); /* convert gray scale or gray scale + alpha to rgb color */ if (ColorType == PNG_COLOR_TYPE_GRAY || ColorType == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); ColorType = PNG_COLOR_TYPE_RGB; } /* add alpha channel if any */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); ColorType = PNG_COLOR_TYPE_RGB_ALPHA; } /* convert rgb to rgba */ if (ColorType == PNG_COLOR_TYPE_RGB) { png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); ColorType = PNG_COLOR_TYPE_RGB_ALPHA; } png_set_bgr(png_ptr); /* set the background color if one is found */ if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD) ) png_get_bKGD(png_ptr, info_ptr, &ImageBackground); /* get the transparent color if one is there */ if ( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) ) png_get_tRNS( png_ptr, info_ptr, &trns, &NumTrans, &TransColors ); img->palette_size = (unsigned short)0; img->bytes_per_palette_entry = 4U; tmp = AllocateBMGImage( img ); if ( tmp != BMG_OK ) longjmp( err_jmp, (int)tmp ); png_read_update_info( png_ptr, info_ptr ); /* create buffer to read data to */ rows = (unsigned char **)malloc(Height*sizeof(unsigned char *)); if ( !rows ) longjmp( err_jmp, (int)errMemoryAllocation ); k = png_get_rowbytes( png_ptr, info_ptr ); rows[0] = (unsigned char *)malloc( Height*k*sizeof(char)); if ( !rows[0] ) longjmp( err_jmp, (int)errMemoryAllocation ); for ( i = 1; i < (int)Height; i++ ) rows[i] = rows[i-1] + k; /* read the entire image into rows */ png_read_image( png_ptr, rows ); bits = img->bits + (Height - 1) * img->scan_width; for ( i = 0; i < (int)Height; i++ ) { memcpy(bits, rows[i], 4*Width); bits -= img->scan_width; } free( rows[0] ); free( rows ); png_read_end( png_ptr, info_ptr ); png_destroy_read_struct((png_structp *) &png_ptr, (png_infop *) &info_ptr, (png_infop *) &end_info); fclose( file ); return BMG_OK; }