/** * 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); }
/** * 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; } }
/** * 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; } }
/** * 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); } }