Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8356137

GifImageDecode can produce opaque image when disposal method changes

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      Tested using Mac 15.4.1, Java v25

      A DESCRIPTION OF THE PROBLEM :
      When the transparent pixel index is non-zero and the frame disposal method changes from 2 to 1 (DISPOSAL_BACKGROUND_COLOR to DISPOSAL_DO_NOT_DISPOSE): the GifImageDecoder can mistakenly produce an opaque rectangle.

      I have identified a potential fix for this issue and will create those PRs shortly when/if these tickets move to the bug database.



      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The test should pass.
      ACTUAL -
      The test failed with an error, because it identified an opaque pixel that should be transparent.

      ---------- BEGIN SOURCE ----------

      import java.awt.*;
      import java.awt.image.BufferedImage;
      import java.awt.image.ColorModel;
      import java.awt.image.ImageConsumer;
      import java.awt.image.IndexColorModel;
      import java.net.URL;
      import java.util.ArrayList;
      import java.util.Hashtable;
      import java.util.concurrent.Semaphore;

      public class GifEmptyBackgroundTest {
          public static void main(String[] args) throws Exception {
              URL srcURL = GifEmptyBackgroundTest.class.getResource("CC0-3D.gif");
              BufferedImage[] frames = getFrames(srcURL, 31);

              boolean pass = true;
              if (new Color(frames[30].getRGB(20, 20), true).getAlpha() != 0) {
                  System.err.println("Sampling at (20,20) failed");
                  pass = false;
              }

              if (!pass)
                  throw new Error("See System.err for details");
          }

          private static BufferedImage[] getFrames(URL gifURL, int numberOfFrames) {
              Image image = Toolkit.getDefaultToolkit().createImage(gifURL);
              ArrayList<BufferedImage> returnValue = new ArrayList<>(numberOfFrames);

              Semaphore semaphore = new Semaphore(1);
              semaphore.acquireUninterruptibly();
              image.getSource().startProduction(new ImageConsumer() {
                  BufferedImage bi;
                  int frameCtr = 0;

                  @Override
                  public void setDimensions(int width, int height) {
                      bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                  }

                  @Override
                  public void setProperties(Hashtable<?, ?> props) {}

                  @Override
                  public void setColorModel(ColorModel model) {}

                  @Override
                  public void setHints(int hintflags) {}

                  @Override
                  public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize) {
                      try {
                          final int yMax = y + h;
                          final int xMax = x + w;

                          IndexColorModel icm = (IndexColorModel) model;
                          int[] colorModelRGBs = new int[icm.getMapSize()];
                          icm.getRGBs(colorModelRGBs);
                          int[] argbRow = new int[bi.getWidth()];

                          for (int y_ = y; y_ < yMax; y_++) {
                              int i = y_ * scansize + off;
                              for (int x_ = x; x_ < xMax; x_++, i++) {
                                  int pixel = pixels[i] & 0xff;
                                  argbRow[x_ - x] = colorModelRGBs[pixel];
                              }
                              bi.getRaster().setDataElements(x, y_, w, 1, argbRow);
                          }
                      } catch (RuntimeException e) {
                          // we don't expect this to happen, but if something goes wrong nobody else
                          // will print our stacktrace for us:
                          e.printStackTrace();
                          throw e;
                      }
                  }

                  @Override
                  public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize) {}

                  @Override
                  public void imageComplete(int status) {
                      try {
                          frameCtr++;

                          BufferedImage copy = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB);
                          Graphics2D g = copy.createGraphics();
                          g.drawImage(bi, 0, 0, null);
                          g.dispose();
                          returnValue.add(copy);

                          if (frameCtr == numberOfFrames) {
                              semaphore.release();
                              // if we don't detach this consumer the producer will loop forever
                              image.getSource().removeConsumer(this);
                              image.flush();
                          }
                      } catch(Exception e) {
                          e.printStackTrace();
                          throw new RuntimeException(e);
                      }
                  }
              });

              semaphore.acquireUninterruptibly();

              return returnValue.toArray(new BufferedImage[0]);
          }
      }

      ---------- END SOURCE ----------

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: