/* PNG ファイル -> DIB 変換 PNG2DIB.C 2000.02 J.Baba /Zp オプションを付加してコンパイル libpng および、zlib ライブラリを使用 */ #include #include #include #include "bmplib.h" #include "png.h" #define alpha_composite(composite, fg, alpha, bg) { \ ushort temp = ((ushort)(fg)*(ushort)(alpha) + \ (ushort)(bg)*(ushort)(255 - (ushort)(alpha)) + (ushort)128); \ (composite) = (uchar)((temp + (temp >> 8)) >> 8); \ } DIB png_read_dib_file( FILE *fp ) { static double LUT_exponent = 1.0; /* lookup table */ static double CRT_exponent = 2.2; /* monitor */ static uchar bg_red=0, bg_green=0, bg_blue=0; /* 特に処理しない */ png_structp png_ptr = NULL; png_infop info_ptr = NULL; uchar sig[8]; long image_width, image_height; int bit_depth, color_type; double display_exponent; double gamma; int num_palette = 0; png_color *palette; int image_channels; ulong image_rowbytes; uchar * image_data = NULL; png_bytepp row_pointers = NULL; DIB dib; BITMAPINFOHEADER bi; uchar * wimage_data = NULL; ulong wimage_rowbytes; int row, wrow=0, i; uchar *src, *dest, r, g, b, a; // (1) PNG の初期化とヘッダー情報の読込み /* ファイルの PNG サインをチェック */ fread(sig, 1, 8, fp); if (!png_check_sig(sig, 8)) return NULL; /* PNG 構造体の取得 */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return NULL; /* PNG 情報構造体の取得 */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return NULL; } /* これ以降の内部エラーはここに戻り、エラーとして返される */ if (setjmp(png_ptr->jmpbuf)) { if( image_data ) free( image_data ); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } /* ファイルハンドルの設定 */ png_init_io(png_ptr, fp); /* 情報ヘッダの読み込み */ png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); /* 必要な情報の取得 */ png_get_IHDR(png_ptr, info_ptr, &image_width, &image_height, &bit_depth, &color_type, NULL, NULL, NULL); /* パレットがあれば、パレット情報を読む */ /* (palette は、自分で開放しない事) */ if( color_type == PNG_COLOR_TYPE_PALETTE ) { png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); } // (2) イメージデータを読む display_exponent = LUT_exponent * CRT_exponent; /* 16/chanel なら 8 ビットにして取り出す */ if (bit_depth == 16) png_set_strip_16(png_ptr); /* ガンマ属性(通常持っていない) */ if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, display_exponent, gamma); /* すべての設定を登録 */ png_read_update_info(png_ptr, info_ptr); /* イメージを格納するメモリーを割り当てる為の情報を取得 */ image_rowbytes = png_get_rowbytes(png_ptr, info_ptr); image_channels = (int)png_get_channels(png_ptr, info_ptr); /* イメージを格納する為の、フラットな格納領域 */ /* (別途、行データの配列(height)を用意する必要があります) */ if ((image_data = (uchar *)malloc(image_rowbytes*image_height)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } /* 行(ポインタ)の配列領域を確保 */ if ((row_pointers = (png_bytepp)malloc(image_height*sizeof(png_bytep))) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(image_data); return NULL; } /* 行配列にフラット領域のそれぞれの位置を設定します */ for (i = 0; i < image_height; ++i) row_pointers[i] = image_data + i*image_rowbytes; /* これで、イメージを読み込み出来る */ png_read_image(png_ptr, row_pointers); /* 読込み後の後始末 */ free(row_pointers); png_read_end(png_ptr, NULL); // (3) image_data を DIB に変換 memset( &bi, 0, sizeof( BITMAPINFOHEADER )); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = image_width; bi.biHeight = image_height; bi.biPlanes = 1; /* 全て 24 bit カラーに直接変換する事も出来るが、*/ /* パレットは、なるべく同じ形式に変換する。 */ if( color_type == PNG_COLOR_TYPE_PALETTE ) { bi.biBitCount = bit_depth; bi.biClrUsed = num_palette; } else bi.biBitCount = 24; /* DIB の領域を確保する */ wimage_rowbytes = ((bi.biBitCount * bi.biWidth ) + 31) / 32 * 4; bi.biSizeImage = wimage_rowbytes * image_height; dib = malloc( bi.biSize+bi.biClrUsed*sizeof(RGBQUAD)+bi.biSizeImage ); if( dib == NULL ) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(image_data); return NULL; } memcpy( dib, &bi, sizeof( BITMAPINFOHEADER )); wimage_data = (char *)dib + sizeof( BITMAPINFOHEADER ); /* パレットがあれば、DIB にコピー */ if( bi.biClrUsed ) { dest = wimage_data; for( i = 0; i < num_palette; i++ ) { *dest++ = palette[ i ].blue; *dest++ = palette[ i ].green; *dest++ = palette[ i ].red; *dest++ = 0; } wimage_data += num_palette * sizeof( RGBQUAD ); } /* PNG のヘッダー情報は、もういらない */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); /* イメージデータのコピー(上下が WINDOWS では逆) */ for (row = image_height-1; row >= 0; row--, wrow++) { src = image_data + row * image_rowbytes; dest = wimage_data + wrow * wimage_rowbytes; if (image_channels == 1) { // パレット(またはグレースケール) for (i = (image_width * bit_depth + 7 ) / 8; i > 0; --i) *dest++ = *src++; } else if (image_channels == 3) { // RGB for (i = image_width; i > 0; --i) { *dest++ = src[2]; // b *dest++ = src[1]; // g *dest++ = src[0]; // r src += 3; } } else /* if (image_channels == 4) */ { // RGBA // アルファチャネル(透明度)を持つ場合 // bgcolor がこのプログラムでは、黒(0,0,0)固定なので、注意 for (i = image_width; i > 0; --i) { r = *src++; g = *src++; b = *src++; a = *src++; if (a == 255) { *dest++ = b; *dest++ = g; *dest++ = r; } else if (a == 0) { *dest++ = bg_blue; *dest++ = bg_green; *dest++ = bg_red; } else { alpha_composite(*dest++, b, a, bg_blue); alpha_composite(*dest++, g, a, bg_green); alpha_composite(*dest++, r, a, bg_red); } } } } free( image_data ); return( dib ); }