Skip to content

Instantly share code, notes, and snippets.

@a-kaneda
Last active December 23, 2015 04:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-kaneda/6577750 to your computer and use it in GitHub Desktop.
Save a-kaneda/6577750 to your computer and use it in GitHub Desktop.
PNGファイルに等間隔で隙間を入れます。隙間には隣接するドットをコピーします。ビット深度4bitのファイルのみ対応しています。libpngを使用しています。
#include <stdio.h>
#include <stdlib.h>
#include "png.h"
/*!
@brief PNG隙間作成処理
PNGファイルのタイルごと間に隙間を挿入する。ビット深度4bitのファイルのみに対応する。
オプションは以下のとおり、
- 1個目:タイルのサイズ
- 2個目:入力画像ファイル
- 3個目:出力画像ファイル
@param argc コマンドラインオプションの数
@param argv コマンドラインオプション
@return 0:正常終了、非0:エラー終了
*/
int main(int argc, char **argv)
{
int ret = 0; // 戻り値
int tilesize = 0; // タイルのサイズ
char *inputfile = NULL; // 入力ファイル名
FILE *inputfp = NULL; // 入力ファイルポインタ
png_byte header[8] = ""; // シグネチャ比較用バッファ
png_structp inputpngstr = NULL; // 入力png_struct構造体ポインタ
png_infop inputinfo = NULL; // 入力png_info構造体ポインタ
png_byte **inputdata = NULL; // 入力ファイルデータ
png_byte buf = 0; // データコピー時の作業用バッファ
char *outputfile = NULL; // 出力ファイル名
FILE *outputfp = NULL; // 出力ファイルポインタ
png_structp outputpngstr = NULL; // 出力png_struct構造体ポインタ
png_infop outputinfo = NULL; // 出力png_info構造体ポインタ
png_byte **outputdata = NULL; // 出力ファイルデータ
unsigned int width = 0; // 入力画像の幅
unsigned int height = 0; // 入力画像の高さ
int bit_depth = 0; // 入力画像のビット深度
int color_type = 0; // カラー/アルファチャンネルの設定
int interlace_type = 0; // インターレースタイプ
int compression_type = 0; // 圧縮方式
int filter_method = 0; // フィルター形式
int num_palette = 0; // パレット数
png_color *inputpalette = NULL; // 入力画像のパレット
png_color *outputpalette = NULL; // 出力画像のパレット
png_color_16p inputbackground = NULL; // 入力画像の背景色
int enablebackground = 0; // 背景色取得に成功したかどうか
png_color_16 outputbackground; // 出力画像の背景色
png_bytep inputtrans = NULL; // 入力画像の透過パレットエントリ
int num_trans = 0; // 透過エントリの数
png_color_16p inputtransvalues = NULL; // 入力画像の非パレット画像用の単一透過色のカラーサンプル値かグレーレベル
png_bytep outputtrans = NULL; // 出力画像の透過パレットエントリ
png_color_16 outputtransvalues; // 出力画像の非パレット画像用の単一透過色のカラーサンプル値かグレーレベル
int x = 0; // x座標ループ用
int y = 0; // y座標ループ用
int xoffset = 0; // x方向の隙間を考慮したオフセット値
int yoffset = 0; // y方向の隙間を考慮したオフセット値
// オプション数が正しくない場合は使い方を表示して終了する
// 1個目:タイルサイズ
// 2個目:入力ファイル名
// 3個目:出力ファイル名
if (argc != 4) {
fprintf(stderr, "Usage : pngspace tilesize input output\n");
ret = -1;
goto PROC_END;
}
// タイルサイズを取得する
tilesize = atoi(argv[1]);
if (tilesize <= 0) {
fprintf(stderr, "Parameter error : tilesize=%d\n", tilesize);
ret = -2;
goto PROC_END;
}
// 入力ファイル名を取得する
inputfile = argv[2];
// 入力ファイルを開く
inputfp = fopen(inputfile, "rb");
if (inputfp == NULL) {
fprintf(stderr, "File open error : %s\n", inputfile);
ret = -3;
goto PROC_END;
}
// ヘッダ部分を取得する
fread(header, 1, sizeof(header), inputfp);
// シグネチャをチェックする
if (png_sig_cmp(header, 0, sizeof(header)) != 0) {
fprintf(stderr, "Input file is not PNG file.\n");
ret = -4;
goto PROC_END;
}
// 入力用png_struct構造体のメモリ確保と初期化を行う
inputpngstr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (inputpngstr == NULL) {
fprintf(stderr, "Memory error png_create_read_struct().\n");
ret = -5;
goto PROC_END;
}
// 入力用info_ptr構造体のメモリ確保と初期化を行う
inputinfo = png_create_info_struct(inputpngstr);
if (inputinfo == NULL) {
fprintf(stderr, "Memory error png_create_info_struct().\n");
ret = -6;
goto PROC_END;
}
// libpng内部でのエラー発生時にlongjmp()で戻れるようにsetjmp()をしておく
if (setjmp(png_jmpbuf(inputpngstr)) == 0) {
// libpngに入力ファイルポインタを設定する
png_init_io(inputpngstr, inputfp);
// 事前に読み込んだシグネチャのサイズをlibpngに知らせる
png_set_sig_bytes(inputpngstr, sizeof(header));
// pngファイルを読み込む
png_read_png(inputpngstr, inputinfo, PNG_TRANSFORM_IDENTITY, NULL);
// 画像データのポインタを取得する
inputdata = png_get_rows(inputpngstr, inputinfo);
// 入力画像の画像ヘッダ・チャンクを取得する
png_get_IHDR(inputpngstr, inputinfo, &width, &height, &bit_depth,
&color_type, &interlace_type, &compression_type, &filter_method);
// ビット深度が4bit以外の場合はサポート対象外とする
if (bit_depth != 4) {
fprintf(stderr, "Support only 4 bit depth : bit_depth=%d\n", bit_depth);
ret = -7;
goto PROC_END;
}
// 入力画像のパレット・チャンクを取得する
png_get_PLTE(inputpngstr, inputinfo, &inputpalette, &num_palette);
// 出力画像パレットのメモリを確保する
outputpalette = (png_color*)malloc(sizeof(png_color) * num_palette);
// 入力画像のパレットをコピーする
memcpy(outputpalette, inputpalette, sizeof(png_color) * num_palette);
// 入力画像の背景色・チャンクを取得する
if (png_get_bKGD(inputpngstr, inputinfo, &inputbackground)) {
// 背景色取得成功を記憶する
enablebackground = 1;
// 入力画像の背景色をコピーする
memcpy(&outputbackground, inputbackground, sizeof(outputbackground));
}
else {
// 背景色取得失敗を記憶する
enablebackground = 0;
// 出力画像の背景色を初期化する
memset(&outputbackground, 0x00, sizeof(outputbackground));
}
// 入力画像の透明度・チャンクを取得する
png_get_tRNS(inputpngstr, inputinfo, &inputtrans, &num_trans, &inputtransvalues);
// 出力画像の透過パレットのメモリを確保する
outputtrans = (png_bytep)malloc(sizeof(png_byte) * num_trans);
// 入力画像の透過パレットをコピーする
memcpy(outputtrans, inputtrans, sizeof(png_byte) * num_trans);
// 出力画像の単一透過色をコピーする
memcpy(&outputtransvalues, inputtransvalues, sizeof(outputtransvalues));
// 出力データ用のメモリを確保する
outputdata = (png_byte**)malloc(sizeof(png_byte*) * (height + (height / tilesize) * 2));
for (y = 0; y < (height + (height / tilesize) * 2); y++) {
outputdata[y] = (png_byte*)malloc(sizeof(png_byte) * (width + (width / tilesize) * 2) / 2);
}
// yが0のときの隙間を無効化するために-1からオフセット値はスタートする
yoffset = -1;
// データを出力データに拡大コピーする
for (y = 0; y < height; y++) {
// 1タイル分処理した場合は間に隙間を入れる
if (y % tilesize == 0) {
yoffset += 2;
}
// xが0のときの隙間を無効化するために-1からオフセット値はスタートする
xoffset = -1;
for (x = 0; x < width; x++) {
// 1タイル分処理した場合は間に隙間を入れる
if (x % tilesize == 0) {
xoffset += 2;
}
// 上位4bitの場合
if (x % 2 == 0) {
buf = inputdata[y][x / 2] >> 4;
}
// 下位4bitの場合
else {
buf = inputdata[y][x / 2] & 0x0F;
}
// 上位4bitの場合
if ((x + xoffset) % 2 == 0) {
outputdata[y + yoffset][(x + xoffset) / 2] |= buf << 4;
}
// 下位4bitの場合
else {
outputdata[y + yoffset][(x + xoffset) / 2] |= buf;
}
// タイル上端の場合、一つ上のドットにもコピーする
if (y % tilesize == 0) {
// 上位4bitの場合
if ((x + xoffset) % 2 == 0) {
outputdata[y + yoffset - 1][(x + xoffset) / 2] |= buf << 4;
}
// 下位4bitの場合
else {
outputdata[y + yoffset - 1][(x + xoffset) / 2] |= buf;
}
}
// タイル下端の場合、一つ下のドットにもコピーする
if (y % tilesize == tilesize - 1) {
// 上位4bitの場合
if ((x + xoffset) % 2 == 0) {
outputdata[y + yoffset + 1][(x + xoffset) / 2] |= buf << 4;
}
// 下位4bitの場合
else {
outputdata[y + yoffset + 1][(x + xoffset) / 2] |= buf;
}
}
// タイル左端の場合、一つ左のドットにもコピーする
if (x % tilesize == 0) {
// 上位4bitの場合
if ((x + xoffset - 1) % 2 == 0) {
outputdata[y + yoffset][(x + xoffset - 1) / 2] |= buf << 4;
}
// 下位4bitの場合
else {
outputdata[y + yoffset][(x + xoffset - 1) / 2] |= buf;
}
}
// タイル右端の場合、一つ右のドットにもコピーする
if (x % tilesize == tilesize - 1) {
// 上位4bitの場合
if ((x + xoffset + 1) % 2 == 0) {
outputdata[y + yoffset][(x + xoffset + 1) / 2] |= buf << 4;
}
// 下位4bitの場合
else {
outputdata[y + yoffset][(x + xoffset + 1) / 2] |= buf;
}
}
}
}
}
else {
fprintf(stderr, "libpng error.\n");
ret = -8;
goto PROC_END;
}
// 入力用のpng_struct構造体、info_ptr構造体のメモリを解放する
if (inputpngstr != NULL || inputinfo != NULL) {
png_destroy_read_struct(&inputpngstr, &inputinfo, (png_infopp)NULL);
inputpngstr = NULL;
inputinfo = NULL;
}
// 出力ファイル名を取得する
outputfile = argv[3];
// 出力ファイルを開く
outputfp = fopen(outputfile, "wb");
if (outputfp == NULL) {
fprintf(stderr, "File open error : %s\n", outputfile);
ret = -9;
goto PROC_END;
}
// 出力用png_struct構造体のメモリ確保と初期化を行う
outputpngstr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (outputpngstr == NULL) {
fprintf(stderr, "Memory error at png_create_write_struct().\n");
ret = -10;
goto PROC_END;
}
// 出力用info_ptr構造体のメモリ確保と初期化を行う
outputinfo = png_create_info_struct(outputpngstr);
if (outputinfo == NULL) {
fprintf(stderr, "Memory error at png_create_info_struct().\n");
ret = -11;
goto PROC_END;
}
// libpng内部でのエラー発生時にlongjmp()で戻れるようにsetjmp()をしておく
if (setjmp(png_jmpbuf(outputpngstr)) == 0) {
// libpngに出力ファイルポインタを設定する
png_init_io(outputpngstr, outputfp);
// 出力画像の画像ヘッダ・チャンクをセットする
png_set_IHDR(outputpngstr, outputinfo, (width + (width / tilesize) * 2), (height + (height / tilesize) * 2), bit_depth,
color_type, interlace_type, compression_type, filter_method);
// 出力画像のパレット・チャンクをセットする
png_set_PLTE(outputpngstr, outputinfo, outputpalette, num_palette);
// 出力画像の背景色・チャンクをセットする
if (enablebackground) {
png_set_background(outputpngstr, &outputbackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
}
else {
png_set_background(outputpngstr, &outputbackground, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
}
// 出力画像の透明度・チャンクを取得する
png_set_tRNS(outputpngstr, outputinfo, outputtrans, num_trans, &outputtransvalues);
// 出力データを出力pngにセットする
png_set_rows(outputpngstr, outputinfo, outputdata);
// pngファイルを書き出す
png_write_png(outputpngstr, outputinfo, PNG_TRANSFORM_IDENTITY, NULL);
}
else {
fprintf(stderr, "libpng error.\n");
ret = -12;
goto PROC_END;
}
PROC_END:
// 出力用透過パレットのメモリを解放する
if (outputtrans != NULL) {
free(outputtrans);
}
// 出力用パレットのメモリを解放する
if (outputpalette != NULL) {
free(outputpalette);
}
// 出力用データのメモリを解放する
if (outputdata != NULL) {
for (y = 0; y < height; y++) {
free(outputdata[y]);
}
free(outputdata);
}
// 入力用のpng_struct構造体、info_ptr構造体のメモリを解放する
if (inputpngstr != NULL || inputinfo != NULL) {
png_destroy_read_struct(&inputpngstr, &inputinfo, (png_infopp)NULL);
}
// 出力用のpng_struct構造体、info_ptr構造体のメモリを解放する
if (outputpngstr != NULL || outputinfo != NULL) {
png_destroy_write_struct(&outputpngstr, &outputinfo);
}
// 入力ファイルを閉じる
if (inputfp != NULL) {
fclose(inputfp);
}
// 出力ファイルを閉じる
if (outputfp != NULL) {
fclose(outputfp);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment