GIF transparent colour turning bright pink

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

GIF transparent colour turning bright pink

Post by Rocketmagnet » Thu Nov 30, 2017 2:39 pm

Hi,

I load a GIF image using wxImage::LoadFile().
Then I draw the image to a wxPaintDc using DrawBitmap().

The image should look like the one on the left, but instead it looks like the one on the right.
GIFs.png
GIFs.png (7.91 KiB) Viewed 2076 times
It seems that the 'transparent' colour has been replaced by a bright pink. Is there some way I can prevent that happening, or turn it back to its original colour? (Note, I don't want to turn on transparency by setting useMask = true).

Hugo

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2955
Joined: Sun Jan 03, 2010 5:45 pm

Re: GIF transparent colour turning bright pink

Post by PB » Thu Nov 30, 2017 4:28 pm

I believe that at least on MSW wxDC does not support transparency aside from the transparency mask. You can easily test this using wxGCDC instead of regular wxDC.

EditActually it seems that PNGs with transparency are drawn properly both by wxDC and wxGCDC while the same image converted to GIF with a transparent color is not drawn with transparency by either (the toucan image is from the image sample):

Code: Select all

#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/dcgraph.h>

class MyCanvas : public wxPanel
{
public:
    MyCanvas(wxWindow* parent)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
    {       
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &MyCanvas::OnPaint, this);  

        m_bmpGIF = wxBitmap("toucan.gif", wxBITMAP_TYPE_GIF);
        m_bmpPNG = wxBitmap("toucan.png", wxBITMAP_TYPE_PNG);        
    }
private:   
    wxBitmap m_bmpGIF, m_bmpPNG;

    void OnPaint(wxPaintEvent&)
    {                
        wxAutoBufferedPaintDC dc(this);   
        
        dc.SetBrush(*wxBLUE_BRUSH);
        dc.DrawRectangle(GetClientRect());        

        dc.DrawBitmap(m_bmpGIF, 0, 0);
        dc.DrawBitmap(m_bmpPNG, m_bmpGIF.GetWidth(), 0);
            
        wxGCDC gcdc(dc);
        gcdc.DrawBitmap(m_bmpGIF, 0, m_bmpGIF.GetHeight());
        gcdc.DrawBitmap(m_bmpPNG, m_bmpGIF.GetWidth(), m_bmpGIF.GetHeight());        
    }
    
};

