Don't convert alpha channel to mask when loading PNG images
This seemed like a good idea many years ago when plenty of code (inside and outside of wxWidgets) couldn't deal with the real alpha correctly, but this is not the case since quite some time any more and producing an image without alpha channel when loading a PNG image with transparency is more surprising than useful now, so don't do it any more. Closes #3019.
This commit is contained in:
parent
526a627fa8
commit
f844b45815
@ -38,40 +38,13 @@
|
||||
// For memcpy
|
||||
#include <string.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// constants
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// image cannot have any transparent pixels at all, have only 100% opaque
|
||||
// and/or 100% transparent pixels in which case a simple mask is enough to
|
||||
// store this information in wxImage or have a real alpha channel in which case
|
||||
// we need to have it in wxImage as well
|
||||
enum Transparency
|
||||
{
|
||||
Transparency_None,
|
||||
Transparency_Mask,
|
||||
Transparency_Alpha
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// local functions
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// return the kind of transparency needed for this image assuming that it does
|
||||
// have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask
|
||||
static Transparency
|
||||
CheckTransparency(unsigned char **lines,
|
||||
png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
|
||||
size_t numColBytes);
|
||||
|
||||
// init the alpha channel for the image and fill it with 1s up to (x, y)
|
||||
static unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y);
|
||||
|
||||
// find a free colour for the mask in the PNG data array
|
||||
static void
|
||||
FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
|
||||
unsigned char& rMask, unsigned char& gMask, unsigned char& bMask);
|
||||
|
||||
// is the pixel with this value of alpha a fully opaque one?
|
||||
static inline
|
||||
bool IsOpaque(unsigned char a)
|
||||
@ -179,52 +152,6 @@ PNGLINKAGEMODE wx_PNG_error(png_structp png_ptr, png_const_charp message)
|
||||
// LoadFile() helpers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// determine the kind of transparency we need for this image: if the only alpha
|
||||
// values it has are 0 (transparent) and 0xff (opaque) then we can simply
|
||||
// create a mask for it, we should be ok with a simple mask but otherwise we
|
||||
// need a full blown alpha channel in wxImage
|
||||
//
|
||||
// parameters:
|
||||
// lines raw PNG data
|
||||
// x, y starting position
|
||||
// w, h size of the image
|
||||
// numColBytes number of colour bytes (1 for grey scale, 3 for RGB)
|
||||
// (NB: alpha always follows the colour bytes)
|
||||
Transparency
|
||||
CheckTransparency(unsigned char **lines,
|
||||
png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
|
||||
size_t numColBytes)
|
||||
{
|
||||
// suppose that a mask will suffice and check all the remaining alpha
|
||||
// values to see if it does
|
||||
for ( ; y < h; y++ )
|
||||
{
|
||||
// each pixel is numColBytes+1 bytes, offset into the current line by
|
||||
// the current x position
|
||||
unsigned const char *ptr = lines[y] + (x * (numColBytes + 1));
|
||||
|
||||
for ( png_uint_32 x2 = x; x2 < w; x2++ )
|
||||
{
|
||||
// skip the grey or colour byte(s)
|
||||
ptr += numColBytes;
|
||||
|
||||
unsigned char a2 = *ptr++;
|
||||
|
||||
if ( !IsTransparent(a2) && !IsOpaque(a2) )
|
||||
{
|
||||
// not fully opaque nor fully transparent, hence need alpha
|
||||
return Transparency_Alpha;
|
||||
}
|
||||
}
|
||||
|
||||
// during the next loop iteration check all the pixels in the row
|
||||
x = 0;
|
||||
}
|
||||
|
||||
// mask will be enough
|
||||
return Transparency_Mask;
|
||||
}
|
||||
|
||||
unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
|
||||
{
|
||||
// create alpha channel
|
||||
@ -243,49 +170,6 @@ unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
|
||||
return alpha;
|
||||
}
|
||||
|
||||
void
|
||||
FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
|
||||
unsigned char& rMask, unsigned char& gMask, unsigned char& bMask)
|
||||
{
|
||||
// choosing the colour for the mask is more
|
||||
// difficult: we need to iterate over the entire
|
||||
// image for this in order to choose an unused
|
||||
// colour (this is not very efficient but what else
|
||||
// can we do?)
|
||||
wxImageHistogram h;
|
||||
unsigned nentries = 0;
|
||||
unsigned char r2, g2, b2;
|
||||
for ( png_uint_32 y2 = 0; y2 < height; y2++ )
|
||||
{
|
||||
const unsigned char *p = lines[y2];
|
||||
for ( png_uint_32 x2 = 0; x2 < width; x2++ )
|
||||
{
|
||||
r2 = *p++;
|
||||
g2 = *p++;
|
||||
b2 = *p++;
|
||||
++p; // jump over alpha
|
||||
|
||||
wxImageHistogramEntry&
|
||||
entry = h[wxImageHistogram:: MakeKey(r2, g2, b2)];
|
||||
|
||||
if ( entry.value++ == 0 )
|
||||
entry.index = nentries++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !h.FindFirstUnusedColour(&rMask, &gMask, &bMask) )
|
||||
{
|
||||
wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
|
||||
|
||||
// use a fixed mask colour and we'll fudge
|
||||
// the real pixels with this colour (see
|
||||
// below)
|
||||
rMask = 0xfe;
|
||||
gMask = 0;
|
||||
bMask = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// reading PNGs
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -308,17 +192,9 @@ void CopyDataFromPNG(wxImage *image,
|
||||
png_uint_32 height,
|
||||
int color_type)
|
||||
{
|
||||
Transparency transparency = Transparency_None;
|
||||
|
||||
// only non NULL if transparency == Transparency_Alpha
|
||||
// allocated on demand if we have any non-opaque pixels
|
||||
unsigned char *alpha = NULL;
|
||||
|
||||
// RGB of the mask colour if transparency == Transparency_Mask
|
||||
// (but init them anyhow to avoid compiler warnings)
|
||||
unsigned char rMask = 0,
|
||||
gMask = 0,
|
||||
bMask = 0;
|
||||
|
||||
unsigned char *ptrDst = image->GetData();
|
||||
if ( !(color_type & PNG_COLOR_MASK_COLOR) )
|
||||
{
|
||||
@ -332,66 +208,16 @@ void CopyDataFromPNG(wxImage *image,
|
||||
unsigned char a = *ptrSrc++;
|
||||
|
||||
// the first time we encounter a transparent pixel we must
|
||||
// decide about what to do about them
|
||||
if ( !IsOpaque(a) && transparency == Transparency_None )
|
||||
{
|
||||
// we'll need at least the mask for this image and
|
||||
// maybe even full alpha channel info: the former is
|
||||
// only enough if we have alpha values of 0 and 0xff
|
||||
// only, otherwisewe need the latter
|
||||
transparency = CheckTransparency
|
||||
(
|
||||
lines,
|
||||
x, y,
|
||||
width, height,
|
||||
1
|
||||
);
|
||||
// allocate alpha channel for the image
|
||||
if ( !IsOpaque(a) && !alpha )
|
||||
alpha = InitAlpha(image, x, y);
|
||||
|
||||
if ( transparency == Transparency_Mask )
|
||||
{
|
||||
// let's choose this colour for the mask: this is
|
||||
// not a problem here as all the other pixels are
|
||||
// grey, i.e. R == G == B which is not the case for
|
||||
// this one so no confusion is possible
|
||||
rMask = 0xff;
|
||||
gMask = 0;
|
||||
bMask = 0xff;
|
||||
}
|
||||
else // transparency == Transparency_Alpha
|
||||
{
|
||||
alpha = InitAlpha(image, x, y);
|
||||
}
|
||||
}
|
||||
if ( alpha )
|
||||
*alpha++ = a;
|
||||
|
||||
switch ( transparency )
|
||||
{
|
||||
case Transparency_Mask:
|
||||
if ( IsTransparent(a) )
|
||||
{
|
||||
*ptrDst++ = rMask;
|
||||
*ptrDst++ = gMask;
|
||||
*ptrDst++ = bMask;
|
||||
break;
|
||||
}
|
||||
// else: !transparent
|
||||
|
||||
// must be opaque then as otherwise we shouldn't be
|
||||
// using the mask at all
|
||||
wxASSERT_MSG( IsOpaque(a), wxT("logic error") );
|
||||
|
||||
wxFALLTHROUGH;
|
||||
|
||||
case Transparency_Alpha:
|
||||
if ( alpha )
|
||||
*alpha++ = a;
|
||||
wxFALLTHROUGH;
|
||||
|
||||
case Transparency_None:
|
||||
*ptrDst++ = g;
|
||||
*ptrDst++ = g;
|
||||
*ptrDst++ = g;
|
||||
break;
|
||||
}
|
||||
*ptrDst++ = g;
|
||||
*ptrDst++ = g;
|
||||
*ptrDst++ = g;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -407,78 +233,19 @@ void CopyDataFromPNG(wxImage *image,
|
||||
unsigned char b = *ptrSrc++;
|
||||
unsigned char a = *ptrSrc++;
|
||||
|
||||
// the logic here is the same as for the grey case except
|
||||
// where noted
|
||||
if ( !IsOpaque(a) && transparency == Transparency_None )
|
||||
{
|
||||
transparency = CheckTransparency
|
||||
(
|
||||
lines,
|
||||
x, y,
|
||||
width, height,
|
||||
3
|
||||
);
|
||||
// the logic here is the same as for the grey case
|
||||
if ( !IsOpaque(a) && !alpha )
|
||||
alpha = InitAlpha(image, x, y);
|
||||
|
||||
if ( transparency == Transparency_Mask )
|
||||
{
|
||||
FindMaskColour(lines, width, height,
|
||||
rMask, gMask, bMask);
|
||||
}
|
||||
else // transparency == Transparency_Alpha
|
||||
{
|
||||
alpha = InitAlpha(image, x, y);
|
||||
}
|
||||
if ( alpha )
|
||||
*alpha++ = a;
|
||||
|
||||
}
|
||||
|
||||
switch ( transparency )
|
||||
{
|
||||
case Transparency_Mask:
|
||||
if ( IsTransparent(a) )
|
||||
{
|
||||
*ptrDst++ = rMask;
|
||||
*ptrDst++ = gMask;
|
||||
*ptrDst++ = bMask;
|
||||
break;
|
||||
}
|
||||
else // !transparent
|
||||
{
|
||||
// must be opaque then as otherwise we shouldn't be
|
||||
// using the mask at all
|
||||
wxASSERT_MSG( IsOpaque(a), wxT("logic error") );
|
||||
|
||||
// if we couldn't find a unique colour for the
|
||||
// mask, we can have real pixels with the same
|
||||
// value as the mask and it's better to slightly
|
||||
// change their colour than to make them
|
||||
// transparent
|
||||
if ( r == rMask && g == gMask && b == bMask )
|
||||
{
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
wxFALLTHROUGH;
|
||||
|
||||
case Transparency_Alpha:
|
||||
if ( alpha )
|
||||
*alpha++ = a;
|
||||
wxFALLTHROUGH;
|
||||
|
||||
case Transparency_None:
|
||||
*ptrDst++ = r;
|
||||
*ptrDst++ = g;
|
||||
*ptrDst++ = b;
|
||||
break;
|
||||
}
|
||||
*ptrDst++ = r;
|
||||
*ptrDst++ = g;
|
||||
*ptrDst++ = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( transparency == Transparency_Mask )
|
||||
{
|
||||
image->SetMaskColour(rMask, gMask, bMask);
|
||||
}
|
||||
}
|
||||
|
||||
// temporarily disable the warning C4611 (interaction between '_setjmp' and
|
||||
|
Loading…
Reference in New Issue
Block a user