/* // 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; }
/****************************************************************************** // 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; }
/* WritePNG - writes the contents of a BMGImageStruct to a JPEG 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; int BitDepth; int ColorType; 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 ); 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 ); return (BMGError)error; } /* open the file */ if ((outfile = fopen(filename, "wb")) == NULL) 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 ); /* setup the output control */ png_init_io( png_ptr, outfile ); /* 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; }