/** Read Image data */
  private boolean readImage(boolean first, int disposal_method, int delay) throws IOException {
    if (curframe != null && !curframe.dispose()) {
      abort();
      return false;
    }

    long tm = 0;

    if (verbose) {
      tm = System.currentTimeMillis();
    }

    // Allocate the buffer
    byte block[] = new byte[256 + 3];

    // Read the image descriptor
    if (readBytes(block, 0, 10) != 0) {
      throw new IOException();
    }
    int x = ExtractWord(block, 0);
    int y = ExtractWord(block, 2);
    int width = ExtractWord(block, 4);
    int height = ExtractWord(block, 6);
    boolean interlace = (block[8] & INTERLACEMASK) != 0;

    IndexColorModel model = global_model;

    if ((block[8] & COLORMAPMASK) != 0) {
      // We read one extra byte above so now when we must
      // transfer that byte as the first colormap byte
      // and manually read the code size when we are done
      int num_local_colors = 1 << ((block[8] & 0x7) + 1);

      // Read local colors
      byte[] local_colormap = new byte[num_local_colors * 3];
      local_colormap[0] = block[9];
      if (readBytes(local_colormap, 1, num_local_colors * 3 - 1) != 0) {
        throw new IOException();
      }

      // Now read the "real" code size byte which follows
      // the local color table
      if (readBytes(block, 9, 1) != 0) {
        throw new IOException();
      }
      model = new IndexColorModel(8, num_local_colors, local_colormap, 0, false, trans_pixel);
    } else if (model == null || trans_pixel != model.getTransparentPixel()) {
      model = new IndexColorModel(8, num_global_colors, global_colormap, 0, false, trans_pixel);
      global_model = model;
    }

    // Notify the consumers
    if (first) {
      if (global_width == 0) global_width = width;
      if (global_height == 0) global_height = height;

      setDimensions(global_width, global_height);
      setProperties(props);
      setColorModel(model);
      headerComplete();
    }

    if (disposal_method == GifFrame.DISPOSAL_SAVE && saved_image == null) {
      saved_image = new byte[global_width * global_height];
    }

    int hints = (interlace ? interlaceflags : normalflags);
    setHints(hints);

    curframe =
        new GifFrame(this, disposal_method, delay, (curframe == null), model, x, y, width, height);

    // allocate the raster data
    byte rasline[] = new byte[width];

    if (verbose) {
      System.out.print(
          "Reading a "
              + width
              + " by "
              + height
              + " "
              + (interlace ? "" : "non-")
              + "interlaced image...");
    }

    boolean ret =
        parseImage(x, y, width, height, interlace, ExtractByte(block, 9), block, rasline, model);

    if (!ret) {
      abort();
    }

    if (verbose) {
      System.out.println("done in " + (System.currentTimeMillis() - tm) + "ms");
    }

    return ret;
  }
  /** produce an image from the stream. */
  public void produceImage() throws IOException, ImageFormatException {
    try {
      readHeader();

      int totalframes = 0;
      int frameno = 0;
      int nloops = -1;
      int disposal_method = 0;
      int delay = -1;
      boolean loopsRead = false;
      boolean isAnimation = false;

      while (!aborted) {
        int code;

        if (!ImageProductionMonitor.allowsProduction()) {
          abort();
          break;
        }

        switch (code = input.read()) {
          case EXBLOCK:
            switch (code = input.read()) {
              case EX_GRAPHICS_CONTROL:
                {
                  byte buf[] = new byte[6];
                  if (readBytes(buf, 0, 6) != 0) {
                    return; // error("corrupt GIF file");
                  }
                  if ((buf[0] != 4) || (buf[5] != 0)) {
                    return; // error("corrupt GIF file (GCE size)");
                  }
                  // Get the index of the transparent color
                  delay = ExtractWord(buf, 2) * 10;
                  if (delay > 0 && !isAnimation) {
                    isAnimation = true;
                    ImageFetcher.startingAnimation();
                  }
                  disposal_method = (buf[1] >> 2) & 7;
                  if ((buf[1] & TRANSPARENCYMASK) != 0) {
                    trans_pixel = ExtractByte(buf, 4);
                  } else {
                    trans_pixel = -1;
                  }
                  break;
                }

              case EX_COMMENT:
              case EX_APPLICATION:
              default:
                boolean loop_tag = false;
                String comment = "";
                while (true) {
                  int n = input.read();
                  if (n <= 0) {
                    break;
                  }
                  byte buf[] = new byte[n];
                  if (readBytes(buf, 0, n) != 0) {
                    return; // error("corrupt GIF file");
                  }
                  if (code == EX_COMMENT) {
                    comment += new String(buf);
                  } else if (code == EX_APPLICATION) {
                    if (loop_tag) {
                      if (n == 3 && buf[0] == 1) {
                        if (loopsRead) {
                          ExtractWord(buf, 1);
                        } else {
                          nloops = ExtractWord(buf, 1);
                          loopsRead = true;
                        }
                      } else {
                        loop_tag = false;
                      }
                    }
                    if ("NETSCAPE2.0".equals(new String(buf))) {
                      loop_tag = true;
                    }
                  }
                }
                if (code == EX_COMMENT) {
                  props.put("comment", comment);
                }
                if (loop_tag && !isAnimation) {
                  isAnimation = true;
                  ImageFetcher.startingAnimation();
                }
                break;

              case -1:
                return; // error("corrupt GIF file");
            }
            break;

          case IMAGESEP:
            if (!isAnimation) {
              input.mark(0); // we don't need the mark buffer
            }
            try {
              if (!readImage(totalframes == 0, disposal_method, delay)) {
                return;
              }
            } catch (Exception e) {
              if (verbose) {
                e.printStackTrace();
              }
              return;
            }
            frameno++;
            totalframes++;
            break;

          default:
          case -1:
            if (verbose) {
              if (code == -1) {
                System.err.println("Premature EOF in GIF file," + " frame " + frameno);
              } else {
                System.err.println("corrupt GIF file (parse) [" + code + "].");
              }
            }
            if (frameno == 0) {
              return;
            }
            // NOBREAK

          case TERMINATOR:
            if (nloops == 0 || nloops-- >= 0) {
              try {
                if (curframe != null) {
                  curframe.dispose();
                  curframe = null;
                }
                input.reset();
                saved_image = null;
                saved_model = null;
                frameno = 0;
                break;
              } catch (IOException e) {
                return; // Unable to reset input buffer
              }
            }
            if (verbose && frameno != 1) {
              System.out.println(
                  "processing GIF terminator," + " frames: " + frameno + " total: " + totalframes);
            }
            imageComplete(ImageConsumer.STATICIMAGEDONE, true);
            return;
        }
      }
    } finally {
      close();
    }
  }