1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.lwawt.macosx; 27 28 import java.awt.*; 29 import java.awt.geom.Dimension2D; 30 import java.awt.image.*; 31 32 import java.util.Arrays; 33 import java.util.List; 34 import java.awt.image.MultiResolutionImage; 35 import java.util.concurrent.atomic.AtomicReference; 36 37 import sun.awt.image.MultiResolutionCachedImage; 38 39 import sun.awt.image.SunWritableRaster; 40 41 public class CImage extends CFRetainedResource { nativeCreateNSImageFromArray(int[] buffer, int w, int h)42 private static native long nativeCreateNSImageFromArray(int[] buffer, int w, int h); nativeCreateNSImageFromBytes(byte[] buffer)43 private static native long nativeCreateNSImageFromBytes(byte[] buffer); nativeCreateNSImageFromArrays(int[][] buffers, int[] w, int[] h)44 private static native long nativeCreateNSImageFromArrays(int[][] buffers, int[] w, int[] h); nativeCreateNSImageFromFileContents(String file)45 private static native long nativeCreateNSImageFromFileContents(String file); nativeCreateNSImageOfFileFromLaunchServices(String file)46 private static native long nativeCreateNSImageOfFileFromLaunchServices(String file); nativeCreateNSImageFromImageName(String name)47 private static native long nativeCreateNSImageFromImageName(String name); nativeCreateNSImageFromIconSelector(int selector)48 private static native long nativeCreateNSImageFromIconSelector(int selector); nativeGetPlatformImageBytes(int[] buffer, int w, int h)49 private static native byte[] nativeGetPlatformImageBytes(int[] buffer, int w, int h); nativeCopyNSImageIntoArray(long image, int[] buffer, int sw, int sh, int dw, int dh)50 private static native void nativeCopyNSImageIntoArray(long image, int[] buffer, int sw, int sh, int dw, int dh); nativeGetNSImageSize(long image)51 private static native Dimension2D nativeGetNSImageSize(long image); nativeSetNSImageSize(long image, double w, double h)52 private static native void nativeSetNSImageSize(long image, double w, double h); nativeResizeNSImageRepresentations(long image, double w, double h)53 private static native void nativeResizeNSImageRepresentations(long image, double w, double h); nativeGetNSImageRepresentationSizes(long image, double w, double h)54 private static native Dimension2D[] nativeGetNSImageRepresentationSizes(long image, double w, double h); 55 56 static Creator creator = new Creator(); getCreator()57 static Creator getCreator() { 58 return creator; 59 } 60 61 // This is used to create a CImage that represents the icon of the given file. createImageOfFile(String file, int width, int height)62 public static Image createImageOfFile(String file, int width, int height) { 63 return getCreator().createImageOfFile(file, width, height); 64 } 65 createSystemImageFromSelector(String iconSelector, int width, int height)66 public static Image createSystemImageFromSelector(String iconSelector, 67 int width, int height) { 68 return getCreator().createSystemImageFromSelector(iconSelector, width, height); 69 } 70 createImageFromFile(String file, double width, double height)71 public static Image createImageFromFile(String file, double width, double height) { 72 return getCreator().createImageFromFile(file, width, height); 73 } 74 75 // This is used to create a CImage from a Image createFromImage(final Image image)76 public static CImage createFromImage(final Image image) { 77 return getCreator().createFromImage(image, null); 78 } 79 80 // This is used to create a CImage from a Image createFromImage(final Image image, CTrayIcon.IconObserver observer)81 public static CImage createFromImage(final Image image, CTrayIcon.IconObserver observer) { 82 return getCreator().createFromImage(image, observer); 83 } 84 85 public static class Creator { 86 CTrayIcon.IconObserver observer; 87 Creator()88 Creator() { } 89 90 // This is used to create a CImage with an NSImage pointer. It MUST be a CFRetained 91 // NSImage, and the CImage takes ownership of the non-GC retain. If callers need the 92 // NSImage themselves, they MUST call retain on the NSImage themselves. createImageUsingNativeSize(final long image)93 public Image createImageUsingNativeSize(final long image) { 94 if (image == 0) return null; 95 final Dimension2D size = nativeGetNSImageSize(image); 96 return createImage(image, size.getWidth(), size.getHeight()); 97 } 98 99 // the width and height passed in as a parameter could differ than the width and the height of the NSImage (image), in that case, the image will be scaled createImage(long image, double width, double height)100 Image createImage(long image, double width, double height) { 101 if (image == 0) throw new Error("Unable to instantiate CImage with null native image reference."); 102 return createImageWithSize(image, width, height); 103 } 104 createImageWithSize(final long image, final double width, final double height)105 public Image createImageWithSize(final long image, final double width, final double height) { 106 final CImage img = new CImage(image); 107 img.resize(width, height); 108 return img.toImage(); 109 } 110 111 // This is used to create a CImage that represents the icon of the given file. createImageOfFile(final String file, final int width, final int height)112 public Image createImageOfFile(final String file, final int width, final int height) { 113 return createImage(nativeCreateNSImageOfFileFromLaunchServices(file), width, height); 114 } 115 createImageFromFile(final String file, final double width, final double height)116 public Image createImageFromFile(final String file, final double width, final double height) { 117 final long image = nativeCreateNSImageFromFileContents(file); 118 nativeSetNSImageSize(image, width, height); 119 return createImage(image, width, height); 120 } 121 createSystemImageFromSelector(final String iconSelector, final int width, final int height)122 public Image createSystemImageFromSelector(final String iconSelector, final int width, final int height) { 123 return createImage(nativeCreateNSImageFromIconSelector(getSelectorAsInt(iconSelector)), width, height); 124 } 125 createImageFromName(final String name, final int width, final int height)126 public Image createImageFromName(final String name, final int width, final int height) { 127 return createImage(nativeCreateNSImageFromImageName(name), width, height); 128 } 129 createImageFromName(final String name)130 public Image createImageFromName(final String name) { 131 return createImageUsingNativeSize(nativeCreateNSImageFromImageName(name)); 132 } 133 imageToArray(Image image, boolean prepareImage, CTrayIcon.IconObserver observer)134 private static int[] imageToArray(Image image, boolean prepareImage, CTrayIcon.IconObserver observer) { 135 if (image == null) return null; 136 137 if (prepareImage && !(image instanceof BufferedImage)) { 138 final MediaTracker mt = new MediaTracker(new Label()); 139 final int id = 0; 140 mt.addImage(image, id); 141 142 try { 143 mt.waitForID(id); 144 } catch (InterruptedException e) { 145 return null; 146 } 147 148 if (mt.isErrorID(id)) { 149 return null; 150 } 151 } 152 153 int w = image.getWidth(null); 154 int h = image.getHeight(null); 155 156 if (w < 0 || h < 0) { 157 return null; 158 } 159 160 BufferedImage bimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE); 161 Graphics2D g2 = bimg.createGraphics(); 162 g2.setComposite(AlphaComposite.Src); 163 g2.drawImage(image, 0, 0, observer); 164 g2.dispose(); 165 166 return ((DataBufferInt)bimg.getRaster().getDataBuffer()).getData(); 167 } 168 getPlatformImageBytes(final Image image)169 public byte[] getPlatformImageBytes(final Image image) { 170 int[] buffer = imageToArray(image, false, null); 171 172 if (buffer == null) { 173 return null; 174 } 175 176 return nativeGetPlatformImageBytes(buffer, image.getWidth(null), image.getHeight(null)); 177 } 178 179 /** 180 * Translates a byte array which contains platform-specific image data in the given format into an Image. 181 */ createImageFromPlatformImageBytes(final byte[] buffer)182 public Image createImageFromPlatformImageBytes(final byte[] buffer) { 183 return createImageUsingNativeSize(nativeCreateNSImageFromBytes(buffer)); 184 } 185 186 // This is used to create a CImage from a Image createFromImage(final Image image)187 public CImage createFromImage(final Image image) { 188 return createFromImage(image, true, null); 189 } 190 191 // This is used to create a CImage from a Image createFromImage(final Image image, CTrayIcon.IconObserver observer)192 public CImage createFromImage(final Image image, CTrayIcon.IconObserver observer) { 193 return createFromImage(image, true, observer); 194 } 195 createFromImageImmediately(final Image image)196 public CImage createFromImageImmediately(final Image image) { 197 return createFromImage(image, false, null); 198 } 199 200 // This is used to create a CImage from a Image createFromImage(final Image image, final boolean prepareImage, CTrayIcon.IconObserver observer)201 private CImage createFromImage(final Image image, final boolean prepareImage, CTrayIcon.IconObserver observer) { 202 if (image instanceof MultiResolutionImage) { 203 List<Image> resolutionVariants 204 = ((MultiResolutionImage) image).getResolutionVariants(); 205 return createFromImages(resolutionVariants, prepareImage); 206 } 207 208 int[] buffer = imageToArray(image, prepareImage, observer); 209 if (buffer == null) { 210 return null; 211 } 212 return new CImage(nativeCreateNSImageFromArray(buffer, image.getWidth(null), image.getHeight(null))); 213 } 214 createFromImages(final List<Image> images)215 public CImage createFromImages(final List<Image> images) { 216 return createFromImages(images, true); 217 } 218 createFromImages(final List<Image> images, final boolean prepareImage)219 private CImage createFromImages(final List<Image> images, final boolean prepareImage) { 220 if (images == null || images.isEmpty()) { 221 return null; 222 } 223 224 int num = images.size(); 225 226 int[][] buffers = new int[num][]; 227 int[] w = new int[num]; 228 int[] h = new int[num]; 229 230 num = 0; 231 232 for (final Image img : images) { 233 buffers[num] = imageToArray(img, prepareImage, null); 234 if (buffers[num] == null) { 235 // Unable to process the image 236 continue; 237 } 238 w[num] = img.getWidth(null); 239 h[num] = img.getHeight(null); 240 num++; 241 } 242 243 if (num == 0) { 244 return null; 245 } 246 247 return new CImage(nativeCreateNSImageFromArrays( 248 Arrays.copyOf(buffers, num), 249 Arrays.copyOf(w, num), 250 Arrays.copyOf(h, num))); 251 } 252 getSelectorAsInt(final String fromString)253 static int getSelectorAsInt(final String fromString) { 254 final byte[] b = fromString.getBytes(); 255 final int len = Math.min(b.length, 4); 256 int result = 0; 257 for (int i = 0; i < len; i++) { 258 if (i > 0) result <<= 8; 259 result |= (b[i] & 0xff); 260 } 261 return result; 262 } 263 } 264 CImage(long nsImagePtr)265 CImage(long nsImagePtr) { 266 super(nsImagePtr, true); 267 } 268 269 /** @return A MultiResolution image created from nsImagePtr, or null. */ toImage()270 private Image toImage() { 271 if (ptr == 0) { 272 return null; 273 } 274 275 AtomicReference<Dimension2D> sizeRef = new AtomicReference<>(); 276 execute(ptr -> { 277 sizeRef.set(nativeGetNSImageSize(ptr)); 278 }); 279 final Dimension2D size = sizeRef.get(); 280 if (size == null) { 281 return null; 282 } 283 final int w = (int)size.getWidth(); 284 final int h = (int)size.getHeight(); 285 AtomicReference<Dimension2D[]> repRef = new AtomicReference<>(); 286 execute(ptr -> { 287 repRef.set(nativeGetNSImageRepresentationSizes(ptr, size.getWidth(), 288 size.getHeight())); 289 }); 290 Dimension2D[] sizes = repRef.get(); 291 292 return sizes == null || sizes.length < 2 ? 293 new MultiResolutionCachedImage(w, h, (width, height) 294 -> toImage(w, h, width, height)) 295 : new MultiResolutionCachedImage(w, h, sizes, (width, height) 296 -> toImage(w, h, width, height)); 297 } 298 toImage(int srcWidth, int srcHeight, int dstWidth, int dstHeight)299 private BufferedImage toImage(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { 300 final BufferedImage bimg = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_INT_ARGB_PRE); 301 final DataBufferInt dbi = (DataBufferInt)bimg.getRaster().getDataBuffer(); 302 final int[] buffer = SunWritableRaster.stealData(dbi, 0); 303 execute(ptr->nativeCopyNSImageIntoArray(ptr, buffer, srcWidth, srcHeight, dstWidth, dstHeight)); 304 SunWritableRaster.markDirty(dbi); 305 return bimg; 306 } 307 308 /** If nsImagePtr != 0 then scale this NSImage. @return *this* */ resize(final double w, final double h)309 CImage resize(final double w, final double h) { 310 execute(ptr -> nativeSetNSImageSize(ptr, w, h)); 311 return this; 312 } 313 resizeRepresentations(double w, double h)314 void resizeRepresentations(double w, double h) { 315 execute(ptr -> nativeResizeNSImageRepresentations(ptr, w, h)); 316 } 317 } 318