/* // converts a color image to a pseudo-gray scale image. This is a implementation // is based upon the code published by Rich Franzen // <http://rocq.home.att.net/pseudoGrey.html>. I have "simplified" the 2 functions // he published into a single function. This implementation creates 1786 gray // scales from a 24-bit image. 16-BPP images are converted to 24-BPP images. 24 // and 32-BPP images will keep the same bitdepth. Paletted images and 16-BPP images // are not supported. // // This function returns BMK_OK if successfull, // errInvalidPixelFormat otherwise */ BMGError ConvertToPseudoGrayScale( struct BMGImageStruct *img ) { unsigned char *p, *p_end; unsigned char *q, *q_end; unsigned char gray; unsigned int bytes_per_pixel; SetLastBMGError( errMemoryAllocation ); if ( img->bits_per_pixel <= 16 ) { SetLastBMGError( errInvalidPixelFormat ); return errInvalidPixelFormat; } bytes_per_pixel = img->bits_per_pixel / 8; p_end = img->bits + img->scan_width * img->height; for ( p = img->bits; p < p_end; p += img->scan_width ) { q_end = p + bytes_per_pixel * img->width; for ( q = p; q < q_end; q += bytes_per_pixel ) { /* Rich's code has 1 function that converts an RGB triplet to a float // bounded by 0 and 1. He has a second function that converts a // float to a pseudo gray value. Pseudo gray values are RGB triplets // whose red, green and blue values differ by no more than 1. I have // combined these two functions into a single function that simply // looks for pseudo gray RGB triplets. If an RGB triplet meets this // criteria, I leave it unchanged; otherwise, I use the common intensity // conversion to create a grayscale value */ unsigned char cmin, cmax; cmin = q[0]; if ( q[1] < cmin ) cmin = q[1]; if ( q[2] < cmin ) cmin = q[2]; cmax = q[0]; if ( q[1] > cmax ) cmax = q[1]; if ( q[2] > cmax ) cmax = q[2]; if ( cmax - cmin > 2 ) { gray = CreateGrayScale( q ); memset( (void *)q, gray, 3 ); } } } return BMG_OK; }
/****************************************************************************** // 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; }
/* 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; }
/* 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; }
/* WriteBMP - writes the contents of an BMGImageStruct to a bmp file. Inputs: filename - the name of the file to be opened img - the BMGImageStruct containing the image data Returns: BMGError - if a write error or a resource error occurred BMG_OK - if the data was successfilly stored in filename Limitations: will not write BMP files using BI_RLE8, BI_RLE4, or BI_BITFIELDS */ BMGError WriteBMP( const char *filename, struct BMGImageStruct img ) { FILE * volatile file = NULL; jmp_buf err_jmp; int error; unsigned char * volatile bits = NULL; unsigned int DIBScanWidth; unsigned int BitsPerPixel; unsigned int bit_size; /*, new_bit_size; */ /* unsigned int rawbit_size; */ unsigned char *p, *q, *r, *t; /* unsigned int cnt; */ unsigned char * volatile pColor = NULL; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; SetLastBMGError( BMG_OK ); /* error handler */ error = setjmp(err_jmp); if (error != 0) { if (file != NULL) fclose(file); if (bits != NULL) free(bits); if (pColor != NULL) free(pColor); SetLastBMGError((BMGError)error); return (BMGError) error; } if ( img.bits == NULL ) longjmp( err_jmp, (int)errInvalidBMGImage ); file = fopen( filename, "wb" ); if ( file == NULL ) longjmp( err_jmp, (int)errFileOpen ); /* abort if we do not support the data */ if ( img.palette != NULL && img.bytes_per_palette_entry < 3 ) longjmp( err_jmp, (int)errInvalidBMGImage ); /* calculate dimensions */ BitsPerPixel = img.bits_per_pixel < 32 ? img.bits_per_pixel : 24U; DIBScanWidth = ( BitsPerPixel * img.width + 7 ) / 8; if ( DIBScanWidth % 4 ) DIBScanWidth += 4 - DIBScanWidth % 4; bit_size = DIBScanWidth * img.height; /* rawbit_size = BITScanWidth * img.height; */ /* allocate memory for bit array - assume that compression will // actually compress the bitmap */ bits = (unsigned char *)calloc( bit_size, 1 ); if ( bits == NULL ) longjmp( err_jmp, (int)errMemoryAllocation ); /* some initialization */ memset( (void *)&bmih, 0, sizeof(BITMAPINFOHEADER) ); bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = img.width; bmih.biHeight = img.height; bmih.biPlanes = 1; /* 32-bit images will be stored as 24-bit images to save space. The BMP format does not use the high word and I do not want to store alpha components in an image format that does not recognize it */ bmih.biBitCount = BitsPerPixel; bmih.biCompression = BI_RGB; // assumed bmih.biSizeImage = bit_size; // assumed bmih.biClrUsed = img.palette == NULL ? 0 : img.palette_size; bmih.biClrImportant = img.palette == NULL ? 0 : img.palette_size; /* if we are not compressed then copy the raw bits to bits */ if ( bmih.biCompression == BI_RGB ) { p = img.bits; /* simple memcpy's for images containing < 32-bits per pixel */ if ( img.bits_per_pixel < 32 ) { for ( q = bits; q < bits + bit_size; q += DIBScanWidth, p += img.scan_width ) { memcpy( (void *)q, (void *)p, img.scan_width ); } } /* store 32-bit images as 24-bit images to save space. alpha terms are lost */ else { DIBScanWidth = 3 * img.width; if ( DIBScanWidth % 4 ) DIBScanWidth += 4 - DIBScanWidth % 4; for ( q = bits; q < bits + bit_size; q += DIBScanWidth, p += img.scan_width ) { t = p; for ( r = q; r < q + DIBScanWidth; r += 3, t += 4 ) memcpy( (void *)r, (void *)t, 3 ); } } } /* create the palette if necessary */ if ( img.palette != NULL ) { pColor = (unsigned char *)calloc( img.palette_size, sizeof(RGBQUAD) ); if ( pColor == NULL ) longjmp( err_jmp, (int)errMemoryAllocation ); if ( img.bytes_per_palette_entry == 3 ) { p = img.palette; for ( q = pColor + 1; q < pColor +img.palette_size*sizeof(RGBQUAD); q += sizeof(RGBQUAD), p += 3 ) { memcpy( (void *)pColor, (void *)p, 3 ); } } else /* img.bytes_per_palette_entry == 4 */ { memcpy( (void *)pColor, (void *)img.palette, img.palette_size * sizeof(RGBQUAD) ); } } /* now that we know how big everything is let's write the file */ memset( (void *)&bmfh, 0, sizeof(BITMAPFILEHEADER) ); bmfh.bfType = BMP_ID; bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + img.palette_size * sizeof(RGBQUAD); bmfh.bfSize = bmfh.bfOffBits + bit_size; if ( fwrite( (void *)&bmfh, sizeof(BITMAPFILEHEADER), 1, file ) != 1 ) longjmp( err_jmp, (int)errFileWrite ); if ( fwrite( (void *)&bmih, sizeof(BITMAPINFOHEADER), 1, file ) != 1 ) longjmp( err_jmp, (int)errFileWrite ); if ( pColor != NULL ) { if ( fwrite( (void *)pColor, sizeof(RGBQUAD), img.palette_size, file ) != (unsigned int)img.palette_size ) { longjmp( err_jmp, (int)errFileWrite ); } } if ( fwrite( (void *)bits, sizeof(unsigned char), bit_size, file ) != bit_size ) { longjmp( err_jmp, (int)errFileWrite ); } fclose( file ); free( bits ); if ( pColor != NULL ) free( pColor ); return BMG_OK; }
/****************************************************************************** // 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; }
/* allocates memory for the bits & palette. Assigned values to scan_line & bits_per_palette_entry as well. Assumes opt_for_bmp has been set before this function is called. Assumes that all images with bits_per_pixel <= 8 require a palette. */ BMGError AllocateBMGImage( struct BMGImageStruct *img ) { unsigned int mempal; SetLastBMGError( BMG_OK ); /* make sure that all REQUIRED parameters are valid */ if ( img->width * img->height <= 0 ) { SetLastBMGError(errInvalidSize); return errInvalidSize; } switch( img->bits_per_pixel ) { case 1: case 4: case 8: case 16: case 24: case 32: break; default: SetLastBMGError( errInvalidPixelFormat ); return errInvalidPixelFormat; } /* delete old memory */ if ( img->bits != NULL ) { free( img->bits ); img->bits = NULL; } if ( img->palette != NULL ) { free( img->palette ); img->palette = NULL; } /* allocate memory for the palette */ if ( img->bits_per_pixel <= 8 ) { if ( img->opt_for_bmp > 0 ) img->bytes_per_palette_entry = 4U; else { /* we only support 3-byte and 4-byte palettes */ if ( img->bytes_per_palette_entry <= 3U ) img->bytes_per_palette_entry = 3U; else img->bytes_per_palette_entry = 4U; } /* use bits_per_pixel to determine palette_size if none was specified */ if ( img->palette_size == 0 ) img->palette_size = (unsigned short)(1 << img->bits_per_pixel); mempal = img->bytes_per_palette_entry * img->palette_size; img->palette = (unsigned char *)calloc( mempal, sizeof(unsigned char) ); if ( img->palette == NULL ) { SetLastBMGError(errMemoryAllocation); return errMemoryAllocation; } } else { img->bytes_per_palette_entry = 0; img->palette_size = 0; } /* set the scan width. Bitmaps optimized for windows have scan widths that are evenly divisible by 4. */ img->scan_width = ( img->bits_per_pixel * img->width + 7 ) / 8; if ( img->opt_for_bmp && img->scan_width % 4 ) img->scan_width += 4 - img->scan_width % 4; /* allocate memory for the bits */ mempal = img->scan_width * img->height; if ( mempal > 0 ) { img->bits = (unsigned char *)calloc( mempal, sizeof( unsigned char) ); if ( img->bits == NULL ) { if ( img->palette != NULL ) { free( img->palette ); img->palette = NULL; } SetLastBMGError(errMemoryAllocation); return errMemoryAllocation; } } else { SetLastBMGError(errInvalidSize); return errInvalidSize; } return BMG_OK; }
/******************************************************************************* // this function creates a bitmap from raw data. Returns an HBITMAP if it // succeeds, otherwise NULL */ HBITMAP CreateBitmapFromData( struct BMGImageStruct img, int alpha_blend ) { HBITMAP hBitmap = NULL; HDC hMemDC = NULL; HWND hWnd = GetForegroundWindow(); HDC hDC = NULL; RGBQUAD *pColor = NULL; BITMAPINFO bmi; unsigned char *rbits; unsigned char *bits; unsigned char *lpBits; unsigned char alpha; unsigned int DIBScanWidth; int i; jmp_buf err_jmp; int error; /* error handler */ error = setjmp( err_jmp ); if ( error != 0 ) { if ( hMemDC != NULL ) DeleteDC( hMemDC ); if ( hDC != NULL ) ReleaseDC( hWnd, hDC ); if ( pColor != NULL && img.bytes_per_palette_entry == 3U ) free( pColor ); SetLastBMGError( (BMGError)error ); return 0; } SetLastBMGError( BMG_OK ); /* create the DIB section that will hold this bitmap */ bmi = InternalCreateBMI( (unsigned int)img.width, (unsigned int)img.height, (unsigned short)img.bits_per_pixel, BI_RGB ); bmi.bmiHeader.biClrUsed = bmi.bmiHeader.biClrImportant = img.palette_size; hDC = GetDC( hWnd ); hBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (void **)&lpBits, NULL, 0 ); if ( !hBitmap || !lpBits ) longjmp( err_jmp, (int)errWindowsAPI ); /* create a palette if needed */ if ( img.palette != NULL ) { /* copy pixel data to pColor */ if ( img.bytes_per_palette_entry == 4U ) pColor = (RGBQUAD *)img.palette; else /* bytes_per_palette_entry === 3 */ { pColor = (RGBQUAD *)calloc(img.palette_size, sizeof(RGBQUAD) ); if ( pColor == NULL ) longjmp( err_jmp, (int)errMemoryAllocation ); bits = img.palette; for ( i = 0; i < (int)bmi.bmiHeader.biClrUsed; i++, bits += 3 ) { pColor[i].rgbRed = bits[0]; pColor[i].rgbGreen = bits[1]; pColor[i].rgbBlue = bits[2]; } } if ( img.transparency_index > -1 ) { unsigned char *color = GetBackgroundColor(); rbits = img.palette + img.bytes_per_palette_entry * img.transparency_index; rbits[0] = color[2]; rbits[1] = color[1]; rbits[2] = color[0]; } /* save color table in bitmap */ hMemDC = CreateCompatibleDC( hDC ); SelectObject( hMemDC, hBitmap ); if ( !SetDIBColorTable( hMemDC, 0, img.palette_size, pColor ) ) longjmp( err_jmp, (int)errWindowsAPI ); DeleteDC( hMemDC ); hMemDC = NULL; if ( img.bytes_per_palette_entry == 3U ) free( pColor ); pColor = NULL; } /* calculate the scan line width */ DIBScanWidth = img.scan_width; if ( DIBScanWidth % 4 ) DIBScanWidth += 4 - DIBScanWidth % 4; if ( img.opt_for_bmp == 0 ) { /* store bits into hBitmap */ rbits = img.bits; for ( bits = lpBits; bits < lpBits + img.height * DIBScanWidth; bits += DIBScanWidth, rbits += img.scan_width ) { memcpy( (void *)bits, (void *)rbits, img.scan_width ); } } else memcpy( (void *)lpBits, (void *)img.bits, img.scan_width * img.height ); /* blend the image with the window background if alpha pixels // are present */ if ( img.bits_per_pixel == 32 ) { /* blend with a bland background */ if ( alpha_blend == 1 ) { unsigned char *color = GetBackgroundColor(); unsigned char red = color[2]; unsigned char green = color[1]; unsigned char blue = color[0]; for ( rbits = lpBits; rbits < lpBits + img.height*DIBScanWidth; rbits += DIBScanWidth ) { for ( bits = rbits; bits < rbits + DIBScanWidth; bits += 4 ) { alpha = bits[3]; bits[2] = AlphaComp( bits[2], alpha, blue ); bits[1] = AlphaComp( bits[1], alpha, green ); bits[0] = AlphaComp( bits[0], alpha, red ); } } } /* blend with a background image */ else if ( alpha_blend == 2 ) { unsigned char *bg_bits; unsigned char *bg_bits_2; unsigned int bg_bytes_per_pixel; struct BMGImageStruct *bg = GetBackgroundImage(); /* make sure we can blend with a background image // I assume that the background image is invalid if it does not // have a valid width */ if ( bg->width <= 0 || bg->height <= 0 ) longjmp( err_jmp, (int)errUndefinedBGImage ); /* I cannot blend a foreground image with a background image that // is smaller than it */ if ( bg->width < img.width || bg->height < img.height ) longjmp( err_jmp, (int)errBGImageTooSmall ); /* the background image was forced to be a 24 or 32-BPP image; // therefore, we can safely divide by 8 to determined the // bytes per pixel*/ bg_bytes_per_pixel = bg->bits_per_pixel / 8; /* I will assume that the upper left corner of the input image // must be aligned with the upper left corner of the background // image. This allows me to have background images that are bigger // than the input image. */ bg_bits = bg->bits; for ( rbits = lpBits; rbits < lpBits + img.height*DIBScanWidth; rbits += DIBScanWidth, bg_bits += bg->scan_width ) { bg_bits_2 = bg_bits; for ( bits = rbits; bits < rbits + DIBScanWidth; bits += 4, bg_bits_2 += bg_bytes_per_pixel ) { alpha = bits[3]; bits[2] = AlphaComp( bits[2], alpha, bg_bits_2[2] ); bits[1] = AlphaComp( bits[1], alpha, bg_bits_2[1] ); bits[0] = AlphaComp( bits[0], alpha, bg_bits_2[0] ); } } } } ReleaseDC( hWnd, hDC ); return hBitmap; }
/******************************************************************************* // 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; }
/* // converts a color image to a gray scale image. If img is a 16 or // 24-BPP image then it is converted to a 256 color grayscale bitmap. // If img is a 1, 4, or 8 BPP image, then it will have the same number // of grayscales as it has palette entries. If it is a 32-BPP bitmap then // it will remain a 32-BPP bitmap to preserve the alpha channel. // // This function returns BMG_OK if successfull, or an error state // otherwise. */ BMGError ConvertToGrayScale( struct BMGImageStruct *img ) { unsigned char *p, *q, *r, *end, gray; SetLastBMGError( BMG_OK ); /* if this is a paletted image then we simply need to convert the // palette entries */ switch ( img->bits_per_pixel ) { default: end = img->palette + img->palette_size * img->bytes_per_palette_entry; for ( p = img->palette; p < end; p += img->bytes_per_palette_entry ) { gray = CreateGrayScale( p ); memset( (void *)p, gray, 3 ); } break; /* 16 BPP image are converted to 24 BPP images */ case 16: { BMGError tmp = Convert16to24( img ); if ( tmp != BMG_OK ) { SetLastBMGError( tmp ); return tmp; } } case 24: { unsigned char *new_bits; unsigned char *s, *s_end; unsigned short i; /* calculate the new scan width */ unsigned int new_scan_width = img->width; if ( new_scan_width % 4 && img->opt_for_bmp ) new_scan_width += 4 - new_scan_width % 4; /* allocate memory for the new pixel values */ new_bits = (unsigned char *)calloc( new_scan_width * img->height, sizeof(unsigned char) ); if ( new_bits == NULL ) { SetLastBMGError( errMemoryAllocation ); return errMemoryAllocation; } /* allocate memory for a 256 gray scale palette */ img->bytes_per_palette_entry = img->opt_for_bmp == 1 ? 4 : 3; img->palette_size = 256; img->palette = (unsigned char *)calloc(img->bytes_per_palette_entry * img->palette_size, sizeof(unsigned char) ); if ( img->palette == NULL ) { free( new_bits ); img->bytes_per_palette_entry = 0; img->palette_size = 0; SetLastBMGError( errMemoryAllocation ); return errMemoryAllocation; } /* assign values to the gray scale palette */ for ( i = 0; i < 256; i++ ) { p = img->palette + i * img->bytes_per_palette_entry; memset( (void *)p, i, 3 ); if ( img->bytes_per_palette_entry == 4 ) p[3] = 0; } /* cycle through the pixels and convert them to gray scale values */ q = new_bits; end = img->bits + img->scan_width * img->height; for ( p = img->bits; p < end; p += img->scan_width, q += new_scan_width ) { s_end = p + 3 * img->width; r = q; for ( s = p; s < s_end; s += 3, r++ ) *r = CreateGrayScale( s ); } free( img->bits ); img->bits = new_bits; img->scan_width = new_scan_width; img->bits_per_pixel = 8; break; } case 32: end = img->bits + img->scan_width * img->height; for ( p = img->bits; p < end; p += img->scan_width ) { r = p + img->scan_width; for ( q = p; q < r; q += 4 ) { gray = CreateGrayScale( q ); memset( (void *)q, gray, 3 ); } } break; } return BMG_OK; }
/******************************************************************************* A utility function for compressing paletted images. Will automatically convert 8-bit paletted images to 1-bit or 4-bit paletted images based upon palette_size. Assumes that indices in img->bits are valid. That is, 0 <= img->bits[i] <= 1 for all i if 1-bit compression is desired, and 0 <= img->bits[i] <= 15 for all i if 4-bit compression is desired Returns BMG_OK if successful, or an error code otherwise. *******************************************************************************/ BMGError CompressBMGImage( struct BMGImageStruct *img ) { unsigned char new_bits_per_pixel; unsigned int new_scan_width; unsigned char *new_bits = NULL; unsigned int new_bit_size; unsigned char *new_row, *old_row, *p, *q; unsigned char *end; unsigned short scale; SetLastBMGError( BMG_OK ); /* if we cannot compress it then do no harm and return "true" */ if ( img->palette == NULL || img->palette_size > 16 || img->bits_per_pixel != 8 ) { return BMG_OK; } /* calculate new dimensions */ new_bits_per_pixel = img->palette_size <= 2 ? 1U : 4U; new_scan_width = ( new_bits_per_pixel * img->width + 7 ) / 8; if ( img->opt_for_bmp > 0 && new_scan_width % 4 ) new_scan_width += 4 - new_scan_width % 4; new_bit_size = new_scan_width * img->height; /* allocate & test memory */ new_bits = (unsigned char *)calloc( new_bit_size, sizeof(unsigned char) ); if ( new_bits == NULL ) { SetLastBMGError( errMemoryAllocation ); return errMemoryAllocation; } old_row = img->bits; for ( new_row = new_bits; new_row < new_bits + new_bit_size; new_row += new_scan_width, old_row += img->scan_width ) { scale = 8 / new_bits_per_pixel; end = new_row + img->width / scale; p = old_row; if ( new_bits_per_pixel == 1 ) { for ( q = new_row; q < end; q++, p += scale ) { *q = (unsigned char)( (p[0] << 7) | (p[1] << 6) | (p[2] << 5) | (p[3] << 4) | (p[4] << 3) | (p[5] << 2) | (p[6] << 1) | p[7] ); } scale = img->width % scale; if ( scale-- > 0 ) { *q = (unsigned char)(p[0] << 7); if ( scale-- ) { *q |= (unsigned char)(p[1] << 6); if ( scale-- ) { *q |= (unsigned char)(p[2] << 5); if ( scale-- ) { *q |= (unsigned char)(p[3] << 4); if ( scale-- ) { *q |= (unsigned char)(p[4] << 3); if ( scale-- ) { *q |= (unsigned char)(p[5] << 2); if ( scale-- ) *q |= (unsigned char)(p[6] << 1); } } } } } } } else /* new_bits_per_pixel == 4 */ { for ( q = new_row; q < end; q++, p += scale ) { *q = (unsigned char)( (p[0] << 4) | (p[1] & 0x0F) ); } if ( img->width % scale ) *q = (unsigned char)(p[0] << 4); } } /* replace old values with new values */ free( img->bits ); img->bits = new_bits; img->scan_width = new_scan_width; img->bits_per_pixel = new_bits_per_pixel; 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; }
/* WritePNG - writes the contents of a BMGImageStruct to a PNG file. Inputs: filename - the name of the file to be opened img - the BMGImageStruct containing the image data Returns: 0 - if the file could not be written or a resource error occurred 1 - if the file was written Comments: 16-BPP BMG Images are converted to 24-BPP images Limitations: Color Type is limited to PNG_COLOR_TYPE_GRAY, PNG_COLOR_TYPE_RGB_ALPHA, PNG_COLOR_TYPE_RGB, & PNG_COLOR_TYPE_PALETTE; */ BMGError WritePNG( const char *filename, struct BMGImageStruct img ) { jmp_buf err_jmp; int error = 0; int BitDepth = 0; int ColorType = 0; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_colorp PNGPalette = NULL; int GrayScale; unsigned char *bits, *p, *q; unsigned char **rows = NULL; int NumColors = 0; int DIBScanWidth; int HasPalette; FILE *outfile = NULL; int i; BMGError tmp; /* error handler */ error = setjmp( err_jmp ); fprintf(stderr,"Writing PNG file %s.\n", filename); if ( error != 0 ) { if ( png_ptr != NULL ) png_destroy_write_struct( &png_ptr, NULL ); if ( rows ) { if ( rows[0] ) { free( rows[0] ); } free( rows ); } if ( PNGPalette ) free( PNGPalette ); if (outfile) { fclose( outfile ); } SetLastBMGError( (BMGError)error ); return (BMGError)error; } SetLastBMGError( BMG_OK ); /* open the file */ if ((outfile = fopen(filename, "wb")) == NULL) { fprintf(stderr, "Error opening %s for reading.\n", filename); longjmp( err_jmp, (int)errFileOpen ); } /* 16 BPP DIBS do not have palettes. libPNG expects 16 BPP images to have a palette. To correct this situation we must convert 16 BPP images to 24 BPP images before saving the data to the file */ if ( img.bits_per_pixel == 16 ) { tmp = Convert16to24( &img ); if ( tmp != BMG_OK ) longjmp( err_jmp, (int)tmp ); } HasPalette = img.bits_per_pixel <= 8; if ( HasPalette ) { NumColors = img.palette_size; /* if this is a grayscale image then set the flag and delete the palette*/ i = 0; bits = img.palette; while ( i < NumColors && bits[0] == bits[1] && bits[0] == bits[2] ) { i++; bits += img.bytes_per_palette_entry; } GrayScale = i == NumColors; } else GrayScale = 0; /* dimensions */ DIBScanWidth = ( img.width * img.bits_per_pixel + 7 ) / 8; /* create the png pointer */ png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if ( !png_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* create the info pointer */ info_ptr = png_create_info_struct( png_ptr ); if ( !info_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* bamboozle the png error handler */ /* error will always == 1 which equals 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 write callbacks */ png_set_write_fn(png_ptr, (png_voidp) outfile, user_write_data, user_flush_data); /* prepare variables needed to create PNG header */ BitDepth = img.bits_per_pixel < 8 ? img.bits_per_pixel : 8; /* determine color type */ if ( GrayScale ) ColorType = PNG_COLOR_TYPE_GRAY; else if ( img.bits_per_pixel == 32 ) ColorType = PNG_COLOR_TYPE_RGB_ALPHA; else if ( img.bits_per_pixel == 24 ) ColorType = PNG_COLOR_TYPE_RGB; else ColorType = PNG_COLOR_TYPE_PALETTE; /* create the PNG header */ png_set_IHDR( png_ptr, info_ptr, img.width, img.height, BitDepth, ColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE ); /* store the palette information if there is any */ if ( img.palette != NULL && !GrayScale ) { PNGPalette = (png_colorp)png_malloc( png_ptr, NumColors*sizeof(png_color)); if ( PNGPalette ) { bits = img.palette; for ( i = 0; i < NumColors; i++, bits += img.bytes_per_palette_entry ) { PNGPalette[i].red = bits[2]; PNGPalette[i].green = bits[1]; PNGPalette[i].blue = bits[0]; } png_set_PLTE( png_ptr, info_ptr, PNGPalette, NumColors ); } else longjmp( err_jmp, (int)errMemoryAllocation ); } /* write the file header information */ png_write_info( png_ptr, info_ptr ); /* create array to store data in */ rows = (unsigned char **)malloc(sizeof(unsigned char*)); if ( !rows ) longjmp( err_jmp, (int)errMemoryAllocation ); rows[0] = (unsigned char *)malloc( DIBScanWidth * sizeof(unsigned char)); if ( !rows[0] ) longjmp( err_jmp, (int)errMemoryAllocation ); /* point to the bottom row of the DIB data. DIBs are stored bottom-to-top, PNGs are stored top-to-bottom. */ bits = img.bits + (img.height - 1) * img.scan_width; /* store bits */ for ( i = 0; i < (int)img.height; i++ ) { switch ( img.bits_per_pixel ) { case 1: case 4: case 8: memcpy( (void *)rows[0], (void *)bits, DIBScanWidth ); break; case 24: q = bits; for ( p = rows[0]; p < rows[0] + DIBScanWidth; p += 3, q += 3 ) { p[0] = q[2]; p[1] = q[1]; p[2] = q[0]; } break; case 32: q = bits; for ( p = rows[0]; p < rows[0] + DIBScanWidth; p += 4, q += 4 ) { p[3] = q[3]; p[0] = q[2]; p[1] = q[1]; p[2] = q[0]; } break; } png_write_rows( png_ptr, rows, 1 ); bits -= img.scan_width; } /* finish writing the rest of the file */ png_write_end( png_ptr, info_ptr ); /* clean up and exit */ if ( PNGPalette ) free( PNGPalette ); free( rows[0] ); free( rows ); png_destroy_write_struct( &png_ptr, NULL ); fclose( outfile ); return BMG_OK; }
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_infop volatile end_info = NULL; png_uint_32 Width, Height; /* 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 (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; 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, (png_infop *) &end_info); fclose( file ); return BMG_OK; }