Added support for bits->PNG conversion, both interlaced and non-interlaced

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4110 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Matthew Wilber 2003-07-27 14:38:24 +00:00
parent 9e53835da9
commit c9f996e088
2 changed files with 438 additions and 13 deletions

View File

@ -417,12 +417,8 @@ status_t
identify_png_header(BPositionIO *inSource, BMessage *ioExtension,
translator_info *outInfo, uint32 outType, ssize_t amtread, uint8 *read)
{
// Can only output to bits for now
if (outType != B_TRANSLATOR_BITMAP)
return B_NO_TRANSLATOR;
if (amtread != 8)
return B_ERROR;
return B_ERROR;
if (!png_check_sig(read, amtread))
// if first 8 bytes of stream don't match PNG signature bail
return B_NO_TRANSLATOR;
@ -543,7 +539,7 @@ translate_from_png_to_bits(BPositionIO *inSource, BPositionIO *outDestination,
bdataonly = settings.SetGetDataOnly();
// for storing decoded PNG row data
png_bytep *prows = NULL, prow = NULL;
uint8 **prows = NULL, *prow = NULL;
png_uint_32 nalloc = 0;
png_structp ppng = NULL;
@ -652,7 +648,7 @@ translate_from_png_to_bits(BPositionIO *inSource, BPositionIO *outDestination,
if (interlace_type == PNG_INTERLACE_NONE) {
// allocate buffer for storing PNG row
prow = new png_byte[rowbytes];
prow = new uint8[rowbytes];
if (!prow) {
result = B_NO_MEMORY;
break;
@ -671,14 +667,14 @@ translate_from_png_to_bits(BPositionIO *inSource, BPositionIO *outDestination,
} else {
// interlaced PNG image
prows = new png_bytep[height];
prows = new uint8 *[height];
if (!prows) {
result = B_NO_MEMORY;
break;
}
// allocate enough memory to store the whole image
for (nalloc = 0; nalloc < height; nalloc++) {
prows[nalloc] = new png_byte[rowbytes];
prows[nalloc] = new uint8[rowbytes];
if (!prows[nalloc])
break;
}
@ -741,6 +737,427 @@ translate_from_png(BPositionIO *inSource, BMessage *ioExtension,
}
}
// Convert width pixels from pbits to PNG format, storing the
// result in ppng
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;
} // switch (fromspace)
return bytescopied;
}
status_t
translate_from_bits_to_png(BPositionIO *inSource, BPositionIO *outDestination,
ssize_t amtread, uint8 *read, PNGTranslatorSettings &settings)
{
TranslatorBitmap bitsHeader;
status_t result;
result = identify_bits_header(inSource, NULL, amtread, read, &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;
// ADD SUPPORT FOR B_CMAP8 HERE (later)
case B_GRAY8:
pngBytesPerPixel = 1;
color_type = PNG_COLOR_TYPE_GRAY;
break;
default:
return B_NO_TRANSLATOR;
}
interlace_type = settings.SetGetInterlace();
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;
// row buffers
// image buffer for writing whole png image at once
uint8 **prows = NULL;
png_uint_32 nalloc = 0;
png_structp ppng = NULL;
png_infop pinfo = NULL;
result = B_NO_TRANSLATOR;
while (ppng == NULL) {
// create PNG read pointer with default error handling routines
ppng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!ppng) {
result = B_ERROR;
break;
}
// alocate / init memory for image information
pinfo = png_create_info_struct(ppng);
if (!pinfo) {
result = B_ERROR;
break;
}
// set error handling
if (setjmp(png_jmpbuf(ppng))) {
// When an error occurs in libpng, it uses
// the longjmp function to continue execution
// from this point
result = B_ERROR;
break;
}
png_set_write_fn(ppng, static_cast<void *>(outDestination),
pngcb_write_data, pngcb_flush_data);
// Allocate memory needed to buffer image data
pbitsrow = new uint8[bitsHeader.rowBytes];
if (!pbitsrow) {
result = B_NO_MEMORY;
break;
}
if (interlace_type == PNG_INTERLACE_NONE) {
prow = new uint8[width * pngBytesPerPixel];
if (!prow) {
result = B_NO_MEMORY;
break;
}
} else {
prows = new uint8 *[height];
if (!prows) {
result = B_NO_MEMORY;
break;
}
// allocate enough memory to store the whole image
for (nalloc = 0; nalloc < height; nalloc++) {
prows[nalloc] = new uint8[width * pngBytesPerPixel];
if (!prows[nalloc])
break;
}
if (nalloc < height) {
result = B_NO_MEMORY;
break;
}
}
// Specify image info
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);
// write out image data
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);
}
png_write_end(ppng, NULL);
result = B_OK;
break;
}
if (ppng) {
delete[] pbitsrow;
pbitsrow = NULL;
delete[] prow;
prow = NULL;
// delete row pointers and array of pointers to rows
while (nalloc) {
nalloc--;
delete[] prows[nalloc];
}
delete[] prows;
prows = NULL;
// free PNG handle / info structures
if (!pinfo)
png_destroy_write_struct(&ppng, png_infopp_NULL);
else
png_destroy_write_struct(&ppng, &pinfo);
}
return result;
}
// ---------------------------------------------------------------
// Translate
//
@ -804,10 +1221,15 @@ PNGTranslator::Translate(BPositionIO *inSource, const translator_info *inInfo,
memcpy(&n32ch, ch, sizeof(uint32));
if (n32ch == nbits) {
// B_TRANSLATOR_BITMAP type
outDestination->Write(ch, 4);
translate_direct_copy(inSource, outDestination);
if (outType == B_TRANSLATOR_BITMAP) {
outDestination->Write(ch, 4);
translate_direct_copy(inSource, outDestination);
return B_OK;
return B_OK;
} else
// Output to PNG
return translate_from_bits_to_png(inSource, outDestination,
4, ch, *fpsettings);
} else {
// Might be PNG image, read in the rest of

View File

@ -436,9 +436,12 @@ PNGTranslatorSettings::SetGetInterlace(int32 *pinterlace)
// set to default value in case FindBool fails
flock.Lock();
fmsgSettings.FindInt32(PNG_SETTING_INTERLACE, &nprevValue);
if (pinterlace)
if (pinterlace && (*pinterlace == PNG_INTERLACE_NONE ||
*pinterlace == PNG_INTERLACE_ADAM7))
fmsgSettings.ReplaceInt32(PNG_SETTING_INTERLACE, *pinterlace);
flock.Unlock();
return nprevValue;