/**@file *@brief lpg形式を PNG形式に変換してファイル保存 *@date Sat,03 Jan,2009 - Sun,04 Jan,2009 *@author Copyright(C)2002-2009 G-HAL *@verbatim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. @endverbatim */ /*! example.c - an example of using libpng */ /*! This is an example of how to use libpng to read and write PNG files. * The file libpng.txt is much more verbose then this. If you have not * read it, do so first. This was designed to be a starting point of an * implementation. This is not officially part of libpng, is hereby placed * in the public domain, and therefore does not require a copyright notice. * * This file does not currently compile, because it is missing certain * parts, like allocating memory to hold an image. You will have to * supply these parts to get it to compile. For an example of a minimal * working PNG reader/writer, see pngtest.c, included in this distribution; * see also the programs in the contrib directory. */ #if defined(HAVE_CONFIG_H) && !defined(__CONFIG_H__) # include "config.h" #endif #include #include # if !defined(__STDC_LIMIT_MACROS) # define __STDC_LIMIT_MACROS # endif # include #include #include #include #include #include #include #include #include #include "png.h" /** 環境設定 */ struct { const int compression_level; /**< 出力PNG の圧縮レベル */ const double default_gamma; /**< デフォルトのガンマ値 */ const char* const default_author; /**< デフォルトの著者 */ const char* const default_desc; /**< デフォルトのDESC */ } settings = { 9, -2.2, "KOGADO", "KOGADO LPG file", }; /** ファイル形式 */ struct { const uintptr_t x; const uintptr_t x_size; const uintptr_t y; const uintptr_t y_size; const uintptr_t size; const uintptr_t size_size; const uintptr_t palette_top; const uintptr_t palette_one_size; } lpg_struct = { 0x10, 4, 0x14, 4, 0x18, 4, 0x1C, 4, }; /** Endian Convert *@param ptr 変換元データ *@param bpp ptr の有効バイト数 *@param lsb_first 変換元データは LSB(byte)が先 *@return 変換後データ */ static inline int64_t endian_conv( const uint8_t* const ptr, int bpp, bool lsb_first ) { int64_t ret; if (lsb_first) { switch (bpp) { case 8: ret = (static_cast(ptr[7]) << 56) + (static_cast(ptr[6]) << 48) + (static_cast(ptr[5]) << 40) + (static_cast(ptr[4]) << 32) + (static_cast(ptr[3]) << 24) + (static_cast(ptr[2]) << 16) + (static_cast(ptr[1]) << 8) + ptr[0]; break; case 7: ret = (static_cast(ptr[6]) << 48) + (static_cast(ptr[5]) << 40) + (static_cast(ptr[4]) << 32) + (static_cast(ptr[3]) << 24) + (static_cast(ptr[2]) << 16) + (static_cast(ptr[1]) << 8) + ptr[0]; break; case 6: ret = (static_cast(ptr[5]) << 40) + (static_cast(ptr[4]) << 32) + (static_cast(ptr[3]) << 24) + (static_cast(ptr[2]) << 16) + (static_cast(ptr[1]) << 8) + ptr[0]; break; case 5: ret = (static_cast(ptr[4]) << 32) + (static_cast(ptr[3]) << 24) + (static_cast(ptr[2]) << 16) + (static_cast(ptr[1]) << 8) + ptr[0]; break; case 4: ret = (static_cast(ptr[3]) << 24) + (static_cast(ptr[2]) << 16) + (static_cast(ptr[1]) << 8) + ptr[0]; break; case 3: ret = (static_cast(ptr[2]) << 16) + (static_cast(ptr[1]) << 8) + ptr[0]; break; case 2: ret = (static_cast(ptr[1]) << 8) + ptr[0]; break; case 1: ret = ptr[0]; break; default: break; } } else { switch (bpp) { case 8: ret = (static_cast(ntohl( *reinterpret_cast(ptr) )) << 32) + ntohl( *reinterpret_cast(ptr + 4) ); break; case 7: ret = (static_cast(ntohl( *reinterpret_cast(ptr) )) << 24) + (static_cast(ntohs( *reinterpret_cast(ptr + 4) )) << 8) + ptr[6]; break; case 6: ret = (static_cast(ntohl( *reinterpret_cast(ptr) )) << 16) + ntohs( *reinterpret_cast(ptr + 4) ); break; case 5: ret = (static_cast(ntohl( *reinterpret_cast(ptr) )) << 8) + ptr[4]; break; case 4: ret = ntohl( *reinterpret_cast(ptr) ); break; case 3: ret = (static_cast(ntohs( *reinterpret_cast(ptr) )) << 8) + ptr[2]; break; case 2: ret = ntohs( *reinterpret_cast(ptr) ); break; case 1: ret = ptr[0]; break; default: break; } } return ret; } /** write a png file *@param file_name 保存するファイル名 *@param img 入力画像 *@param img_size 入力画像のサイズ *@param timestamp 入力画像のタイムスタンプ *@param compression_level 圧縮レベル *@retval true 成功 *@retval false 失敗 */ bool conv_lpg2png( const char* const file_name, const uint8_t* const img, size_t img_size, time_t timestamp, int compression_level ) { if (img_size < lpg_struct.palette_top) { fprintf(stderr,"Illigal image size.\n"); return false; } const png_uint_32 img_width = static_cast( endian_conv( &(img[lpg_struct.x]), lpg_struct.x_size, true )); const png_uint_32 img_height = static_cast( endian_conv( &(img[lpg_struct.y]), lpg_struct.y_size, true )); const size_t realimg_size = static_cast( endian_conv( &(img[lpg_struct.size]), lpg_struct.size_size, true )); const size_t img_headersize = img_size - realimg_size; const png_uint_16 img_bpp = realimg_size / (img_width * img_height); const png_uint_16 img_bpl = img_width * img_bpp; const bool img_bo_lsbf = true; png_colorp palette = NULL; bool palette_mode = (1 == img_bpp); int bpp = palette_mode ? 1 : 4; png_uint_16 bit_depth = 8; png_bytep image = NULL; if (2 < img_bpp) { fprintf(stderr,"WARNING: BPP is %d, but not supported completely.\n", img_bpp ); } /* open the file */ FILE* const fp = fopen( file_name, "wb" ); if (NULL == fp) { perror("Error in fopen()."); return false; } /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if (NULL == png_ptr) { fclose( fp ); fprintf(stderr,"Error in png_create_write_struct().\n"); return false; } /* Allocate/initialize the image information data. REQUIRED */ png_infop info_ptr = png_create_info_struct(png_ptr); if (NULL == info_ptr) { fclose( fp ); png_destroy_write_struct( &png_ptr, static_cast(NULL) ); fprintf(stderr,"Error in png_create_info_struct().\n"); return false; } /* Set error handling. REQUIRED if you aren't supplying your own * error handling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png_ptr))) { /* If we get here, we had a problem reading the file */ fclose( fp ); png_destroy_write_struct( &png_ptr, &info_ptr ); fprintf(stderr,"Error in png_jmpbuf().\n"); return false; } /* set up the output control if you are using standard C streams */ png_init_io( png_ptr, fp ); png_set_compression_level( png_ptr, compression_level ); /* Set the image information here. Width and height are up to 2^31, * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED */ if (palette_mode) { png_set_IHDR( png_ptr, info_ptr, img_width, img_height, bit_depth, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE ); } else { png_set_IHDR( png_ptr, info_ptr, img_width, img_height, bit_depth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE ); } const int colors_org = (img_headersize - lpg_struct.palette_top) / lpg_struct.palette_one_size; const int colors = (colors_org < PNG_MAX_PALETTE_LENGTH) ? colors_org : PNG_MAX_PALETTE_LENGTH; { /* set the palette if there is one. REQUIRED for indexed-color images */ palette = static_cast(png_malloc(png_ptr, colors * sizeof(png_color))); if (palette) { const uint8_t* ptr = img + lpg_struct.palette_top; for (int i = 0; i < colors; ++i) { palette[i].blue = ptr[0]; palette[i].green = ptr[1]; palette[i].red = ptr[2]; ptr += lpg_struct.palette_one_size; } } } if (palette_mode) { png_set_PLTE( png_ptr, info_ptr, palette, colors ); /* You must not free palette here, because png_set_PLTE only makes a link to the palette that you malloced. Wait until you are about to destroy the png structure. */ } { #if 0 /* optional significant bit chunk */ /* if we are dealing with a grayscale image then */ sig_bit.gray = true_bit_depth; /* otherwise, if we are dealing with a color image then */ sig_bit.red = true_red_bit_depth; sig_bit.green = true_green_bit_depth; sig_bit.blue = true_blue_bit_depth; /* if the image has an alpha channel then */ sig_bit.alpha = true_alpha_bit_depth; png_set_sBIT(png_ptr, info_ptr, sig_bit); #endif } /* Optional gamma chunk is strongly suggested if you have any guess * as to the correct gamma of the image. */ if (0.0 <= settings.default_gamma) { png_set_gAMA( png_ptr, info_ptr, settings.default_gamma ); } /* Optionally write comments into the image */ { png_text text_ptr[3]; text_ptr[0].key = const_cast("Title"); text_ptr[0].text = const_cast(file_name); text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[1].key = const_cast("Author"); text_ptr[1].text = const_cast(settings.default_author); text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[2].key = const_cast("Description"); text_ptr[2].text = const_cast(settings.default_desc); text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt; #ifdef PNG_iTXt_SUPPORTED text_ptr[0].lang = NULL; text_ptr[1].lang = NULL; text_ptr[2].lang = NULL; #endif png_set_text( png_ptr, info_ptr, text_ptr, 3 ); } if (0 < timestamp) { png_time mod_time; png_convert_from_time_t( &mod_time, timestamp ); png_set_tIME( png_ptr, info_ptr, &mod_time ); } /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */ /* note that if sRGB is present the gAMA and cHRM chunks must be ignored * on read and must be written in accordance with the sRGB profile */ /* Write the file header information. REQUIRED */ png_write_info( png_ptr, info_ptr ); /* set up the transformations you want. Note that these are * all optional. Only call them if you want them. */ /* invert monochrome pixels */ // png_set_invert_mono(png_ptr); /* Shift the pixels up to a legal bit depth and fill in * as appropriate to correctly scale the image. */ // png_set_shift(png_ptr, &sig_bit); /* pack pixels into bytes */ if (bit_depth < 8) { png_set_packing( png_ptr ); } /* swap location of alpha bytes from ARGB to RGBA */ // png_set_swap_alpha(png_ptr); /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels). The second parameter is not used. */ // png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); /* flip BGR pixels to RGB */ // png_set_bgr(png_ptr); /* swap bytes of 16-bit files to most significant byte first */ // png_set_swap(png_ptr); /* swap bits of 1, 2, 4 bit packed pixel formats */ // png_set_packswap(png_ptr); { /* The easiest way to write the image (you may have a different memory * layout, however, so choose what fits your needs best). You need to * use the first method if you aren't handling interlacing yourself. */ png_bytep row_pointers[img_height]; const uint8_t* const s_ptr = img + img_headersize; image = static_cast(png_malloc(png_ptr, img_height * img_width * bpp * sizeof (png_byte))); if (image) { uint8_t* d_pix = reinterpret_cast(image); const uint8_t* s_ptr_line = s_ptr; const uint8_t* s_ptr_dot; if (palette_mode) { /* 256色モード */ for (png_uint_32 y = 0; y < img_height; ++y) { s_ptr_dot = s_ptr_line; row_pointers[img_height -1 - y] = reinterpret_cast(d_pix); for (png_uint_32 x = 0; x < img_width; ++x) { d_pix[0] = endian_conv( s_ptr_dot, img_bpp, img_bo_lsbf ); s_ptr_dot += img_bpp; d_pix += bpp; } s_ptr_line += img_bpl; } } else { /* lpg原画は 256色 + アルファ8bit モード * png形式では、多階調アルファを持てるのは24bitフルカラーモードのみなので、変換する。 */ for (png_uint_32 y = 0; y < img_height; ++y) { s_ptr_dot = s_ptr_line; row_pointers[img_height -1 - y] = reinterpret_cast(d_pix); for (png_uint_32 x = 0; x < img_width; ++x) { uint16_t data = endian_conv( s_ptr_dot, img_bpp, img_bo_lsbf ); uint8_t color = static_cast(0xFF & data); uint8_t palette_num = (color < colors) ? color : 0; d_pix[0] = palette[palette_num].red; d_pix[1] = palette[palette_num].green; d_pix[2] = palette[palette_num].blue; d_pix[3] = static_cast(data >> 8); s_ptr_dot += img_bpp; d_pix += bpp; } s_ptr_line += img_bpl; } } } else { for (png_uint_32 y = 0; y < img_height; ++y) { row_pointers[y] = NULL; } } png_write_rows( png_ptr, row_pointers, img_height ); } /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end( png_ptr, info_ptr ); /* If you png_malloced a palette, free it here (don't free info_ptr->palette, as recommended in versions 1.0.5m and earlier of this example; if libpng mallocs info_ptr->palette, libpng will free it). If you allocated it with malloc() instead of png_malloc(), use free() instead of png_free(). */ if (NULL != image) { png_free( png_ptr, image ); image = NULL; } if (NULL != palette) { png_free( png_ptr, palette ); palette = NULL; } /* clean up after the write, and free any memory allocated */ png_destroy_write_struct( &png_ptr, &info_ptr ); /* close the file */ fclose( fp ); /* that's it */ return true; } /** main() */ int main( int argc, char *argv[], __unused char *env[] ) { if (argc < 2) { fprintf(stderr,"No argment.\n"); return 1; } int in = open( argv[1], O_RDONLY ); if (in < 0) { fprintf(stderr,"Error in open().\n"); return 2; } struct stat st; if (stat(argv[1], &st) < 0) { fprintf(stderr,"Error in stat().\n"); return 3; } size_t in_size = st.st_size; uint8_t* const img = static_cast( malloc( in_size ) ); if (NULL == img) { fprintf(stderr,"Error in malloc().\n"); return 5; } ssize_t read_size = read( in, img, in_size ); if (read_size < 0) { fprintf(stderr,"Error in read().\n"); return 6; } close( in ); bool conv_ret = conv_lpg2png( argv[2], img, read_size, st.st_mtime, settings.compression_level ); if (!conv_ret) { fflush(stderr); fprintf(stderr,"Error in conv_lpg2png().\n"); return 7; } free( img ); return 0; } /* [ End of File ] */