#include "PNGTranslator.h"
#include <stdio.h>
#include <string.h>
#include <Catalog.h>
#include <OS.h>
#define PNG_NO_PEDANTIC_WARNINGS
#include <png.h>
#include "PNGView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PNGTranslator"
static const translation_format sInputFormats[] = {
{
B_PNG_FORMAT,
B_TRANSLATOR_BITMAP,
PNG_IN_QUALITY,
PNG_IN_CAPABILITY,
"image/png",
"PNG image"
},
{
B_PNG_FORMAT,
B_TRANSLATOR_BITMAP,
PNG_IN_QUALITY,
PNG_IN_CAPABILITY,
"image/x-png",
"PNG image"
},
{
B_TRANSLATOR_BITMAP,
B_TRANSLATOR_BITMAP,
BBT_IN_QUALITY,
BBT_IN_CAPABILITY,
"image/x-be-bitmap",
"Be Bitmap Format (PNGTranslator)"
}
};
static const translation_format sOutputFormats[] = {
{
B_PNG_FORMAT,
B_TRANSLATOR_BITMAP,
PNG_OUT_QUALITY,
PNG_OUT_CAPABILITY,
"image/png",
"PNG image"
},
{
B_TRANSLATOR_BITMAP,
B_TRANSLATOR_BITMAP,
BBT_OUT_QUALITY,
BBT_OUT_CAPABILITY,
"image/x-be-bitmap",
"Be Bitmap Format (PNGTranslator)"
}
};
static const TranSetting sDefaultSettings[] = {
{B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
{B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false},
{PNG_SETTING_INTERLACE, TRAN_SETTING_INT32, PNG_INTERLACE_NONE}
};
const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
BTranslator *
make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
if (!n)
return new(std::nothrow) PNGTranslator();
else
return NULL;
}
#ifndef png_jmpbuf
# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
#endif
BPositionIO *
get_pio(png_structp ppng)
{
BPositionIO *pio = NULL;
pio = static_cast<BPositionIO *>(png_get_io_ptr(ppng));
return pio;
}
void
pngcb_read_data(png_structp ppng, png_bytep pdata, png_size_t length)
{
BPositionIO *pio = get_pio(ppng);
pio->Read(pdata, static_cast<size_t>(length));
}
void
pngcb_write_data(png_structp ppng, png_bytep pdata, png_size_t length)
{
BPositionIO *pio = get_pio(ppng);
pio->Write(pdata, static_cast<size_t>(length));
}
void
pngcb_flush_data(png_structp ppng)
{
}
PNGTranslator::PNGTranslator()
: BaseTranslator(B_TRANSLATE("PNG images"),
B_TRANSLATE("PNG image translator"),
PNG_TRANSLATOR_VERSION,
sInputFormats, kNumInputFormats,
sOutputFormats, kNumOutputFormats,
"PNGTranslator_Settings",
sDefaultSettings, kNumDefaultSettings,
B_TRANSLATOR_BITMAP, B_PNG_FORMAT)
{
}
PNGTranslator::~PNGTranslator()
{
}
status_t
identify_png_header(BPositionIO *inSource, translator_info *outInfo)
{
const int32 kSigSize = 8;
uint8 buf[kSigSize];
if (inSource->Read(buf, kSigSize) != kSigSize)
return B_NO_TRANSLATOR;
if (png_sig_cmp(buf, 0, kSigSize))
return B_NO_TRANSLATOR;
if (outInfo) {
outInfo->type = B_PNG_FORMAT;
outInfo->group = B_TRANSLATOR_BITMAP;
outInfo->quality = PNG_IN_QUALITY;
outInfo->capability = PNG_IN_CAPABILITY;
strcpy(outInfo->MIME, "image/png");
strlcpy(outInfo->name, B_TRANSLATE("PNG image"),
sizeof(outInfo->name));
}
return B_OK;
}
status_t
PNGTranslator::DerivedIdentify(BPositionIO *inSource,
const translation_format *inFormat, BMessage *ioExtension,
translator_info *outInfo, uint32 outType)
{
return identify_png_header(inSource, outInfo);
}
void throw_error(png_structp ppng, png_const_charp error_msg)
{
throw std::exception();
}
void alert_warning(png_structp ppng, png_const_charp error_msg)
{
}
status_t
PNGTranslator::translate_from_png_to_bits(BPositionIO *inSource,
BPositionIO *outDestination)
{
if (identify_png_header(inSource, NULL) != B_OK)
return B_NO_TRANSLATOR;
status_t result = B_ERROR;
bool bheaderonly = false, bdataonly = false;
uint8 **prows = NULL, *prow = NULL;
png_uint_32 nalloc = 0;
png_structp ppng = NULL;
png_infop pinfo = NULL;
while (ppng == NULL) {
ppng = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!ppng)
break;
pinfo = png_create_info_struct(ppng);
if (!pinfo)
break;
png_set_error_fn(ppng, png_get_error_ptr(ppng), throw_error,
alert_warning);
try {
png_set_read_fn(ppng, static_cast<void *>(inSource), pngcb_read_data);
png_set_sig_bytes(ppng, 8);
png_read_info(ppng, pinfo);
png_uint_32 width, height;
int bit_depth, color_type, interlace_type;
png_get_IHDR(ppng, pinfo, &width, &height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
bool balpha = false;
if (bit_depth == 16)
png_set_strip_16(ppng);
else if (bit_depth < 8)
png_set_packing(ppng);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(ppng);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(ppng);
}
if (png_get_valid(ppng, pinfo, PNG_INFO_tRNS)) {
balpha = true;
png_set_tRNS_to_alpha(ppng);
}
if (color_type & PNG_COLOR_MASK_COLOR)
png_set_bgr(ppng);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(ppng);
}
if (color_type & PNG_COLOR_MASK_ALPHA) {
balpha = true;
}
if (!balpha) {
png_set_filler(ppng, 0xff, PNG_FILLER_AFTER);
}
const int32 kbytes = 4;
png_uint_32 rowbytes = png_get_rowbytes(ppng, pinfo);
if (rowbytes < kbytes * width)
rowbytes = kbytes * width;
if (!bdataonly) {
TranslatorBitmap bitsHeader;
bitsHeader.magic = B_TRANSLATOR_BITMAP;
bitsHeader.bounds.left = 0;
bitsHeader.bounds.top = 0;
bitsHeader.bounds.right = width - 1;
bitsHeader.bounds.bottom = height - 1;
bitsHeader.rowBytes = 4 * width;
if (balpha)
bitsHeader.colors = B_RGBA32;
else
bitsHeader.colors = B_RGB32;
bitsHeader.dataSize = bitsHeader.rowBytes * height;
if (swap_data(B_UINT32_TYPE, &bitsHeader,
sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
result = B_ERROR;
break;
}
outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
if (bheaderonly) {
result = B_OK;
break;
}
}
if (interlace_type == PNG_INTERLACE_NONE) {
prow = new(std::nothrow) uint8[rowbytes];
if (!prow) {
result = B_NO_MEMORY;
break;
}
for (png_uint_32 i = 0; i < height; i++) {
png_read_row(ppng, prow, NULL);
outDestination->Write(prow, width * kbytes);
}
png_read_end(ppng, NULL);
result = B_OK;
break;
} else {
prows = new(std::nothrow) uint8 *[height];
if (!prows) {
result = B_NO_MEMORY;
break;
}
for (nalloc = 0; nalloc < height; nalloc++) {
prows[nalloc] = new(std::nothrow) uint8[rowbytes];
if (!prows[nalloc])
break;
}
if (nalloc < height)
result = B_NO_MEMORY;
else {
png_read_image(ppng, prows);
for (png_uint_32 i = 0; i < height; i++)
outDestination->Write(prows[i], width * kbytes);
result = B_OK;
png_read_end(ppng, NULL);
}
break;
}
} catch (std::exception& e) {
break;
}
}
if (ppng) {
delete[] prow;
prow = NULL;
while (nalloc) {
nalloc--;
delete[] prows[nalloc];
}
delete[] prows;
prows = NULL;
if (!pinfo)
png_destroy_read_struct(&ppng, NULL, NULL);
else
png_destroy_read_struct(&ppng, &pinfo, NULL);
}
return result;
}
status_t
PNGTranslator::translate_from_png(BPositionIO *inSource, uint32 outType,
BPositionIO *outDestination)
{
if (outType == B_TRANSLATOR_BITMAP)
return translate_from_png_to_bits(inSource, outDestination);
else {
translate_direct_copy(inSource, outDestination);
return B_OK;
}
}
status_t
pix_bits_to_png(uint8 *pbits, uint8 *ppng, color_space fromspace,
uint32 width, const color_map *pmap, int32 bitsBytesPerPixel)
{
status_t bytescopied = 0;
uint16 val;
switch (fromspace) {
case B_RGBA32:
bytescopied = width * bitsBytesPerPixel;
memcpy(ppng, pbits, bytescopied);
break;
case B_RGB32:
case B_RGB24:
bytescopied = width * bitsBytesPerPixel;
while (width--) {
memcpy(ppng, pbits, 3);
ppng += 3;
pbits += bitsBytesPerPixel;
}
break;
case B_RGBA32_BIG:
bytescopied = width * 4;
while (width--) {
ppng[0] = pbits[3];
ppng[1] = pbits[2];
ppng[2] = pbits[1];
ppng[3] = pbits[0];
ppng += 4;
pbits += 4;
}
break;
case B_CMYA32:
bytescopied = width * 4;
while (width--) {
ppng[0] = 255 - pbits[2];
ppng[1] = 255 - pbits[1];
ppng[2] = 255 - pbits[0];
ppng[3] = pbits[3];
ppng += 4;
pbits += 4;
}
break;
case B_CMYK32:
{
int32 comp;
bytescopied = width * 3;
while (width--) {
comp = 255 - pbits[2] - pbits[3];
ppng[0] = (comp < 0) ? 0 : comp;
comp = 255 - pbits[1] - pbits[3];
ppng[1] = (comp < 0) ? 0 : comp;
comp = 255 - pbits[0] - pbits[3];
ppng[2] = (comp < 0) ? 0 : comp;
ppng += 3;
pbits += 4;
}
break;
}
case B_CMY32:
case B_CMY24:
bytescopied = width * 3;
while (width--) {
ppng[0] = 255 - pbits[2];
ppng[1] = 255 - pbits[1];
ppng[2] = 255 - pbits[0];
ppng += 3;
pbits += bitsBytesPerPixel;
}
break;
case B_RGB16:
case B_RGB16_BIG:
bytescopied = width * 3;
while (width--) {
if (fromspace == B_RGB16)
val = pbits[0] + (pbits[1] << 8);
else
val = pbits[1] + (pbits[0] << 8);
ppng[0] =
((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
ppng[1] =
((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
ppng[2] =
((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
ppng += 3;
pbits += 2;
}
break;
case B_RGB15:
case B_RGB15_BIG:
bytescopied = width * 3;
while (width--) {
if (fromspace == B_RGB15)
val = pbits[0] + (pbits[1] << 8);
else
val = pbits[1] + (pbits[0] << 8);
ppng[0] =
((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
ppng[1] =
((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
ppng[2] =
((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
ppng += 3;
pbits += 2;
}
break;
case B_RGBA15:
case B_RGBA15_BIG:
bytescopied = width * 4;
while (width--) {
if (fromspace == B_RGBA15)
val = pbits[0] + (pbits[1] << 8);
else
val = pbits[1] + (pbits[0] << 8);
ppng[0] =
((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
ppng[1] =
((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
ppng[2] =
((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
ppng[3] = (val & 0x8000) ? 255 : 0;
ppng += 4;
pbits += 2;
}
break;
case B_RGB32_BIG:
bytescopied = width * 3;
while (width--) {
ppng[0] = pbits[3];
ppng[1] = pbits[2];
ppng[2] = pbits[1];
ppng += 3;
pbits += 4;
}
break;
case B_RGB24_BIG:
bytescopied = width * 3;
while (width--) {
ppng[0] = pbits[2];
ppng[1] = pbits[1];
ppng[2] = pbits[0];
ppng += 3;
pbits += 3;
}
break;
case B_CMAP8:
{
rgb_color c;
bytescopied = width * 3;
while (width--) {
c = pmap->color_list[pbits[0]];
ppng[0] = c.blue;
ppng[1] = c.green;
ppng[2] = c.red;
ppng += 3;
pbits++;
}
break;
}
case B_GRAY8:
bytescopied = width;
memcpy(ppng, pbits, bytescopied);
break;
default:
bytescopied = B_ERROR;
break;
}
return bytescopied;
}
status_t
PNGTranslator::translate_from_bits_to_png(BPositionIO *inSource,
BPositionIO *outDestination)
{
TranslatorBitmap bitsHeader;
status_t result;
result = identify_bits_header(inSource, NULL, &bitsHeader);
if (result != B_OK)
return result;
const color_map *pmap = NULL;
if (bitsHeader.colors == B_CMAP8) {
pmap = system_colors();
if (!pmap)
return B_ERROR;
}
png_uint_32 width, height;
width = static_cast<png_uint_32>(bitsHeader.bounds.Width() + 1);
height = static_cast<png_uint_32>(bitsHeader.bounds.Height() + 1);
int32 pngBytesPerPixel = 0;
int bit_depth, color_type, interlace_type;
bit_depth = 8;
switch (bitsHeader.colors) {
case B_RGBA32:
case B_RGBA32_BIG:
case B_CMYA32:
case B_RGBA15:
case B_RGBA15_BIG:
pngBytesPerPixel = 4;
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case B_RGB32:
case B_RGB32_BIG:
case B_RGB24:
case B_RGB24_BIG:
case B_CMY32:
case B_CMYK32:
case B_CMY24:
case B_RGB16:
case B_RGB16_BIG:
case B_RGB15:
case B_RGB15_BIG:
pngBytesPerPixel = 3;
color_type = PNG_COLOR_TYPE_RGB;
break;
case B_GRAY8:
pngBytesPerPixel = 1;
color_type = PNG_COLOR_TYPE_GRAY;
break;
default:
return B_NO_TRANSLATOR;
}
interlace_type = fSettings->SetGetInt32(PNG_SETTING_INTERLACE);
int32 bitsBytesPerPixel = 0;
switch (bitsHeader.colors) {
case B_RGBA32:
case B_RGBA32_BIG:
case B_RGB32:
case B_RGB32_BIG:
case B_CMYA32:
case B_CMYK32:
case B_CMY32:
bitsBytesPerPixel = 4;
break;
case B_RGB24:
case B_RGB24_BIG:
case B_CMY24:
bitsBytesPerPixel = 3;
break;
case B_RGB16:
case B_RGB16_BIG:
case B_RGBA15:
case B_RGBA15_BIG:
case B_RGB15:
case B_RGB15_BIG:
bitsBytesPerPixel = 2;
break;
case B_GRAY8:
case B_CMAP8:
bitsBytesPerPixel = 1;
break;
default:
return B_NO_TRANSLATOR;
};
uint8 *pbitsrow = NULL, *prow = NULL;
uint8 **prows = NULL;
png_uint_32 nalloc = 0;
png_structp ppng = NULL;
png_infop pinfo = NULL;
result = B_NO_TRANSLATOR;
while (ppng == NULL) {
ppng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!ppng) {
result = B_ERROR;
break;
}
pinfo = png_create_info_struct(ppng);
if (!pinfo) {
result = B_ERROR;
break;
}
png_set_error_fn(ppng, png_get_error_ptr(ppng), throw_error,
alert_warning);
try {
png_set_write_fn(ppng, static_cast<void *>(outDestination),
pngcb_write_data, pngcb_flush_data);
pbitsrow = new(std::nothrow) uint8[bitsHeader.rowBytes];
if (!pbitsrow) {
result = B_NO_MEMORY;
break;
}
if (interlace_type == PNG_INTERLACE_NONE) {
prow = new(std::nothrow) uint8[width * pngBytesPerPixel];
if (!prow) {
result = B_NO_MEMORY;
break;
}
} else {
prows = new(std::nothrow) uint8 *[height];
if (!prows) {
result = B_NO_MEMORY;
break;
}
for (nalloc = 0; nalloc < height; nalloc++) {
prows[nalloc] =
new(std::nothrow) uint8[width * pngBytesPerPixel];
if (!prows[nalloc])
break;
}
if (nalloc < height) {
result = B_NO_MEMORY;
for (; nalloc < height; nalloc++)
prows[nalloc] = NULL;
break;
}
}
png_set_IHDR(ppng, pinfo, width, height, bit_depth, color_type,
interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(ppng, pinfo);
png_set_bgr(ppng);
if (interlace_type == PNG_INTERLACE_NONE) {
for (png_uint_32 i = 0; i < height; i++) {
inSource->Read(pbitsrow, bitsHeader.rowBytes);
pix_bits_to_png(pbitsrow, prow, bitsHeader.colors, width,
pmap, bitsBytesPerPixel);
png_write_row(ppng, prow);
}
} else {
for (png_uint_32 i = 0; i < height; i++) {
inSource->Read(pbitsrow, bitsHeader.rowBytes);
pix_bits_to_png(pbitsrow, prows[i], bitsHeader.colors,
width, pmap, bitsBytesPerPixel);
}
png_write_image(ppng, prows);
}
result = B_OK;
png_write_end(ppng, NULL);
break;
} catch(std::exception& e) {
break;
}
}
if (ppng) {
delete[] pbitsrow;
pbitsrow = NULL;
delete[] prow;
prow = NULL;
while (nalloc) {
nalloc--;
delete[] prows[nalloc];
}
delete[] prows;
prows = NULL;
if (!pinfo)
png_destroy_write_struct(&ppng, NULL);
else
png_destroy_write_struct(&ppng, &pinfo);
}
return result;
}
status_t
PNGTranslator::DerivedTranslate(BPositionIO *inSource,
const translator_info *inInfo, BMessage *ioExtension, uint32 outType,
BPositionIO *outDestination, int32 baseType)
{
if (baseType == 1)
return translate_from_bits_to_png(inSource, outDestination);
else if (baseType == 0)
return translate_from_png(inSource, outType, outDestination);
else
return B_NO_TRANSLATOR;
}
BView *
PNGTranslator::NewConfigView(TranslatorSettings *settings)
{
return new(std::nothrow) PNGView(BRect(0, 0, PNG_VIEW_WIDTH, PNG_VIEW_HEIGHT),
B_TRANSLATE("PNGTranslator Settings"), B_FOLLOW_ALL,
B_WILL_DRAW, settings);
}