blob: dde4e83ed387cb34dbdb908d33c97998524add26 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.tools.internal;
import java.io.*;
/**
* Customize the icon of a Windows exe
*
* WARNING! This class is not part of SWT API. It is NOT API. It is an internal
* tool that may be changed or removed at anytime.
*
* Based on MSDN "An In-Depth Look into the Win32 Portable Executable File Format"
*/
public class IconExe {
/**
* Replace the Desktop icons provided in the Windows executable program
* with matching icons provided by the user.
*
* Takes 2 arguments
* argument 0: the Windows executable e.g c:/eclipse/eclipse.exe
* argument 1: The .ico file to write to the given executable e.g. c:/myApp.ico
*
* Note 1. Write access to the executable program is required. As a result, that
* program must not be currently running or edited elsewhere.
*
* Note 2. The Eclipse 3.1 launcher requires a .ico file with the following 6 images
* 1. 32x32, 4 bit (Windows 16 colors palette)
* 2. 16x16, 4 bit (Windows 16 colors palette)
* 3. 16x16, 8 bit (256 colors)
* 4. 32x32, 8 bit (256 colors)
* 5. 48x48, 4 bit (Windows 16 colors palette)
* 6. 48x48, 8 bit (256 colors)
* A user icon matching exactly the width/height/depth of an executable icon will be written
* to the executable and will replace that executable icon. If an executable icon
* does not match a user icon, it is silently left as is.
*
* Note 3. This function modifies the content of the executable program and may cause
* its corruption.
*/
public static void main(String[] args) {
if (args.length < 2) {
System.err.println("Usage: IconExe <windows executable> <ico file>");
return;
}
ImageLoader loader = new ImageLoader();
try {
ImageData[] data = null;
if (args.length == 2) {
/* ICO case */
data = loader.load(args[1]);
} else {
/* BMP case - each following argument is a single BMP file
* BMP is handled for testing purpose only. The ICO file is the
* official Microsoft format for image resources.
*/
data = new ImageData[args.length - 1];
for (int i = 1; i < args.length; i++) {
ImageData[] current = loader.load(args[i]);
data[i - 1] = current[0];
}
}
int nMissing = unloadIcons(args[0], data);
if (nMissing != 0) System.err.println("Error - "+nMissing+" icon(s) not replaced in "+args[0]+" using "+args[1]);
} catch (Exception e) {
e.printStackTrace();
}
}
/* Implementation */
/**
* Retrieve the Desktop icons provided in the Windows executable program.
* These icons are typically shown in various places of the Windows desktop.
*
* Note. The Eclipse 3.1 launcher returns the following 6 images
* 1. 32x32, 4 bit (Windows 16 colors palette)
* 2. 16x16, 4 bit (Windows 16 colors palette)
* 3. 16x16, 8 bit (256 colors)
* 4. 32x32, 8 bit (256 colors)
* 5. 48x48, 4 bit (Windows 16 colors palette)
* 6. 48x48, 8 bit (256 colors)
*
* @param program the Windows executable e.g c:/eclipse/eclipse.exe
*/
static ImageData[] loadIcons(String program) throws FileNotFoundException, IOException {
try (RandomAccessFile raf = new RandomAccessFile(program, "r")) {
IconExe iconExe = new IconExe();
IconResInfo[] iconInfo = iconExe.getIcons(raf);
ImageData[] data = new ImageData[iconInfo.length];
for (int i = 0; i < data.length; i++) data[i] = iconInfo[i].data;
return data;
}
}
/**
* Replace the Desktop icons provided in the Windows executable program
* with icons provided by the user.
*
* Note 1. Write access to the executable program is required. As a result, that
* program must not be currently running or edited elsewhere.
*
* Note 2. Use loadIcons to determine which set of icons (width, height, depth)
* is required to replace the icons in the executable program. A user icon
* matching exactly the width/height/depth of an executable icon will be written
* to the executable and will replace that executable icon. If an executable icon
* does not match a user icon, it is left as is. Verify the return value matches
* the number of icons to write. Finally, use loadIcons after this operation
* to verify the icons have changed as expected.
*
* Note 3. The Eclipse 3.1 launcher requires the following 6 images (in any order).
* 1. 32x32, 4 bit (Windows 16 colors palette)
* 2. 16x16, 4 bit (Windows 16 colors palette)
* 3. 16x16, 8 bit (256 colors)
* 4. 32x32, 8 bit (256 colors)
* 5. 48x48, 4 bit (Windows 16 colors palette)
* 6. 48x48, 8 bit (256 colors)
*
* Note 4. This function modifies the content of the executable program and may cause
* its corruption.
*
* @param program the Windows executable e.g c:/eclipse/eclipse.exe
* @param icons to write to the given executable
* @return the number of icons from the original program that were not successfully replaced (0 if success)
*/
static int unloadIcons(String program, ImageData[] icons) throws FileNotFoundException, IOException {
try (RandomAccessFile raf = new RandomAccessFile(program, "rw")) {
IconExe iconExe = new IconExe();
IconResInfo[] iconInfo = iconExe.getIcons(raf);
int cnt = 0;
for (int i = 0; i < iconInfo.length; i++) {
for (int j = 0; j < icons.length; j++)
if (iconInfo[i].data.width == icons[j].width &&
iconInfo[i].data.height == icons[j].height &&
iconInfo[i].data.depth == icons[j].depth) {
raf.seek(iconInfo[i].offset);
unloadIcon(raf, icons[j]);
cnt++;
}
}
return iconInfo.length - cnt;
}
}
public static final String VERSION = "20050124";
static final boolean DEBUG = false;
public static class IconResInfo {
ImageData data;
int offset;
int size;
}
IconResInfo[] iconInfo = null;
int iconCnt;
IconResInfo[] getIcons(RandomAccessFile raf) throws IOException {
iconInfo = new IconResInfo[4];
iconCnt = 0;
IMAGE_DOS_HEADER imageDosHeader = new IMAGE_DOS_HEADER();
read(raf, imageDosHeader);
if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE) return null;
int imageNtHeadersOffset = imageDosHeader.e_lfanew;
raf.seek(imageNtHeadersOffset);
IMAGE_NT_HEADERS imageNtHeaders = new IMAGE_NT_HEADERS();
read(raf, imageNtHeaders);
if (imageNtHeaders.Signature != IMAGE_NT_SIGNATURE) return null;
// DumpResources
int resourcesRVA = imageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
if (resourcesRVA == 0) return null;
if (DEBUG) System.out.println("* Resources (RVA= "+resourcesRVA+")");
IMAGE_SECTION_HEADER imageSectionHeader = new IMAGE_SECTION_HEADER();
int firstSectionOffset = imageNtHeadersOffset + IMAGE_NT_HEADERS.FIELD_OFFSET_OptionalHeader + imageNtHeaders.FileHeader.SizeOfOptionalHeader;
raf.seek(firstSectionOffset);
boolean found = false;
for (int i = 0; i < imageNtHeaders.FileHeader.NumberOfSections; i++) {
read(raf, imageSectionHeader);
if (resourcesRVA >= imageSectionHeader.VirtualAddress && resourcesRVA < imageSectionHeader.VirtualAddress + imageSectionHeader.Misc_VirtualSize) {
// could check the imageSectionHeader name is .rsrc
found = true;
break;
}
}
if (!found) return null;
int delta = imageSectionHeader.VirtualAddress - imageSectionHeader.PointerToRawData;
int imageResourceDirectoryOffset = resourcesRVA - delta;
dumpResourceDirectory(raf, imageResourceDirectoryOffset, imageResourceDirectoryOffset, delta, 0, 0, false);
if (iconCnt < iconInfo.length) {
IconResInfo[] newArray = new IconResInfo[iconCnt];
System.arraycopy(iconInfo, 0, newArray, 0, iconCnt);
iconInfo = newArray;
}
return iconInfo;
}
void dumpResourceDirectory(RandomAccessFile raf, int imageResourceDirectoryOffset, int resourceBase, int delta, int type, int level, boolean rt_icon_root) throws IOException {
if (DEBUG) System.out.println("** LEVEL "+level);
IMAGE_RESOURCE_DIRECTORY imageResourceDirectory = new IMAGE_RESOURCE_DIRECTORY();
raf.seek(imageResourceDirectoryOffset);
read(raf, imageResourceDirectory);
if (DEBUG) {
String sType = ""+type;
// level 1 resources are resource types
if (level == 1) {
System.out.println("___________________________");
if (type == RT_ICON) sType = "RT_ICON";
if (type == RT_GROUP_ICON) sType = "RT_GROUP_ICON";
}
System.out.println("Resource Directory ["+sType+"]"+" (Named "+imageResourceDirectory.NumberOfNamedEntries+", ID "+imageResourceDirectory.NumberOfIdEntries+")");
}
// int IRDE_StartOffset = imageResourceDirectoryOffset + IMAGE_RESOURCE_DIRECTORY.SIZEOF;
IMAGE_RESOURCE_DIRECTORY_ENTRY[] imageResourceDirectoryEntries = new IMAGE_RESOURCE_DIRECTORY_ENTRY[imageResourceDirectory.NumberOfIdEntries];
for (int i = 0; i < imageResourceDirectoryEntries.length; i++) {
imageResourceDirectoryEntries[i] = new IMAGE_RESOURCE_DIRECTORY_ENTRY();
read(raf, imageResourceDirectoryEntries[i]);
}
for (int i = 0; i < imageResourceDirectoryEntries.length; i++) {
if (imageResourceDirectoryEntries[i].DataIsDirectory) {
dumpResourceDirectory(raf, imageResourceDirectoryEntries[i].OffsetToDirectory + resourceBase, resourceBase, delta, imageResourceDirectoryEntries[i].Id, level + 1, rt_icon_root ? true : type == RT_ICON);
} else {
// Resource found
/// pResDirEntry->Name
IMAGE_RESOURCE_DIRECTORY_ENTRY irde = imageResourceDirectoryEntries[i];
IMAGE_RESOURCE_DATA_ENTRY data = new IMAGE_RESOURCE_DATA_ENTRY();
raf.seek(imageResourceDirectoryEntries[i].OffsetToData + resourceBase);
read(raf, data);
if (DEBUG) System.out.println("Resource Id "+irde.Id+" Data Offset RVA "+data.OffsetToData+", Size "+data.Size);
if (rt_icon_root) {
if (DEBUG) System.out.println("iconcnt "+iconCnt+" |"+iconInfo.length);
iconInfo[iconCnt] = new IconResInfo();
iconInfo[iconCnt].data = parseIcon(raf, data.OffsetToData - delta, data.Size);
iconInfo[iconCnt].offset = data.OffsetToData - delta;
iconInfo[iconCnt].size = data.Size;
iconCnt++;
if (iconCnt == iconInfo.length) {
IconResInfo[] newArray = new IconResInfo[iconInfo.length + 4];
System.arraycopy(iconInfo, 0, newArray, 0, iconInfo.length);
iconInfo = newArray;
}
}
}
}
}
static ImageData parseIcon(RandomAccessFile raf, int offset, int size) throws IOException {
raf.seek(offset);
BITMAPINFO bitmapInfo = new BITMAPINFO();
read(raf, bitmapInfo);
bitmapInfo.bmiHeader.biHeight /= 2;
int width = bitmapInfo.bmiHeader.biWidth;
int height = bitmapInfo.bmiHeader.biHeight;
int depth = bitmapInfo.bmiHeader.biBitCount;
PaletteData palette = loadPalette(bitmapInfo.bmiHeader, raf);
byte[] shapeData = loadData(bitmapInfo.bmiHeader, raf);
bitmapInfo.bmiHeader.biBitCount = 1;
byte[] maskData = loadData(bitmapInfo.bmiHeader, raf);
maskData = convertPad(maskData, width, height, 1, 4, 2);
bitInvertData(maskData, 0, maskData.length);
return ImageData.internal_new(
width,
height,
depth,
palette,
4,
shapeData,
2,
maskData,
null,
-1,
-1,
SWT.IMAGE_ICO,
0,
0,
0,
0);
}
static byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
// Destructively bit invert data in the given byte array.
for (int i = startIndex; i < endIndex; i++) {
data[i] = (byte)(255 - data[i - startIndex]);
}
return data;
}
static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
if (pad == newPad) return data;
int stride = (width * depth + 7) / 8;
int bpl = (stride + (pad - 1)) / pad * pad;
int newBpl = (stride + (newPad - 1)) / newPad * newPad;
byte[] newData = new byte[height * newBpl];
int srcIndex = 0, destIndex = 0;
for (int y = 0; y < height; y++) {
System.arraycopy(data, srcIndex, newData, destIndex, newBpl);
srcIndex += bpl;
destIndex += newBpl;
}
return newData;
}
static PaletteData loadPalette(BITMAPINFOHEADER bih, RandomAccessFile raf) throws IOException {
int depth = bih.biBitCount;
if (depth <= 8) {
int numColors = bih.biClrUsed;
if (numColors == 0) {
numColors = 1 << depth;
} else {
if (numColors > 256)
numColors = 256;
}
byte[] buf = new byte[numColors * 4];
raf.read(buf);
return paletteFromBytes(buf, numColors);
}
if (depth == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F);
if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
}
static PaletteData paletteFromBytes(byte[] bytes, int numColors) {
int bytesOffset = 0;
RGB[] colors = new RGB[numColors];
for (int i = 0; i < numColors; i++) {
colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
bytes[bytesOffset + 1] & 0xFF,
bytes[bytesOffset] & 0xFF);
bytesOffset += 4;
}
return new PaletteData(colors);
}
static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf) throws IOException {
int stride = (bih.biWidth * bih.biBitCount + 7) / 8;
stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
byte[] data = loadData(bih, raf, stride);
flipScanLines(data, stride, bih.biHeight);
return data;
}
static void flipScanLines(byte[] data, int stride, int height) {
int i1 = 0;
int i2 = (height - 1) * stride;
for (int i = 0; i < height / 2; i++) {
for (int index = 0; index < stride; index++) {
byte b = data[index + i1];
data[index + i1] = data[index + i2];
data[index + i2] = b;
}
i1 += stride;
i2 -= stride;
}
}
static byte[] loadData(BITMAPINFOHEADER bih, RandomAccessFile raf, int stride) throws IOException {
int dataSize = bih.biHeight * stride;
byte[] data = new byte[dataSize];
int cmp = bih.biCompression;
if (cmp == 0) { // BMP_NO_COMPRESSION
raf.read(data);
} else {
if (DEBUG) System.out.println("ICO cannot be compressed?");
}
return data;
}
static void unloadIcon(RandomAccessFile raf, ImageData icon) throws IOException {
int sizeImage = (((icon.width * icon.depth + 31) / 32 * 4) +
((icon.width + 31) / 32 * 4)) * icon.height;
write4(raf, BMPHeaderFixedSize);
write4(raf, icon.width);
write4(raf, icon.height * 2);
writeU2(raf, 1);
writeU2(raf, icon.depth);
write4(raf, 0);
write4(raf, sizeImage);
write4(raf, 0);
write4(raf, 0);
write4(raf, icon.palette.colors != null ? icon.palette.colors.length : 0);
write4(raf, 0);
byte[] rgbs = paletteToBytes(icon.palette);
raf.write(rgbs);
unloadShapeData(raf, icon);
unloadMaskData(raf, icon);
}
static byte[] paletteToBytes(PaletteData pal) {
int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
byte[] bytes = new byte[n * 4];
int offset = 0;
for (int i = 0; i < n; i++) {
RGB col = pal.colors[i];
bytes[offset] = (byte)col.blue;
bytes[offset + 1] = (byte)col.green;
bytes[offset + 2] = (byte)col.red;
offset += 4;
}
return bytes;
}
static void unloadMaskData(RandomAccessFile raf, ImageData icon) {
ImageData mask = icon.getTransparencyMask();
int bpl = (icon.width + 7) / 8;
int pad = mask.scanlinePad;
int srcBpl = (bpl + pad - 1) / pad * pad;
int destBpl = (bpl + 3) / 4 * 4;
byte[] buf = new byte[destBpl];
int offset = (icon.height - 1) * srcBpl;
byte[] data = mask.data;
try {
for (int i = 0; i < icon.height; i++) {
System.arraycopy(data, offset, buf, 0, bpl);
bitInvertData(buf, 0, bpl);
raf.write(buf, 0, destBpl);
offset -= srcBpl;
}
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
}
static void unloadShapeData(RandomAccessFile raf, ImageData icon) {
int bpl = (icon.width * icon.depth + 7) / 8;
int pad = icon.scanlinePad;
int srcBpl = (bpl + pad - 1) / pad * pad;
int destBpl = (bpl + 3) / 4 * 4;
byte[] buf = new byte[destBpl];
int offset = (icon.height - 1) * srcBpl;
byte[] data = icon.data;
try {
for (int i = 0; i < icon.height; i++) {
System.arraycopy(data, offset, buf, 0, bpl);
raf.write(buf, 0, destBpl);
offset -= srcBpl;
}
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
}
static boolean readIconGroup(RandomAccessFile raf, int offset, int size) throws IOException {
raf.seek(offset);
NEWHEADER newHeader = new NEWHEADER();
read(raf, newHeader);
if (newHeader.ResType != RES_ICON) return false;
RESDIR[] resDir = new RESDIR[newHeader.ResCount];
for (int i = 0; i < newHeader.ResCount; i++) {
resDir[i] = new RESDIR();
read(raf, resDir[i]);
}
return true;
}
static void copyFile(String src, String dst) throws FileNotFoundException, IOException {
File srcFile = new File(src);
File dstFile = new File(dst);
try (FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(dstFile)) {
int c;
while ((c = in.read()) != -1)
out.write(c);
}
}
/* IO utilities to parse Windows executable */
static final int IMAGE_DOS_SIGNATURE = 0x5a4d;
static final int IMAGE_NT_SIGNATURE = 0x00004550;
static final int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
static final int RES_ICON = 1;
static final int RT_ICON = 3;
static final int RT_GROUP_ICON = 14;
static final int BMPHeaderFixedSize = 40;
public static class IMAGE_DOS_HEADER {
int e_magic; // WORD
int e_cblp; // WORD
int e_cp; // WORD
int e_crlc; // WORD
int e_cparhdr; // WORD
int e_minalloc; // WORD
int e_maxalloc; // WORD
int e_ss; // WORD
int e_sp; // WORD
int e_csum; // WORD
int e_ip; // WORD
int e_cs; // WORD
int e_lfarlc; // WORD
int e_ovno; // WORD
int[] e_res = new int[4]; // WORD[4]
int e_oemid; // WORD
int e_oeminfo; // WORD
int[] e_res2 = new int[10]; // WORD[10]
int e_lfanew; // LONG
}
public static class IMAGE_FILE_HEADER {
int Machine; // WORD
int NumberOfSections; // WORD
int TimeDateStamp; // DWORD
int PointerToSymbolTable; // DWORD
int NumberOfSymbols; // DWORD
int SizeOfOptionalHeader; // WORD
int Characteristics; // WORD
}
public static class IMAGE_DATA_DIRECTORY {
int VirtualAddress; // DWORD
int Size; // DWORD
}
public static class IMAGE_OPTIONAL_HEADER {
int Magic; // WORD
int MajorLinkerVersion; // BYTE
int MinorLinkerVersion; // BYTE
int SizeOfCode; // DWORD
int SizeOfInitializedData; // DWORD
int SizeOfUninitializedData; // DWORD
int AddressOfEntryPoint; // DWORD
int BaseOfCode; // DWORD
int BaseOfData; // DWORD
int ImageBase; // DWORD
int SectionAlignment; // DWORD
int FileAlignment; // DWORD
int MajorOperatingSystemVersion; // WORD
int MinorOperatingSystemVersion; // WORD
int MajorImageVersion; // WORD
int MinorImageVersion; // WORD
int MajorSubsystemVersion; // WORD
int MinorSubsystemVersion; // WORD
int Win32VersionValue; // DWORD
int SizeOfImage; // DWORD
int SizeOfHeaders; // DWORD
int CheckSum; // DWORD
int Subsystem; // WORD
int DllCharacteristics; // WORD
int SizeOfStackReserve; // DWORD
int SizeOfStackCommit; // DWORD
int SizeOfHeapReserve; // DWORD
int SizeOfHeapCommit; // DWORD
int LoaderFlags; // DWORD
int NumberOfRvaAndSizes; // DWORD
IMAGE_DATA_DIRECTORY[] DataDirectory = new IMAGE_DATA_DIRECTORY[16];
}
public static class IMAGE_NT_HEADERS {
int Signature; // DWORD
IMAGE_FILE_HEADER FileHeader = new IMAGE_FILE_HEADER();
IMAGE_OPTIONAL_HEADER OptionalHeader = new IMAGE_OPTIONAL_HEADER();
final static int FIELD_OFFSET_OptionalHeader = 24;
}
public static class IMAGE_SECTION_HEADER {
int[] Name = new int[8]; // BYTE[8]
int Misc_VirtualSize; // DWORD (union Misc { DWORD PhysicalAddress; DWORD VirtualSize }
int VirtualAddress; // DWORD
int SizeOfRawData; // DWORD
int PointerToRawData; // DWORD
int PointerToRelocations; // DWORD
int PointerToLinenumbers; // DWORD
int NumberOfRelocations; // WORD
int NumberOfLinenumbers; // WORD
int Characteristics; // DWORD
}
public static class IMAGE_RESOURCE_DIRECTORY {
int Characteristics; // DWORD
int TimeDateStamp; // DWORD
int MajorVersion; // WORD
int MinorVersion; // WORD
int NumberOfNamedEntries; // WORD - used
int NumberOfIdEntries; // WORD - used
final static int SIZEOF = 16;
}
public static class IMAGE_RESOURCE_DIRECTORY_ENTRY {
// union
int NameOffset; // DWORD 31 bits
boolean NameIsString; // DWORD 1 bit
int Name; // DWORD
int Id; // WORD
// union
int OffsetToData; // DWORD
int OffsetToDirectory; // DWORD 31 bits
boolean DataIsDirectory; // DWORD 1 bit
}
public static class IMAGE_RESOURCE_DATA_ENTRY {
int OffsetToData; // DWORD
int Size; // DWORD
int CodePage; // DWORD
int Reserved; // DWORD
}
public static class NEWHEADER {
int Reserved; // WORD
int ResType; // WORD
int ResCount; // WORD
}
public static class ICONRESDIR {
int Width; // BYTE
int Height; // BYTE
int ColorCount; // BYTE
int reserved; // BYTE
}
public static class CURSORDIR {
int Width; // WORD
int Height; // WORD
}
public static class RESDIR {
// union
ICONRESDIR Icon = new ICONRESDIR();
CURSORDIR Cursor = new CURSORDIR();
int Planes; // WORD
int BitCount; // WORD
int BytesInRes; // DWORD
int IconCursorId; // WORD
}
public static class BITMAPINFOHEADER {
int biSize; // DWORD
int biWidth; // LONG
int biHeight; // LONG
int biPlanes; // WORD
int biBitCount; // WORD
int biCompression; // DWORD
int biSizeImage; // DWORD
int biXPelsPerMeter; // LONG
int biYPelsPerMeter; // LONG
int biClrUsed; // DWORD
int biClrImportant; // DWORD
}
static class RGBQUAD {
int rgBlue; // BYTE
int rgbGreen; // BYTE
int rgbRed; // BYTE
int rgbReserved; // BYTE
}
static class BITMAPINFO {
BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
RGBQUAD[] bmiColors = null;
}
static void read(RandomAccessFile raf, BITMAPINFOHEADER bih) throws IOException {
bih.biSize = read4(raf);
bih.biWidth = read4(raf);
bih.biHeight = read4(raf);
bih.biPlanes = readU2(raf);
bih.biBitCount = readU2(raf);
bih.biCompression = read4(raf);
bih.biSizeImage = read4(raf);
bih.biXPelsPerMeter = read4(raf);
bih.biYPelsPerMeter = read4(raf);
bih.biClrUsed = read4(raf);
bih.biClrImportant = read4(raf);
}
static void read(RandomAccessFile raf, BITMAPINFO bi) throws IOException {
read(raf, bi.bmiHeader);
}
/* Little Endian helpers */
static int readU2(RandomAccessFile raf) throws IOException {
int b0 = raf.readByte() & 0xFF;
int b1 = raf.readByte() & 0xFF;
return (b1 << 8 | b0);
}
static int read4(RandomAccessFile raf) throws IOException {
int b0 = raf.readByte() & 0xFF;
int b1 = raf.readByte() & 0xFF;
int b2 = raf.readByte() & 0xFF;
int b3 = raf.readByte() & 0xFF;
return b3 << 24 | b2 << 16 | b1 << 8 | b0;
}
static void write4(RandomAccessFile raf, int value) throws IOException {
raf.write(value & 0xFF);
raf.write((value >> 8) & 0xFF);
raf.write((value >> 16) & 0xFF);
raf.write((value >> 24) & 0xFF);
}
static void writeU2(RandomAccessFile raf, int value) throws IOException {
raf.write(value & 0xFF);
raf.write((value >> 8) & 0xFF);
}
static void read(RandomAccessFile raf, IMAGE_DOS_HEADER idh) throws IOException {
idh.e_magic = readU2(raf);
idh.e_cblp = readU2(raf);
idh.e_cp = readU2(raf);
idh.e_crlc = readU2(raf);
idh.e_cparhdr = readU2(raf);
idh.e_minalloc = readU2(raf);
idh.e_maxalloc = readU2(raf);
idh.e_ss = readU2(raf);
idh.e_sp = readU2(raf);
idh.e_csum = readU2(raf);
idh.e_ip = readU2(raf);
idh.e_cs = readU2(raf);
idh.e_lfarlc = readU2(raf);
idh.e_ovno = readU2(raf);
for (int i = 0; i < idh.e_res.length; i++) idh.e_res[i] = readU2(raf);
idh.e_oemid = readU2(raf);
idh.e_oeminfo = readU2(raf);
for (int i = 0; i < idh.e_res2.length; i++) idh.e_res2[i] = readU2(raf);
idh.e_lfanew = read4(raf);
}
static void read(RandomAccessFile raf, IMAGE_FILE_HEADER ifh) throws IOException {
ifh.Machine = readU2(raf);
ifh.NumberOfSections = readU2(raf);
ifh.TimeDateStamp = read4(raf);
ifh.PointerToSymbolTable = read4(raf);
ifh.NumberOfSymbols = read4(raf);
ifh.SizeOfOptionalHeader = readU2(raf);
ifh.Characteristics = readU2(raf);
}
static void read(RandomAccessFile raf, IMAGE_DATA_DIRECTORY idd) throws IOException {
idd.VirtualAddress = read4(raf);
idd.Size = read4(raf);
}
static void read(RandomAccessFile raf, IMAGE_OPTIONAL_HEADER ioh) throws IOException {
ioh.Magic = readU2(raf);
ioh.MajorLinkerVersion = raf.read();
ioh.MinorLinkerVersion = raf.read();
ioh.SizeOfCode = read4(raf);
ioh.SizeOfInitializedData = read4(raf);
ioh.SizeOfUninitializedData = read4(raf);
ioh.AddressOfEntryPoint = read4(raf);
ioh.BaseOfCode = read4(raf);
ioh.BaseOfData = read4(raf);
ioh.ImageBase = read4(raf);
ioh.SectionAlignment = read4(raf);
ioh.FileAlignment = read4(raf);
ioh.MajorOperatingSystemVersion = readU2(raf);
ioh.MinorOperatingSystemVersion = readU2(raf);
ioh.MajorImageVersion = readU2(raf);
ioh.MinorImageVersion = readU2(raf);
ioh.MajorSubsystemVersion = readU2(raf);
ioh.MinorSubsystemVersion = readU2(raf);
ioh.Win32VersionValue = read4(raf);
ioh.SizeOfImage = read4(raf);
ioh.SizeOfHeaders = read4(raf);
ioh.CheckSum = read4(raf);
ioh.Subsystem = readU2(raf);
ioh.DllCharacteristics = readU2(raf);
ioh.SizeOfStackReserve = read4(raf);
ioh.SizeOfStackCommit = read4(raf);
ioh.SizeOfHeapReserve = read4(raf);
ioh.SizeOfHeapCommit = read4(raf);
ioh.LoaderFlags = read4(raf);
ioh.NumberOfRvaAndSizes = read4(raf);
for (int i = 0 ; i < ioh.DataDirectory.length; i++) {
ioh.DataDirectory[i] = new IMAGE_DATA_DIRECTORY();
read(raf, ioh.DataDirectory[i]);
}
}
static void read(RandomAccessFile raf, IMAGE_NT_HEADERS inh) throws IOException {
inh.Signature = read4(raf);
read(raf, inh.FileHeader);
read(raf, inh.OptionalHeader);
}
static void read(RandomAccessFile raf, IMAGE_SECTION_HEADER ish) throws IOException {
for (int i = 0 ; i < ish.Name.length; i++) ish.Name[i] = raf.read();
ish.Misc_VirtualSize = read4(raf);
ish.VirtualAddress = read4(raf);
ish.SizeOfRawData = read4(raf);
ish.PointerToRawData = read4(raf);
ish.PointerToRelocations = read4(raf);
ish.PointerToLinenumbers = read4(raf);
ish.NumberOfRelocations = readU2(raf);
ish.NumberOfLinenumbers = readU2(raf);
ish.Characteristics = read4(raf);
}
static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY ird) throws IOException {
ird.Characteristics = read4(raf);
ird.TimeDateStamp = read4(raf);
ird.MajorVersion = readU2(raf);
ird.MinorVersion = readU2(raf);
ird.NumberOfNamedEntries = readU2(raf);
ird.NumberOfIdEntries = readU2(raf);
}
static void read(RandomAccessFile raf, IMAGE_RESOURCE_DIRECTORY_ENTRY irde) throws IOException {
irde.Name = read4(raf);
irde.OffsetToData = read4(raf);
// construct other union members
irde.NameOffset = irde.Name & ~ (1 << 31);
irde.NameIsString = (irde.Name & (1 << 31)) != 0;
irde.Id = irde.Name & 0xFFFF;
irde.OffsetToDirectory = irde.OffsetToData & ~ (1 << 31);
irde.DataIsDirectory = (irde.OffsetToData & (1 << 31)) != 0;
}
static void read(RandomAccessFile raf, IMAGE_RESOURCE_DATA_ENTRY irde) throws IOException {
irde.OffsetToData = read4(raf);
irde.Size = read4(raf);
irde.CodePage = read4(raf);
irde.Reserved = read4(raf);
}
static void read(RandomAccessFile raf, NEWHEADER nh) throws IOException {
nh.Reserved = readU2(raf);
nh.ResType = readU2(raf);
nh.ResCount = readU2(raf);
}
static void read(RandomAccessFile raf, ICONRESDIR i) throws IOException {
i.Width = raf.read();
i.Height = raf.read();
i.ColorCount = raf.read();
i.reserved = raf.read();
}
static void read(RandomAccessFile raf, CURSORDIR c) throws IOException {
c.Width = readU2(raf);
c.Height = readU2(raf);
}
static void read(RandomAccessFile raf, RESDIR rs) throws IOException {
long start = raf.getFilePointer();
read(raf, rs.Icon);
raf.seek(start);
read(raf, rs.Cursor);
rs.Planes = readU2(raf);
rs.BitCount = readU2(raf);
rs.BytesInRes = read4(raf);
rs.IconCursorId = readU2(raf);
}
/* ImageData and Image Decoder inlining to avoid dependency on SWT
* The following section can be entirely removed if SWT can be used.
*/
static class RGB {
/**
* the red component of the RGB
*/
public int red;
/**
* the green component of the RGB
*/
public int green;
/**
* the blue component of the RGB
*/
public int blue;
static final long serialVersionUID = 3258415023461249074L;
/**
* Constructs an instance of this class with the given
* red, green and blue values.
*
* @param red the red component of the new instance
* @param green the green component of the new instance
* @param blue the blue component of the new instance
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
* </ul>
*/
public RGB(int red, int green, int blue) {
if ((red > 255) || (red < 0) ||
(green > 255) || (green < 0) ||
(blue > 255) || (blue < 0))
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
this.red = red;
this.green = green;
this.blue = blue;
}
/**
* Compares the argument to the receiver, and returns true
* if they represent the <em>same</em> object using a class
* specific comparison.
*
* @param object the object to compare with this object
* @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
*
* @see #hashCode()
*/
@Override
public boolean equals (Object object) {
if (object == this) return true;
if (!(object instanceof RGB)) return false;
RGB rgb = (RGB)object;
return (rgb.red == this.red) && (rgb.green == this.green) && (rgb.blue == this.blue);
}
/**
* Returns an integer hash code for the receiver. Any two
* objects which return <code>true</code> when passed to
* <code>equals</code> must return the same value for this
* method.
*
* @return the receiver's hash
*
* @see #equals(Object)
*/
@Override
public int hashCode () {
return (blue << 16) | (green << 8) | red;
}
/**
* Returns a string containing a concise, human-readable
* description of the receiver.
*
* @return a string representation of the <code>RGB</code>
*/
@Override
public String toString () {
return "RGB {" + red + ", " + green + ", " + blue + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
//$NON-NLS-4$
}
}
static class PaletteData {
/**
* true if the receiver is a direct palette,
* and false otherwise
*/
public boolean isDirect;
/**
* the RGB values for an indexed palette, where the
* indices of the array correspond to pixel values
*/
public RGB[] colors;
/**
* the red mask for a direct palette
*/
public int redMask;
/**
* the green mask for a direct palette
*/
public int greenMask;
/**
* the blue mask for a direct palette
*/
public int blueMask;
/**
* the red shift for a direct palette
*/
public int redShift;
/**
* the green shift for a direct palette
*/
public int greenShift;
/**
* the blue shift for a direct palette
*/
public int blueShift;
/**
* Constructs a new indexed palette given an array of RGB values.
*
* @param colors the array of <code>RGB</code>s for the palette
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
* </ul>
*/
public PaletteData(RGB[] colors) {
if (colors == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
this.colors = colors;
this.isDirect = false;
}
/**
* Constructs a new direct palette given the red, green and blue masks.
*
* @param redMask the red mask
* @param greenMask the green mask
* @param blueMask the blue mask
*/
public PaletteData(int redMask, int greenMask, int blueMask) {
this.redMask = redMask;
this.greenMask = greenMask;
this.blueMask = blueMask;
this.isDirect = true;
this.redShift = shiftForMask(redMask);
this.greenShift = shiftForMask(greenMask);
this.blueShift = shiftForMask(blueMask);
}
/**
* Returns the pixel value corresponding to the given <code>RBG</code>.
*
* @param rgb the RGB to get the pixel value for
* @return the pixel value for the given RGB
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the RGB is not found in the palette</li>
* </ul>
*/
public int getPixel(RGB rgb) {
if (rgb == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (isDirect) {
int pixel = 0;
pixel |= (redShift < 0 ? rgb.red << -redShift : rgb.red >>> redShift) & redMask;
pixel |= (greenShift < 0 ? rgb.green << -greenShift : rgb.green >>> greenShift) & greenMask;
pixel |= (blueShift < 0 ? rgb.blue << -blueShift : rgb.blue >>> blueShift) & blueMask;
return pixel;
} else {
for (int i = 0; i < colors.length; i++) {
if (colors[i].equals(rgb)) return i;
}
/* The RGB did not exist in the palette */
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
return 0;
}
}
/**
* Returns an <code>RGB</code> corresponding to the given pixel value.
*
* @param pixel the pixel to get the RGB value for
* @return the RGB value for the given pixel
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the pixel does not exist in the palette</li>
* </ul>
*/
public RGB getRGB(int pixel) {
if (isDirect) {
int r = pixel & redMask;
r = (redShift < 0) ? r >>> -redShift : r << redShift;
int g = pixel & greenMask;
g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
int b = pixel & blueMask;
b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
return new RGB(r, g, b);
} else {
if (pixel < 0 || pixel >= colors.length) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
return colors[pixel];
}
}
/**
* Returns all the RGB values in the receiver if it is an
* indexed palette, or null if it is a direct palette.
*
* @return the <code>RGB</code>s for the receiver or null
*/
public RGB[] getRGBs() {
return colors;
}
/**
* Computes the shift value for a given mask.
*
* @param mask the mask to compute the shift for
* @return the shift amount
*
* @see PaletteData
*/
int shiftForMask(int mask) {
for (int i = 31; i >= 0; i--) {
if (((mask >> i) & 0x1) != 0) return 7 - i;
}
return 32;
}
}
static class ImageLoader {
/**
* the array of ImageData objects in this ImageLoader.
* This array is read in when the load method is called,
* and it is written out when the save method is called
*/
public ImageData[] data;
/**
* the width of the logical screen on which the images
* reside, in pixels (this corresponds to the GIF89a
* Logical Screen Width value)
*/
public int logicalScreenWidth;
/**
* the height of the logical screen on which the images
* reside, in pixels (this corresponds to the GIF89a
* Logical Screen Height value)
*/
public int logicalScreenHeight;
/**
* the background pixel for the logical screen (this
* corresponds to the GIF89a Background Color Index value).
* The default is -1 which means 'unspecified background'
*
*/
public int backgroundPixel;
/**
* the number of times to repeat the display of a sequence
* of animated images (this corresponds to the commonly-used
* GIF application extension for "NETSCAPE 2.0 01")
*/
public int repeatCount;
/**
* Construct a new empty ImageLoader.
*/
public ImageLoader() {
reset();
}
/**
* Resets the fields of the ImageLoader, except for the
* <code>imageLoaderListeners</code> field.
*/
void reset() {
data = null;
logicalScreenWidth = 0;
logicalScreenHeight = 0;
backgroundPixel = -1;
repeatCount = 1;
}
/**
* Loads an array of <code>ImageData</code> objects from the
* specified input stream. Throws an error if either an error
* occurs while loading the images, or if the images are not
* of a supported type. Returns the loaded image data array.
*
* @param stream the input stream to load the images from
* @return an array of <code>ImageData</code> objects loaded from the specified input stream
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
* <li>ERROR_IO - if an input/output error occurs while reading data</li>
* <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
* </ul>
*/
public ImageData[] load(InputStream stream) {
if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
reset();
data = FileFormat.load(stream, this);
return data;
}
/**
* Loads an array of <code>ImageData</code> objects from the
* file with the specified name. Throws an error if either
* an error occurs while loading the images, or if the images are
* not of a supported type. Returns the loaded image data array.
*
* @param filename the name of the file to load the images from
* @return an array of <code>ImageData</code> objects loaded from the specified file
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
* <li>ERROR_IO - if an IO error occurs while reading data</li>
* <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
* </ul>
*/
public ImageData[] load(String filename) {
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
try (InputStream stream = new FileInputStream(filename)) {
return load(stream);
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
return null;
}
}
static class ImageData {
/**
* The width of the image, in pixels.
*/
public int width;
/**
* The height of the image, in pixels.
*/
public int height;
/**
* The color depth of the image, in bits per pixel.
* <p>
* Note that a depth of 8 or less does not necessarily
* mean that the image is palette indexed, or
* conversely that a depth greater than 8 means that
* the image is direct color. Check the associated
* PaletteData's isDirect field for such determinations.
*/
public int depth;
/**
* The scanline padding.
* <p>
* If one scanline of the image is not a multiple of
* this number, it will be padded with zeros until it is.
* </p>
*/
public int scanlinePad;
/**
* The number of bytes per scanline.
* <p>
* This is a multiple of the scanline padding.
* </p>
*/
public int bytesPerLine;
/**
* The pixel data of the image.
* <p>
* Note that for 16 bit depth images the pixel data is stored
* in least significant byte order; however, for 24bit and
* 32bit depth images the pixel data is stored in most
* significant byte order.
* </p>
*/
public byte[] data;
/**
* The color table for the image.
*/
public PaletteData palette;
/**
* The transparent pixel.
* <p>
* Pixels with this value are transparent.
* </p><p>
* The default is -1 which means 'no transparent pixel'.
* </p>
*/
public int transparentPixel;
/**
* An icon-specific field containing the data from the icon mask.
* <p>
* This is a 1 bit bitmap stored with the most significant
* bit first. The number of bytes per scanline is
* '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'.
* </p><p>
* The default is null which means 'no transparency mask'.
* </p>
*/
public byte[] maskData;
/**
* An icon-specific field containing the scanline pad of the mask.
* <p>
* If one scanline of the transparency mask is not a
* multiple of this number, it will be padded with zeros until
* it is.
* </p>
*/
public int maskPad;
/**
* The alpha data of the image.
* <p>
* Every pixel can have an <em>alpha blending</em> value that
* varies from 0, meaning fully transparent, to 255 meaning
* fully opaque. The number of bytes per scanline is
* 'width'.
* </p>
*/
public byte[] alphaData;
/**
* The global alpha value to be used for every pixel.
* <p>
* If this value is set, the <code>alphaData</code> field
* is ignored and when the image is rendered each pixel
* will be blended with the background an amount
* proportional to this value.
* </p><p>
* The default is -1 which means 'no global alpha value'
* </p>
*/
public int alpha;
/**
* The type of file from which the image was read.
*
* It is expressed as one of the following values:
* <dl>
* <dt><code>IMAGE_BMP</code></dt>
* <dd>Windows BMP file format, no compression</dd>
* <dt><code>IMAGE_BMP_RLE</code></dt>
* <dd>Windows BMP file format, RLE compression if appropriate</dd>
* <dt><code>IMAGE_GIF</code></dt>
* <dd>GIF file format</dd>
* <dt><code>IMAGE_ICO</code></dt>
* <dd>Windows ICO file format</dd>
* <dt><code>IMAGE_JPEG</code></dt>
* <dd>JPEG file format</dd>
* <dt><code>IMAGE_PNG</code></dt>
* <dd>PNG file format</dd>
* </dl>
*/
public int type;
/**
* The x coordinate of the top left corner of the image
* within the logical screen (this field corresponds to
* the GIF89a Image Left Position value).
*/
public int x;
/**
* The y coordinate of the top left corner of the image
* within the logical screen (this field corresponds to
* the GIF89a Image Top Position value).
*/
public int y;
/**
* A description of how to dispose of the current image
* before displaying the next.
*
* It is expressed as one of the following values:
* <dl>
* <dt><code>DM_UNSPECIFIED</code></dt>
* <dd>disposal method not specified</dd>
* <dt><code>DM_FILL_NONE</code></dt>
* <dd>do nothing - leave the image in place</dd>
* <dt><code>DM_FILL_BACKGROUND</code></dt>
* <dd>fill with the background color</dd>
* <dt><code>DM_FILL_PREVIOUS</code></dt>
* <dd>restore the previous picture</dd>
* </dl>
* (this field corresponds to the GIF89a Disposal Method value)
*/
public int disposalMethod;
/**
* The time to delay before displaying the next image
* in an animation (this field corresponds to the GIF89a
* Delay Time value).
*/
public int delayTime;
/**
* Arbitrary channel width data to 8-bit conversion table.
*/
static final byte[][] ANY_TO_EIGHT = new byte[9][];
static {
for (int b = 0; b < 9; ++b) {
byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
if (b == 0) continue;
int inc = 0;
for (int bit = 0x10000; (bit >>= b) != 0;) inc |= bit;
for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = (byte)(v >> 8);
}
}
static final byte[] ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8];
/**
* Scaled 8x8 Bayer dither matrix.
*/
static final int[][] DITHER_MATRIX = {
{ 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000, 0x740000, 0xd40000, 0x540000 },
{ 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000, 0xb40000, 0x140000, 0x940000 },
{ 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000, 0x440000, 0xe40000, 0x640000 },
{ 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000, 0x840000, 0x240000, 0xa40000 },
{ 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000, 0x780000, 0xd80000, 0x580000 },
{ 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000, 0xb80000, 0x180000, 0x980000 },
{ 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000, 0x480000, 0xe80000, 0x680000 },
{ 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000, 0x880000, 0x280000, 0xa80000 }
};
/**
* Constructs a new, empty ImageData with the given width, height,
* depth and palette. The data will be initialized to an (all zero)
* array of the appropriate size.
*
* @param width the width of the image
* @param height the height of the image
* @param depth the depth of the image
* @param palette the palette of the image (must not be null)
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
* one of 1, 2, 4, 8, 16, 24 or 32</li>
* <li>ERROR_NULL_ARGUMENT - if the palette is null</li>
* </ul>
*/
public ImageData(int width, int height, int depth, PaletteData palette) {
this(width, height, depth, palette,
4, null, 0, null,
null, -1, -1, SWT.IMAGE_UNDEFINED,
0, 0, 0, 0);
}
/**
* Constructs a new, empty ImageData with the given width, height,
* depth, palette, scanlinePad and data.
*
* @param width the width of the image
* @param height the height of the image
* @param depth the depth of the image
* @param palette the palette of the image
* @param scanlinePad the padding of each line, in bytes
* @param data the data of the image
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
* one of 1, 2, 4, 8, 16, 24 or 32</li>
* <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li>
* <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li>
* </ul>
*/
public ImageData(int width, int height, int depth, PaletteData palette, int scanlinePad, byte[] data) {
this(width, height, depth, palette,
scanlinePad, checkData(data), 0, null,
null, -1, -1, SWT.IMAGE_UNDEFINED,
0, 0, 0, 0);
}
/**
* Constructs an <code>ImageData</code> loaded from a file with the
* specified name. Throws an error if an error occurs loading the
* image, or if the image has an unsupported type.
* <p>
* This constructor is provided for convenience when loading a single
* image only. If the file contains multiple images, only the first
* one will be loaded. To load multiple images, use
* <code>ImageLoader.load()</code>.
* </p>
*
* @param filename the name of the file to load the image from (must not be null)
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
* <li>ERROR_IO if an IO error occurs while reading data</li>
* <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
* </ul>
*/
public ImageData(String filename) {
ImageData[] data = new ImageLoader().load(filename);
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
ImageData i = data[0];
setAllFields(
i.width,
i.height,
i.depth,
i.scanlinePad,
i.bytesPerLine,
i.data,
i.palette,
i.transparentPixel,
i.maskData,
i.maskPad,
i.alphaData,
i.alpha,
i.type,
i.x,
i.y,
i.disposalMethod,
i.delayTime);
}
/**
* Prevents uninitialized instances from being created outside the package.
*/
ImageData() {
}
/**
* Constructs an image data by giving values for all non-computable fields.
* <p>
* This method is for internal use, and is not described further.
* </p>
*/
ImageData(
int width, int height, int depth, PaletteData palette,
int scanlinePad, byte[] data, int maskPad, byte[] maskData,
byte[] alphaData, int alpha, int transparentPixel, int type,
int x, int y, int disposalMethod, int delayTime)
{
if (palette == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (!(depth == 1 || depth == 2 || depth == 4 || depth == 8
|| depth == 16 || depth == 24 || depth == 32)) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (width <= 0 || height <= 0) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (scanlinePad == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
/ scanlinePad * scanlinePad;
setAllFields(
width,
height,
depth,
scanlinePad,
bytesPerLine,
data != null ? data : new byte[bytesPerLine * height],
palette,
transparentPixel,
maskData,
maskPad,
alphaData,
alpha,
type,
x,
y,
disposalMethod,
delayTime);
}
/**
* Initializes all fields in the receiver. This method must be called
* by all public constructors to ensure that all fields are initialized
* for a new ImageData object. If a new field is added to the class,
* then it must be added to this method.
* <p>
* This method is for internal use, and is not described further.
* </p>
*/
void setAllFields(int width, int height, int depth, int scanlinePad,
int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel,
byte[] maskData, int maskPad, byte[] alphaData, int alpha,
int type, int x, int y, int disposalMethod, int delayTime) {
this.width = width;
this.height = height;
this.depth = depth;
this.scanlinePad = scanlinePad;
this.bytesPerLine = bytesPerLine;
this.data = data;
this.palette = palette;
this.transparentPixel = transparentPixel;
this.maskData = maskData;
this.maskPad = maskPad;
this.alphaData = alphaData;
this.alpha = alpha;
this.type = type;
this.x = x;
this.y = y;
this.disposalMethod = disposalMethod;
this.delayTime = delayTime;
}
/**
* Invokes internal SWT functionality to create a new instance of
* this class.
* <p>
* <b>IMPORTANT:</b> This method is <em>not</em> part of the public
* API for <code>ImageData</code>. It is marked public only so that it
* can be shared within the packages provided by SWT. It is subject
* to change without notice, and should never be called from
* application code.
* </p>
* <p>
* This method is for internal use, and is not described further.
* </p>
*/
public static ImageData internal_new(
int width, int height, int depth, PaletteData palette,
int scanlinePad, byte[] data, int maskPad, byte[] maskData,
byte[] alphaData, int alpha, int transparentPixel, int type,
int x, int y, int disposalMethod, int delayTime)
{
return new ImageData(
width, height, depth, palette, scanlinePad, data, maskPad, maskData,
alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime);
}
ImageData colorMaskImage(int pixel) {
ImageData mask = new ImageData(width, height, 1, bwPalette(),
2, null, 0, null, null, -1, -1, SWT.IMAGE_UNDEFINED,
0, 0, 0, 0);
int[] row = new int[width];
for (int y = 0; y < height; y++) {
getPixels(0, y, width, row, 0);
for (int i = 0; i < width; i++) {
if (pixel != -1 && row[i] == pixel) {
row[i] = 0;
} else {
row[i] = 1;
}
}
mask.setPixels(0, y, width, row, 0);
}
return mask;
}
static byte[] checkData(byte [] data) {
if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
return data;
}
/**
* Returns <code>getWidth</code> pixel values starting at offset
* <code>x</code> in scanline <code>y</code> in the receiver's
* data starting at <code>startIndex</code>.
*
* @param x the x position of the first pixel to get
* @param y the y position of the first pixel to get
* @param getWidth the width of the data to get
* @param pixels the buffer in which to put the pixels
* @param startIndex the offset into the byte array to begin storing pixels
*
* @exception IndexOutOfBoundsException if getWidth is too large
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
* <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
* <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
* (For higher depths, use the int[] version of this method.)</li>
* </ul>
*/
public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) {
if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error
(SWT.ERROR_INVALID_ARGUMENT);
if (getWidth == 0) return;
int index;
int theByte;
int mask = 0;
int n = getWidth;
int i = startIndex;
int srcX = x, srcY = y;
if (depth == 1) {
index = (y * bytesPerLine) + (x >> 3);
theByte = data[index] & 0xFF;
while (n > 0) {
mask = 1 << (7 - (srcX & 0x7));
if ((theByte & mask) == 0) {
pixels[i] = 0;
} else {
pixels[i] = 1;
}
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
if (n > 0) theByte = data[index] & 0xFF;
srcX = 0;
} else {
if (mask == 1) {
index++;
if (n > 0) theByte = data[index] & 0xFF;
}
}
}
return;
}
if (depth == 2) {
index = (y * bytesPerLine) + (x >> 2);
theByte = data[index] & 0xFF;
int offset;
while (n > 0) {
offset = 3 - (srcX % 4);
mask = 3 << (offset * 2);
pixels[i] = (byte)((theByte & mask) >> (offset * 2));
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
if (n > 0) theByte = data[index] & 0xFF;
srcX = 0;
} else {
if (offset == 0) {
index++;
theByte = data[index] & 0xFF;
}
}
}
return;
}
if (depth == 4) {
index = (y * bytesPerLine) + (x >> 1);
if ((x & 0x1) == 1) {
theByte = data[index] & 0xFF;
pixels[i] = (byte)(theByte & 0x0F);
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
while (n > 1) {
theByte = data[index] & 0xFF;
pixels[i] = (byte)(theByte >> 4);
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
pixels[i] = (byte)(theByte & 0x0F);
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
}
if (n > 0) {
theByte = data[index] & 0xFF;
pixels[i] = (byte)(theByte >> 4);
}
return;
}
if (depth == 8) {
index = (y * bytesPerLine) + x;
for (int j = 0; j < getWidth; j++) {
pixels[i] = data[index];
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
return;
}
SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
}
/**
* Returns <code>getWidth</code> pixel values starting at offset
* <code>x</code> in scanline <code>y</code> in the receiver's
* data starting at <code>startIndex</code>.
*
* @param x the x position of the first pixel to get
* @param y the y position of the first pixel to get
* @param getWidth the width of the data to get
* @param pixels the buffer in which to put the pixels
* @param startIndex the offset into the buffer to begin storing pixels
*
* @exception IndexOutOfBoundsException if getWidth is too large
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
* <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
* <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
* </ul>
*/
public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) {
if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error
(SWT.ERROR_INVALID_ARGUMENT);
if (getWidth == 0) return;
int index;
int theByte;
int mask;
int n = getWidth;
int i = startIndex;
int srcX = x, srcY = y;
if (depth == 1) {
index = (y * bytesPerLine) + (x >> 3);
theByte = data[index] & 0xFF;
while (n > 0) {
mask = 1 << (7 - (srcX & 0x7));
if ((theByte & mask) == 0) {
pixels[i] = 0;
} else {
pixels[i] = 1;
}
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
if (n > 0) theByte = data[index] & 0xFF;
srcX = 0;
} else {
if (mask == 1) {
index++;
if (n > 0) theByte = data[index] & 0xFF;
}
}
}
return;
}
if (depth == 2) {
index = (y * bytesPerLine) + (x >> 2);
theByte = data[index] & 0xFF;
int offset;
while (n > 0) {
offset = 3 - (srcX % 4);
mask = 3 << (offset * 2);
pixels[i] = (byte)((theByte & mask) >> (offset * 2));
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
if (n > 0) theByte = data[index] & 0xFF;
srcX = 0;
} else {
if (offset == 0) {
index++;
theByte = data[index] & 0xFF;
}
}
}
return;
}
if (depth == 4) {
index = (y * bytesPerLine) + (x >> 1);
if ((x & 0x1) == 1) {
theByte = data[index] & 0xFF;
pixels[i] = theByte & 0x0F;
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
while (n > 1) {
theByte = data[index] & 0xFF;
pixels[i] = theByte >> 4;
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
pixels[i] = theByte & 0x0F;
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
}
if (n > 0) {
theByte = data[index] & 0xFF;
pixels[i] = theByte >> 4;
}
return;
}
if (depth == 8) {
index = (y * bytesPerLine) + x;
for (int j = 0; j < getWidth; j++) {
pixels[i] = data[index] & 0xFF;
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
return;
}
if (depth == 16) {
index = (y * bytesPerLine) + (x * 2);
for (int j = 0; j < getWidth; j++) {
pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index += 2;
}
}
return;
}
if (depth == 24) {
index = (y * bytesPerLine) + (x * 3);
for (int j = 0; j < getWidth; j++) {
pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8)
| (data[index+2] & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index += 3;
}
}
return;
}
if (depth == 32) {
index = (y * bytesPerLine) + (x * 4);
i = startIndex;
for (int j = 0; j < getWidth; j++) {
pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16)
| ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index += 4;
}
}
return;
}
SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
}
/**
* Returns an array of <code>RGB</code>s which comprise the
* indexed color table of the receiver, or null if the receiver
* has a direct color model.
*
* @return the RGB values for the image or null if direct color
*
* @see PaletteData#getRGBs()
*/
public RGB[] getRGBs() {
return palette.getRGBs();
}
/**
* Returns an <code>ImageData</code> which specifies the
* transparency mask information for the receiver, or null if the
* receiver has no transparency and is not an icon.
*
* @return the transparency mask or null if none exists
*/
public ImageData getTransparencyMask() {
if (getTransparencyType() == SWT.TRANSPARENCY_MASK) {
return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
} else {
return colorMaskImage(transparentPixel);
}
}
/**
* Returns the image transparency type.
*
* @return the receiver's transparency type
*/
public int getTransparencyType() {
if (maskData != null) return SWT.TRANSPARENCY_MASK;
if (transparentPixel != -1) return SWT.TRANSPARENCY_PIXEL;
if (alphaData != null) return SWT.TRANSPARENCY_ALPHA;
return SWT.TRANSPARENCY_NONE;
}
/**
* Returns the byte order of the receiver.
*
* @return MSB_FIRST or LSB_FIRST
*/
int getByteOrder() {
return depth != 16 ? MSB_FIRST : LSB_FIRST;
}
/**
* Sets the pixel values starting at offset <code>x</code> in
* scanline <code>y</code> in the receiver's data to the
* values from the array <code>pixels</code> starting at
* <code>startIndex</code>.
*
* @param x the x position of the pixel to set
* @param y the y position of the pixel to set
* @param putWidth the width of the pixels to set
* @param pixels the pixels to set
* @param startIndex the index at which to begin setting
*
* @exception IndexOutOfBoundsException if putWidth is too large
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
* <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
* <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
* (For higher depths, use the int[] version of this method.)</li>
* </ul>
*/
public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) {
if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
if (putWidth == 0) return;
int index;
int theByte;
int mask;
int n = putWidth;
int i = startIndex;
int srcX = x, srcY = y;
if (depth == 1) {
index = (y * bytesPerLine) + (x >> 3);
while (n > 0) {
mask = 1 << (7 - (srcX & 0x7));
if ((pixels[i] & 0x1) == 1) {
data[index] = (byte)((data[index] & 0xFF) | mask);
} else {
data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
}
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
if (mask == 1) {
index++;
}
}
}
return;
}
if (depth == 2) {
byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
index = (y * bytesPerLine) + (x >> 2);
int offset = 3 - (x % 4);
while (n > 0) {
theByte = pixels[i] & 0x3;
data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
offset = 0;
srcX = 0;
} else {
if (offset == 0) {
index++;
offset = 3;
} else {
offset--;
}
}
}
return;
}
if (depth == 4) {
index = (y * bytesPerLine) + (x >> 1);
boolean high = (x & 0x1) == 0;
while (n > 0) {
theByte = pixels[i] & 0x0F;
if (high) {
data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
} else {
data[index] = (byte)((data[index] & 0xF0) | theByte);
}
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
high = true;
srcX = 0;
} else {
if (!high) index++;
high = !high;
}
}
return;
}
if (depth == 8) {
index = (y * bytesPerLine) + x;
for (int j = 0; j < putWidth; j++) {
data[index] = (byte)(pixels[i] & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
return;
}
SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
}
/**
* Sets the pixel values starting at offset <code>x</code> in
* scanline <code>y</code> in the receiver's data to the
* values from the array <code>pixels</code> starting at
* <code>startIndex</code>.
*
* @param x the x position of the pixel to set
* @param y the y position of the pixel to set
* @param putWidth the width of the pixels to set
* @param pixels the pixels to set
* @param startIndex the index at which to begin setting
*
* @exception IndexOutOfBoundsException if putWidth is too large
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
* <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
* <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
* </ul>
*/
public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) {
if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
if (putWidth == 0) return;
int index;
int theByte;
int mask;
int n = putWidth;
int i = startIndex;
int pixel;
int srcX = x, srcY = y;
if (depth == 1) {
index = (y * bytesPerLine) + (x >> 3);
while (n > 0) {
mask = 1 << (7 - (srcX & 0x7));
if ((pixels[i] & 0x1) == 1) {
data[index] = (byte)((data[index] & 0xFF) | mask);
} else {
data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
}
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
if (mask == 1) {
index++;
}
}
}
return;
}
if (depth == 2) {
byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
index = (y * bytesPerLine) + (x >> 2);
int offset = 3 - (x % 4);
while (n > 0) {
theByte = pixels[i] & 0x3;
data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
offset = 3;
srcX = 0;
} else {
if (offset == 0) {
index++;
offset = 3;
} else {
offset--;
}
}
}
return;
}
if (depth == 4) {
index = (y * bytesPerLine) + (x >> 1);
boolean high = (x & 0x1) == 0;
while (n > 0) {
theByte = pixels[i] & 0x0F;
if (high) {
data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
} else {
data[index] = (byte)((data[index] & 0xF0) | theByte);
}
i++;
n--;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
high = true;
srcX = 0;
} else {
if (!high) index++;
high = !high;
}
}
return;
}
if (depth == 8) {
index = (y * bytesPerLine) + x;
for (int j = 0; j < putWidth; j++) {
data[index] = (byte)(pixels[i] & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index++;
}
}
return;
}
if (depth == 16) {
index = (y * bytesPerLine) + (x * 2);
for (int j = 0; j < putWidth; j++) {
pixel = pixels[i];
data[index] = (byte)(pixel & 0xFF);
data[index + 1] = (byte)((pixel >> 8) & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index += 2;
}
}
return;
}
if (depth == 24) {
index = (y * bytesPerLine) + (x * 3);
for (int j = 0; j < putWidth; j++) {
pixel = pixels[i];
data[index] = (byte)((pixel >> 16) & 0xFF);
data[index + 1] = (byte)((pixel >> 8) & 0xFF);
data[index + 2] = (byte)(pixel & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index += 3;
}
}
return;
}
if (depth == 32) {
index = (y * bytesPerLine) + (x * 4);
for (int j = 0; j < putWidth; j++) {
pixel = pixels[i];
data[index] = (byte)((pixel >> 24) & 0xFF);
data[index + 1] = (byte)((pixel >> 16) & 0xFF);
data[index + 2] = (byte)((pixel >> 8) & 0xFF);
data[index + 3] = (byte)(pixel & 0xFF);
i++;
srcX++;
if (srcX >= width) {
srcY++;
index = srcY * bytesPerLine;
srcX = 0;
} else {
index += 4;
}
}
return;
}
SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
}
/**
* Returns a palette with 2 colors: black & white.
*/
static PaletteData bwPalette() {
return new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)});
}
/**
* Gets the offset of the most significant bit for
* the given mask.
*/
static int getMSBOffset(int mask) {
for (int i = 31; i >= 0; i--) {
if (((mask >> i) & 0x1) != 0) return i + 1;
}
return 0;
}
/**
* Finds the closest match.
*/
static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) {
if (depth > 8) {
int rshift = 32 - getMSBOffset(redMask);
int gshift = 32 - getMSBOffset(greenMask);
int bshift = 32 - getMSBOffset(blueMask);
return (((red << 24) >>> rshift) & redMask) |
(((green << 24) >>> gshift) & greenMask) |
(((blue << 24) >>> bshift) & blueMask);
}
int r, g, b;
int minDistance = 0x7fffffff;
int nearestPixel = 0;
int n = reds.length;
for (int j = 0; j < n; j++) {
r = (reds[j] & 0xFF) - (red & 0xFF);
g = (greens[j] & 0xFF) - (green & 0xFF);
b = (blues[j] & 0xFF) - (blue & 0xFF);
int distance = r*r + g*g + b*b;
if (distance < minDistance) {
nearestPixel = j;
if (distance == 0) break;
minDistance = distance;
}
}
return nearestPixel;
}
static final ImageData convertMask(ImageData mask) {
if (mask.depth == 1) return mask;
PaletteData palette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255,255,255)});
ImageData newMask = new ImageData(mask.width, mask.height, 1, palette);
/* Find index of black in mask palette */
int blackIndex = 0;
RGB[] rgbs = mask.getRGBs();
if (rgbs != null) {
while (blackIndex < rgbs.length) {
if (rgbs[blackIndex].equals(palette.colors[0])) break;
blackIndex++;
}
}
int[] pixels = new int[mask.width];
for (int y = 0; y < mask.height; y++) {
mask.getPixels(0, y, mask.width, pixels, 0);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == blackIndex) {
pixels[i] = 0;
} else {
pixels[i] = 1;
}
}
newMask.setPixels(0, y, mask.width, pixels, 0);
}
return newMask;
}
static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
if (pad == newPad) return data;
int stride = (width * depth + 7) / 8;
int bpl = (stride + (pad - 1)) / pad * pad;
int newBpl = (stride + (newPad - 1)) / newPad * newPad;
byte[] newData = new byte[height * newBpl];
int srcIndex = 0, destIndex = 0;
for (int y = 0; y < height; y++) {
System.arraycopy(data, srcIndex, newData, destIndex, stride);
srcIndex += bpl;
destIndex += newBpl;
}
return newData;
}
/**
* Blit operation bits to be OR'ed together to specify the desired operation.
*/
static final int
BLIT_SRC = 1, // copy source directly, else applies logic operations
BLIT_ALPHA = 2, // enable alpha blending
BLIT_DITHER = 4; // enable dithering in low color modes
/**
* Alpha mode, values 0 - 255 specify global alpha level
*/
static final int
ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data)
ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data)
ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData
ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData
ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData
ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array
ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array
/**
* Byte and bit order constants.
*/
static final int LSB_FIRST = 0;
static final int MSB_FIRST = 1;
/**
* Data types (internal)
*/
//private static final int
// // direct / true color formats with arbitrary masks & shifts
// TYPE_GENERIC_8 = 0,
// TYPE_GENERIC_16_MSB = 1,
// TYPE_GENERIC_16_LSB = 2,
// TYPE_GENERIC_24 = 3,
// TYPE_GENERIC_32_MSB = 4,
// TYPE_GENERIC_32_LSB = 5,
// // palette indexed color formats
// TYPE_INDEX_8 = 6,
// TYPE_INDEX_4 = 7,
// TYPE_INDEX_2 = 8,
// TYPE_INDEX_1_MSB = 9,
// TYPE_INDEX_1_LSB = 10;
/**
* Computes the required channel shift from a mask.
*/
static int getChannelShift(int mask) {
if (mask == 0) return 0;
int i;
for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) {
mask >>>= 1;
}
return i;
}
/**
* Computes the required channel width (depth) from a mask.
*/
static int getChannelWidth(int mask, int shift) {
if (mask == 0) return 0;
int i;
mask >>>= shift;
for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) {
mask >>>= 1;
}
return i - shift;
}
/**
* Extracts a field from packed RGB data given a mask for that field.
*/
static byte getChannelField(int data, int mask) {
final int shift = getChannelShift(mask);
return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift];
}
/*
* Fill in dithered gradated values for a color channel
*/
static final void buildDitheredGradientChannel(int from, int to, int steps,
int bandWidth, int bandHeight, boolean vertical,
byte[] bitmapData, int dp, int bytesPerLine, int bits) {
final int mask = 0xff00 >>> bits;
int val = from << 16;
final int inc = ((to << 16) - val) / steps + 1;
if (vertical) {
for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
final int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
int temp = val + thresh;
if (temp > 0xffffff) bitmapData[dptr] = -1;
else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
}
val += inc;
}
} else {
for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
final int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
int temp = val + thresh;
if (temp > 0xffffff) bitmapData[dptr] = -1;
else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
}
val += inc;
}
}
}
}
static class LEDataInputStream extends InputStream {
int position;
InputStream in;
/**
* The byte array containing the bytes to read.
*/
protected byte[] buf;
/**
* The current position within the byte array <code>buf</code>. A value
* equal to buf.length indicates no bytes available. A value of
* 0 indicates the buffer is full.
*/
protected int pos;
public LEDataInputStream(InputStream input) {
this(input, 512);
}
public LEDataInputStream(InputStream input, int bufferSize) {
this.in = input;
if (bufferSize > 0) {
buf = new byte[bufferSize];
pos = bufferSize;
}
else throw new IllegalArgumentException();
}
@Override
public void close() throws IOException {
buf = null;
if (in != null) {
in.close();
in = null;
}
}
/**
* Answer how many bytes were read.
*/
public int getPosition() {
return position;
}
/**
* Answers how many bytes are available for reading without blocking
*/
@Override
public int available() throws IOException {
if (buf == null) throw new IOException();
return (buf.length - pos) + in.available();
}
/**
* Answer the next byte of the input stream.
*/
@Override
public int read() throws IOException {
if (buf == null) throw new IOException();
position++;
if (pos < buf.length) return (buf[pos++] & 0xFF);
return in.read();
}
/**
* Don't imitate the JDK behaviour of reading a random number
* of bytes when you can actually read them all.
*/
@Override
public int read(byte b[], int off, int len) throws IOException {
int result;
int left = len;
result = readData(b, off, len);
while (true) {
if (result == -1) return -1;
position += result;
if (result == left) return len;
left -= result;
off += result;
result = readData(b, off, left);
}
}
/**
* Reads at most <code>length</code> bytes from this LEDataInputStream and
* stores them in byte array <code>buffer</code> starting at <code>offset</code>.
* <p>
* Answer the number of bytes actually read or -1 if no bytes were read and
* end of stream was encountered. This implementation reads bytes from
* the pushback buffer first, then the target stream if more bytes are required
* to satisfy <code>count</code>.
* </p>
* @param buffer the byte array in which to store the read bytes.
* @param offset the offset in <code>buffer</code> to store the read bytes.
* @param length the maximum number of bytes to store in <code>buffer</code>.
*
* @return int the number of bytes actually read or -1 if end of stream.
*
* @exception java.io.IOException if an IOException occurs.
*/
private int readData(byte[] buffer, int offset, int length) throws IOException {
if (buf == null) throw new IOException();
if (offset < 0 || offset > buffer.length ||
length < 0 || (length > buffer.length - offset)) {
throw new ArrayIndexOutOfBoundsException();
}
int cacheCopied = 0;
int newOffset = offset;
// Are there pushback bytes available?
int available = buf.length - pos;
if (available > 0) {
cacheCopied = (available >= length) ? length : available;
System.arraycopy(buf, pos, buffer, newOffset, cacheCopied);
newOffset += cacheCopied;
pos += cacheCopied;
}
// Have we copied enough?
if (cacheCopied == length) return length;
int inCopied = in.read(buffer, newOffset, length - cacheCopied);
if (inCopied > 0) return inCopied + cacheCopied;
if (cacheCopied == 0) return inCopied;
return cacheCopied;
}
/**
* Answer an integer comprised of the next
* four bytes of the input stream.
*/
public int readInt() throws IOException {
byte[] buf = new byte[4];
read(buf);
return ((((((buf[3] & 0xFF) << 8) |
(buf[2] & 0xFF)) << 8) |
(buf[1] & 0xFF)) << 8) |
(buf[0] & 0xFF);
}
/**
* Answer a short comprised of the next
* two bytes of the input stream.
*/
public short readShort() throws IOException {
byte[] buf = new byte[2];
read(buf);
return (short)(((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF));
}
/**
* Push back the entire content of the given buffer <code>b</code>.
* <p>
* The bytes are pushed so that they would be read back b[0], b[1], etc.
* If the push back buffer cannot handle the bytes copied from <code>b</code>,
* an IOException will be thrown and no byte will be pushed back.
* </p>
*
* @param b the byte array containing bytes to push back into the stream
*
* @exception java.io.IOException if the pushback buffer is too small
*/
public void unread(byte[] b) throws IOException {
int length = b.length;
if (length > pos) throw new IOException();
position -= length;
pos -= length;
System.arraycopy(b, 0, buf, pos, length);
}
}
public static abstract class FileFormat {
LEDataInputStream inputStream;
ImageLoader loader;
int compression;
byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
// Destructively bit invert data in the given byte array.
for (int i = startIndex; i < endIndex; i++) {
data[i] = (byte)(255 - data[i - startIndex]);
}
return data;
}
/**
* Return whether or not the specified input stream
* represents a supported file format.
*/
abstract boolean isFileFormat(LEDataInputStream stream);
abstract ImageData[] loadFromByteStream();
public ImageData[] loadFromStream(LEDataInputStream stream) {
try {
inputStream = stream;
return loadFromByteStream();
} catch (Exception e) {
SWT.error(SWT.ERROR_IO, e);
return null;
}
}
public static ImageData[] load(InputStream is, ImageLoader loader) {
LEDataInputStream stream = new LEDataInputStream(is);
boolean isSupported = false;
FileFormat fileFormat = new WinICOFileFormat();
if (fileFormat.isFileFormat(stream)) isSupported = true;
else {
fileFormat = new WinBMPFileFormat();
if (fileFormat.isFileFormat(stream)) isSupported = true;
}
if (!isSupported) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
fileFormat.loader = loader;
return fileFormat.loadFromStream(stream);
}
}
static class WinBMPFileFormat extends FileFormat {
static final int BMPFileHeaderSize = 14;
static final int BMPHeaderFixedSize = 40;
int importantColors;
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);
}
int decompressRLE4Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) {
int sp = 0;
int se = numBytes;
int dp = 0;
int de = destSize;
int x = 0, y = 0;
while (sp < se) {
int len = src[sp] & 0xFF;
sp++;
if (len == 0) {
len = src[sp] & 0xFF;
sp++;
switch (len) {
case 0: /* end of line */
y++;
x = 0;
dp = y * stride;
if (dp >= de)
return -1;
break;
case 1: /* end of bitmap */
return 1;
case 2: /* delta */
x += src[sp] & 0xFF;
sp++;
y += src[sp] & 0xFF;
sp++;
dp = y * stride + x / 2;
if (dp >= de)
return -1;
break;
default: /* absolute mode run */
if ((len & 1) != 0) /* odd run lengths not currently supported */
return -1;
x += len;
len = len / 2;
if (len > (se - sp))
return -1;
if (len > (de - dp))
return -1;
for (int i = 0; i < len; i++) {
dest[dp] = src[sp];
dp++;
sp++;
}
if ((sp & 1) != 0)
sp++; /* word align sp? */
break;
}
} else {
if ((len & 1) != 0)
return -1;
x += len;
len = len / 2;
byte theByte = src[sp];
sp++;
if (len > (de - dp))
return -1;
for (int i = 0; i < len; i++) {
dest[dp] = theByte;
dp++;
}
}
}
return 1;
}
int decompressRLE8Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) {
int sp = 0;
int se = numBytes;
int dp = 0;
int de = destSize;
int x = 0, y = 0;
while (sp < se) {
int len = src[sp] & 0xFF;
sp++;
if (len == 0) {
len = src[sp] & 0xFF;
sp++;
switch (len) {
case 0: /* end of line */
y++;
x = 0;
dp = y * stride;
if (dp >= de)
return -1;
break;
case 1: /* end of bitmap */
return 1;
case 2: /* delta */
x += src[sp] & 0xFF;
sp++;
y += src[sp] & 0xFF;
sp++;
dp = y * stride + x;
if (dp >= de)
return -1;
break;
default: /* absolute mode run */
if (len > (se - sp))
return -1;
if (len > (de - dp))
return -1;
for (int i = 0; i < len; i++) {
dest[dp] = src[sp];
dp++;
sp++;
}
if ((sp & 1) != 0)
sp++; /* word align sp? */
x += len;
break;
}
} else {
byte theByte = src[sp];
sp++;
if (len > (de - dp))
return -1;
for (int i = 0; i < len; i++) {
dest[dp] = theByte;
dp++;
}
x += len;
}
}
return 1;
}
@Override
boolean isFileFormat(LEDataInputStream stream) {
try {
byte[] header = new byte[18];
stream.read(header);
stream.unread(header);
int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24);
return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize >= BMPHeaderFixedSize;
} catch (Exception e) {
return false;
}
}
byte[] loadData(byte[] infoHeader) {
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);
int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
int stride = (width * bitCount + 7) / 8;
stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
byte[] data = loadData(infoHeader, stride);
flipScanLines(data, stride, height);
return data;
}
byte[] loadData(byte[] infoHeader, int stride) {
int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
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) { // 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;
}
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;
}
@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);
int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
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.compression = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24);
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);
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)
};
}
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) return new PaletteData(0x7C00, 0x3E0, 0x1F);
if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
}
PaletteData paletteFromBytes(byte[] bytes, int numColors) {
int bytesOffset = 0;
RGB[] colors = new RGB[numColors];
for (int i = 0; i < numColors; i++) {
colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
bytes[bytesOffset + 1] & 0xFF,
bytes[bytesOffset] & 0xFF);
bytesOffset += 4;
}
return new PaletteData(colors);
}
/**
* Answer a byte array containing the BMP representation of
* the given device independent palette.
*/
static byte[] paletteToBytes(PaletteData pal) {
int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
byte[] bytes = new byte[n * 4];
int offset = 0;
for (int i = 0; i < n; i++) {
RGB col = pal.colors[i];
bytes[offset] = (byte)col.blue;
bytes[offset + 1] = (byte)col.green;
bytes[offset + 2] = (byte)col.red;
offset += 4;
}
return bytes;
}
void flipScanLines(byte[] data, int stride, int height) {
int i1 = 0;
int i2 = (height - 1) * stride;
for (int i = 0; i < height / 2; i++) {
for (int index = 0; index < stride; index++) {
byte b = data[index + i1];
data[index + i1] = data[index + i2];
data[index + i2] = b;
}
i1 += stride;
i2 -= stride;
}
}
}
static class WinICOFileFormat extends FileFormat {
static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
if (pad == newPad) return data;
int stride = (width * depth + 7) / 8;
int bpl = (stride + (pad - 1)) / pad * pad;
int newBpl = (stride + (newPad - 1)) / newPad * newPad;
byte[] newData = new byte[height * newBpl];
int srcIndex = 0, destIndex = 0;
for (int y = 0; y < height; y++) {
System.arraycopy(data, srcIndex, newData, destIndex, newBpl);
srcIndex += bpl;
destIndex += newBpl;
}
return newData;
}
/**
* Answer the size in bytes of the file representation of the given
* icon
*/
int iconSize(ImageData i) {
int shapeDataStride = (i.width * i.depth + 31) / 32 * 4;
int maskDataStride = (i.width + 31) / 32 * 4;
int dataSize = (shapeDataStride + maskDataStride) * i.height;
int paletteSize = i.palette.colors != null ? i.palette.colors.length * 4 : 0;
return WinBMPFileFormat.BMPHeaderFixedSize + paletteSize + dataSize;
}
@Override
boolean isFileFormat(LEDataInputStream stream) {
try {
byte[] header = new byte[4];
stream.read(header);
stream.unread(header);
return header[0] == 0 && header[1] == 0 && header[2] == 1 && header[3] == 0;
} catch (Exception e) {
return false;
}
}
boolean isValidIcon(ImageData i) {
switch (i.depth) {
case 1:
case 4:
case 8:
if (i.palette.isDirect) return false;
int size = i.palette.colors.length;
return size == 2 || size == 16 || size == 32 || size == 256;
case 24:
case 32:
return i.palette.isDirect;
}
return false;
}
int loadFileHeader(LEDataInputStream byteStream) {
int[] fileHeader = new int[3];
try {
fileHeader[0] = byteStream.readShort();
fileHeader[1] = byteStream.readShort();
fileHeader[2] = byteStream.readShort();
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
SWT.error(SWT.ERROR_INVALID_IMAGE);
int numIcons = fileHeader[2];
if (numIcons <= 0)
SWT.error(SWT.ERROR_INVALID_IMAGE);
return numIcons;
}
int loadFileHeader(LEDataInputStream byteStream, boolean hasHeader) {
int[] fileHeader = new int[3];
try {
if (hasHeader) {
fileHeader[0] = byteStream.readShort();
fileHeader[1] = byteStream.readShort();
} else {
fileHeader[0] = 0;
fileHeader[1] = 1;
}
fileHeader[2] = byteStream.readShort();
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
SWT.error(SWT.ERROR_INVALID_IMAGE);
int numIcons = fileHeader[2];
if (numIcons <= 0)
SWT.error(SWT.ERROR_INVALID_IMAGE);
return numIcons;
}
@Override
ImageData[] loadFromByteStream() {
int numIcons = loadFileHeader(inputStream);
int[][] headers = loadIconHeaders(numIcons);
ImageData[] icons = new ImageData[headers.length];
for (int i = 0; i < icons.length; i++) {
icons[i] = loadIcon(headers[i]);
}
return icons;
}
/**
* Load one icon from the byte stream.
*/
ImageData loadIcon(int[] iconHeader) {
byte[] infoHeader = loadInfoHeader(iconHeader);
WinBMPFileFormat bmpFormat = new WinBMPFileFormat();
bmpFormat.inputStream = inputStream;
PaletteData palette = bmpFormat.loadPalette(infoHeader);
byte[] shapeData = bmpFormat.loadData(infoHeader);
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);
int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
infoHeader[14] = 1;
infoHeader[15] = 0;
byte[] maskData = bmpFormat.loadData(infoHeader);
maskData = convertPad(maskData, width, height, 1, 4, 2);
bitInvertData(maskData, 0, maskData.length);
return ImageData.internal_new(
width,
height,
depth,
palette,
4,
shapeData,
2,
maskData,
null,
-1,
-1,
SWT.IMAGE_ICO,
0,
0,
0,
0);
}
int[][] loadIconHeaders(int numIcons) {
int[][] headers = new int[numIcons][7];
try {
for (int i = 0; i < numIcons; i++) {
headers[i][0] = inputStream.read();
headers[i][1] = inputStream.read();
headers[i][2] = inputStream.readShort();
headers[i][3] = inputStream.readShort();
headers[i][4] = inputStream.readShort();
headers[i][5] = inputStream.readInt();
headers[i][6] = inputStream.readInt();
}
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
return headers;
}
byte[] loadInfoHeader(int[] iconHeader) {
int width = iconHeader[0];
int height = iconHeader[1];
int numColors = iconHeader[2]; // the number of colors is in the low byte, but the high byte must be 0
if (numColors == 0) numColors = 256; // this is specified: '00' represents '256' (0x100) colors
if ((numColors != 2) && (numColors != 8) && (numColors != 16) &&
(numColors != 32) && (numColors != 256))
SWT.error(SWT.ERROR_INVALID_IMAGE);
if (inputStream.getPosition() < iconHeader[6]) {
// Seek to the specified offset
try {
inputStream.skip(iconHeader[6] - inputStream.getPosition());
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
return null;
}
}
byte[] infoHeader = new byte[WinBMPFileFormat.BMPHeaderFixedSize];
try {
inputStream.read(infoHeader);
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}
if (((infoHeader[12] & 0xFF) | ((infoHeader[13] & 0xFF) << 8)) != 1)
SWT.error(SWT.ERROR_INVALID_IMAGE);
int infoWidth = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
int infoHeight = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
if (height == infoHeight && bitCount == 1) height /= 2;
if (!((width == infoWidth) && (height * 2 == infoHeight) &&
(bitCount == 1 || bitCount == 4 || bitCount == 8 || bitCount == 24 || bitCount == 32)))
SWT.error(SWT.ERROR_INVALID_IMAGE);
infoHeader[8] = (byte)(height & 0xFF);
infoHeader[9] = (byte)((height >> 8) & 0xFF);
infoHeader[10] = (byte)((height >> 16) & 0xFF);
infoHeader[11] = (byte)((height >> 24) & 0xFF);
return infoHeader;
}
}
static class SWT {
public static final int IMAGE_ICO = 3;
public static final int ERROR_IO = 39;
public static final int ERROR_INVALID_IMAGE = 40;
public static final int ERROR_NULL_ARGUMENT = 4;
public static final int ERROR_INVALID_ARGUMENT = 5;
public static final int ERROR_CANNOT_BE_ZERO = 7;
public static final int IMAGE_UNDEFINED = -1;
public static final int ERROR_UNSUPPORTED_DEPTH = 38;
public static final int TRANSPARENCY_MASK = 1 << 1;
public static final int ERROR_UNSUPPORTED_FORMAT = 42;
public static final int TRANSPARENCY_ALPHA = 1 << 0;
public static final int TRANSPARENCY_NONE = 0x0;
public static final int TRANSPARENCY_PIXEL = 1 << 2;
public static final int IMAGE_BMP = 0;
public static final int IMAGE_BMP_RLE = 1;
public static void error(int code) {
throw new RuntimeException("Error "+code);
}
public static void error(int code, Throwable t) {
throw new RuntimeException(t);
}
}
}