示例#1
0
  /**
   * Write the device independent image array stored in the specified loader to the specified output
   * stream using the specified file format.
   */
  public static void save(OutputStream os, int format, ImageLoader loader) {
    if (format < 0 || format >= FORMATS.length) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
    if (FORMATS[format] == null) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
    if (loader.data == null || loader.data.length < 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);

    LEDataOutputStream stream = new LEDataOutputStream(os);
    FileFormat fileFormat = null;
    try {
      Class clazz = Class.forName(FORMAT_PACKAGE + '.' + FORMATS[format] + FORMAT_SUFFIX);
      fileFormat = (FileFormat) clazz.newInstance();
    } catch (Exception e) {
      SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
    }
    if (format == SWT.IMAGE_BMP_RLE) {
      switch (loader.data[0].depth) {
        case 8:
          fileFormat.compression = 1;
          break;
        case 4:
          fileFormat.compression = 2;
          break;
      }
    }
    fileFormat.unloadIntoStream(loader, stream);
  }
 /** Read a palette from the input stream. */
 PaletteData readPalette(int numColors) {
   byte[] bytes = new byte[numColors * 3];
   try {
     if (inputStream.read(bytes) != bytes.length) SWT.error(SWT.ERROR_INVALID_IMAGE);
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   RGB[] colors = new RGB[numColors];
   for (int i = 0; i < numColors; i++)
     colors[i] = new RGB(bytes[i * 3] & 0xFF, bytes[i * 3 + 1] & 0xFF, bytes[i * 3 + 2] & 0xFF);
   return new PaletteData(colors);
 }
 void decompressData(byte[] src, byte[] dest, int stride, int cmp) {
   if (cmp == 1) { // BMP_RLE8_COMPRESSION
     if (decompressRLE8Data(src, src.length, stride, dest, dest.length) <= 0)
       SWT.error(SWT.ERROR_INVALID_IMAGE);
     return;
   }
   if (cmp == 2) { // BMP_RLE4_COMPRESSION
     if (decompressRLE4Data(src, src.length, stride, dest, dest.length) <= 0)
       SWT.error(SWT.ERROR_INVALID_IMAGE);
     return;
   }
   SWT.error(SWT.ERROR_INVALID_IMAGE);
 }
示例#4
0
 /**
  * Read the specified input stream, and return the device independent image array represented by
  * the stream.
  */
 public ImageData[] loadFromStream(LEDataInputStream stream) {
   try {
     inputStream = stream;
     return loadFromByteStream();
   } catch (Exception e) {
     if (e instanceof IOException) {
       SWT.error(SWT.ERROR_IO, e);
     } else {
       SWT.error(SWT.ERROR_INVALID_IMAGE, e);
     }
     return null;
   }
 }
 int[] loadFileHeader() {
   int[] header = new int[5];
   try {
     header[0] = inputStream.readShort();
     header[1] = inputStream.readInt();
     header[2] = inputStream.readShort();
     header[3] = inputStream.readShort();
     header[4] = inputStream.readInt();
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   if (header[0] != 0x4D42) SWT.error(SWT.ERROR_INVALID_IMAGE);
   return header;
 }
 /**
  * We have just read the GraphicsControl extension identifier from the input stream. Read in the
  * control information, store it, and return it.
  */
 byte[] readGraphicsControlExtension() {
   try {
     // Read size of block = 0x04.
     inputStream.read();
     // Read the control block.
     byte[] controlBlock = new byte[4];
     inputStream.read(controlBlock);
     byte bitField = controlBlock[0];
     // Store the user input field.
     // userInput = (bitField & 0x02) != 0;
     // Store the disposal method.
     disposalMethod = (bitField >> 2) & 0x07;
     // Store the delay time.
     delayTime = (controlBlock[1] & 0xFF) | ((controlBlock[2] & 0xFF) << 8);
     // Store the transparent color.
     if ((bitField & 0x01) != 0) {
       transparentPixel = controlBlock[3] & 0xFF;
     } else {
       transparentPixel = -1;
     }
     return controlBlock;
   } catch (Exception e) {
     SWT.error(SWT.ERROR_IO, e);
     return null;
   }
 }
 /**
  * Unload the given image's data into the given byte stream using the given compression strategy.
  * Answer the number of bytes written. Method modified to use the passed data if it is not null.
  */
 int unloadData(ImageData image, byte[] data, OutputStream out, int comp) {
   int totalSize = 0;
   try {
     if (comp == 0) return unloadDataNoCompression(image, data, out);
     int bpl = (image.width * image.depth + 7) / 8;
     int bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
     int imageBpl = image.bytesPerLine;
     // Compression can actually take twice as much space, in worst case
     byte[] buf = new byte[bmpBpl * 2];
     int srcOffset = imageBpl * (image.height - 1); // Start at last line
     if (data == null) data = image.data;
     totalSize = 0;
     byte[] buf2 = new byte[32768];
     int buf2Offset = 0;
     for (int y = image.height - 1; y >= 0; y--) {
       int lineSize = compress(comp, data, srcOffset, bpl, buf, y == 0);
       if (buf2Offset + lineSize > buf2.length) {
         out.write(buf2, 0, buf2Offset);
         buf2Offset = 0;
       }
       System.arraycopy(buf, 0, buf2, buf2Offset, lineSize);
       buf2Offset += lineSize;
       totalSize += lineSize;
       srcOffset -= imageBpl;
     }
     if (buf2Offset > 0) out.write(buf2, 0, buf2Offset);
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   return totalSize;
 }
 /** Write out a GraphicsControlBlock to describe the specified device independent image. */
 void writeGraphicsControlBlock(ImageData image) {
   try {
     outputStream.write(GIF_EXTENSION_BLOCK_ID);
     outputStream.write(GIF_GRAPHICS_CONTROL_BLOCK_ID);
     byte[] gcBlock = new byte[4];
     gcBlock[0] = 0;
     gcBlock[1] = 0;
     gcBlock[2] = 0;
     gcBlock[3] = 0;
     if (image.transparentPixel != -1) {
       gcBlock[0] = (byte) 0x01;
       gcBlock[3] = (byte) image.transparentPixel;
     }
     if (image.disposalMethod != 0) {
       gcBlock[0] |= (byte) ((image.disposalMethod & 0x07) << 2);
     }
     if (image.delayTime != 0) {
       gcBlock[1] = (byte) (image.delayTime & 0xFF);
       gcBlock[2] = (byte) ((image.delayTime >> 8) & 0xFF);
     }
     outputStream.write((byte) gcBlock.length);
     outputStream.write(gcBlock);
     outputStream.write(0); // Block terminator
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
 }
 /** Read and return the next block or extension identifier from the file. */
 int readID() {
   try {
     return inputStream.read();
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   return -1;
 }
 /**
  * Compress numBytes bytes of image data from src, storing in dest (starting at 0), using the
  * technique specified by comp. If last is true, this indicates the last line of the image. Answer
  * the size of the compressed data.
  */
 int compress(int comp, byte[] src, int srcOffset, int numBytes, byte[] dest, boolean last) {
   if (comp == 1) { // BMP_RLE8_COMPRESSION
     return compressRLE8Data(src, srcOffset, numBytes, dest, last);
   }
   if (comp == 2) { // BMP_RLE4_COMPRESSION
     return compressRLE4Data(src, srcOffset, numBytes, dest, last);
   }
   SWT.error(SWT.ERROR_INVALID_IMAGE);
   return 0;
 }
 PaletteData loadPalette(byte[] infoHeader) {
   int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
   if (depth <= 8) {
     int numColors =
         (infoHeader[32] & 0xFF)
             | ((infoHeader[33] & 0xFF) << 8)
             | ((infoHeader[34] & 0xFF) << 16)
             | ((infoHeader[35] & 0xFF) << 24);
     if (numColors == 0) {
       numColors = 1 << depth;
     } else {
       if (numColors > 256) numColors = 256;
     }
     byte[] buf = new byte[numColors * 4];
     try {
       if (inputStream.read(buf) != buf.length) SWT.error(SWT.ERROR_INVALID_IMAGE);
     } catch (IOException e) {
       SWT.error(SWT.ERROR_IO, e);
     }
     return paletteFromBytes(buf, numColors);
   }
   if (depth == 16) {
     if (this.compression == 3) {
       try {
         return new PaletteData(
             inputStream.readInt(), inputStream.readInt(), inputStream.readInt());
       } catch (IOException e) {
         SWT.error(SWT.ERROR_IO, e);
       }
     }
     return new PaletteData(0x7C00, 0x3E0, 0x1F);
   }
   if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
   if (this.compression == 3) {
     try {
       return new PaletteData(inputStream.readInt(), inputStream.readInt(), inputStream.readInt());
     } catch (IOException e) {
       SWT.error(SWT.ERROR_IO, e);
     }
   }
   return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
 }
 /** Read a control extension. Return the extension block data. */
 byte[] readExtension() {
   int extensionID = readID();
   if (extensionID == GIF_COMMENT_BLOCK_ID) return readCommentExtension();
   if (extensionID == GIF_PLAIN_TEXT_BLOCK_ID) return readPlainTextExtension();
   if (extensionID == GIF_GRAPHICS_CONTROL_BLOCK_ID) return readGraphicsControlExtension();
   if (extensionID == GIF_APPLICATION_EXTENSION_BLOCK_ID) return readApplicationExtension();
   // Otherwise, we don't recognize the block. If the
   // field size is correct, we can just skip over
   // the block contents.
   try {
     int extSize = inputStream.read();
     if (extSize < 0) {
       SWT.error(SWT.ERROR_INVALID_IMAGE);
     }
     byte[] ext = new byte[extSize];
     inputStream.read(ext, 0, extSize);
     return ext;
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
     return null;
   }
 }
示例#13
0
 /**
  * Write the device independent image array stored in the specified loader to the specified output
  * stream.
  */
 public void unloadIntoStream(ImageLoader loader, LEDataOutputStream stream) {
   try {
     outputStream = stream;
     unloadIntoByteStream(loader);
     outputStream.flush();
   } catch (Exception e) {
     try {
       outputStream.flush();
     } catch (Exception f) {
     }
     SWT.error(SWT.ERROR_IO, e);
   }
 }
 byte[] loadData(byte[] infoHeader, int stride) {
   int height =
       (infoHeader[8] & 0xFF)
           | ((infoHeader[9] & 0xFF) << 8)
           | ((infoHeader[10] & 0xFF) << 16)
           | ((infoHeader[11] & 0xFF) << 24);
   if (height < 0) height = -height;
   int dataSize = height * stride;
   byte[] data = new byte[dataSize];
   int cmp =
       (infoHeader[16] & 0xFF)
           | ((infoHeader[17] & 0xFF) << 8)
           | ((infoHeader[18] & 0xFF) << 16)
           | ((infoHeader[19] & 0xFF) << 24);
   if (cmp == 0 || cmp == 3) { // BMP_NO_COMPRESSION
     try {
       if (inputStream.read(data) != dataSize) SWT.error(SWT.ERROR_INVALID_IMAGE);
     } catch (IOException e) {
       SWT.error(SWT.ERROR_IO, e);
     }
   } else {
     int compressedSize =
         (infoHeader[20] & 0xFF)
             | ((infoHeader[21] & 0xFF) << 8)
             | ((infoHeader[22] & 0xFF) << 16)
             | ((infoHeader[23] & 0xFF) << 24);
     byte[] compressed = new byte[compressedSize];
     try {
       if (inputStream.read(compressed) != compressedSize) SWT.error(SWT.ERROR_INVALID_IMAGE);
     } catch (IOException e) {
       SWT.error(SWT.ERROR_IO, e);
     }
     decompressData(compressed, data, stride, cmp);
   }
   return data;
 }
 /** Write the specified palette to the output stream. */
 void writePalette(PaletteData palette, int depth) {
   byte[] bytes = new byte[(1 << depth) * 3];
   int offset = 0;
   for (int i = 0; i < palette.colors.length; i++) {
     RGB color = palette.colors[i];
     bytes[offset] = (byte) color.red;
     bytes[offset + 1] = (byte) color.green;
     bytes[offset + 2] = (byte) color.blue;
     offset += 3;
   }
   try {
     outputStream.write(bytes);
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
 }
 /**
  * We have just read the Comment extension identifier from the input stream. Read in the rest of
  * the comment and return it. GIF comment blocks are variable size.
  */
 byte[] readCommentExtension() {
   try {
     byte[] comment = new byte[0];
     byte[] block = new byte[255];
     int size = inputStream.read();
     while ((size > 0) && (inputStream.read(block, 0, size) != -1)) {
       byte[] oldComment = comment;
       comment = new byte[oldComment.length + size];
       System.arraycopy(oldComment, 0, comment, 0, oldComment.length);
       System.arraycopy(block, 0, comment, oldComment.length, size);
       size = inputStream.read();
     }
     return comment;
   } catch (Exception e) {
     SWT.error(SWT.ERROR_IO, e);
     return null;
   }
 }
示例#17
0
 /**
  * Read the specified input stream using the specified loader, and return the device independent
  * image array represented by the stream.
  */
 public static ImageData[] load(InputStream is, ImageLoader loader) {
   FileFormat fileFormat = null;
   LEDataInputStream stream = new LEDataInputStream(is);
   for (int i = 1; i < FORMATS.length; i++) {
     if (FORMATS[i] != null) {
       try {
         fileFormat = getFileFormat(stream, FORMATS[i]);
         if (fileFormat != null) break;
       } catch (ClassNotFoundException e) {
         FORMATS[i] = null;
       } catch (Exception e) {
       }
     }
   }
   if (fileFormat == null) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
   fileFormat.loader = loader;
   return fileFormat.loadFromStream(stream);
 }
 /**
  * Prepare the given image's data for unloading into a byte stream using no compression strategy.
  * Answer the number of bytes written. Method modified to use the passed data if it is not null.
  */
 int unloadDataNoCompression(ImageData image, byte[] data, OutputStream out) {
   int bmpBpl = 0;
   try {
     int bpl = (image.width * image.depth + 7) / 8;
     bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
     int linesPerBuf = 32678 / bmpBpl;
     byte[] buf = new byte[linesPerBuf * bmpBpl];
     if (data == null) data = image.data;
     int imageBpl = image.bytesPerLine;
     int dataIndex = imageBpl * (image.height - 1); // Start at last line
     if (image.depth == 16) {
       for (int y = 0; y < image.height; y += linesPerBuf) {
         int count = image.height - y;
         if (linesPerBuf < count) count = linesPerBuf;
         int bufOffset = 0;
         for (int i = 0; i < count; i++) {
           for (int wIndex = 0; wIndex < bpl; wIndex += 2) {
             buf[bufOffset + wIndex + 1] = data[dataIndex + wIndex + 1];
             buf[bufOffset + wIndex] = data[dataIndex + wIndex];
           }
           bufOffset += bmpBpl;
           dataIndex -= imageBpl;
         }
         out.write(buf, 0, bufOffset);
       }
     } else {
       for (int y = 0; y < image.height; y += linesPerBuf) {
         int tmp = image.height - y;
         int count = tmp < linesPerBuf ? tmp : linesPerBuf;
         int bufOffset = 0;
         for (int i = 0; i < count; i++) {
           System.arraycopy(data, dataIndex, buf, bufOffset, bpl);
           bufOffset += bmpBpl;
           dataIndex -= imageBpl;
         }
         out.write(buf, 0, bufOffset);
       }
     }
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   return bmpBpl * image.height;
 }
 /**
  * We have just read the Application extension identifier from the input stream. Read in the rest
  * of the extension, look for and store 'number of repeats', and return the data.
  */
 byte[] readApplicationExtension() {
   try {
     // Read block data.
     int blockSize = inputStream.read();
     byte[] blockData = new byte[blockSize];
     inputStream.read(blockData);
     // Read application data.
     byte[] data = new byte[0];
     byte[] block = new byte[255];
     int size = inputStream.read();
     while ((size > 0) && (inputStream.read(block, 0, size) != -1)) {
       byte[] oldData = data;
       data = new byte[oldData.length + size];
       System.arraycopy(oldData, 0, data, 0, oldData.length);
       System.arraycopy(block, 0, data, oldData.length, size);
       size = inputStream.read();
     }
     // Look for the NETSCAPE 'repeat count' field for an animated GIF.
     boolean netscape =
         blockSize > 7
             && blockData[0] == 'N'
             && blockData[1] == 'E'
             && blockData[2] == 'T'
             && blockData[3] == 'S'
             && blockData[4] == 'C'
             && blockData[5] == 'A'
             && blockData[6] == 'P'
             && blockData[7] == 'E';
     boolean authentic =
         blockSize > 10 && blockData[8] == '2' && blockData[9] == '.' && blockData[10] == '0';
     if (netscape && authentic && data[0] == 01) { // $NON-NLS-1$ //$NON-NLS-2$
       repeatCount = (data[1] & 0xFF) | ((data[2] & 0xFF) << 8);
       loader.repeatCount = repeatCount;
     }
     return data;
   } catch (Exception e) {
     SWT.error(SWT.ERROR_IO, e);
     return null;
   }
 }
 /**
  * We have just read the PlainText extension identifier from the input stream. Read in the plain
  * text info and text, and return the text. GIF plain text blocks are variable size.
  */
 byte[] readPlainTextExtension() {
   try {
     // Read size of block = 0x0C.
     inputStream.read();
     // Read the text information (x, y, width, height, colors).
     byte[] info = new byte[12];
     inputStream.read(info);
     // Read the text.
     byte[] text = new byte[0];
     byte[] block = new byte[255];
     int size = inputStream.read();
     while ((size > 0) && (inputStream.read(block, 0, size) != -1)) {
       byte[] oldText = text;
       text = new byte[oldText.length + size];
       System.arraycopy(oldText, 0, text, 0, oldText.length);
       System.arraycopy(block, 0, text, oldText.length, size);
       size = inputStream.read();
     }
     return text;
   } catch (Exception e) {
     SWT.error(SWT.ERROR_IO, e);
     return null;
   }
 }
 @Override
 ImageData[] loadFromByteStream() {
   int[] fileHeader = loadFileHeader();
   byte[] infoHeader = new byte[BMPHeaderFixedSize];
   try {
     inputStream.read(infoHeader);
   } catch (Exception e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   int width =
       (infoHeader[4] & 0xFF)
           | ((infoHeader[5] & 0xFF) << 8)
           | ((infoHeader[6] & 0xFF) << 16)
           | ((infoHeader[7] & 0xFF) << 24);
   int height =
       (infoHeader[8] & 0xFF)
           | ((infoHeader[9] & 0xFF) << 8)
           | ((infoHeader[10] & 0xFF) << 16)
           | ((infoHeader[11] & 0xFF) << 24);
   if (height < 0) height = -height;
   int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
   this.compression =
       (infoHeader[16] & 0xFF)
           | ((infoHeader[17] & 0xFF) << 8)
           | ((infoHeader[18] & 0xFF) << 16)
           | ((infoHeader[19] & 0xFF) << 24);
   PaletteData palette = loadPalette(infoHeader);
   if (inputStream.getPosition() < fileHeader[4]) {
     // Seek to the specified offset
     try {
       inputStream.skip(fileHeader[4] - inputStream.getPosition());
     } catch (IOException e) {
       SWT.error(SWT.ERROR_IO, e);
     }
   }
   byte[] data = loadData(infoHeader);
   this.importantColors =
       (infoHeader[36] & 0xFF)
           | ((infoHeader[37] & 0xFF) << 8)
           | ((infoHeader[38] & 0xFF) << 16)
           | ((infoHeader[39] & 0xFF) << 24);
   int xPelsPerMeter =
       (infoHeader[24] & 0xFF)
           | ((infoHeader[25] & 0xFF) << 8)
           | ((infoHeader[26] & 0xFF) << 16)
           | ((infoHeader[27] & 0xFF) << 24);
   int yPelsPerMeter =
       (infoHeader[28] & 0xFF)
           | ((infoHeader[29] & 0xFF) << 8)
           | ((infoHeader[30] & 0xFF) << 16)
           | ((infoHeader[31] & 0xFF) << 24);
   this.pelsPerMeter = new Point(xPelsPerMeter, yPelsPerMeter);
   int type =
       (this.compression == 1 /*BMP_RLE8_COMPRESSION*/)
               || (this.compression == 2 /*BMP_RLE4_COMPRESSION*/)
           ? SWT.IMAGE_BMP_RLE
           : SWT.IMAGE_BMP;
   return new ImageData[] {
     ImageData.internal_new(
         width, height, bitCount, palette, 4, data, 0, null, null, -1, -1, type, 0, 0, 0, 0)
   };
 }
  void convertPixelsToBGR(ImageData image, byte[] dest) {
    /*
     * For direct palette, uncompressed image, BMP encoders expect the
     * pixels to be in BGR format for 24 & 32 bit and RGB 1555 for 16 bit
     * On Linux and MacOS, the pixels are in RGB format. Also, in
     * MacOS, the alpha byte may be first.
     * Hence, we use the palette information of the image and convert
     * the pixels to the required format. Converted pixels are stored
     * in dest byte array.
     */
    byte[] data = image.data;
    PaletteData palette = image.palette;
    for (int y = 0; y < image.height; y++) {
      int index;
      int srcX = 0, srcY = y;
      int numOfBytes = image.depth / 8;
      index = (y * image.bytesPerLine);

      for (int i = 0; i < image.width; i++) {
        int pixel = 0;
        switch (image.depth) {
          case 32:
            pixel =
                ((data[index] & 0xFF) << 24)
                    | ((data[index + 1] & 0xFF) << 16)
                    | ((data[index + 2] & 0xFF) << 8)
                    | (data[index + 3] & 0xFF);
            break;
          case 24:
            pixel =
                ((data[index] & 0xFF) << 16)
                    | ((data[index + 1] & 0xFF) << 8)
                    | (data[index + 2] & 0xFF);
            break;
          case 16:
            pixel = ((data[index + 1] & 0xFF) << 8) | (data[index] & 0xFF);
            break;
          default:
            SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
        }

        if (image.depth == 16) {
          /* convert to RGB 555 format */
          int r = pixel & palette.redMask;
          r = ((palette.redShift < 0) ? r >>> -palette.redShift : r << palette.redShift);

          int g = pixel & palette.greenMask;
          g = ((palette.greenShift < 0) ? g >>> -palette.greenShift : g << palette.greenShift);
          g = (g & 0xF8); /* In 565 format, G is 6 bit, mask it to 5 bit */

          int b = pixel & palette.blueMask;
          b = ((palette.blueShift < 0) ? b >>> -palette.blueShift : b << palette.blueShift);

          int modPixel = (r << 7) | (g << 2) | (b >> 3);
          dest[index] = (byte) (modPixel & 0xFF);
          dest[index + 1] = (byte) ((modPixel >> 8) & 0xFF);
        } else {
          /* convert to BGR format */
          int b = pixel & palette.blueMask;
          dest[index] =
              (byte) ((palette.blueShift < 0) ? b >>> -palette.blueShift : b << palette.blueShift);

          int g = pixel & palette.greenMask;
          dest[index + 1] =
              (byte)
                  ((palette.greenShift < 0) ? g >>> -palette.greenShift : g << palette.greenShift);

          int r = pixel & palette.redMask;
          dest[index + 2] =
              (byte) ((palette.redShift < 0) ? r >>> -palette.redShift : r << palette.redShift);

          if (numOfBytes == 4) dest[index + 3] = 0;
        }

        srcX++;
        if (srcX >= image.width) {
          srcY++;
          index = srcY * image.bytesPerLine;
          srcX = 0;
        } else {
          index += numOfBytes;
        }
      }
    }
  }
 /**
  * Return a DeviceIndependentImage representing the image block at the current position in the
  * input stream. Throw an error if an error occurs.
  */
 ImageData readImageBlock(PaletteData defaultPalette) {
   int depth;
   PaletteData palette;
   byte[] block = new byte[9];
   try {
     inputStream.read(block);
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   int left = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8);
   int top = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8);
   int width = (block[4] & 0xFF) | ((block[5] & 0xFF) << 8);
   int height = (block[6] & 0xFF) | ((block[7] & 0xFF) << 8);
   byte bitField = block[8];
   boolean interlaced = (bitField & 0x40) != 0;
   // boolean sorted = (bitField & 0x20) != 0;
   if ((bitField & 0x80) != 0) {
     // Local palette.
     depth = (bitField & 0x7) + 1;
     palette = readPalette(1 << depth);
   } else {
     // No local palette.
     depth = defaultDepth;
     palette = defaultPalette;
   }
   /* Work around: Ignore the case where a GIF specifies an
    * invalid index for the transparent pixel that is larger
    * than the number of entries in the palette. */
   if (transparentPixel > 1 << depth) {
     transparentPixel = -1;
   }
   // Promote depth to next highest supported value.
   if (!(depth == 1 || depth == 4 || depth == 8)) {
     if (depth < 4) depth = 4;
     else depth = 8;
   }
   if (palette == null) {
     palette = grayRamp(1 << depth);
   }
   int initialCodeSize = -1;
   try {
     initialCodeSize = inputStream.read();
   } catch (IOException e) {
     SWT.error(SWT.ERROR_IO, e);
   }
   if (initialCodeSize < 0) {
     SWT.error(SWT.ERROR_INVALID_IMAGE);
   }
   ImageData image =
       ImageData.internal_new(
           width,
           height,
           depth,
           palette,
           4,
           null,
           0,
           null,
           null,
           -1,
           transparentPixel,
           SWT.IMAGE_GIF,
           left,
           top,
           disposalMethod,
           delayTime);
   LZWCodec codec = new LZWCodec();
   codec.decode(inputStream, loader, image, interlaced, initialCodeSize);
   return image;
 }
  /**
   * Load the GIF image(s) stored in the input stream. Return an array of ImageData representing the
   * image(s).
   */
  @Override
  ImageData[] loadFromByteStream() {
    byte[] signature = new byte[3];
    byte[] versionBytes = new byte[3];
    byte[] block = new byte[7];
    try {
      inputStream.read(signature);
      if (!(signature[0] == 'G' && signature[1] == 'I' && signature[2] == 'F'))
        SWT.error(SWT.ERROR_INVALID_IMAGE);

      inputStream.read(versionBytes);

      inputStream.read(block);
    } catch (IOException e) {
      SWT.error(SWT.ERROR_IO, e);
    }
    screenWidth = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8);
    loader.logicalScreenWidth = screenWidth;
    screenHeight = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8);
    loader.logicalScreenHeight = screenHeight;
    byte bitField = block[4];
    backgroundPixel = block[5] & 0xFF;
    // aspect = block[6] & 0xFF;
    bitsPerPixel = ((bitField >> 4) & 0x07) + 1;
    defaultDepth = (bitField & 0x7) + 1;
    PaletteData palette = null;
    if ((bitField & 0x80) != 0) {
      // Global palette.
      // sorted = (bitField & 0x8) != 0;
      palette = readPalette(1 << defaultDepth);
    } else {
      // No global palette.
      // sorted = false;
      backgroundPixel = -1;
      defaultDepth = bitsPerPixel;
    }
    loader.backgroundPixel = backgroundPixel;

    ImageData[] images = new ImageData[0];
    int id = readID();
    while (id != GIF_TRAILER_ID && id != -1) {
      if (id == GIF_IMAGE_BLOCK_ID) {
        ImageData image = readImageBlock(palette);
        if (loader.hasListeners()) {
          loader.notifyListeners(new ImageLoaderEvent(loader, image, 3, true));
        }
        ImageData[] oldImages = images;
        images = new ImageData[oldImages.length + 1];
        System.arraycopy(oldImages, 0, images, 0, oldImages.length);
        images[images.length - 1] = image;
      } else if (id == GIF_EXTENSION_BLOCK_ID) {
        /* Read the extension block. Currently, only the
         * interesting parts of certain extensions are kept,
         * and the rest is discarded. In future, if we want
         * to keep extensions, they should be grouped with
         * the image data before which they appear.
         */
        readExtension();
      } else {
        /* The GIF is not to spec, but try to salvage it
         * if we read at least one image. */
        if (images.length > 0) break;
        SWT.error(SWT.ERROR_INVALID_IMAGE);
      }
      id = readID(); // block terminator (0)
      if (id == 0) id = readID(); // next block ID (unless we just read it)
    }
    return images;
  }
  /** Unload a DeviceIndependentImage using Windows .BMP format into the given byte stream. */
  @Override
  void unloadIntoByteStream(ImageLoader loader) {
    ImageData image = loader.data[0];
    byte[] rgbs;
    int numCols;
    if (!((image.depth == 1)
        || (image.depth == 4)
        || (image.depth == 8)
        || (image.depth == 16)
        || (image.depth == 24)
        || (image.depth == 32))) SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
    int comp = this.compression;
    if (!((comp == 0)
        || ((comp == 1) && (image.depth == 8))
        || ((comp == 2) && (image.depth == 4)))) SWT.error(SWT.ERROR_INVALID_IMAGE);
    PaletteData pal = image.palette;
    if ((image.depth == 16) || (image.depth == 24) || (image.depth == 32)) {
      if (!pal.isDirect) SWT.error(SWT.ERROR_INVALID_IMAGE);
      numCols = 0;
      rgbs = null;
    } else {
      if (pal.isDirect) SWT.error(SWT.ERROR_INVALID_IMAGE);
      numCols = pal.colors.length;
      rgbs = paletteToBytes(pal);
    }
    // Fill in file header, except for bfsize, which is done later.
    int headersSize = BMPFileHeaderSize + BMPHeaderFixedSize;
    int[] fileHeader = new int[5];
    fileHeader[0] = 0x4D42; // Signature
    fileHeader[1] = 0; // File size - filled in later
    fileHeader[2] = 0; // Reserved 1
    fileHeader[3] = 0; // Reserved 2
    fileHeader[4] = headersSize; // Offset to data
    if (rgbs != null) {
      fileHeader[4] += rgbs.length;
    }

    byte iData[] = null;
    // If the pixels are not in the expected BMP format, convert them.
    if (pal.isDirect && !isPaletteBMP(pal, image.depth)) {
      // array to store the converted pixels
      iData = new byte[image.data.length];
      convertPixelsToBGR(image, iData);
    }

    // Prepare data. This is done first so we don't have to try to rewind
    // the stream and fill in the details later.
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    unloadData(image, iData, out, comp);
    byte[] data = out.toByteArray();

    // Calculate file size
    fileHeader[1] = fileHeader[4] + data.length;

    // Write the headers
    try {
      outputStream.writeShort(fileHeader[0]);
      outputStream.writeInt(fileHeader[1]);
      outputStream.writeShort(fileHeader[2]);
      outputStream.writeShort(fileHeader[3]);
      outputStream.writeInt(fileHeader[4]);
    } catch (IOException e) {
      SWT.error(SWT.ERROR_IO, e);
    }
    try {
      outputStream.writeInt(BMPHeaderFixedSize);
      outputStream.writeInt(image.width);
      outputStream.writeInt(image.height);
      outputStream.writeShort(1);
      outputStream.writeShort((short) image.depth);
      outputStream.writeInt(comp);
      outputStream.writeInt(data.length);
      outputStream.writeInt(pelsPerMeter.x);
      outputStream.writeInt(pelsPerMeter.y);
      outputStream.writeInt(numCols);
      outputStream.writeInt(importantColors);
    } catch (IOException e) {
      SWT.error(SWT.ERROR_IO, e);
    }

    // Unload palette
    if (numCols > 0) {
      try {
        outputStream.write(rgbs);
      } catch (IOException e) {
        SWT.error(SWT.ERROR_IO, e);
      }
    }

    // Unload the data
    try {
      outputStream.write(data);
    } catch (IOException e) {
      SWT.error(SWT.ERROR_IO, e);
    }
  }
  @Override
  void unloadIntoByteStream(ImageLoader loader) {

    /* Step 1: Acquire GIF parameters. */
    ImageData[] data = loader.data;
    int frameCount = data.length;
    boolean multi = frameCount > 1;
    ImageData firstImage = data[0];
    int logicalScreenWidth = multi ? loader.logicalScreenWidth : firstImage.width;
    int logicalScreenHeight = multi ? loader.logicalScreenHeight : firstImage.height;
    int backgroundPixel = loader.backgroundPixel;
    int depth = firstImage.depth;
    PaletteData palette = firstImage.palette;
    RGB[] colors = palette.getRGBs();
    short globalTable = 1;

    /* Step 2: Check for validity and global/local color map. */
    if (!(depth == 1 || depth == 4 || depth == 8)) {
      SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
    }
    for (int i = 0; i < frameCount; i++) {
      if (data[i].palette.isDirect) {
        SWT.error(SWT.ERROR_INVALID_IMAGE);
      }
      if (multi) {
        if (!(data[i].height <= logicalScreenHeight
            && data[i].width <= logicalScreenWidth
            && data[i].depth == depth)) {
          SWT.error(SWT.ERROR_INVALID_IMAGE);
        }
        if (globalTable == 1) {
          RGB rgbs[] = data[i].palette.getRGBs();
          if (rgbs.length != colors.length) {
            globalTable = 0;
          } else {
            for (int j = 0; j < colors.length; j++) {
              if (!(rgbs[j].red == colors[j].red
                  && rgbs[j].green == colors[j].green
                  && rgbs[j].blue == colors[j].blue)) globalTable = 0;
            }
          }
        }
      }
    }

    try {
      /* Step 3: Write the GIF89a Header and Logical Screen Descriptor. */
      outputStream.write(GIF89a);
      int bitField = globalTable * 128 + (depth - 1) * 16 + depth - 1;
      outputStream.writeShort((short) logicalScreenWidth);
      outputStream.writeShort((short) logicalScreenHeight);
      outputStream.write(bitField);
      outputStream.write(backgroundPixel);
      outputStream.write(0); // Aspect ratio is 1:1
    } catch (IOException e) {
      SWT.error(SWT.ERROR_IO, e);
    }

    /* Step 4: Write Global Color Table if applicable. */
    if (globalTable == 1) {
      writePalette(palette, depth);
    }

    /* Step 5: Write Application Extension if applicable. */
    if (multi) {
      int repeatCount = loader.repeatCount;
      try {
        outputStream.write(GIF_EXTENSION_BLOCK_ID);
        outputStream.write(GIF_APPLICATION_EXTENSION_BLOCK_ID);
        outputStream.write(NETSCAPE2_0.length);
        outputStream.write(NETSCAPE2_0);
        outputStream.write(3); // Three bytes follow
        outputStream.write(1); // Extension type
        outputStream.writeShort((short) repeatCount);
        outputStream.write(0); // Block terminator
      } catch (IOException e) {
        SWT.error(SWT.ERROR_IO, e);
      }
    }

    for (int frame = 0; frame < frameCount; frame++) {

      /* Step 6: Write Graphics Control Block for each frame if applicable. */
      if (multi || data[frame].transparentPixel != -1) {
        writeGraphicsControlBlock(data[frame]);
      }

      /* Step 7: Write Image Header for each frame. */
      int x = data[frame].x;
      int y = data[frame].y;
      int width = data[frame].width;
      int height = data[frame].height;
      try {
        outputStream.write(GIF_IMAGE_BLOCK_ID);
        byte[] block = new byte[9];
        block[0] = (byte) (x & 0xFF);
        block[1] = (byte) ((x >> 8) & 0xFF);
        block[2] = (byte) (y & 0xFF);
        block[3] = (byte) ((y >> 8) & 0xFF);
        block[4] = (byte) (width & 0xFF);
        block[5] = (byte) ((width >> 8) & 0xFF);
        block[6] = (byte) (height & 0xFF);
        block[7] = (byte) ((height >> 8) & 0xFF);
        block[8] = (byte) (globalTable == 0 ? (depth - 1) | 0x80 : 0x00);
        outputStream.write(block);
      } catch (IOException e) {
        SWT.error(SWT.ERROR_IO, e);
      }

      /* Step 8: Write Local Color Table for each frame if applicable. */
      if (globalTable == 0) {
        writePalette(data[frame].palette, depth);
      }

      /* Step 9: Write the actual data for each frame. */
      try {
        outputStream.write(depth); // Minimum LZW Code size
      } catch (IOException e) {
        SWT.error(SWT.ERROR_IO, e);
      }
      new LZWCodec().encode(outputStream, data[frame]);
    }

    /* Step 10: Write GIF terminator. */
    try {
      outputStream.write(0x3B);
    } catch (IOException e) {
      SWT.error(SWT.ERROR_IO, e);
    }
  }