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