Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AnimGIF not transparent #5548

Closed
doublex opened this issue Jun 18, 2021 · 7 comments · Fixed by #5557
Closed

AnimGIF not transparent #5548

doublex opened this issue Jun 18, 2021 · 7 comments · Fixed by #5557
Labels

Comments

@doublex
Copy link

doublex commented Jun 18, 2021

Loading some anim-GIFs result in opaque background.
Example to reproduce: PNG has a black border on the right inside (should be transparent):

import base64, io
import PIL.Image

animgif = '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'
with PIL.Image.open(io.BytesIO(base64.b64decode(animgif)), 'r') as img:
    img.save( '/tmp/blackborder.png', 'PNG' )
@radarhere radarhere added the GIF label Jun 18, 2021
@radarhere
Copy link
Member

The first frame has a dispose_extent of (0, 0, 27, 32) - which is not the full size of the image.

#3434 would fix this image, by setting the uncovered parts of the first frame to transparency.

I just don't understand why this is the solution on a theoretical level, as the spec states that

The Background Color is the color used for those pixels on the screen that are not covered by an image.

rather than the transparency.

@radarhere
Copy link
Member

@RLaursen since you're thinking a lot about GIFs at the moment, I'll ask you.

See this 75px by 50px image.

out

The first frame (well, the only frame) has an Image Descriptor with an Image Left Position of (25, 0) - in Pillow terms, those are the x0 and y0 of dispose_extent.

So the leftmost 25 pixels of the image are not covered. Since the spec states that the background color should be "used for those pixels on the screen that are not covered by an image", and the background color is #0f0 (0 index, meaning the first entry in the palette), I would think those pixels should be green.

Instead, the browser renders those pixels as transparent.

Any thoughts as to why this is? This is not a question about how Pillow behaves, rather a question about how GIFs behave.

@RLaursen
Copy link

RLaursen commented Jun 21, 2021

@radarhere That's certainly interesting, and I agree that the uncovered portion on the left should be the background color. Even if the background index was the same as the transparency index, which it isn't, I still think it should display the background color, purely based on the 89a specs.

My guess is that viewers are generally set up to only use the background color when disposal method 2 is used, even though this seems like an incorrect interpretation of the format. Perhaps Pillow should mirror this behavior, if only to display GIFs consistently.

@RLaursen
Copy link

RLaursen commented Jun 21, 2021

yeah, strangely enough, even with the transparency flag off, transparency is used for uncovered canvas

weirdy

@raygard
Copy link
Contributor

raygard commented Jun 22, 2021

I'm also trying to get Pillow working better with animated GIFs. Not much progress yet. @radarhere has already noticed some problems with the spec. E.g., when method 3 is used in the first frame, to dispose to previous, when there is no previous? Or what happens with disposal method 2 when there's no GCT, so no background color?

Apparently the interpretation "in the wild" may follow historic conventions.

A clue: http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html says of the background color index: "GIFLIB supports reading and setting this byte, but modern viewers and browsers generally have no use for it." (Also says of the size in the Logical Screen Descriptor: "The canvas width and height are usually ignored by modern viewers.")

A page apparently authored when animated GIFs were a new thing: http://www6.uniovi.es/gifanim/gifabout.htm says this: "The Logical Screen Block also chooses one of the colors in the Global Color Table to be the Background color of the screen. This color selection is ignored by Netscape Navigator. If a GIF's background area shows through, Navigator displays the color set in the BGCOLOR of the page's body or, if none is specified, the background color set in the menus under OPTIONS/GENERAL PREFERENCES/COLORS. Now, of course, the question arises; how do I get it to be transparent? Well, this SHOULDN'T work, but it does. Apparently, if Netscape's decoder finds a Control block (it must be first, before any images) with Transparency turned on (any color) the background of the GIF will be transparent. This will allows background GIFs to fill in the logical screen background."

It's not clear to me what Navigator did when no transparency was set, since it ignored the background color set in the LSD. But I'm guessing browsers may have always ignored it. That page also says "If your logical screen is larger than your image, you will have space around the image when displayed." But doesn't say what was in that space. Maybe just the page bgcolor or browser preference.

That page also says "The logical screen area should be large enough to display all of your individual frames in it. If an image in the GIF file is larger than the logical screen or, by its positioning, extends beyond the screen, the portion that is off-screen will not be displayed." Which contradicts Pillow's decision to expand the size when a frame exceeds its boundaries.

I opened 75x50 GIF in several programs. Irfanview indicated its size as 50x50, consistent with what giflib whatsinagif says. It does the same with @RLaursen's GIF above.

I haven't found any more information about how programs actually interpret these features (background, transparency, image size, disposal methods.) I'll keep digging.

@radarhere
Copy link
Member

Ok. I've given up and created #5557 to fix the image from the start of this issue.

@radarhere
Copy link
Member

I've managed to find https://legacy.imagemagick.org/Usage/anim_basics/#dispose. In discussing why ImageMagick uses transparency for disposal method 2, it states (emphasis mine)

There is some thinking that rather than clearing the overlaid area to the transparent color, this disposal should clear it to the 'background' color meta-data setting stored in the GIF animation. In fact the old "Netscape" browser (version 2 and 3), did exactly that. But then it also failed to implement the 'Previous' dispose method correctly.

On the other hand the initial canvas should also be set from the formats 'background' color too, and that is also not done. However all modern web browsers clear just the area that was last overlaid to transparency, as such this is now accepted practice, and what IM now follows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants