On Mon, 26 May 2025 05:29:45 GMT, Jeremy Wood <d...@openjdk.org> wrote:

>> This resolves a gif parsing bug where an unwanted opaque rectangle could 
>> appear under these conditions:
>> 
>> 1. The disposal method for frames is 1 (meaning "do not dispose", aka 
>> "DISPOSAL_SAVE")
>> 2. The transparent pixel is non-zero
>> 3. There's more than one such consecutive frame
>> 
>> Previously: the GifImageDecoder would leave the saved_image pixels as zero 
>> when they were supposed to be transparent. This works great if the 
>> transparent pixel index is zero, but it flood fills the background of your 
>> frame with the zeroeth color otherwise.
>> 
>> I wrote four PRs that share the GifComparison class in this PR. Once any of 
>> them clear code review the other PRs will be much simpler:
>> 
>> 1. [8357034](https://github.com/openjdk/jdk/pull/25264)
>> 2. [8356137](https://github.com/openjdk/jdk/pull/25044) (this one)
>> 3. [8356320](https://github.com/openjdk/jdk/pull/25076)
>> 4. [8351913](https://github.com/openjdk/jdk/pull/24271)
>
> Jeremy Wood has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   8356137: Adding copyright to GifComparison

I think we a need a gif with only 2 frames having transpixel>0 and disposal 
method save to reproduce this scenario.

Also its better if we can actually create test gif file using ImageIO(There are 
examples in test/jdk/javax/imageio/plugins/gif/) programatically and use it 
instead of external GIF files for testing.

> For reference, the commented out lines here are my first approach at 
> resolving this problem.
> <img alt="image" width="687" 
> src="https://private-user-images.githubusercontent.com/7669569/440467227-8caa3372-ee42-412d-959c-6d55d258e4ba.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDgzNTU3OTUsIm5iZiI6MTc0ODM1NTQ5NSwicGF0aCI6Ii83NjY5NTY5LzQ0MDQ2NzIyNy04Y2FhMzM3Mi1lZTQyLTQxMmQtOTU5Yy02ZDU1ZDI1OGU0YmEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDUyNyUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTA1MjdUMTQxODE1WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NGUzNmFkYjg1MjEyOTk4OWMwZWNiMzViOTg3NzY5M2QyMmFjMjNkMGY4YTdhOTJiNzEzOTQzM2UwN2Y0OGU1YSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.p-JV9ycUKEB7RuDArvsHhWTNGWwnCcYOpwZH9UvpyiE";>
> 
> This works, and it may be more readable/intuitive than this current PR. When 
> we initialize saved_image we should flood fill it with transparent pixels.
> 
> I chose the approach in this PR though because it's lazier. (That is: it 
> should be a little bit less work, CPU-wise.)
> 
> If anyone wants we can switch to this approach.

I think this is better then current approach of having check multiple times.

src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java line 419:

> 417:                 // are NOT the transparent pixel: we need to replace it
> 418:                 // with the appropriate transparent pixel.
> 419:                 boolean replaceTransPixelsInSavedImage = save &&

We can move this check to the place where we actually create and initialize 
saved image.

src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java line 436:

> 434:                         }
> 435:                         runstart = -1;
> 436:                         if (replaceTransPixelsInSavedImage) {

We are hitting this check for each transparent pixel for all frames with 
transparent pixel index > 0 and not initial frame until saved_image is not 
null. In the .gif present in this PR we are checking this condition for each 
transparent pixel of 2nd and 3rd frame. If GIF contains many frames and 
disposal method save comes in later part of animation we will hit this check 
many times.

I think its better to just initialize saved_image with proper transparent pixel 
index when it is getting created. Then we can completely remove state 
management of `replaceTransPixelsInSavedImage` and this check.

test/jdk/sun/awt/image/gif/GifComparison.java line 117:

> 115:      *         conditions are met.
> 116:      */
> 117:     public static BufferedImage run(URL srcURL) throws Throwable {

If we can actually update this helper to check a specific frame is GIF and not 
run throw all the frame its better.
In this PR we just need the info of 4th frame.

Is this way of checking all frames useful in any other PR's which you have 
raised?

test/jdk/sun/awt/image/gif/GifEmptyBackgroundTest.java line 40:

> 38:         BufferedImage bi = GifComparison.run(srcURL);
> 39: 
> 40:         if (new Color(bi.getRGB(20, 20), true).getAlpha() != 0) {

Without the product fix this test fails while checking opacity of (0,0) itself 
and we don't hit this check.

`Exception in thread "main" java.lang.Error: pixels at (0, 0) have different 
opacities: 0 vs ff000000`

-------------

PR Review: https://git.openjdk.org/jdk/pull/25044#pullrequestreview-2871107750
PR Comment: https://git.openjdk.org/jdk/pull/25044#issuecomment-2912699099
PR Review Comment: https://git.openjdk.org/jdk/pull/25044#discussion_r2109208157
PR Review Comment: https://git.openjdk.org/jdk/pull/25044#discussion_r2109288435
PR Review Comment: https://git.openjdk.org/jdk/pull/25044#discussion_r2109298270
PR Review Comment: https://git.openjdk.org/jdk/pull/25044#discussion_r2109318427

Reply via email to