diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index e7c9f37a294..ca04f0ae0bb 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -810,186 +810,6 @@ public static long win32_getHandle (Image image, int zoom) { return image.getImageMetadata(zoom).handle; } -ImageHandle initNative(String filename, int zoom) { - ImageHandle imageMetadata = null; - long handle = 0; - int width = -1; - int height = -1; - device.checkGDIP(); - boolean gdip = true; - /* - * Bug in GDI+. For some reason, Bitmap.LockBits() segment faults - * when loading GIF files in 64-bit Windows. The fix is to not use - * GDI+ image loading in this case. - */ - if (gdip && C.PTR_SIZEOF == 8 && filename.toLowerCase().endsWith(".gif")) gdip = false; - /* - * Bug in GDI+. Bitmap.LockBits() fails to load GIF files in - * Windows 7 when the image has a position offset in the first frame. - * The fix is to not use GDI+ image loading in this case. - */ - if (filename.toLowerCase().endsWith(".gif")) gdip = false; - if (gdip) { - int length = filename.length(); - char[] chars = new char[length+1]; - filename.getChars(0, length, chars, 0); - long bitmap = Gdip.Bitmap_new(chars, false); - if (bitmap != 0) { - int error = SWT.ERROR_NO_HANDLES; - int status = Gdip.Image_GetLastStatus(bitmap); - if (status == 0) { - if (filename.toLowerCase().endsWith(".ico")) { - this.type = SWT.ICON; - long[] hicon = new long[1]; - status = Gdip.Bitmap_GetHICON(bitmap, hicon); - handle = hicon[0]; - imageMetadata = new ImageHandle(handle, zoom); - } else { - this.type = SWT.BITMAP; - width = Gdip.Image_GetWidth(bitmap); - height = Gdip.Image_GetHeight(bitmap); - int pixelFormat = Gdip.Image_GetPixelFormat(bitmap); - switch (pixelFormat) { - case Gdip.PixelFormat16bppRGB555: - case Gdip.PixelFormat16bppRGB565: - handle = createDIB(width, height, 16); - break; - case Gdip.PixelFormat24bppRGB: - case Gdip.PixelFormat32bppCMYK: - handle = createDIB(width, height, 24); - break; - case Gdip.PixelFormat32bppRGB: - // These will lose either precision or transparency - case Gdip.PixelFormat16bppGrayScale: - case Gdip.PixelFormat48bppRGB: - case Gdip.PixelFormat32bppPARGB: - case Gdip.PixelFormat64bppARGB: - case Gdip.PixelFormat64bppPARGB: - handle = createDIB(width, height, 32); - break; - } - if (handle != 0) { - /* - * This performs better than getting the bits with Bitmap.LockBits(), - * but it cannot be used when there is transparency. - */ - long hDC = device.internal_new_GC(null); - long srcHDC = OS.CreateCompatibleDC(hDC); - long oldSrcBitmap = OS.SelectObject(srcHDC, handle); - long graphics = Gdip.Graphics_new(srcHDC); - if (graphics != 0) { - Rect rect = new Rect(); - rect.Width = width; - rect.Height = height; - status = Gdip.Graphics_DrawImage(graphics, bitmap, rect, 0, 0, width, height, Gdip.UnitPixel, 0, 0, 0); - if (status != 0) { - error = SWT.ERROR_INVALID_IMAGE; - OS.DeleteObject(handle); - handle = 0; - } - Gdip.Graphics_delete(graphics); - } - OS.SelectObject(srcHDC, oldSrcBitmap); - OS.DeleteDC(srcHDC); - device.internal_dispose_GC(hDC, null); - imageMetadata = new ImageHandle(handle, zoom); - } else { - long lockedBitmapData = Gdip.BitmapData_new(); - if (lockedBitmapData != 0) { - status = Gdip.Bitmap_LockBits(bitmap, 0, 0, pixelFormat, lockedBitmapData); - if (status == 0) { - BitmapData bitmapData = new BitmapData(); - Gdip.MoveMemory(bitmapData, lockedBitmapData); - int stride = bitmapData.Stride; - long pixels = bitmapData.Scan0; - int depth = 0, scanlinePad = 4, transparentPixel = -1; - switch (bitmapData.PixelFormat) { - case Gdip.PixelFormat1bppIndexed: depth = 1; break; - case Gdip.PixelFormat4bppIndexed: depth = 4; break; - case Gdip.PixelFormat8bppIndexed: depth = 8; break; - case Gdip.PixelFormat16bppARGB1555: - case Gdip.PixelFormat16bppRGB555: - case Gdip.PixelFormat16bppRGB565: depth = 16; break; - case Gdip.PixelFormat24bppRGB: depth = 24; break; - case Gdip.PixelFormat32bppRGB: - case Gdip.PixelFormat32bppARGB: depth = 32; break; - } - if (depth != 0) { - PaletteData paletteData = null; - switch (bitmapData.PixelFormat) { - case Gdip.PixelFormat1bppIndexed: - case Gdip.PixelFormat4bppIndexed: - case Gdip.PixelFormat8bppIndexed: - int paletteSize = Gdip.Image_GetPaletteSize(bitmap); - long hHeap = OS.GetProcessHeap(); - long palette = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, paletteSize); - if (palette == 0) SWT.error(SWT.ERROR_NO_HANDLES); - Gdip.Image_GetPalette(bitmap, palette, paletteSize); - ColorPalette colorPalette = new ColorPalette(); - Gdip.MoveMemory(colorPalette, palette, ColorPalette.sizeof); - int[] entries = new int[colorPalette.Count]; - OS.MoveMemory(entries, palette + 8, entries.length * 4); - OS.HeapFree(hHeap, 0, palette); - RGB[] rgbs = new RGB[colorPalette.Count]; - paletteData = new PaletteData(rgbs); - for (int i = 0; i < entries.length; i++) { - if (((entries[i] >> 24) & 0xFF) == 0 && (colorPalette.Flags & Gdip.PaletteFlagsHasAlpha) != 0) { - transparentPixel = i; - } - rgbs[i] = new RGB(((entries[i] & 0xFF0000) >> 16), ((entries[i] & 0xFF00) >> 8), ((entries[i] & 0xFF) >> 0)); - } - break; - case Gdip.PixelFormat16bppARGB1555: - case Gdip.PixelFormat16bppRGB555: paletteData = new PaletteData(0x7C00, 0x3E0, 0x1F); break; - case Gdip.PixelFormat16bppRGB565: paletteData = new PaletteData(0xF800, 0x7E0, 0x1F); break; - case Gdip.PixelFormat24bppRGB: paletteData = new PaletteData(0xFF, 0xFF00, 0xFF0000); break; - case Gdip.PixelFormat32bppRGB: - case Gdip.PixelFormat32bppARGB: paletteData = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); break; - } - byte[] data = new byte[stride * height], alphaData = null; - OS.MoveMemory(data, pixels, data.length); - switch (bitmapData.PixelFormat) { - case Gdip.PixelFormat16bppARGB1555: - alphaData = new byte[width * height]; - for (int i = 1, j = 0; i < data.length; i += 2, j++) { - alphaData[j] = (byte)((data[i] & 0x80) != 0 ? 255 : 0); - } - break; - case Gdip.PixelFormat32bppARGB: - alphaData = new byte[width * height]; - for (int i = 3, j = 0; i < data.length; i += 4, j++) { - alphaData[j] = data[i]; - } - break; - } - ImageData img = new ImageData(width, height, depth, paletteData, scanlinePad, data); - img.transparentPixel = transparentPixel; - img.alphaData = alphaData; - - ImageData newData = adaptImageDataIfDisabledOrGray(img); - init(newData, zoom); - imageMetadata = zoomLevelToImageHandle.get(zoom); - handle = imageMetadata.handle; - } - Gdip.Bitmap_UnlockBits(bitmap, lockedBitmapData); - } else { - error = SWT.ERROR_INVALID_IMAGE; - } - Gdip.BitmapData_delete(lockedBitmapData); - } - } - } - } - Gdip.Bitmap_delete(bitmap); - if (status == 0) { - if (handle == 0) SWT.error(error); - if (imageMetadata == null) SWT.error(error); - } - } - } - return imageMetadata; -} - long [] createGdipImage() { return createGdipImage(this.getZoom()); } @@ -1193,6 +1013,12 @@ void destroyHandlesExcept(Set zoomLevels) { return false; }); } +private void destroyHandleForZoom(int zoom) { + ImageHandle imageHandle = zoomLevelToImageHandle.remove(zoom); + if (imageHandle != null) { + destroyHandle(imageHandle.handle); + } +} private void destroyHandle(long handle) { if (type == SWT.ICON) { @@ -2213,27 +2039,38 @@ protected Rectangle getBounds(int zoom) { @Override ImageData getImageData(int zoom) { - ElementAtZoom fileName = DPIUtil.validateAndGetImagePathAtZoom (provider, zoom); - ElementAtZoom imageData = ImageDataLoader.load(fileName.element(), fileName.zoom(), zoom); - return DPIUtil.scaleImageData (device, imageData, zoom); - } - - @Override - ImageHandle getImageMetadata(int zoom) { ElementAtZoom fileForZoom = DPIUtil.validateAndGetImagePathAtZoom (provider, zoom); - ImageHandle nativeInitializedImage = null; - if (fileForZoom.zoom() == zoom) { - nativeInitializedImage = initNative(fileForZoom.element(), zoom); + ImageHandle nativeInitializedImage; + if (zoomLevelToImageHandle.containsKey(fileForZoom.zoom())) { + nativeInitializedImage = zoomLevelToImageHandle.get(fileForZoom.zoom()); + } else { + nativeInitializedImage = initNative(fileForZoom.element(), fileForZoom.zoom()); } + + ElementAtZoom imageDataAtZoom; if (nativeInitializedImage == null) { - ElementAtZoom imageDataAtZoom = ImageDataLoader.load(fileForZoom.element(), fileForZoom.zoom(), zoom); - ImageData imageData = imageDataAtZoom.element(); - if (imageDataAtZoom.zoom() != zoom) { - imageData = DPIUtil.scaleImageData(device, imageDataAtZoom, zoom); - } - imageData = adaptImageDataIfDisabledOrGray(imageData); - init(imageData, zoom); + imageDataAtZoom = ImageDataLoader.load(fileForZoom.element(), fileForZoom.zoom(), zoom); + } else { + imageDataAtZoom = new ElementAtZoom<>(nativeInitializedImage.getImageData(), fileForZoom.zoom()); + destroyHandleForZoom(zoom); + } + ImageData imageData = scaleIfNecessary(imageDataAtZoom, zoom); + imageData = adaptImageDataIfDisabledOrGray(imageData); + return imageData; + } + + private ImageData scaleIfNecessary(ElementAtZoom imageDataAtZoom, int zoom) { + if (imageDataAtZoom.zoom() != zoom) { + return DPIUtil.scaleImageData(device, imageDataAtZoom, zoom); + } else { + return imageDataAtZoom.element(); } + } + + @Override + ImageHandle getImageMetadata(int zoom) { + ImageData imageData = getImageData(zoom); + init(imageData, zoom); return zoomLevelToImageHandle.get(zoom); } @@ -2256,6 +2093,199 @@ public int hashCode() { ImageFileNameProviderWrapper createCopy(Image image) { return image.new ImageFileNameProviderWrapper(provider); } + + ImageHandle initNative(String filename, int zoom) { + ImageHandle imageMetadata = null; + long handle = 0; + int width = -1; + int height = -1; + device.checkGDIP(); + boolean gdip = true; + /* + * Bug in GDI+. For some reason, Bitmap.LockBits() segment faults + * when loading GIF files in 64-bit Windows. The fix is to not use + * GDI+ image loading in this case. + */ + if (gdip && C.PTR_SIZEOF == 8 && filename.toLowerCase().endsWith(".gif")) gdip = false; + /* + * Bug in GDI+. Bitmap.LockBits() fails to load GIF files in + * Windows 7 when the image has a position offset in the first frame. + * The fix is to not use GDI+ image loading in this case. + */ + if (filename.toLowerCase().endsWith(".gif")) gdip = false; + + if(!gdip) return null; + + int length = filename.length(); + char[] chars = new char[length+1]; + filename.getChars(0, length, chars, 0); + long bitmap = Gdip.Bitmap_new(chars, false); + if (bitmap == 0) return null; + + int error = SWT.ERROR_NO_HANDLES; + int status = Gdip.Image_GetLastStatus(bitmap); + if (status == 0) { + if (filename.toLowerCase().endsWith(".ico")) { + type = SWT.ICON; + long[] hicon = new long[1]; + status = Gdip.Bitmap_GetHICON(bitmap, hicon); + handle = hicon[0]; + imageMetadata = new ImageHandle(handle, zoom); + } else { + type = SWT.BITMAP; + width = Gdip.Image_GetWidth(bitmap); + height = Gdip.Image_GetHeight(bitmap); + int pixelFormat = Gdip.Image_GetPixelFormat(bitmap); + handle = extractHandleForPixelFormat(width, height, pixelFormat); + if (handle != 0) { + /* + * This performs better than getting the bits with Bitmap.LockBits(), + * but it cannot be used when there is transparency. + */ + long hDC = device.internal_new_GC(null); + long srcHDC = OS.CreateCompatibleDC(hDC); + long oldSrcBitmap = OS.SelectObject(srcHDC, handle); + long graphics = Gdip.Graphics_new(srcHDC); + if (graphics != 0) { + Rect rect = new Rect(); + rect.Width = width; + rect.Height = height; + status = Gdip.Graphics_DrawImage(graphics, bitmap, rect, 0, 0, width, height, Gdip.UnitPixel, 0, 0, 0); + if (status != 0) { + error = SWT.ERROR_INVALID_IMAGE; + OS.DeleteObject(handle); + handle = 0; + } + Gdip.Graphics_delete(graphics); + } + OS.SelectObject(srcHDC, oldSrcBitmap); + OS.DeleteDC(srcHDC); + device.internal_dispose_GC(hDC, null); + imageMetadata = new ImageHandle(handle, zoom); + } else { + long lockedBitmapData = Gdip.BitmapData_new(); + if (lockedBitmapData != 0) { + status = Gdip.Bitmap_LockBits(bitmap, 0, 0, pixelFormat, lockedBitmapData); + if (status == 0) { + BitmapData bitmapData = new BitmapData(); + Gdip.MoveMemory(bitmapData, lockedBitmapData); + int stride = bitmapData.Stride; + long pixels = bitmapData.Scan0; + int depth = 0, scanlinePad = 4, transparentPixel = -1; + depth = extractDepthForPixelFormat(bitmapData); + if (depth != 0) { + PaletteData paletteData = null; + switch (bitmapData.PixelFormat) { + case Gdip.PixelFormat1bppIndexed: + case Gdip.PixelFormat4bppIndexed: + case Gdip.PixelFormat8bppIndexed: + int paletteSize = Gdip.Image_GetPaletteSize(bitmap); + long hHeap = OS.GetProcessHeap(); + long palette = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, paletteSize); + if (palette == 0) SWT.error(SWT.ERROR_NO_HANDLES); + Gdip.Image_GetPalette(bitmap, palette, paletteSize); + ColorPalette colorPalette = new ColorPalette(); + Gdip.MoveMemory(colorPalette, palette, ColorPalette.sizeof); + int[] entries = new int[colorPalette.Count]; + OS.MoveMemory(entries, palette + 8, entries.length * 4); + OS.HeapFree(hHeap, 0, palette); + RGB[] rgbs = new RGB[colorPalette.Count]; + paletteData = new PaletteData(rgbs); + for (int i = 0; i < entries.length; i++) { + if (((entries[i] >> 24) & 0xFF) == 0 && (colorPalette.Flags & Gdip.PaletteFlagsHasAlpha) != 0) { + transparentPixel = i; + } + rgbs[i] = new RGB(((entries[i] & 0xFF0000) >> 16), ((entries[i] & 0xFF00) >> 8), ((entries[i] & 0xFF) >> 0)); + } + break; + case Gdip.PixelFormat16bppARGB1555: + case Gdip.PixelFormat16bppRGB555: paletteData = new PaletteData(0x7C00, 0x3E0, 0x1F); break; + case Gdip.PixelFormat16bppRGB565: paletteData = new PaletteData(0xF800, 0x7E0, 0x1F); break; + case Gdip.PixelFormat24bppRGB: paletteData = new PaletteData(0xFF, 0xFF00, 0xFF0000); break; + case Gdip.PixelFormat32bppRGB: + case Gdip.PixelFormat32bppARGB: paletteData = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); break; + } + byte[] data = new byte[stride * height], alphaData = null; + OS.MoveMemory(data, pixels, data.length); + switch (bitmapData.PixelFormat) { + case Gdip.PixelFormat16bppARGB1555: + alphaData = new byte[width * height]; + for (int i = 1, j = 0; i < data.length; i += 2, j++) { + alphaData[j] = (byte)((data[i] & 0x80) != 0 ? 255 : 0); + } + break; + case Gdip.PixelFormat32bppARGB: + alphaData = new byte[width * height]; + for (int i = 3, j = 0; i < data.length; i += 4, j++) { + alphaData[j] = data[i]; + } + break; + } + ImageData img = new ImageData(width, height, depth, paletteData, scanlinePad, data); + img.transparentPixel = transparentPixel; + img.alphaData = alphaData; + + init(img, zoom); + imageMetadata = zoomLevelToImageHandle.get(zoom); + handle = imageMetadata.handle; + } + Gdip.Bitmap_UnlockBits(bitmap, lockedBitmapData); + } else { + error = SWT.ERROR_INVALID_IMAGE; + } + Gdip.BitmapData_delete(lockedBitmapData); + } + } + } + } + Gdip.Bitmap_delete(bitmap); + if (status == 0) { + if (handle == 0) SWT.error(error); + if (imageMetadata == null) SWT.error(error); + } + + return imageMetadata; + } + + private int extractDepthForPixelFormat(BitmapData bitmapData) { + int depth = 0; + switch (bitmapData.PixelFormat) { + case Gdip.PixelFormat1bppIndexed: depth = 1; break; + case Gdip.PixelFormat4bppIndexed: depth = 4; break; + case Gdip.PixelFormat8bppIndexed: depth = 8; break; + case Gdip.PixelFormat16bppARGB1555: + case Gdip.PixelFormat16bppRGB555: + case Gdip.PixelFormat16bppRGB565: depth = 16; break; + case Gdip.PixelFormat24bppRGB: depth = 24; break; + case Gdip.PixelFormat32bppRGB: + case Gdip.PixelFormat32bppARGB: depth = 32; break; + } + return depth; + } + + private long extractHandleForPixelFormat(int width, int height, int pixelFormat) { + long handle = 0; + switch (pixelFormat) { + case Gdip.PixelFormat16bppRGB555: + case Gdip.PixelFormat16bppRGB565: + handle = createDIB(width, height, 16); + break; + case Gdip.PixelFormat24bppRGB: + case Gdip.PixelFormat32bppCMYK: + handle = createDIB(width, height, 24); + break; + case Gdip.PixelFormat32bppRGB: + // These will lose either precision or transparency + case Gdip.PixelFormat16bppGrayScale: + case Gdip.PixelFormat48bppRGB: + case Gdip.PixelFormat32bppPARGB: + case Gdip.PixelFormat64bppARGB: + case Gdip.PixelFormat64bppPARGB: + handle = createDIB(width, height, 32); + break; + } + return handle; + } } private class ImageDataProviderWrapper extends DynamicImageProviderWrapper {