blob: 6f31c075b0c492fea371e4d9a6b5141f16e8c7f0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 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.internal;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
public class ImageList {
long /*int*/ handle;
int style, refCount;
Image [] images;
public ImageList (int style) {
this (style, 32, 32);
}
public ImageList (int style, int width, int height) {
this.style = style;
int flags = OS.ILC_MASK;
if (OS.IsWinCE) {
flags |= OS.ILC_COLOR;
} else {
if (OS.COMCTL32_MAJOR >= 6) {
flags |= OS.ILC_COLOR32;
} else {
long /*int*/ hDC = OS.GetDC (0);
int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
OS.ReleaseDC (0, hDC);
int depth = bits * planes;
switch (depth) {
case 4: flags |= OS.ILC_COLOR4; break;
case 8: flags |= OS.ILC_COLOR8; break;
case 16: flags |= OS.ILC_COLOR16; break;
case 24: flags |= OS.ILC_COLOR24; break;
case 32: flags |= OS.ILC_COLOR32; break;
default: flags |= OS.ILC_COLOR; break;
}
}
}
if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR;
handle = OS.ImageList_Create (width, height, flags, 16, 16);
images = new Image [4];
}
public int add (Image image) {
int count = OS.ImageList_GetImageCount (handle);
int index = 0;
while (index < count) {
if (images [index] != null) {
if (images [index].isDisposed ()) images [index] = null;
}
if (images [index] == null) break;
index++;
}
if (count == 0) {
Rectangle rect = image.getBounds ();
OS.ImageList_SetIconSize (handle, rect.width, rect.height);
}
set (index, image, count);
if (index == images.length) {
Image [] newImages = new Image [images.length + 4];
System.arraycopy (images, 0, newImages, 0, images.length);
images = newImages;
}
images [index] = image;
return index;
}
public int addRef() {
return ++refCount;
}
long /*int*/ copyBitmap (long /*int*/ hImage, int width, int height) {
BITMAP bm = new BITMAP ();
OS.GetObject (hImage, BITMAP.sizeof, bm);
long /*int*/ hDC = OS.GetDC (0);
long /*int*/ hdc1 = OS.CreateCompatibleDC (hDC);
OS.SelectObject (hdc1, hImage);
long /*int*/ hdc2 = OS.CreateCompatibleDC (hDC);
/*
* Feature in Windows. If a bitmap has a 32-bit depth and any
* pixel has an alpha value different than zero, common controls
* version 6.0 assumes that the bitmap should be alpha blended.
* AlphaBlend() composes the alpha channel of a destination 32-bit
* depth image with the alpha channel of the source image. This
* may cause opaque images to draw transparently. The fix is
* remove the alpha channel of opaque images by down sampling
* it to 24-bit depth.
*/
long /*int*/ hBitmap;
if (bm.bmBitsPixel == 32 && OS.COMCTL32_MAJOR >= 6) {
BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
bmiHeader.biWidth = width;
bmiHeader.biHeight = -height;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = (short)24;
if (OS.IsWinCE) bmiHeader.biCompression = OS.BI_BITFIELDS;
else bmiHeader.biCompression = OS.BI_RGB;
byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + (OS.IsWinCE ? 12 : 0)];
OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
/* Set the rgb colors into the bitmap info */
if (OS.IsWinCE) {
int redMask = 0xFF00;
int greenMask = 0xFF0000;
int blueMask = 0xFF000000;
/* big endian */
int offset = BITMAPINFOHEADER.sizeof;
bmi[offset] = (byte)((redMask & 0xFF000000) >> 24);
bmi[offset + 1] = (byte)((redMask & 0xFF0000) >> 16);
bmi[offset + 2] = (byte)((redMask & 0xFF00) >> 8);
bmi[offset + 3] = (byte)((redMask & 0xFF) >> 0);
bmi[offset + 4] = (byte)((greenMask & 0xFF000000) >> 24);
bmi[offset + 5] = (byte)((greenMask & 0xFF0000) >> 16);
bmi[offset + 6] = (byte)((greenMask & 0xFF00) >> 8);
bmi[offset + 7] = (byte)((greenMask & 0xFF) >> 0);
bmi[offset + 8] = (byte)((blueMask & 0xFF000000) >> 24);
bmi[offset + 9] = (byte)((blueMask & 0xFF0000) >> 16);
bmi[offset + 10] = (byte)((blueMask & 0xFF00) >> 8);
bmi[offset + 11] = (byte)((blueMask & 0xFF) >> 0);
}
long /*int*/[] pBits = new long /*int*/[1];
hBitmap = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
} else {
hBitmap = OS.CreateCompatibleBitmap (hDC, width, height);
}
OS.SelectObject (hdc2, hBitmap);
if (width != bm.bmWidth || height != bm.bmHeight) {
if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
OS.StretchBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, OS.SRCCOPY);
} else {
OS.BitBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, OS.SRCCOPY);
}
OS.DeleteDC (hdc1);
OS.DeleteDC (hdc2);
OS.ReleaseDC (0, hDC);
return hBitmap;
}
long /*int*/ copyIcon (long /*int*/ hImage, int width, int height) {
if (OS.IsWinCE) SWT.error(SWT.ERROR_NOT_IMPLEMENTED);
long /*int*/ hIcon = OS.CopyImage (hImage, OS.IMAGE_ICON, width, height, 0);
return hIcon != 0 ? hIcon : hImage;
}
long /*int*/ copyWithAlpha (long /*int*/ hBitmap, int background, byte[] alphaData, int destWidth, int destHeight) {
BITMAP bm = new BITMAP ();
OS.GetObject (hBitmap, BITMAP.sizeof, bm);
int srcWidth = bm.bmWidth;
int srcHeight = bm.bmHeight;
/* Create resources */
long /*int*/ hdc = OS.GetDC (0);
long /*int*/ srcHdc = OS.CreateCompatibleDC (hdc);
long /*int*/ oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap);
long /*int*/ memHdc = OS.CreateCompatibleDC (hdc);
BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER ();
bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
bmiHeader.biWidth = srcWidth;
bmiHeader.biHeight = -srcHeight;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 32;
bmiHeader.biCompression = OS.BI_RGB;
byte [] bmi = new byte[BITMAPINFOHEADER.sizeof];
OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
long /*int*/ [] pBits = new long /*int*/ [1];
long /*int*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES);
long /*int*/ oldMemBitmap = OS.SelectObject (memHdc, memDib);
BITMAP dibBM = new BITMAP ();
OS.GetObject (memDib, BITMAP.sizeof, dibBM);
int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
/* Get the foreground pixels */
OS.BitBlt (memHdc, 0, 0, srcWidth, srcHeight, srcHdc, 0, 0, OS.SRCCOPY);
byte[] srcData = new byte [sizeInBytes];
OS.MoveMemory (srcData, dibBM.bmBits, sizeInBytes);
/* Merge the alpha channel in place */
if (alphaData != null) {
int spinc = dibBM.bmWidthBytes - srcWidth * 4;
int ap = 0, sp = 3;
for (int y = 0; y < srcHeight; ++y) {
for (int x = 0; x < srcWidth; ++x) {
srcData [sp] = alphaData [ap++];
sp += 4;
}
sp += spinc;
}
} else {
byte transRed = (byte)(background & 0xFF);
byte transGreen = (byte)((background >> 8) & 0xFF);
byte transBlue = (byte)((background >> 16) & 0xFF);
final int spinc = dibBM.bmWidthBytes - srcWidth * 4;
int sp = 3;
for (int y = 0; y < srcHeight; ++y) {
for (int x = 0; x < srcWidth; ++x) {
srcData [sp] = (srcData[sp-1] == transRed && srcData[sp-2] == transGreen && srcData[sp-3] == transBlue) ? 0 : (byte)255;
sp += 4;
}
sp += spinc;
}
}
OS.MoveMemory (dibBM.bmBits, srcData, sizeInBytes);
/* Stretch and free resources */
if (srcWidth != destWidth || srcHeight != destHeight) {
BITMAPINFOHEADER bmiHeader2 = new BITMAPINFOHEADER ();
bmiHeader2.biSize = BITMAPINFOHEADER.sizeof;
bmiHeader2.biWidth = destWidth;
bmiHeader2.biHeight = -destHeight;
bmiHeader2.biPlanes = 1;
bmiHeader2.biBitCount = 32;
bmiHeader2.biCompression = OS.BI_RGB;
byte [] bmi2 = new byte[BITMAPINFOHEADER.sizeof];
OS.MoveMemory (bmi2, bmiHeader2, BITMAPINFOHEADER.sizeof);
long /*int*/ [] pBits2 = new long /*int*/ [1];
long /*int*/ memDib2 = OS.CreateDIBSection (0, bmi2, OS.DIB_RGB_COLORS, pBits2, 0, 0);
long /*int*/ memHdc2 = OS.CreateCompatibleDC (hdc);
long /*int*/ oldMemBitmap2 = OS.SelectObject (memHdc2, memDib2);
if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc2, OS.COLORONCOLOR);
OS.StretchBlt (memHdc2, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
OS.SelectObject (memHdc2, oldMemBitmap2);
OS.DeleteDC (memHdc2);
OS.SelectObject (memHdc, oldMemBitmap);
OS.DeleteDC (memHdc);
OS.DeleteObject (memDib);
memDib = memDib2;
} else {
OS.SelectObject (memHdc, oldMemBitmap);
OS.DeleteDC (memHdc);
}
OS.SelectObject (srcHdc, oldSrcBitmap);
OS.DeleteDC (srcHdc);
OS.ReleaseDC (0, hdc);
return memDib;
}
long /*int*/ createMaskFromAlpha (ImageData data, int destWidth, int destHeight) {
int srcWidth = data.width;
int srcHeight = data.height;
ImageData mask = ImageData.internal_new (srcWidth, srcHeight, 1,
new PaletteData(new RGB [] {new RGB (0, 0, 0), new RGB (0xff, 0xff, 0xff)}),
2, null, 1, null, null, -1, -1, -1, 0, 0, 0, 0);
int ap = 0;
for (int y = 0; y < mask.height; y++) {
for (int x = 0; x < mask.width; x++) {
mask.setPixel (x, y, (data.alphaData [ap++] & 0xff) <= 127 ? 1 : 0);
}
}
long /*int*/ hMask = OS.CreateBitmap (srcWidth, srcHeight, 1, 1, mask.data);
if (srcWidth != destWidth || srcHeight != destHeight) {
long /*int*/ hdc = OS.GetDC (0);
long /*int*/ hdc1 = OS.CreateCompatibleDC (hdc);
OS.SelectObject (hdc1, hMask);
long /*int*/ hdc2 = OS.CreateCompatibleDC (hdc);
long /*int*/ hMask2 = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
OS.SelectObject (hdc2, hMask2);
if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
OS.DeleteDC (hdc1);
OS.DeleteDC (hdc2);
OS.ReleaseDC (0, hdc);
OS.DeleteObject(hMask);
hMask = hMask2;
}
return hMask;
}
long /*int*/ createMask (long /*int*/ hBitmap, int destWidth, int destHeight, int background, int transparentPixel) {
BITMAP bm = new BITMAP ();
OS.GetObject (hBitmap, BITMAP.sizeof, bm);
int srcWidth = bm.bmWidth;
int srcHeight = bm.bmHeight;
long /*int*/ hMask = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
long /*int*/ hDC = OS.GetDC (0);
long /*int*/ hdc1 = OS.CreateCompatibleDC (hDC);
if (background != -1) {
OS.SelectObject (hdc1, hBitmap);
/*
* If the image has a palette with multiple entries having
* the same color and one of those entries is the transparentPixel,
* only the first entry becomes transparent. To avoid this
* problem, temporarily change the image palette to a palette
* where the transparentPixel is white and everything else is
* black.
*/
boolean isDib = bm.bmBits != 0;
byte[] originalColors = null;
if (!OS.IsWinCE && transparentPixel != -1 && isDib && bm.bmBitsPixel <= 8) {
int maxColors = 1 << bm.bmBitsPixel;
byte[] oldColors = new byte[maxColors * 4];
OS.GetDIBColorTable(hdc1, 0, maxColors, oldColors);
int offset = transparentPixel * 4;
byte[] newColors = new byte[oldColors.length];
newColors[offset] = (byte)0xFF;
newColors[offset+1] = (byte)0xFF;
newColors[offset+2] = (byte)0xFF;
OS.SetDIBColorTable(hdc1, 0, maxColors, newColors);
originalColors = oldColors;
OS.SetBkColor (hdc1, 0xFFFFFF);
} else {
OS.SetBkColor (hdc1, background);
}
long /*int*/ hdc2 = OS.CreateCompatibleDC (hDC);
OS.SelectObject (hdc2, hMask);
if (destWidth != srcWidth || destHeight != srcHeight) {
if (!OS.IsWinCE) OS.SetStretchBltMode (hdc2, OS.COLORONCOLOR);
OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
} else {
OS.BitBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, OS.SRCCOPY);
}
OS.DeleteDC (hdc2);
/* Put back the original palette */
if (originalColors != null) OS.SetDIBColorTable(hdc1, 0, 1 << bm.bmBitsPixel, originalColors);
} else {
long /*int*/ hOldBitmap = OS.SelectObject (hdc1, hMask);
OS.PatBlt (hdc1, 0, 0, destWidth, destHeight, OS.BLACKNESS);
OS.SelectObject (hdc1, hOldBitmap);
}
OS.ReleaseDC (0, hDC);
OS.DeleteDC (hdc1);
return hMask;
}
public void dispose () {
if (handle != 0) OS.ImageList_Destroy (handle);
handle = 0;
images = null;
}
public Image get (int index) {
return images [index];
}
public int getStyle () {
return style;
}
public long /*int*/ getHandle () {
return handle;
}
public Point getImageSize() {
int [] cx = new int [1], cy = new int [1];
OS.ImageList_GetIconSize (handle, cx, cy);
return new Point (cx [0], cy [0]);
}
public int indexOf (Image image) {
int count = OS.ImageList_GetImageCount (handle);
for (int i=0; i<count; i++) {
if (images [i] != null) {
if (images [i].isDisposed ()) images [i] = null;
if (images [i] != null && images [i].equals (image)) return i;
}
}
return -1;
}
public void put (int index, Image image) {
int count = OS.ImageList_GetImageCount (handle);
if (!(0 <= index && index < count)) return;
if (image != null) set(index, image, count);
images [index] = image;
}
public void remove (int index) {
int count = OS.ImageList_GetImageCount (handle);
if (!(0 <= index && index < count)) return;
OS.ImageList_Remove (handle, index);
System.arraycopy (images, index + 1, images, index, --count - index);
images [index] = null;
}
public int removeRef() {
return --refCount;
}
void set (int index, Image image, int count) {
long /*int*/ hImage = image.handle;
int [] cx = new int [1], cy = new int [1];
OS.ImageList_GetIconSize (handle, cx, cy);
switch (image.type) {
case SWT.BITMAP: {
/*
* Note that the image size has to match the image list icon size.
*/
long /*int*/ hBitmap = 0, hMask = 0;
ImageData data = image.getImageData ();
switch (data.getTransparencyType ()) {
case SWT.TRANSPARENCY_ALPHA:
/*
* Fully transparent image is rendered as a black image, so such
* image needs to be rendered using ImageData mask approach.
* Refer bug 426247
*
* TODO: Explore using createMaskFromAlpha() method even
* for newer versions of the COMCTL32 library.
*/
boolean fullyTransparent = true;
if (data.alphaData == null) {
fullyTransparent = false;
}
else {
for (byte alphaData : data.alphaData) {
if (alphaData != 0) {
fullyTransparent = false;
break;
}
}
}
if (OS.COMCTL32_MAJOR >= 6 && !fullyTransparent) {
hBitmap = copyWithAlpha (hImage, -1, data.alphaData, cx [0], cy [0]);
} else {
hBitmap = copyBitmap (hImage, cx [0], cy [0]);
hMask = createMaskFromAlpha (data, cx [0], cy [0]);
}
break;
case SWT.TRANSPARENCY_PIXEL:
int background = -1;
Color color = image.getBackground ();
if (color != null) background = color.handle;
hBitmap = copyBitmap (hImage, cx [0], cy [0]);
hMask = createMask (hImage, cx [0], cy [0], background, data.transparentPixel);
break;
case SWT.TRANSPARENCY_NONE:
default:
hBitmap = copyBitmap (hImage, cx [0], cy [0]);
if (index != count) hMask = createMask (hImage, cx [0], cy [0], -1, -1);
break;
}
if (index == count) {
OS.ImageList_Add (handle, hBitmap, hMask);
} else {
/* Note that the mask must always be replaced even for TRANSPARENCY_NONE */
OS.ImageList_Replace (handle, index, hBitmap, hMask);
}
if (hMask != 0) OS.DeleteObject (hMask);
if (hBitmap != hImage) OS.DeleteObject (hBitmap);
break;
}
case SWT.ICON: {
if (OS.IsWinCE) {
OS.ImageList_ReplaceIcon (handle, index == count ? -1 : index, hImage);
} else {
long /*int*/ hIcon = copyIcon (hImage, cx [0], cy [0]);
OS.ImageList_ReplaceIcon (handle, index == count ? -1 : index, hIcon);
OS.DestroyIcon (hIcon);
}
break;
}
}
}
public int size () {
int result = 0;
int count = OS.ImageList_GetImageCount (handle);
for (int i=0; i<count; i++) {
if (images [i] != null) {
if (images [i].isDisposed ()) images [i] = null;
if (images [i] != null) result++;
}
}
return result;
}
}