class MyApp : public wxApp
{
public:
    virtual bool OnInit()
    {     
        wxInitAllImageHandlers();
        wxFrame* f = new wxFrame(NULL, wxID_ANY, _("Test"), wxDefaultPosition, wxSize(800, 800));
        new MyCanvas(f);
        f->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
toucan.png
toucan.png (49.02 KiB) Viewed 2064 times
TBH, I myself would not use GIF in this situation.

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Thu Nov 30, 2017 4:52 pm

It does support transparency. You just have to enable it by adding 'true' at the end:

Code: Select all

dc.DrawBitmap(bitmap, x, y, true);
Then it renders the pink stuff as transparent. But I don't want it to be rendered transparent. I want the pink stuff to be rendered in its correct colour, which is grey.

My question is: which bit of code is causing the colour to change? And how can I tell it not to do that?

(As for why I'm using GIF: I'm making an image browser, so I don't have a choice about which images someone browses to.)

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2955
Joined: Sun Jan 03, 2010 5:45 pm

Re: GIF transparent colour turning bright pink

Post by PB » Thu Nov 30, 2017 5:06 pm

I do not know why, but the magenta color has been used since I can remember when drawing the "transparent colour".

I also do not understand why you are surprised when the transparency mask is not used when you tell wxWidgets to not use it?

The difference being that GIFs are 8-bit only so they use transparency mask while PNGs can use true alpha (RGBA), right?

You can convert mask to alpha with something like this

Code: Select all

wxImage img("toucan.gif");
wxBitmap bmp;

img.InitAlpha();
bmp = wxBitmap(img);
but why?

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 10:26 am

Hi PB,

Perhaps I'm not describing the problem correctly. There should be no pink colour at all. In fact, the original GIF is a greyscale image.

As I said in my original post, the image should look like the one on the left, not the one on the right. Somewhere the pink colour is being added to an image that had no pink colour.

> I also do not understand why you are surprised when the transparency mask is not used when you tell wxWidgets to not use it?

My surprise is not that there is no transparency (I wanted no transparency). My surprise is the appearance of a colour which is not in the original greyscale image.

Here is the original image. Please can you show me the code that will make this image display correctly on the screen, with transparency disabled and no pink in it.
cat.gif
cat.gif (2.92 KiB) Viewed 2013 times
Thanks
Hugo

User avatar
doublemax
Moderator
Moderator
Posts: 15853
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: GIF transparent colour turning bright pink

Post by doublemax » Fri Dec 01, 2017 10:43 am

It's quite obvious that this image does not have a grey background, it's transparent. You can clearly see the blue-ish background color of the forum behind the image.

This is how it looks when loaded into PhotoShop:
cat-ps.png
cat-ps.png (6.37 KiB) Viewed 2012 times
Use the source, Luke!

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 11:37 am

Hi Doublemax,

As I'm sure you know, in an 8-bit GIF format image, there are three main parts:
  • the compressed 8-bit pixel data
  • the palette
  • the transparency tag
The pixel data itself is stored without a concept of transparency. In this case, the image has a background colour of 210. The palette is a lookup table mapping the 8-bit pixels to 24-bit colours. In addition to the image, there's optional transparency information, which labels one of the palette colours as transparent. In this case, the colour 210 is labelled as transparent.

When a GIF image is displayed, there is typically a choice about how to display it. Most modern image editing software will convert the 8-bit GIF to a 32-bit image with alpha channel. The alpha channel will be fully transparent for each pixel that matches the 'transparent' colour (in this case 210), and opaque for every other pixel. Alternatively, the transparency tag can be ignored, and the image can be shown without the alpha channel (or the alpha channel set to opaque everywhere). For example, this is what it looks like opened in a very old version of Paintshop Pro, which doesn't support transparency:
cat_notrans_window.png
cat_notrans_window.png (8.13 KiB) Viewed 2002 times
And here is the palette. As it's a greyscale image, we just have 256 grey levels. The colour 210 is outlined. As you can see, it is not pink.
cat_palette.png
cat_palette.png (7.66 KiB) Viewed 2002 times
What's happening in wxWidgets seems to be an additional step which I don't want. The pallet colour marked as transparent is being changed from 0xD2D2D2 to 0xFF00FF.

Is there some way to stop this happening?

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 11:48 am

I think I have found the place it's happening:

on line 162 of gifdecod.cpp, in the function wxGIFDecoder::ConvertToImage()

Code: Select all

        pal[3 * transparent + 0] = 255,
        pal[3 * transparent + 1] = 0,
        pal[3 * transparent + 2] = 255;

        image->SetMaskColour(255, 0, 255);
The palette colour marked as transparent is actually being changed to pink.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2955
Joined: Sun Jan 03, 2010 5:45 pm

Re: GIF transparent colour turning bright pink

Post by PB » Fri Dec 01, 2017 11:53 am

Rocketmagnet wrote:Is there some way to stop this happening?
As I told you before, magenta (RGB 255, 0, 255) is used by convenience when simple graphics APIs draw the parts of image that should be fully transparent.

If you do not want to draw the transparent pixels (and you do not want that), either tell the wxDC to use the mask when drawing the bitmap or convert the mask to alpha, as shown in my previous post. There is nothing else you can or should do.

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 12:25 pm

Hi PB,

Perhaps I'm failing to communicate my needs, or perhaps I'm being very thick. What I want to do is draw this GIF, so that it looks exactly like this:
cat_no_transparency_info.gif
This version of the GIF has had its transparency information removed so that it displays correctly on the forum.
cat_no_transparency_info.gif (2.91 KiB) Viewed 1980 times
Shouldn't the code be more like this:

Code: Select all

    transparent = GetTransparentColourIndex(frame);

    // set transparent colour mask
    if (transparent != -1)
    {
        image->SetMaskColour(pal[3 * transparent + 0], pal[3 * transparent + 1], pal[3 * transparent + 2]);        
    }
    else
    {
        image->SetMask(false);
    }
Or, even better, like this:

Code: Select all

    transparent = GetTransparentColourIndex(frame);

    // set transparent colour mask
    if (transparent != -1)
    {
        for (i = 0; i < GetNcolours(frame); i++)    // Prevent duplicate colours becoming transparent
        {                                           // by changing them slightly
            if ((pal[3 * i + 0] == pal[3 * transparent + 0]) &&
                (pal[3 * i + 1] == pal[3 * transparent + 1]) &&
                (pal[3 * i + 2] == pal[3 * transparent + 2]))
            {
                pal[3 * i + 2] ^= 1;
            }
        }
        
        image->SetMaskColour(pal[3 * transparent + 0], pal[3 * transparent + 1], pal[3 * transparent + 2]);        
    }
    else
    {
        image->SetMask(false);
    }
I tried this, and it seems to work. The cat image renders correctly.
Last edited by Rocketmagnet on Fri Dec 01, 2017 12:40 pm, edited 1 time in total.

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 12:39 pm

There's another reason why the code shouldn't really be changing the colours. Look what happens when my application creates a thumbnail of a GIF that contains transparency information.
cats_rescaled.png
Bilinear rescaled GIF. Left: Drawn with useMask=true. Right: useMask=false.
cats_rescaled.png (7.87 KiB) Viewed 1979 times

Code: Select all

    if (image.LoadFile(fullPath.GetFullPath()))
    {
        wxSize newSize = GetTnImageSize(image.GetSize(), size);
        image.Rescale(newSize.GetWidth(), newSize.GetHeight(), wxIMAGE_QUALITY_BILINEAR);
        bitmap = wxBitmap(image);
        image.Destroy();
    }
When I create a thumbnail of this image, using bilinear rescaling, the pink colour leaks into all adjacent pixels. Now it's impossible to draw the image without some pink showing.

Using the modified code in gifdecod.cpp, the rescaled image now looks like this:
cats_rescaled_correctly.png
Bilinear rescaled GIF. Left: Drawn with useMask=true. Right: useMask=false.
cats_rescaled_correctly.png (6.79 KiB) Viewed 1978 times

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2955
Joined: Sun Jan 03, 2010 5:45 pm

Re: GIF transparent colour turning bright pink

Post by PB » Fri Dec 01, 2017 1:20 pm

So, all you want is to draw the GIF on a gray background? Just draw the gray background first and then draw the GIF using transparent mask. Goal achieved. I must admit I am really confused...

Edit
I can confirm magenta "leaking" into the image when rescaling with wxIMAGE_QUALITY_BILINEAR. I have no idea why this happens and cannot allocate more time to investigate. The image looks as expected when wxIMAGE_QUALITY_HIGH is used instead:
cats.png
cats.png (32.3 KiB) Viewed 1961 times

Code: Select all

#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/dcgraph.h>

class MyCanvas : public wxPanel
{
public:
    MyCanvas(wxWindow* parent)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
    {       
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &MyCanvas::OnPaint, this); 
        
        m_gifBmp = wxBitmap("cat.gif", wxBITMAP_TYPE_GIF);
        
        wxImage img("cat.gif");
        // wxSize newSize = m_gifBmp.GetSize() * 2;
        wxSize newSize = m_gifBmp.GetSize() / 2;
        
        img.InitAlpha();
        img.Rescale(newSize.GetWidth(), newSize.GetHeight(), wxIMAGE_QUALITY_HIGH);
        m_gifBmpResized = wxBitmap(img);
    }
private:   
    wxBitmap m_gifBmp;
    wxBitmap m_gifBmpResized;    

    void DrawTransparentBitmap(wxDC& dc, const wxPoint& pos, const wxBitmap& bmp, 
                               const wxColour& backClr)
    {
        wxRect rect(pos, bmp.GetSize());
        wxBrush brush(backClr);
        wxPen pen(backClr);

        dc.SetBrush(brush);
        dc.DrawRectangle(rect);
        dc.SetPen(pen);

        dc.DrawBitmap(bmp, pos, bmp.GetMask() != NULL);
        
        dc.SetBrush(wxNullBrush);
        dc.SetPen(wxNullPen);
    }
   
    void OnPaint(wxPaintEvent&)
    {               
        wxAutoBufferedPaintDC dc(this);                           
        wxBrush brush(*wxBLUE);
        
        dc.SetBrush(*wxBLUE_BRUSH);
        dc.SetPen(*wxBLUE_PEN);
        dc.DrawRectangle(GetClientRect());       

        DrawTransparentBitmap(dc, wxPoint(0, 0), m_gifBmp, *wxRED);
        DrawTransparentBitmap(dc, wxPoint(m_gifBmp.GetWidth(), 0), m_gifBmp, *wxLIGHT_GREY);
        DrawTransparentBitmap(dc, wxPoint(m_gifBmp.GetWidth() * 2, 0), m_gifBmp, *wxBLACK);

        DrawTransparentBitmap(dc, wxPoint(0, m_gifBmp.GetHeight()), 
            m_gifBmpResized, *wxRED);
        DrawTransparentBitmap(dc, wxPoint(m_gifBmpResized.GetWidth(), m_gifBmp.GetHeight()), 
            m_gifBmpResized, *wxLIGHT_GREY);
        DrawTransparentBitmap(dc, wxPoint(m_gifBmpResized.GetWidth() * 2, m_gifBmp.GetHeight()), 
            m_gifBmpResized, *wxBLACK);
    }

};

class MyApp : public wxApp
{
public:
    virtual bool OnInit()
    {     
        wxInitAllImageHandlers();
        wxFrame* f = new wxFrame(NULL, wxID_ANY, _("Test"), wxDefaultPosition, wxSize(600, 240));
        new MyCanvas(f);
        f->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 2:06 pm

Hi PB,

Thanks for being patient with me. :)

The application I'm making is an image browser. It shows thumbnails of the images in a directory. Here are some screenshots of my application so far:

This is what I started with:
Pink_Unmasked.png
Original gifdecod, useMask=true
So I tried setting useMask=true, but that still shows lots of pink because of the bilinear filtering.
Pink_Masked.png
Original gifdecod, useMask=false
After I modified gifdecod.cpp, it now looks how it should look. The cats and flag are drawn with the correct background colour as it appears in the original file.
Correct_Unmasked.png
Fixed gifdecod, useMask=false
As you can see, there are a couple of different cat GIFs there, with two different background colours. Can you see that your suggestion of drawing the cat over a grey rectangle is not a solution? It's not a solution for the following two reasons:
  • There's no way for my application to know what colour rectangle to draw underneath.
  • Because of the bilinear filtering (which happens after the pink is added), you'd still end up with lots of pink pixels which shouldn't be there.

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 2:10 pm

Furthermore, two other applications agree with me.


This is another image browser, ACDSee. It draws the GIFs correctly, without any pink:
ACDSee.png
GIFs drawn correctly in ACDSee
And this is Paint Shop Pro:
PSP.png
Cat GIF drawn correctly in Paint ShoP Pro
PSP.png (25.65 KiB) Viewed 1957 times
Would you at least agree with me that adding the pink colour is not standard, and that there are applications which draw GIFs correctly?

Hugo

Rocketmagnet
Experienced Solver
Experienced Solver
Posts: 79
Joined: Wed Aug 09, 2006 11:14 pm
Location: London
Contact:

Re: GIF transparent colour turning bright pink

Post by Rocketmagnet » Fri Dec 01, 2017 2:12 pm

Hi PB

> I have no idea why this happens and cannot allocate more time to investigate.

Well, my modifications to gifdecod.cpp seem to fix this problem. Do you think I should suggest this as a modification?

Hugo

Post Reply