1 /*******************************************************************************
2 * Copyright (c) 2000, 2017 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.internal;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.internal.win32.*;
21 public class ImageList {
26 public ImageList (int style) {
30 public ImageList (int style, int width, int height) {
32 int flags = OS.ILC_MASK | OS.ILC_COLOR32;
33 if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR;
34 handle = OS.ImageList_Create (width, height, flags, 16, 16);
35 images = new Image [4];
38 public int add (Image image) {
39 int count = OS.ImageList_GetImageCount (handle);
41 while (index < count) {
42 if (images [index] != null) {
43 if (images [index].isDisposed ()) images [index] = null;
45 if (images [index] == null) break;
49 Rectangle rect = image.getBoundsInPixels ();
50 OS.ImageList_SetIconSize (handle, rect.width, rect.height);
52 set (index, image, count);
53 if (index == images.length) {
54 Image [] newImages = new Image [images.length + 4];
55 System.arraycopy (images, 0, newImages, 0, images.length);
58 images [index] = image;
66 long copyBitmap (long hImage, int width, int height) {
67 BITMAP bm = new BITMAP ();
68 OS.GetObject (hImage, BITMAP.sizeof, bm);
69 long hDC = OS.GetDC (0);
70 long hdc1 = OS.CreateCompatibleDC (hDC);
71 OS.SelectObject (hdc1, hImage);
72 long hdc2 = OS.CreateCompatibleDC (hDC);
74 * Feature in Windows. If a bitmap has a 32-bit depth and any
75 * pixel has an alpha value different than zero, common controls
76 * version 6.0 assumes that the bitmap should be alpha blended.
77 * AlphaBlend() composes the alpha channel of a destination 32-bit
78 * depth image with the alpha channel of the source image. This
79 * may cause opaque images to draw transparently. The fix is
80 * remove the alpha channel of opaque images by down sampling
84 if (bm.bmBitsPixel == 32) {
85 BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
86 bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
87 bmiHeader.biWidth = width;
88 bmiHeader.biHeight = -height;
89 bmiHeader.biPlanes = 1;
90 bmiHeader.biBitCount = (short)24;
91 bmiHeader.biCompression = OS.BI_RGB;
92 byte[] bmi = new byte[BITMAPINFOHEADER.sizeof];
93 OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
94 long[] pBits = new long[1];
95 hBitmap = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
97 hBitmap = OS.CreateCompatibleBitmap (hDC, width, height);
99 OS.SelectObject (hdc2, hBitmap);
100 if (width != bm.bmWidth || height != bm.bmHeight) {
101 OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
102 OS.StretchBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, OS.SRCCOPY);
104 OS.BitBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, OS.SRCCOPY);
108 OS.ReleaseDC (0, hDC);
112 long copyIcon (long hImage, int width, int height) {
113 long hIcon = OS.CopyImage (hImage, OS.IMAGE_ICON, width, height, 0);
114 return hIcon != 0 ? hIcon : hImage;
117 long copyWithAlpha (long hBitmap, int background, byte[] alphaData, int destWidth, int destHeight) {
118 BITMAP bm = new BITMAP ();
119 OS.GetObject (hBitmap, BITMAP.sizeof, bm);
120 int srcWidth = bm.bmWidth;
121 int srcHeight = bm.bmHeight;
123 /* Create resources */
124 long hdc = OS.GetDC (0);
125 long srcHdc = OS.CreateCompatibleDC (hdc);
126 long oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap);
127 long memHdc = OS.CreateCompatibleDC (hdc);
128 BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER ();
129 bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
130 bmiHeader.biWidth = srcWidth;
131 bmiHeader.biHeight = -srcHeight;
132 bmiHeader.biPlanes = 1;
133 bmiHeader.biBitCount = 32;
134 bmiHeader.biCompression = OS.BI_RGB;
135 byte [] bmi = new byte[BITMAPINFOHEADER.sizeof];
136 OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
137 long [] pBits = new long [1];
138 long memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
139 if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES);
140 long oldMemBitmap = OS.SelectObject (memHdc, memDib);
142 BITMAP dibBM = new BITMAP ();
143 OS.GetObject (memDib, BITMAP.sizeof, dibBM);
144 int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
146 /* Get the foreground pixels */
147 OS.BitBlt (memHdc, 0, 0, srcWidth, srcHeight, srcHdc, 0, 0, OS.SRCCOPY);
148 byte[] srcData = new byte [sizeInBytes];
149 OS.MoveMemory (srcData, dibBM.bmBits, sizeInBytes);
151 /* Merge the alpha channel in place */
152 if (alphaData != null) {
153 int spinc = dibBM.bmWidthBytes - srcWidth * 4;
155 for (int y = 0; y < srcHeight; ++y) {
156 for (int x = 0; x < srcWidth; ++x) {
157 srcData [sp] = alphaData [ap++];
163 byte transRed = (byte)(background & 0xFF);
164 byte transGreen = (byte)((background >> 8) & 0xFF);
165 byte transBlue = (byte)((background >> 16) & 0xFF);
166 final int spinc = dibBM.bmWidthBytes - srcWidth * 4;
168 for (int y = 0; y < srcHeight; ++y) {
169 for (int x = 0; x < srcWidth; ++x) {
170 srcData [sp] = (srcData[sp-1] == transRed && srcData[sp-2] == transGreen && srcData[sp-3] == transBlue) ? 0 : (byte)255;
176 OS.MoveMemory (dibBM.bmBits, srcData, sizeInBytes);
178 /* Stretch and free resources */
179 if (srcWidth != destWidth || srcHeight != destHeight) {
180 BITMAPINFOHEADER bmiHeader2 = new BITMAPINFOHEADER ();
181 bmiHeader2.biSize = BITMAPINFOHEADER.sizeof;
182 bmiHeader2.biWidth = destWidth;
183 bmiHeader2.biHeight = -destHeight;
184 bmiHeader2.biPlanes = 1;
185 bmiHeader2.biBitCount = 32;
186 bmiHeader2.biCompression = OS.BI_RGB;
187 byte [] bmi2 = new byte[BITMAPINFOHEADER.sizeof];
188 OS.MoveMemory (bmi2, bmiHeader2, BITMAPINFOHEADER.sizeof);
189 long [] pBits2 = new long [1];
190 long memDib2 = OS.CreateDIBSection (0, bmi2, OS.DIB_RGB_COLORS, pBits2, 0, 0);
191 long memHdc2 = OS.CreateCompatibleDC (hdc);
192 long oldMemBitmap2 = OS.SelectObject (memHdc2, memDib2);
193 OS.SetStretchBltMode(memHdc2, OS.COLORONCOLOR);
194 OS.StretchBlt (memHdc2, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
195 OS.SelectObject (memHdc2, oldMemBitmap2);
196 OS.DeleteDC (memHdc2);
197 OS.SelectObject (memHdc, oldMemBitmap);
198 OS.DeleteDC (memHdc);
199 OS.DeleteObject (memDib);
202 OS.SelectObject (memHdc, oldMemBitmap);
203 OS.DeleteDC (memHdc);
205 OS.SelectObject (srcHdc, oldSrcBitmap);
206 OS.DeleteDC (srcHdc);
207 OS.ReleaseDC (0, hdc);
211 long createMaskFromAlpha (ImageData data, int destWidth, int destHeight) {
212 int srcWidth = data.width;
213 int srcHeight = data.height;
214 ImageData mask = ImageData.internal_new (srcWidth, srcHeight, 1,
215 new PaletteData(new RGB [] {new RGB (0, 0, 0), new RGB (0xff, 0xff, 0xff)}),
216 2, null, 1, null, null, -1, -1, -1, 0, 0, 0, 0);
218 for (int y = 0; y < mask.height; y++) {
219 for (int x = 0; x < mask.width; x++) {
220 mask.setPixel (x, y, (data.alphaData [ap++] & 0xff) <= 127 ? 1 : 0);
223 long hMask = OS.CreateBitmap (srcWidth, srcHeight, 1, 1, mask.data);
224 if (srcWidth != destWidth || srcHeight != destHeight) {
225 long hdc = OS.GetDC (0);
226 long hdc1 = OS.CreateCompatibleDC (hdc);
227 OS.SelectObject (hdc1, hMask);
228 long hdc2 = OS.CreateCompatibleDC (hdc);
229 long hMask2 = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
230 OS.SelectObject (hdc2, hMask2);
231 OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
232 OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
235 OS.ReleaseDC (0, hdc);
236 OS.DeleteObject(hMask);
242 long createMask (long hBitmap, int destWidth, int destHeight, int background, int transparentPixel) {
243 BITMAP bm = new BITMAP ();
244 OS.GetObject (hBitmap, BITMAP.sizeof, bm);
245 int srcWidth = bm.bmWidth;
246 int srcHeight = bm.bmHeight;
247 long hMask = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
248 long hDC = OS.GetDC (0);
249 long hdc1 = OS.CreateCompatibleDC (hDC);
250 if (background != -1) {
251 OS.SelectObject (hdc1, hBitmap);
254 * If the image has a palette with multiple entries having
255 * the same color and one of those entries is the transparentPixel,
256 * only the first entry becomes transparent. To avoid this
257 * problem, temporarily change the image palette to a palette
258 * where the transparentPixel is white and everything else is
261 boolean isDib = bm.bmBits != 0;
262 byte[] originalColors = null;
263 if (transparentPixel != -1 && isDib && bm.bmBitsPixel <= 8) {
264 int maxColors = 1 << bm.bmBitsPixel;
265 byte[] oldColors = new byte[maxColors * 4];
266 OS.GetDIBColorTable(hdc1, 0, maxColors, oldColors);
267 int offset = transparentPixel * 4;
268 byte[] newColors = new byte[oldColors.length];
269 newColors[offset] = (byte)0xFF;
270 newColors[offset+1] = (byte)0xFF;
271 newColors[offset+2] = (byte)0xFF;
272 OS.SetDIBColorTable(hdc1, 0, maxColors, newColors);
273 originalColors = oldColors;
274 OS.SetBkColor (hdc1, 0xFFFFFF);
276 OS.SetBkColor (hdc1, background);
279 long hdc2 = OS.CreateCompatibleDC (hDC);
280 OS.SelectObject (hdc2, hMask);
281 if (destWidth != srcWidth || destHeight != srcHeight) {
282 OS.SetStretchBltMode (hdc2, OS.COLORONCOLOR);
283 OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
285 OS.BitBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, OS.SRCCOPY);
289 /* Put back the original palette */
290 if (originalColors != null) OS.SetDIBColorTable(hdc1, 0, 1 << bm.bmBitsPixel, originalColors);
292 long hOldBitmap = OS.SelectObject (hdc1, hMask);
293 OS.PatBlt (hdc1, 0, 0, destWidth, destHeight, OS.BLACKNESS);
294 OS.SelectObject (hdc1, hOldBitmap);
296 OS.ReleaseDC (0, hDC);
301 public void dispose () {
302 if (handle != 0) OS.ImageList_Destroy (handle);
307 public Image get (int index) {
308 return images [index];
311 public int getStyle () {
315 public long getHandle () {
319 public Point getImageSize() {
320 int [] cx = new int [1], cy = new int [1];
321 OS.ImageList_GetIconSize (handle, cx, cy);
322 return new Point (cx [0], cy [0]);
325 public int indexOf (Image image) {
326 int count = OS.ImageList_GetImageCount (handle);
327 for (int i=0; i<count; i++) {
328 if (images [i] != null) {
329 if (images [i].isDisposed ()) images [i] = null;
330 if (images [i] != null && images [i].equals (image)) return i;
336 public void put (int index, Image image) {
337 int count = OS.ImageList_GetImageCount (handle);
338 if (!(0 <= index && index < count)) return;
339 if (image != null) set(index, image, count);
340 images [index] = image;
343 public void remove (int index) {
344 int count = OS.ImageList_GetImageCount (handle);
345 if (!(0 <= index && index < count)) return;
346 OS.ImageList_Remove (handle, index);
347 System.arraycopy (images, index + 1, images, index, --count - index);
348 images [index] = null;
351 public int removeRef() {
355 void set (int index, Image image, int count) {
356 long hImage = image.handle;
357 int [] cx = new int [1], cy = new int [1];
358 OS.ImageList_GetIconSize (handle, cx, cy);
359 switch (image.type) {
362 * Note that the image size has to match the image list icon size.
364 long hBitmap = 0, hMask = 0;
365 ImageData data = image.getImageData (DPIUtil.getDeviceZoom ());
366 switch (data.getTransparencyType ()) {
367 case SWT.TRANSPARENCY_ALPHA:
369 * Fully transparent image is rendered as a black image, so such
370 * image needs to be rendered using ImageData mask approach.
373 * TODO: Explore using createMaskFromAlpha() method even
374 * for newer versions of the COMCTL32 library.
376 boolean fullyTransparent = true;
377 if (data.alphaData == null) {
378 fullyTransparent = false;
381 for (byte alphaData : data.alphaData) {
382 if (alphaData != 0) {
383 fullyTransparent = false;
388 if (!fullyTransparent) {
389 hBitmap = copyWithAlpha (hImage, -1, data.alphaData, cx [0], cy [0]);
391 hBitmap = copyBitmap (hImage, cx [0], cy [0]);
392 hMask = createMaskFromAlpha (data, cx [0], cy [0]);
395 case SWT.TRANSPARENCY_PIXEL:
397 Color color = image.getBackground ();
398 if (color != null) background = color.handle;
399 hBitmap = copyBitmap (hImage, cx [0], cy [0]);
400 hMask = createMask (hImage, cx [0], cy [0], background, data.transparentPixel);
402 case SWT.TRANSPARENCY_NONE:
404 hBitmap = copyBitmap (hImage, cx [0], cy [0]);
405 if (index != count) hMask = createMask (hImage, cx [0], cy [0], -1, -1);
408 if (index == count) {
409 OS.ImageList_Add (handle, hBitmap, hMask);
411 /* Note that the mask must always be replaced even for TRANSPARENCY_NONE */
412 OS.ImageList_Replace (handle, index, hBitmap, hMask);
414 if (hMask != 0) OS.DeleteObject (hMask);
415 if (hBitmap != hImage) OS.DeleteObject (hBitmap);
419 long hIcon = copyIcon (hImage, cx [0], cy [0]);
420 OS.ImageList_ReplaceIcon (handle, index == count ? -1 : index, hIcon);
421 OS.DestroyIcon (hIcon);
429 int count = OS.ImageList_GetImageCount (handle);
430 for (int i=0; i<count; i++) {
431 if (images [i] != null) {
432 if (images [i].isDisposed ()) images [i] = null;
433 if (images [i] != null) result++;