]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/internal/ImageList.java
6b8905b6a9e169459c4fec50ab127bc9262045d7
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / internal / ImageList.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 IBM Corporation and others.
3  *
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/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.internal;
15
16
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.internal.win32.*;
20
21 public class ImageList {
22         long handle;
23         int style, refCount;
24         Image [] images;
25
26 public ImageList (int style) {
27         this (style, 32, 32);
28 }
29
30 public ImageList (int style, int width, int height) {
31         this.style = style;
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];
36 }
37
38 public int add (Image image) {
39         int count = OS.ImageList_GetImageCount (handle);
40         int index = 0;
41         while (index < count) {
42                 if (images [index] != null) {
43                         if (images [index].isDisposed ()) images [index] = null;
44                 }
45                 if (images [index] == null) break;
46                 index++;
47         }
48         if (count == 0) {
49                 Rectangle rect = image.getBoundsInPixels ();
50                 OS.ImageList_SetIconSize (handle, rect.width, rect.height);
51         }
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);
56                 images = newImages;
57         }
58         images [index] = image;
59         return index;
60 }
61
62 public int addRef() {
63         return ++refCount;
64 }
65
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);
73         /*
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
81         * it to 24-bit depth.
82         */
83         long hBitmap;
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);
96         } else {
97                 hBitmap = OS.CreateCompatibleBitmap (hDC, width, height);
98         }
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);
103         } else {
104                 OS.BitBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, OS.SRCCOPY);
105         }
106         OS.DeleteDC (hdc1);
107         OS.DeleteDC (hdc2);
108         OS.ReleaseDC (0, hDC);
109         return hBitmap;
110 }
111
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;
115 }
116
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;
122
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);
141
142         BITMAP dibBM = new BITMAP ();
143         OS.GetObject (memDib, BITMAP.sizeof, dibBM);
144         int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
145
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);
150
151         /* Merge the alpha channel in place */
152         if (alphaData != null) {
153                 int spinc = dibBM.bmWidthBytes - srcWidth * 4;
154                 int ap = 0, sp = 3;
155                 for (int y = 0; y < srcHeight; ++y) {
156                         for (int x = 0; x < srcWidth; ++x) {
157                                 srcData [sp] = alphaData [ap++];
158                                 sp += 4;
159                         }
160                         sp += spinc;
161                 }
162         } else {
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;
167                 int sp = 3;
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;
171                                 sp += 4;
172                         }
173                         sp += spinc;
174                 }
175         }
176         OS.MoveMemory (dibBM.bmBits, srcData, sizeInBytes);
177
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);
200                 memDib = memDib2;
201         } else {
202                 OS.SelectObject (memHdc, oldMemBitmap);
203                 OS.DeleteDC (memHdc);
204         }
205         OS.SelectObject (srcHdc, oldSrcBitmap);
206         OS.DeleteDC (srcHdc);
207         OS.ReleaseDC (0, hdc);
208         return memDib;
209 }
210
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);
217         int ap = 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);
221                 }
222         }
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);
233                 OS.DeleteDC (hdc1);
234                 OS.DeleteDC (hdc2);
235                 OS.ReleaseDC (0, hdc);
236                 OS.DeleteObject(hMask);
237                 hMask = hMask2;
238         }
239         return hMask;
240 }
241
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);
252
253                 /*
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
259                 * black.
260                 */
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);
275                 } else {
276                         OS.SetBkColor (hdc1, background);
277                 }
278
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);
284                 } else {
285                         OS.BitBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, OS.SRCCOPY);
286                 }
287                 OS.DeleteDC (hdc2);
288
289                 /* Put back the original palette */
290                 if (originalColors != null) OS.SetDIBColorTable(hdc1, 0, 1 << bm.bmBitsPixel, originalColors);
291         } else {
292                 long hOldBitmap = OS.SelectObject (hdc1, hMask);
293                 OS.PatBlt (hdc1, 0, 0, destWidth, destHeight, OS.BLACKNESS);
294                 OS.SelectObject (hdc1, hOldBitmap);
295         }
296         OS.ReleaseDC (0, hDC);
297         OS.DeleteDC (hdc1);
298         return hMask;
299 }
300
301 public void dispose () {
302         if (handle != 0) OS.ImageList_Destroy (handle);
303         handle = 0;
304         images = null;
305 }
306
307 public Image get (int index) {
308         return images [index];
309 }
310
311 public int getStyle () {
312         return style;
313 }
314
315 public long getHandle () {
316         return handle;
317 }
318
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]);
323 }
324
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;
331                 }
332         }
333         return -1;
334 }
335
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;
341 }
342
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;
349 }
350
351 public int removeRef() {
352         return --refCount;
353 }
354
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) {
360                 case SWT.BITMAP: {
361                         /*
362                         * Note that the image size has to match the image list icon size.
363                         */
364                         long hBitmap = 0, hMask = 0;
365                         ImageData data = image.getImageData (DPIUtil.getDeviceZoom ());
366                         switch (data.getTransparencyType ()) {
367                                 case SWT.TRANSPARENCY_ALPHA:
368                                         /*
369                                          * Fully transparent image is rendered as a black image, so such
370                                          * image needs to be rendered using ImageData mask approach.
371                                          * Refer bug 426247
372                                          *
373                                          * TODO: Explore using createMaskFromAlpha() method even
374                                          * for newer versions of the COMCTL32 library.
375                                          */
376                                         boolean fullyTransparent = true;
377                                         if (data.alphaData == null) {
378                                                 fullyTransparent = false;
379                                         }
380                                         else {
381                                                 for (byte alphaData : data.alphaData) {
382                                                         if (alphaData != 0) {
383                                                                 fullyTransparent = false;
384                                                                 break;
385                                                         }
386                                                 }
387                                         }
388                                         if (!fullyTransparent) {
389                                                 hBitmap = copyWithAlpha (hImage, -1, data.alphaData, cx [0], cy [0]);
390                                         } else {
391                                                 hBitmap = copyBitmap (hImage, cx [0], cy [0]);
392                                                 hMask = createMaskFromAlpha (data, cx [0], cy [0]);
393                                         }
394                                         break;
395                                 case SWT.TRANSPARENCY_PIXEL:
396                                         int background = -1;
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);
401                                         break;
402                                 case SWT.TRANSPARENCY_NONE:
403                                 default:
404                                         hBitmap = copyBitmap (hImage, cx [0], cy [0]);
405                                         if (index != count) hMask = createMask (hImage, cx [0], cy [0], -1, -1);
406                                         break;
407                         }
408                         if (index == count) {
409                                 OS.ImageList_Add (handle, hBitmap, hMask);
410                         } else {
411                                 /* Note that the mask must always be replaced even for TRANSPARENCY_NONE */
412                                 OS.ImageList_Replace (handle, index, hBitmap, hMask);
413                         }
414                         if (hMask != 0) OS.DeleteObject (hMask);
415                         if (hBitmap != hImage) OS.DeleteObject (hBitmap);
416                         break;
417                 }
418                 case SWT.ICON: {
419                         long hIcon = copyIcon (hImage, cx [0], cy [0]);
420                         OS.ImageList_ReplaceIcon (handle, index == count ? -1 : index, hIcon);
421                         OS.DestroyIcon (hIcon);
422                         break;
423                 }
424         }
425 }
426
427 public int size () {
428         int result = 0;
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++;
434                 }
435         }
436         return result;
437 }
438
439 }