Commit c7f38d77 authored by ROOL's avatar ROOL 🤖
Browse files

Palette bit depth & transparency mask changes

Detail:
* Honours the bit depth of the source image rather than setting 8bpp flat.
  This is based on the palette size and is now restricted to 2, 4, 16 or
  256 colours.  Non-paletted images are 24 bit colour.
* Add option to add tRNS chunk for paletted images to have a mask set by
  substituting one of the paletted entries for a transparency.
* Change palette docs in CompressPNG - 0x00BBGGRR not just a 3 byte entity.
* Refuse to start a compression run if ZLib module is missing.
Admin:
  Submission for PNG bounty.

Version 0.06. Tagged as 'CompressPNG-0_06'
parent df5a1ced
......@@ -15,3 +15,6 @@ buffer:The supplied buffer is too small to hold the PNG image
writefail:Failed to write PNG data to file
noalfapal:Alpha channels are not supported for palettised images
badblock:Memory block not known
badbits:Palette data may only be 2, 4, 8, 16 or 256 entries
badtrns:Transparency count exceeds palette size
nozlib:ZLib module is not loaded
/* (0.05)
/* (0.06)
*
* This file is automatically maintained by srccommit, do not edit manually.
*
*/
#define Module_MajorVersion_CMHG 0.05
#define Module_MajorVersion_CMHG 0.06
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 18 Dec 2020
#define Module_Date_CMHG 17 Mar 2021
#define Module_MajorVersion "0.05"
#define Module_Version 5
#define Module_MajorVersion "0.06"
#define Module_Version 6
#define Module_MinorVersion ""
#define Module_Date "18 Dec 2020"
#define Module_Date "17 Mar 2021"
#define Module_ApplicationDate "18-Dec-20"
#define Module_ApplicationDate "17-Mar-21"
#define Module_ComponentName "CompressPNG"
#define Module_FullVersion "0.05"
#define Module_HelpVersion "0.05 (18 Dec 2020)"
#define Module_LibraryVersionInfo "0:5"
#define Module_FullVersion "0.06"
#define Module_HelpVersion "0.06 (17 Mar 2021)"
#define Module_LibraryVersionInfo "0:6"
......@@ -139,6 +139,18 @@ _kernel_oserror *compresspng_tag_valid(int tag)
return ((png_opt *)tag)->magic == _tag_valid ? NULL : report_error (CompressPNG_E_BadTag, "badtag", NULL);
}
/* Return log2 of the number of colours, or -1 if out of range for a palette depth */
static int bit_depth(int colours)
{
switch (colours)
{ case 2: return 1;
case 4: return 2;
case 16: return 4;
case 256: return 8;
default: return -1;
}
}
/* Start PNG compression. Allocate store and prepare to receive image data */
_kernel_oserror *compresspng_start(_kernel_swi_regs *r)
{
......@@ -153,6 +165,12 @@ _kernel_oserror *compresspng_start(_kernel_swi_regs *r)
_kernel_oserror *e = NULL;
cpngmem *mem;
/* we need ZLib if we're going to do any compression */
if (_swix (OS_SWINumberFromString, _IN(1), "ZLib_Compress"))
{ messagetrans_lookup ("nozlib", png_write_error, 128);
return report_error (CompressPNG_E_InitX2, "pnginitx2", png_write_error);
}
/* create a dynamic area to handle the libpng workspace bits */
e = cpngmem_create_area (&mem);
if (e) return e;
......@@ -257,13 +275,24 @@ _kernel_oserror *compresspng_start(_kernel_swi_regs *r)
pixel_size += 1;
}
opt->palette_bits = 8; /* default bit depth is 8 unless overridden by a palette */
/* look for a palette option */
for (i = 0; block->parameter[i].parameter != png_param_end_of_list; i++)
{ if (block->parameter[i].parameter == png_param_palette)
{ if ((block->flags & CompressPNG_Flags_HasAlpha) || (block->flags & CompressPNG_Flags_SkipAlpha))
{ messagetrans_lookup ("noalfapal", png_write_error, 128); /* we don't support palettised images with an alpha channel */
png_pending_error = png_write_error;
goto abort_png;
}
opt->palette_bits = bit_depth(block->parameter[i].param.palette.size);
if (opt->palette_bits == -1)
{ messagetrans_lookup ("badbits", png_write_error, 128); /* only 2, 4, 16, 256 colours permitted in a palette */
png_pending_error = png_write_error;
goto abort_png;
}
png_colour = PNG_COLOR_TYPE_PALETTE;
pixel_size = 1;
......@@ -312,10 +341,24 @@ _kernel_oserror *compresspng_start(_kernel_swi_regs *r)
opt->row_ptrs[row] = opt->full_source + row * opt->stride;
}
break;
case png_param_transparency:
if (block->parameter[i].param.transparency.size > (1 << opt->palette_bits))
{ messagetrans_lookup ("badtrns", png_write_error, 128); /* only 2, 4, 16, 256 colours permitted in a palette */
png_pending_error = png_write_error;
goto abort_png;
}
png_set_tRNS (opt->png_ptr, opt->info_ptr,
(png_bytep) block->parameter[i].param.transparency.transdata,
block->parameter[i].param.transparency.size, NULL);
break;
}
png_set_IHDR (opt->png_ptr, opt->info_ptr, block->width, block->height * opt->rowdup,
8, png_colour, png_interlace, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
opt->palette_bits, png_colour, png_interlace, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
opt->width = block->width;
/* returns tag address in R0 */
r->r[0] = (int) opt;
......@@ -377,6 +420,7 @@ _kernel_oserror *compresspng_writeline(_kernel_swi_regs *r)
png_opt *opt = (png_opt *) r->r[0];
int rowcount;
_kernel_oserror *e=NULL;
png_bytep rowbuffer = (png_bytep) r->r[1];
if (setjmp (png_jmpbuf (opt->png_ptr))) goto abort_png;
......@@ -386,13 +430,37 @@ _kernel_oserror *compresspng_writeline(_kernel_swi_regs *r)
if (opt->filler) png_set_filler (opt->png_ptr, 0, PNG_FILLER_AFTER);
}
if (opt->palette_bits < 8)
{ /* compact the stream by combining pixels into bytes */
int src, dest;
for (src = 0, dest = 0; src < opt->width; src += 8 / opt->palette_bits, dest += 1)
{ switch (opt->palette_bits)
{ case 1:
*(dest + rowbuffer) = (*(src + rowbuffer + 0) << 7) | (*(src + rowbuffer + 1) << 6) |
(*(src + rowbuffer + 2) << 5) | (*(src + rowbuffer + 3) << 4) |
(*(src + rowbuffer + 4) << 3) | (*(src + rowbuffer + 5) << 2) |
(*(src + rowbuffer + 6) << 1) | (*(src + rowbuffer + 7) << 0);
break;
case 2:
*(dest + rowbuffer) = (*(src + rowbuffer + 0) << 6) | (*(src + rowbuffer + 1) << 4) |
(*(src + rowbuffer + 2) << 2) | (*(src + rowbuffer + 3) << 0);
break;
case 4:
*(dest + rowbuffer) = (*(src + rowbuffer + 0) << 4) | (*(src + rowbuffer + 1) << 0);
break;
}
}
}
/* if interlaced, store for a full write later; otherwise add the row data to the PNG itself */
for (rowcount=0; rowcount<opt->rowdup; rowcount++)
{ if (opt->current_row >= opt->height) return report_error (CompressPNG_E_ManyRows, "manyrows", NULL);
if (opt->interlaced)
memcpy (opt->row_ptrs[opt->current_row], (png_bytep) r->r[1], opt->stride);
memcpy (opt->row_ptrs[opt->current_row], rowbuffer, opt->stride);
else
png_write_row (opt->png_ptr, (png_bytep) r->r[1]);
png_write_row (opt->png_ptr, rowbuffer);
opt->current_row++;
}
......
......@@ -46,7 +46,8 @@ typedef enum
png_param_gamma, /**< 2: gamma correction value. Double value passed to png_set_gAMA */
png_param_compression, /**< 3: compression level (0-9). Hint passed to ZLib for the compression level */
png_param_interlace, /**< 4: flag indicating the image is interlaced or not. 0 if not, 1 if it is */
png_param_palette /**< 5: pointer to palette data present for images <=256 colours and no alpha channel */
png_param_palette, /**< 5: pointer to palette data present for images <=256 colours and no alpha channel */
png_param_transparency /**< 6: transparency data for paletted images <=8bpp */
} compresspng_param_type;
/** @brief Parameter block for the CompressPNG_Start call
......@@ -66,9 +67,14 @@ typedef struct
double value_d; /**< value of an FP parameter (png_param_gamma) */
struct
{ int size; /**< number of 3 byte colour entries in the palette data (2, 4, 16, 256) */
char *palettedata; /**< pointer to the palette data itself. Data is in the form of an R/G/B bytestream, one byte per channel */
{ int size; /**< number of 4 byte colour entries in the palette data (2, 4, 16, 256) */
int *palettedata; /**< pointer to the palette data itself. Data is in the form of 0x00BBGGRR */
} palette;
struct
{ int size; /**< number of transparency entries (must match palette size) */
char *transdata; /**< pointer to the transparency data itself, one byte per colour value */
} transparency;
} param;
} compresspng_param;
......
......@@ -64,6 +64,8 @@ typedef struct
int dest_length; /**< Length of the destination buffer */
int write_offset; /**< Current write offset within the buffer */
int width; /**< Image width in pixels */
int palette_bits; /**< Bit depth of palette (1, 2, 4 or 8) */
int rowdup; /**< Number of times each row must be duplicated in PNG output (for images where ydpi<xdpi) */
int info_written; /**< flag indicating if the PNG info block has been written yet */
int filler; /**< flag indicating if a PNG filler is required (for RGB0, G0 data streams) */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment