root/dwt/internal/ImageList.d

Revision 213:36f5cb12e1a2, 17.8 kB (checked in by Frank Benoit <benoit@tionex.de>, 7 months ago)

Update to SWT 3.4M7

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.internal.ImageList;
14
15
16 import dwt.DWT;
17 import dwt.graphics.Color;
18 import dwt.graphics.Image;
19 import dwt.graphics.ImageData;
20 import dwt.graphics.PaletteData;
21 import dwt.graphics.Point;
22 import dwt.graphics.RGB;
23 import dwt.graphics.Rectangle;
24 import dwt.internal.win32.OS;
25
26 import dwt.dwthelper.utils;
27 import dwt.dwthelper.System;
28
29 public class ImageList {
30     HIMAGELIST handle;
31     int style, refCount;
32     Image [] images;
33
34 public this (int style) {
35     this.style = style;
36     int flags = OS.ILC_MASK;
37     static if (OS.IsWinCE) {
38         flags |= OS.ILC_COLOR;
39     } else {
40         if (OS.COMCTL32_MAJOR >= 6) {
41             flags |= OS.ILC_COLOR32;
42         } else {
43             auto hDC = OS.GetDC (null);
44             auto bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
45             auto planes = OS.GetDeviceCaps (hDC, OS.PLANES);
46             OS.ReleaseDC (null, hDC);
47             int depth = bits * planes;
48             switch (depth) {
49                 case 4: flags |= OS.ILC_COLOR4; break;
50                 case 8: flags |= OS.ILC_COLOR8; break;
51                 case 16: flags |= OS.ILC_COLOR16; break;
52                 case 24: flags |= OS.ILC_COLOR24; break;
53                 case 32: flags |= OS.ILC_COLOR32; break;
54                 default: flags |= OS.ILC_COLOR; break;
55             }
56         }
57     }
58     if ((style & DWT.RIGHT_TO_LEFT) !is 0) flags |= OS.ILC_MIRROR;
59     handle = OS.ImageList_Create (32, 32, flags, 16, 16);
60     images = new Image [4];
61 }
62
63 public int add (Image image) {
64     int count = OS.ImageList_GetImageCount (handle);
65     int index = 0;
66     while (index < count) {
67         if (images [index] !is null) {
68             if (images [index].isDisposed ()) images [index] = null;
69         }
70         if (images [index] is null) break;
71         index++;
72     }
73     if (count is 0) {
74         Rectangle rect = image.getBounds ();
75         OS.ImageList_SetIconSize (handle, rect.width, rect.height);
76     }
77     set (index, image, count);
78     if (index is images.length) {
79         Image [] newImages = new Image [images.length + 4];
80         System.arraycopy (images, 0, newImages, 0, images.length);
81         images = newImages;
82     }
83     images [index] = image;
84     return index;
85 }
86
87 public int addRef() {
88     return ++refCount;
89 }
90
91 HBITMAP copyBitmap (HBITMAP hImage, int width, int height) {
92     BITMAP bm;
93     OS.GetObject (hImage, BITMAP.sizeof, &bm);
94     auto hDC = OS.GetDC (null);
95     auto hdc1 = OS.CreateCompatibleDC (hDC);
96     OS.SelectObject (hdc1, hImage);
97     auto hdc2 = OS.CreateCompatibleDC (hDC);
98     /*
99     * Feature in Windows.  If a bitmap has a 32-bit depth and any
100     * pixel has an alpha value different than zero, common controls
101     * version 6.0 assumes that the bitmap should be alpha blended.
102     * AlphaBlend() composes the alpha channel of a destination 32-bit
103     * depth image with the alpha channel of the source image. This
104     * may cause opaque images to draw transparently.  The fix is
105     * remove the alpha channel of opaque images by down sampling
106     * it to 24-bit depth.
107     */
108     HBITMAP hBitmap;
109     if (bm.bmBitsPixel is 32 && OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
110         BITMAPINFOHEADER bmiHeader;
111         bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
112         bmiHeader.biWidth = width;
113         bmiHeader.biHeight = -height;
114         bmiHeader.biPlanes = 1;
115         bmiHeader.biBitCount = cast(short)24;
116         static if (OS.IsWinCE) bmiHeader.biCompression = OS.BI_BITFIELDS;
117         else bmiHeader.biCompression = OS.BI_RGB;
118         byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + (OS.IsWinCE ? 12 : 0)];
119         *cast(BITMAPINFOHEADER*)bmi.ptr = bmiHeader;
120         //OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
121         /* Set the rgb colors into the bitmap info */
122         static if (OS.IsWinCE) {
123             int redMask = 0xFF00;
124             int greenMask = 0xFF0000;
125             int blueMask = 0xFF000000;
126             /* big endian */
127             int offset = BITMAPINFOHEADER.sizeof;
128             bmi[offset] = cast(byte)((redMask & 0xFF000000) >> 24);
129             bmi[offset + 1] = cast(byte)((redMask & 0xFF0000) >> 16);
130             bmi[offset + 2] = cast(byte)((redMask & 0xFF00) >> 8);
131             bmi[offset + 3] = cast(byte)((redMask & 0xFF) >> 0);
132             bmi[offset + 4] = cast(byte)((greenMask & 0xFF000000) >> 24);
133             bmi[offset + 5] = cast(byte)((greenMask & 0xFF0000) >> 16);
134             bmi[offset + 6] = cast(byte)((greenMask & 0xFF00) >> 8);
135             bmi[offset + 7] = cast(byte)((greenMask & 0xFF) >> 0);
136             bmi[offset + 8] = cast(byte)((blueMask & 0xFF000000) >> 24);
137             bmi[offset + 9] = cast(byte)((blueMask & 0xFF0000) >> 16);
138             bmi[offset + 10] = cast(byte)((blueMask & 0xFF00) >> 8);
139             bmi[offset + 11] = cast(byte)((blueMask & 0xFF) >> 0);
140         }
141         void* pBits;
142         hBitmap = OS.CreateDIBSection(null, cast(BITMAPINFO*)bmi.ptr, OS.DIB_RGB_COLORS, &pBits, null, 0);
143     } else {
144         hBitmap = OS.CreateCompatibleBitmap (hDC, width, height);
145     }
146     OS.SelectObject (hdc2, hBitmap);
147     if (width !is bm.bmWidth || height !is bm.bmHeight) {
148         static if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
149         OS.StretchBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, OS.SRCCOPY);
150     } else {
151         OS.BitBlt (hdc2, 0, 0, width, height, hdc1, 0, 0, OS.SRCCOPY);
152     }
153     OS.DeleteDC (hdc1);
154     OS.DeleteDC (hdc2);
155     OS.ReleaseDC (null, hDC);
156     return hBitmap;
157 }
158
159 HBITMAP copyIcon (HBITMAP hImage, int width, int height) {
160     static if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
161     auto hIcon = OS.CopyImage (hImage, OS.IMAGE_ICON, width, height, 0);
162     return hIcon !is null ? hIcon : hImage;
163 }
164
165 HBITMAP copyWithAlpha (HBITMAP hBitmap, int background, byte[] alphaData, int destWidth, int destHeight) {
166     BITMAP bm;
167     OS.GetObject (hBitmap, BITMAP.sizeof, &bm);
168     int srcWidth = bm.bmWidth;
169     int srcHeight = bm.bmHeight;
170
171     /* Create resources */
172     auto hdc = OS.GetDC (null);
173     auto srcHdc = OS.CreateCompatibleDC (hdc);
174     auto oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap);
175     auto memHdc = OS.CreateCompatibleDC (hdc);
176     BITMAPINFOHEADER bmiHeader;
177     bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
178     bmiHeader.biWidth = srcWidth;
179     bmiHeader.biHeight = -srcHeight;
180     bmiHeader.biPlanes = 1;
181     bmiHeader.biBitCount = 32;
182     bmiHeader.biCompression = OS.BI_RGB;
183     byte [] bmi = new byte[BITMAPINFOHEADER.sizeof];
184     *cast(BITMAPINFOHEADER*)bmi.ptr = bmiHeader;
185     //OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
186     void* pBits;
187     auto memDib = OS.CreateDIBSection (null, cast(BITMAPINFO*)bmi.ptr, OS.DIB_RGB_COLORS, &pBits, null, 0);
188     if (memDib is null) DWT.error (DWT.ERROR_NO_HANDLES);
189     auto oldMemBitmap = OS.SelectObject (memHdc, memDib);
190
191     BITMAP dibBM;
192     OS.GetObject (memDib, BITMAP.sizeof, &dibBM);
193     int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
194
195     /* Get the foreground pixels */
196     OS.BitBlt (memHdc, 0, 0, srcWidth, srcHeight, srcHdc, 0, 0, OS.SRCCOPY);
197     byte[] srcData = (cast(byte*)dibBM.bmBits)[ 0 .. sizeInBytes].dup;
198     //OS.MoveMemory (srcData, dibBM.bmBits, sizeInBytes);
199
200     /* Merge the alpha channel in place */
201     if (alphaData !is null) {
202         int spinc = dibBM.bmWidthBytes - srcWidth * 4;
203         int ap = 0, sp = 3;
204         for (int y = 0; y < srcHeight; ++y) {
205             for (int x = 0; x < srcWidth; ++x) {
206                 srcData [sp] = alphaData [ap++];
207                 sp += 4;
208             }
209             sp += spinc;
210         }
211     } else {
212         byte transRed = cast(byte)(background & 0xFF);
213         byte transGreen = cast(byte)((background >> 8) & 0xFF);
214         byte transBlue = cast(byte)((background >> 16) & 0xFF);
215         final int spinc = dibBM.bmWidthBytes - srcWidth * 4;
216         int sp = 3;
217         for (int y = 0; y < srcHeight; ++y) {
218             for (int x = 0; x < srcWidth; ++x) {
219                 srcData [sp] = (srcData[sp-1] is transRed && srcData[sp-2] is transGreen && srcData[sp-3] is transBlue) ? 0 : cast(byte)255;
220                 sp += 4;
221             }
222             sp += spinc;
223         }
224     }
225     (cast(byte*)dibBM.bmBits)[ 0 .. sizeInBytes] = srcData;
226     //OS.MoveMemory (dibBM.bmBits, srcData, sizeInBytes);
227
228     /* Stretch and free resources */
229     if (srcWidth !is destWidth || srcHeight !is destHeight) {
230         BITMAPINFOHEADER bmiHeader2;
231         bmiHeader2.biSize = BITMAPINFOHEADER.sizeof;
232         bmiHeader2.biWidth = destWidth;
233         bmiHeader2.biHeight = -destHeight;
234         bmiHeader2.biPlanes = 1;
235         bmiHeader2.biBitCount = 32;
236         bmiHeader2.biCompression = OS.BI_RGB;
237         byte [] bmi2 = new byte[BITMAPINFOHEADER.sizeof];
238         *cast(BITMAPINFOHEADER*)bmi2.ptr = bmiHeader2;
239         //OS.MoveMemory (bmi2, bmiHeader2, BITMAPINFOHEADER.sizeof);
240         void* pBits2;
241         auto memDib2 = OS.CreateDIBSection (null, cast(BITMAPINFO*)bmi2.ptr, OS.DIB_RGB_COLORS, &pBits2, null, 0);
242         auto memHdc2 = OS.CreateCompatibleDC (hdc);
243         auto oldMemBitmap2 = OS.SelectObject (memHdc2, memDib2);
244         static if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc2, OS.COLORONCOLOR);
245         OS.StretchBlt (memHdc2, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
246         OS.SelectObject (memHdc2, oldMemBitmap2);
247         OS.DeleteDC (memHdc2);
248         OS.SelectObject (memHdc, oldMemBitmap);
249         OS.DeleteDC (memHdc);
250         OS.DeleteObject (memDib);
251         memDib = memDib2;
252     } else {
253         OS.SelectObject (memHdc, oldMemBitmap);
254         OS.DeleteDC (memHdc);
255     }
256     OS.SelectObject (srcHdc, oldSrcBitmap);
257     OS.DeleteDC (srcHdc);
258     OS.ReleaseDC (null, hdc);
259     return memDib;
260 }
261
262 HBITMAP createMaskFromAlpha (ImageData data, int destWidth, int destHeight) {
263     int srcWidth = data.width;
264     int srcHeight = data.height;
265     ImageData mask = ImageData.internal_new (srcWidth, srcHeight, 1,
266             new PaletteData([new RGB (0, 0, 0), new RGB (0xff, 0xff, 0xff)]),
267             2, null, 1, null, null, -1, -1, -1, 0, 0, 0, 0);
268     int ap = 0;
269     for (int y = 0; y < mask.height; y++) {
270         for (int x = 0; x < mask.width; x++) {
271             mask.setPixel (x, y, (data.alphaData [ap++] & 0xff) <= 127 ? 1 : 0);
272         }
273     }
274     auto hMask = OS.CreateBitmap (srcWidth, srcHeight, 1, 1, mask.data.ptr);
275     if (srcWidth !is destWidth || srcHeight !is destHeight) {
276         auto hdc = OS.GetDC (null);
277         auto hdc1 = OS.CreateCompatibleDC (hdc);
278         OS.SelectObject (hdc1, hMask);
279         auto hdc2 = OS.CreateCompatibleDC (hdc);
280         auto hMask2 = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
281         OS.SelectObject (hdc2, hMask2);
282         static if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
283         OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
284         OS.DeleteDC (hdc1);
285         OS.DeleteDC (hdc2);
286         OS.ReleaseDC (null, hdc);
287         OS.DeleteObject(hMask);
288         hMask = hMask2;
289     }
290     return hMask;
291 }
292
293 HBITMAP createMask (HBITMAP hBitmap, int destWidth, int destHeight, int background, int transparentPixel) {
294     BITMAP bm;
295     OS.GetObject (hBitmap, BITMAP.sizeof, &bm);
296     int srcWidth = bm.bmWidth;
297     int srcHeight = bm.bmHeight;
298     auto hMask = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
299     auto hDC = OS.GetDC (null);
300     auto hdc1 = OS.CreateCompatibleDC (hDC);
301     if (background !is -1) {
302         OS.SelectObject (hdc1, hBitmap);
303
304         /*
305         * If the image has a palette with multiple entries having
306         * the same color and one of those entries is the transparentPixel,
307         * only the first entry becomes transparent. To avoid this
308         * problem, temporarily change the image palette to a palette
309         * where the transparentPixel is white and everything else is
310         * black.
311         */
312         bool isDib = bm.bmBits !is null;
313         byte[] originalColors = null;
314         if (!OS.IsWinCE && transparentPixel !is -1 && isDib && bm.bmBitsPixel <= 8) {
315             int maxColors = 1 << bm.bmBitsPixel;
316             byte[] oldColors = new byte[maxColors * 4];
317             OS.GetDIBColorTable(hdc1, 0, maxColors, cast(RGBQUAD*)oldColors.ptr);
318             int offset = transparentPixel * 4;
319             byte[] newColors = new byte[oldColors.length];
320             newColors[offset] = cast(byte)0xFF;
321             newColors[offset+1] = cast(byte)0xFF;
322             newColors[offset+2] = cast(byte)0xFF;
323             OS.SetDIBColorTable(hdc1, 0, maxColors, cast(RGBQUAD*)newColors.ptr);
324             originalColors = oldColors;
325             OS.SetBkColor (hdc1, 0xFFFFFF);
326         } else {
327             OS.SetBkColor (hdc1, background);
328         }
329
330         auto hdc2 = OS.CreateCompatibleDC (hDC);
331         OS.SelectObject (hdc2, hMask);
332         if (destWidth !is srcWidth || destHeight !is srcHeight) {
333             static if (!OS.IsWinCE) OS.SetStretchBltMode (hdc2, OS.COLORONCOLOR);
334             OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
335         } else {
336             OS.BitBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, OS.SRCCOPY);
337         }
338         OS.DeleteDC (hdc2);
339
340         /* Put back the original palette */
341         if (originalColors !is null) OS.SetDIBColorTable(hdc1, 0, 1 << bm.bmBitsPixel, cast(RGBQUAD*)originalColors.ptr);
342     } else {
343         auto hOldBitmap = OS.SelectObject (hdc1, hMask);
344         OS.PatBlt (hdc1, 0, 0, destWidth, destHeight, OS.BLACKNESS);
345         OS.SelectObject (hdc1, hOldBitmap);
346     }
347     OS.ReleaseDC (null, hDC);
348     OS.DeleteDC (hdc1);
349     return hMask;
350 }
351
352 public void dispose () {
353     if (handle !is null) OS.ImageList_Destroy (handle);
354     handle = null;
355     images = null;
356 }
357
358 public Image get (int index) {
359     return images [index];
360 }
361
362 public int getStyle () {
363     return style;
364 }
365
366 public HIMAGELIST getHandle () {
367     return handle;
368 }
369
370 public Point getImageSize() {
371     int cx, cy;
372     OS.ImageList_GetIconSize (handle, &cx, &cy);
373     return new Point (cx, cy);
374 }
375
376 public int indexOf (Image image) {
377     int count = OS.ImageList_GetImageCount (handle);
378     for (int i=0; i<count; i++) {
379         if (images [i] !is null) {
380             if (images [i].isDisposed ()) images [i] = null;
381             if (images [i] !is null && images [i]==/*eq*/image) return i;
382         }
383     }
384     return -1;
385 }
386
387 public void put (int index, Image image) {
388     int count = OS.ImageList_GetImageCount (handle);
389     if (!(0 <= index && index < count)) return;
390     if (image !is null) set(index, image, count);
391     images [index] = image;
392 }
393
394 public void remove (int index) {
395     int count = OS.ImageList_GetImageCount (handle);
396     if (!(0 <= index && index < count)) return;
397     OS.ImageList_Remove (handle, index);
398     System.arraycopy (images, index + 1, images, index, --count - index);
399     images [index] = null;
400 }
401
402 public int removeRef() {
403     return --refCount;
404 }
405
406 void set (int index, Image image, int count) {
407     auto hImage = image.handle;
408     int cx , cy ;
409     OS.ImageList_GetIconSize (handle, &cx, &cy);
410     switch (image.type) {
411         case DWT.BITMAP: {
412             /*
413             * Note that the image size has to match the image list icon size.
414             */
415             HBITMAP hBitmap, hMask;
416             ImageData data = image.getImageData ();
417             switch (data.getTransparencyType ()) {
418                 case DWT.TRANSPARENCY_ALPHA:
419                     if (OS.COMCTL32_MAJOR >= 6) {
420                         hBitmap = copyWithAlpha (hImage, -1, data.alphaData, cx, cy);
421                     } else {
422                         hBitmap = copyBitmap (hImage, cx, cy);
423                         hMask = createMaskFromAlpha (data, cx, cy);
424                     }
425                     break;
426                 case DWT.TRANSPARENCY_PIXEL:
427                     int background = -1;
428                     Color color = image.getBackground ();
429                     if (color !is null) background = color.handle;
430                     hBitmap = copyBitmap (hImage, cx, cy);
431                     hMask = createMask (hImage, cx, cy, background, data.transparentPixel);
432                     break;
433