/*$
Copyright (C) 2013-2020 Azel.
This file is part of AzPainter.
AzPainter is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
AzPainter is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
$*/
/*****************************************
* PNG 保存
*****************************************/
#include
#include
#include
#include "mDef.h"
#include "mSaveImage.h"
//--------------------
typedef struct
{
mSaveImage *param;
png_struct *png;
png_info *pnginfo;
FILE *fp;
uint8_t *rowbuf,
*palbuf;
mBool start_image;
}SAVEPNG;
//--------------------
//=======================
// sub
//=======================
/** PNG エラー関数 */
static void _png_error_func(png_struct *png,png_const_charp mes)
{
mSaveImage_setMessage((mSaveImage *)png_get_error_ptr(png), mes);
longjmp(png_jmpbuf(png), 1);
}
/** PNG 警告関数 */
static void _png_warn_func(png_struct *png,png_const_charp mes)
{
}
//===========================
// 書き込みセット
//===========================
/** ヘッダ */
static void _set_header(SAVEPNG *p,mSaveImage *param)
{
int type,bits;
bits = param->sample_bits;
//カラータイプ
switch(param->coltype)
{
case MSAVEIMAGE_COLTYPE_PALETTE:
type = PNG_COLOR_TYPE_PALETTE;
bits = param->bits;
break;
case MSAVEIMAGE_COLTYPE_RGBA:
type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case MSAVEIMAGE_COLTYPE_GRAY:
type = PNG_COLOR_TYPE_GRAY;
break;
case MSAVEIMAGE_COLTYPE_GRAY_ALPHA:
type = PNG_COLOR_TYPE_GRAY_ALPHA;
break;
default:
type = PNG_COLOR_TYPE_RGB;
break;
}
png_set_IHDR(p->png, p->pnginfo,
param->width, param->height, bits, type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
/** パレット */
static int _set_palette(SAVEPNG *p)
{
p->palbuf = mSaveImage_convertPalette_RGB(p->param);
if(!p->palbuf) return MSAVEIMAGE_ERR_ALLOC;
png_set_PLTE(p->png, p->pnginfo, (png_colorp)p->palbuf, p->param->palette_num);
return 0;
}
//===========================
// main
//===========================
/** 初期化 */
static int _init(SAVEPNG *p)
{
png_struct *png;
png_info *pnginfo;
//png_struct
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
p->param, _png_error_func, _png_warn_func);
if(!png) return MSAVEIMAGE_ERR_ALLOC;
p->png = png;
//png_info
pnginfo = png_create_info_struct(png);
if(!pnginfo) return MSAVEIMAGE_ERR_ALLOC;
p->pnginfo = pnginfo;
//エラー時
if(setjmp(png_jmpbuf(png)))
return -1;
//出力
p->fp = mSaveImage_openFILE(&p->param->output);
if(!p->fp) return MSAVEIMAGE_ERR_OPENFILE;
png_init_io(png, p->fp);
return 0;
}
/** イメージ前処理 */
static int _before_image(SAVEPNG *p)
{
int ret,h,v;
//IHDR
_set_header(p, p->param);
//パレット
if(p->param->coltype == MSAVEIMAGE_COLTYPE_PALETTE)
{
if((ret = _set_palette(p)))
return ret;
}
//解像度 (dpi or dpm)
if(mSaveImage_getResolution_dpm(p->param, &h, &v))
png_set_pHYs(p->png, p->pnginfo, h, v, PNG_RESOLUTION_METER);
//他設定
if(p->param->set_option)
{
if((ret = (p->param->set_option)(p->param)))
return ret;
}
//書き込み
png_write_info(p->png, p->pnginfo);
return 0;
}
/** メイン処理 */
static int _main_proc(SAVEPNG *p)
{
mSaveImage *param = p->param;
int ret,i,pitch;
//初期化
if((ret = _init(p)))
return ret;
//イメージ前の処理
if((ret = _before_image(p)))
return ret;
//Y1行バッファ確保
pitch = png_get_rowbytes(p->png, p->pnginfo);
p->rowbuf = (uint8_t *)mMalloc(pitch, FALSE);
if(!p->rowbuf) return MSAVEIMAGE_ERR_ALLOC;
//イメージ書き込み
p->start_image = TRUE;
for(i = param->height; i > 0; i--)
{
//取得
ret = (param->send_row)(param, p->rowbuf, pitch);
if(ret) return ret;
//書き込み
png_write_row(p->png, (png_bytep)p->rowbuf);
}
return 0;
}
/** 解放 */
static void _png_free(SAVEPNG *p)
{
if(p->png)
{
//イメージ書き込み終了
if(p->start_image)
png_write_end(p->png, NULL);
//png_struct,png_info 削除
png_destroy_write_struct(&p->png, (p->pnginfo)? &p->pnginfo: NULL);
}
if(p->fp)
{
if(p->param->output.type == MSAVEIMAGE_OUTPUT_TYPE_PATH)
fclose(p->fp);
else
fflush(p->fp);
}
mFree(p->rowbuf);
mFree(p->palbuf);
mFree(p);
}
//===================
/**
@addtogroup saveimage
@{
*/
/** PNG ファイルに保存 */
mBool mSaveImagePNG(mSaveImage *param)
{
SAVEPNG *p;
int ret;
//確保
p = (SAVEPNG *)mMalloc(sizeof(SAVEPNG), TRUE);
if(!p) return FALSE;
p->param = param;
param->internal = p;
//処理
ret = _main_proc(p);
if(ret)
{
if(ret > 0)
mSaveImage_setMessage_errno(param, ret);
}
//解放
_png_free(p);
return (ret == 0);
}
/** PNG 圧縮レベル設定 */
void mSaveImagePNG_setCompressionLevel(mSaveImage *p,int level)
{
png_set_compression_level(((SAVEPNG *)p->internal)->png, level);
}
/** PNG 透過色セット
*
* パレットカラー、グレイスケール、RGB [8bit] 用 */
void mSaveImagePNG_setTransparent_8bit(mSaveImage *p,uint32_t col)
{
SAVEPNG *pp = (SAVEPNG *)p->internal;
uint8_t r,g,b,*trbuf,*ps;
png_color_16 pngcol;
int i,pnum;
r = M_GET_R(col);
g = M_GET_G(col);
b = M_GET_B(col);
switch(pp->param->coltype)
{
//RGB
case MSAVEIMAGE_COLTYPE_RGB:
pngcol.red = r;
pngcol.green = g;
pngcol.blue = b;
png_set_tRNS(pp->png, pp->pnginfo, NULL, 0, &pngcol);
break;
//グレイスケール
case MSAVEIMAGE_COLTYPE_GRAY:
pngcol.gray = r;
png_set_tRNS(pp->png, pp->pnginfo, NULL, 0, &pngcol);
break;
//パレット
case MSAVEIMAGE_COLTYPE_PALETTE:
pnum = pp->param->palette_num;
trbuf = (uint8_t *)mMalloc(pnum, FALSE);
if(!trbuf) return;
memset(trbuf, 255, pnum);
ps = pp->param->palette_buf;
for(i = 0; i < pnum && !(ps[0] == r && ps[1] == g && ps[2] == b); i++, ps += 4);
if(i < pnum)
{
trbuf[i] = 0;
png_set_tRNS(pp->png, pp->pnginfo, trbuf, i + 1, 0);
}
mFree(trbuf);
break;
}
}
/* @} */