You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libksquirrel/kernel/kls_png/fmt_codec_png.cpp

656 lines
16 KiB

/* This file is part of ksquirrel-libs (http://ksquirrel.sf.net)
Copyright (c) 2004 Dmitry Baryshev <ksquirrel@tut.by>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
as32 with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ksquirrel-libs-png/png.h"
#include "ksquirrel-libs/fmt_types.h"
#include "ksquirrel-libs/fileio.h"
#include "ksquirrel-libs/error.h"
#include "ksquirrel-libs/fmt_utils.h"
#include "fmt_codec_png_defs.h"
#include "fmt_codec_png.h"
#if defined CODEC_SVG || defined CODEC_DICOM
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdio>
#endif
#ifdef CODEC_SVG
#include "../xpm/codec_svg.xpm"
#elif defined CODEC_DICOM
#include "../xpm/codec_dicom.xpm"
#else
#include "../xpm/codec_png.xpm"
#endif
/*
*
* PNG (pronounced "ping") is a bitmap file format used to transmit and
* store bitmapped images. PNG supports the capability of storing up to
* 16 bits (gray-scale) or 48 bits (truecolor) per pixel, and up to 16 bits
* of alpha data. It handles the progressive display
* of image data and the storage of gamma,
* transparency and textual information, and it uses an efficient and
* lossless form of data compression.
*
*/
inline bool MALLOC_ROWS(png_bytep **A, const int RB, const int H)
{
*A = (png_bytep*)malloc(H * sizeof(png_bytep*));
if(!*A)
return false;
for(s32 row = 0; row < H; row++)
(*A)[row] = 0;
for(s32 row = 0; row < (s32)H; row++)
{
(*A)[row] = (png_bytep)malloc(RB);
if(!(*A)[row])
return false;
memset((*A)[row], 0, RB);
}
return true;
}
inline void FREE_ROWS(png_bytep **A, const int H)
{
if(*A)
{
for(s32 i = 0;i < H;i++)
{
if((*A)[i])
free((*A)[i]);
}
free(*A);
*A = 0;
}
}
fmt_codec::fmt_codec() : fmt_codec_base()
{}
fmt_codec::~fmt_codec()
{}
void fmt_codec::options(codec_options *o)
{
#ifdef CODEC_SVG
o->version = "0.1.2";
o->name = "Scalable Vector Graphics";
o->filter = "*.svg *.svgz ";
o->config = std::string(SVG_UI); // SVG_UI comes from Makefile.am
o->mime = "";
o->mimetype = "image/svg+xml";
o->pixmap = codec_svg;
o->readable = true;
o->canbemultiple = false;
o->writestatic = false;
o->writeanimated = false;
o->needtempfile = true;
#elif defined CODEC_DICOM
o->version = "1.1.3";
o->name = "DICOM";
o->filter = "*.dcm ";
o->config = "";
o->mime = "";
o->mimetype = "image/x-dicom";
o->pixmap = codec_dicom;
o->readable = true;
o->canbemultiple = false;
o->writestatic = false;
o->writeanimated = false;
o->needtempfile = true;
#else
o->version = "1.1.3";
o->name = "Portable Network Graphics";
o->filter = "*.png ";
o->config = "";
o->mime = "\x0089\x0050\x004E\x0047\x000D\x000A\x001A\x000A";
o->mimetype = "image/png";
o->pixmap = codec_png;
o->readable = true;
o->canbemultiple = false;
o->writestatic = true;
o->writeanimated = false;
o->needtempfile = false;
#endif
}
#ifdef CODEC_ANOTHER
void fmt_codec::fill_default_settings()
{
settings_value val;
// scale factor in percents
val.type = settings_value::v_int;
val.iVal = 1;
m_settings["scale"] = val;
}
#endif
s32 fmt_codec::read_init(const std::string &file)
{
png_ptr = 0;
info_ptr = 0;
fptr = 0;
frame = 0;
prev = 0;
cur = 0;
zerror = false;
#ifdef CODEC_SVG
int status;
fmt_settings::iterator it = m_settings.find("scale");
// percents / 100
int scale = (it == m_settings.end() || (*it).second.type != settings_value::v_int)
? 1 : (*it).second.iVal;
if(scale < 1 || scale > 10)
scale = 1;
char z[32];
snprintf(z, 32, "%d", scale);
pid_t pid = fork();
if(!pid)
{
execlp(SVG2PNG, SVG2PNG, "--binary", RSVG, "--input", file.c_str(), "--output", tmp.c_str(), "-z", z, (char *)0);
exit(1);
}
else if(pid == -1)
return SQE_R_BADFILE;
::waitpid(pid, &status, 0);
if(WIFEXITED(status))
if(WEXITSTATUS(status))
return SQE_R_BADFILE;
else;
else
return SQE_R_BADFILE;
fptr = fopen(tmp.c_str(), "rb");
#elif defined CODEC_DICOM
int status;
pid_t pid = fork();
if(!pid)
{
execlp(DICOM, DICOM, file.c_str(), tmp.c_str(), (char *)0);
exit(1);
}
else if(pid == -1)
return SQE_R_BADFILE;
::waitpid(pid, &status, 0);
if(WIFEXITED(status))
if(WEXITSTATUS(status))
return SQE_R_BADFILE;
else;
else
return SQE_R_BADFILE;
fptr = fopen(tmp.c_str(), "rb");
#else
fptr = fopen(file.c_str(), "rb");
#endif
if(!fptr)
return SQE_R_NOFILE;
currentImage = -1;
if((png_ptr = my_png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)) == NULL)
{
zerror = true;
return SQE_R_NOMEMORY;
}
if((info_ptr = my_png_create_info_struct(png_ptr)) == NULL)
{
zerror = true;
return SQE_R_NOMEMORY;
}
if(setjmp(png_jmpbuf(png_ptr)))
{
zerror = true;
return SQE_R_BADFILE;
}
my_png_init_io(png_ptr, fptr);
my_png_read_info(png_ptr, info_ptr);
my_png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, (int*)0, (int*)0);
img.w = next_frame_width = width;
img.h = next_frame_height = height;
img.bpp = bit_depth;
if(img.bpp == 16)
my_png_set_strip_16(png_ptr);
if(img.bpp < 8)
my_png_set_packing(png_ptr);
if(color_type == PNG_COLOR_TYPE_GRAY && img.bpp < 8)
my_png_set_gray_1_2_4_to_8(png_ptr);
if(color_type == PNG_COLOR_TYPE_PALETTE)
my_png_set_palette_to_rgb(png_ptr);
if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
my_png_set_gray_to_rgb(png_ptr);
if(my_png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
my_png_set_tRNS_to_alpha(png_ptr);
my_png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
number_passes = my_png_set_interlace_handling(png_ptr);
my_png_read_update_info(png_ptr, info_ptr);
finfo.animated = !!my_png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL);
frames = finfo.animated ? my_png_get_num_frames(png_ptr, info_ptr) : 1;
if(!frames) return SQE_R_BADFILE;
img.interlaced = number_passes > 1;
img.passes = finfo.animated ? 1 : number_passes;
if(finfo.animated)
{
if(!MALLOC_ROWS(&prev, width * sizeof(RGBA), height))
return SQE_R_NOMEMORY;
if(!MALLOC_ROWS(&cur, width * sizeof(RGBA), height))
return SQE_R_NOMEMORY;
}
std::string color_;
img.hasalpha = (color_type & PNG_COLOR_MASK_ALPHA);
switch((color_type & ~PNG_COLOR_MASK_ALPHA))
{
case PNG_COLOR_TYPE_RGB: color_ = "RGB"; break;
case PNG_COLOR_TYPE_PALETTE: color_ = "Color indexed"; break;
case PNG_COLOR_TYPE_GRAY: color_ = "Grayscale"; break;
default:
color_ = "Unknown";
}
if(img.hasalpha)
color_ += " with ALPHA";
img.compression = "Deflate method 8, 32K window";
img.colorspace = color_;
if(!finfo.animated) img.delay = 0;
#ifdef PNG_TEXT_SUPPORTED
png_textp lines = info_ptr->text;
if(!lines || !info_ptr->num_text)
return SQE_OK;
for(s32 i = 0;i < info_ptr->num_text;i++)
{
fmt_metaentry mt;
mt.group = lines[i].key;
mt.data = lines[i].text;
addmeta(mt);
}
#endif
return SQE_OK;
}
s32 fmt_codec::read_next()
{
currentImage++;
if(currentImage == frames)
return SQE_NOTOK;
if(setjmp(png_jmpbuf(png_ptr)))
{
zerror = true;
return SQE_R_BADFILE;
}
if(finfo.animated)
{
if(currentImage)
{
if(next_frame_dispose_op == PNG_DISPOSE_OP_BACKGROUND)
{
for(u32 j = next_frame_y_offset,i = 0;i < next_frame_height;j++,i++)
memset(cur[j]+next_frame_x_offset*sizeof(RGBA), 0, next_frame_width * sizeof(RGBA));
}
else if(next_frame_dispose_op == PNG_DISPOSE_OP_PREVIOUS)
{
for(u32 i = 0;i < height;i++)
memcpy(cur[i], prev[i], width*sizeof(RGBA));
}
else // next_frame_dispose_op == PNG_DISPOSE_OP_NONE
{
}
for(u32 i = 0;i < height;i++)
memcpy(prev[i], cur[i], width*sizeof(RGBA));
}
else if(my_png_get_first_frame_is_hidden(png_ptr, info_ptr))
{
if(!MALLOC_ROWS(&frame, width * sizeof(RGBA), height))
return SQE_R_NOMEMORY;
my_png_read_frame_head(png_ptr, info_ptr);
my_png_read_image(png_ptr, frame);
FREE_ROWS(&frame, height);
frames--;
if(frames == 1)
{
my_png_read_frame_head(png_ptr, info_ptr);
finfo.animated = false;
img.passes = number_passes;
finfo.image.push_back(img);
return SQE_OK;
}
else if(!frames)
return SQE_R_BADFILE; // oops?
}
FREE_ROWS(&frame, next_frame_height);
my_png_read_frame_head(png_ptr, info_ptr);
if(my_png_get_valid(png_ptr, info_ptr, PNG_INFO_fcTL))
{
my_png_get_next_frame_fcTL(png_ptr, info_ptr,
&next_frame_width, &next_frame_height,
&next_frame_x_offset, &next_frame_y_offset,
&next_frame_delay_num, &next_frame_delay_den,
&next_frame_dispose_op, &next_frame_blend_op);
}
else
{
next_frame_width = width;
next_frame_height = height;
next_frame_x_offset = 0;
next_frame_y_offset = 0;
next_frame_dispose_op = PNG_DISPOSE_OP_BACKGROUND;
next_frame_blend_op = PNG_BLEND_OP_SOURCE;
}
if(!next_frame_delay_den) next_frame_delay_den = 100;
img.delay = (s32)(((double)next_frame_delay_num / next_frame_delay_den) * 1000);
if(next_frame_width + next_frame_x_offset > width || next_frame_height + next_frame_y_offset > height)
return SQE_R_BADFILE;
if(!MALLOC_ROWS(&frame, next_frame_width * sizeof(RGBA), next_frame_height))
return SQE_R_NOMEMORY;
my_png_read_image(png_ptr, frame);
// copy all pixel values including alpha
if(!currentImage || next_frame_blend_op == PNG_BLEND_OP_SOURCE)
{
for(u32 j = next_frame_y_offset,i = 0;i < next_frame_height;j++,i++)
memcpy(cur[j]+next_frame_x_offset*sizeof(RGBA), frame[i], next_frame_width * sizeof(RGBA));
}
else // over
{
RGBA *src, *dst;
for(u32 j = next_frame_y_offset,i = 0;i < next_frame_height;j++,i++)
{
src = (RGBA *)frame[i];
dst = (RGBA *)(cur[j]+next_frame_x_offset*sizeof(RGBA));
u32 k = next_frame_width;
while(k--)
{
// fully transparent foreground
if(src->a == 0)
;
else if(src->a == 255 || dst->a == 0)
*dst = *src;
else // composite
{
dst->r = ((src->a * (src->r - dst->r))>>8) + dst->r;
dst->g = ((src->a * (src->g - dst->g))>>8) + dst->g;
dst->b = ((src->a * (src->b - dst->b))>>8) + dst->b;
//dst->a = ((src->a * (src->a - dst->a))>>8) + dst->a;
}
src++;
dst++;
}
}
}
}
finfo.image.push_back(img);
return SQE_OK;
}
s32 fmt_codec::read_next_pass()
{
line = -1;
return SQE_OK;
}
s32 fmt_codec::read_scanline(RGBA *scan)
{
fmt_image *im = image(currentImage);
line++;
if(zerror || setjmp(png_jmpbuf(png_ptr)))
{
zerror = true;
return SQE_R_BADFILE;
}
if(finfo.animated)
memcpy(scan, cur[line], im->w * sizeof(RGBA));
else
my_png_read_row(png_ptr, (png_bytep)scan, png_bytep_NULL);
return SQE_OK;
}
void fmt_codec::read_close()
{
if(png_ptr) my_png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
if(fptr) fclose(fptr);
FREE_ROWS(&frame, next_frame_height);
FREE_ROWS(&prev, height);
FREE_ROWS(&cur, height);
finfo.meta.clear();
finfo.image.clear();
}
#ifdef CODEC_PNG
void fmt_codec::getwriteoptions(fmt_writeoptionsabs *opt)
{
opt->interlaced = true;
opt->compression_scheme = CompressionInternal;
opt->compression_min = 1;
opt->compression_max = 9;
opt->compression_def = 7;
opt->passes = 8;
opt->needflip = false;
opt->palette_flags = 0 | fmt_image::pure32;
}
s32 fmt_codec::write_init(const std::string &file, const fmt_image &image, const fmt_writeoptions &opt)
{
m_png_ptr = 0;
m_info_ptr = 0;
m_fptr = 0;
m_zerror = false;
if(!image.w || !image.h || file.empty())
return SQE_W_WRONGPARAMS;
writeimage = image;
writeopt = opt;
m_fptr = fopen(file.c_str(), "wb");
if(!m_fptr)
return SQE_W_NOFILE;
m_png_ptr = my_png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if(!m_png_ptr)
{
m_zerror = true;
return SQE_W_NOMEMORY;
}
m_info_ptr = my_png_create_info_struct(m_png_ptr);
if(!m_info_ptr)
{
m_zerror = true;
return SQE_W_NOMEMORY;
}
if(setjmp(png_jmpbuf(m_png_ptr)))
{
m_zerror = true;
return SQE_W_ERROR;
}
my_png_init_io(m_png_ptr, m_fptr);
my_png_set_IHDR(m_png_ptr, m_info_ptr, writeimage.w, writeimage.h, 8, PNG_COLOR_TYPE_RGB_ALPHA,
((writeopt.interlaced) ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE),
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_color_8 sig_bit;
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
sig_bit.alpha = 8;
my_png_set_sBIT(m_png_ptr, m_info_ptr, &sig_bit);
s32 factor = (writeopt.compression_level < 1 || writeopt.compression_level > 9) ? 1 : writeopt.compression_level;
my_png_set_compression_level(m_png_ptr, factor);
my_png_write_info(m_png_ptr, m_info_ptr);
my_png_set_shift(m_png_ptr, &sig_bit);
return SQE_OK;
}
s32 fmt_codec::write_next()
{
my_png_set_swap(m_png_ptr);
my_png_set_packswap(m_png_ptr);
my_png_set_interlace_handling(m_png_ptr);
return SQE_OK;
}
s32 fmt_codec::write_next_pass()
{
return SQE_OK;
}
s32 fmt_codec::write_scanline(RGBA *scan)
{
if(m_zerror || setjmp(png_jmpbuf(m_png_ptr)))
{
m_zerror = true;
return SQE_W_ERROR;
}
m_row_pointer = (png_bytep)scan;
my_png_write_rows(m_png_ptr, &m_row_pointer, 1);
return SQE_OK;
}
void fmt_codec::write_close()
{
if(m_png_ptr && !m_zerror) my_png_write_end(m_png_ptr, m_info_ptr);
if(m_png_ptr) my_png_destroy_write_struct(&m_png_ptr, &m_info_ptr);
if(m_fptr) fclose(m_fptr);
}
std::string fmt_codec::extension(const s32 /*bpp*/)
{
return std::string("png");
}
#endif
#include "fmt_codec_cd_func.h"