CImage.java revision 10444:f08705540498
1325326Sjkim/*
2325326Sjkim * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
3325326Sjkim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4325326Sjkim *
5325326Sjkim * This code is free software; you can redistribute it and/or modify it
6325326Sjkim * under the terms of the GNU General Public License version 2 only, as
7325326Sjkim * published by the Free Software Foundation.  Oracle designates this
8325326Sjkim * particular file as subject to the "Classpath" exception as provided
9325326Sjkim * by Oracle in the LICENSE file that accompanied this code.
10325326Sjkim *
11325326Sjkim * This code is distributed in the hope that it will be useful, but WITHOUT
12325326Sjkim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13325326Sjkim * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14325326Sjkim * version 2 for more details (a copy is included in the LICENSE file that
15325326Sjkim * accompanied this code).
16325326Sjkim *
17325326Sjkim * You should have received a copy of the GNU General Public License version
18325326Sjkim * 2 along with this work; if not, write to the Free Software Foundation,
19325326Sjkim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20325326Sjkim *
21325326Sjkim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22325326Sjkim * or visit www.oracle.com if you need additional information or have any
23325326Sjkim * questions.
24325326Sjkim */
25325326Sjkim
26325326Sjkimpackage sun.lwawt.macosx;
27325326Sjkim
28325326Sjkimimport java.awt.*;
29325326Sjkimimport java.awt.geom.Dimension2D;
30325326Sjkimimport java.awt.image.*;
31325326Sjkim
32325326Sjkimimport java.util.Arrays;
33325326Sjkimimport java.util.List;
34325326Sjkimimport sun.awt.image.MultiResolutionImage;
35325326Sjkimimport sun.awt.image.MultiResolutionCachedImage;
36325326Sjkim
37325326Sjkimimport sun.awt.image.SunWritableRaster;
38325326Sjkim
39325326Sjkimpublic class CImage extends CFRetainedResource {
40325326Sjkim    private static native long nativeCreateNSImageFromArray(int[] buffer, int w, int h);
41325326Sjkim    private static native long nativeCreateNSImageFromBytes(byte[] buffer);
42325326Sjkim    private static native long nativeCreateNSImageFromArrays(int[][] buffers, int w[], int h[]);
43325326Sjkim    private static native long nativeCreateNSImageFromFileContents(String file);
44325326Sjkim    private static native long nativeCreateNSImageOfFileFromLaunchServices(String file);
45325326Sjkim    private static native long nativeCreateNSImageFromImageName(String name);
46325326Sjkim    private static native long nativeCreateNSImageFromIconSelector(int selector);
47325326Sjkim    private static native byte[] nativeGetPlatformImageBytes(int[] buffer, int w, int h);
48325326Sjkim    private static native void nativeCopyNSImageIntoArray(long image, int[] buffer, int sw, int sh, int dw, int dh);
49325326Sjkim    private static native Dimension2D nativeGetNSImageSize(long image);
50325326Sjkim    private static native void nativeSetNSImageSize(long image, double w, double h);
51325326Sjkim    private static native void nativeResizeNSImageRepresentations(long image, double w, double h);
52325326Sjkim    private static native Dimension2D[] nativeGetNSImageRepresentationSizes(long image, double w, double h);
53325326Sjkim
54325326Sjkim    static Creator creator = new Creator();
55325326Sjkim    static Creator getCreator() {
56325326Sjkim        return creator;
57325326Sjkim    }
58325326Sjkim
59325326Sjkim    public static class Creator {
60325326Sjkim        Creator() { }
61325326Sjkim
62325326Sjkim        // This is used to create a CImage with an NSImage pointer. It MUST be a CFRetained
63325326Sjkim        // NSImage, and the CImage takes ownership of the non-GC retain. If callers need the
64        // NSImage themselves, they MUST call retain on the NSImage themselves.
65        public Image createImageUsingNativeSize(final long image) {
66            if (image == 0) return null;
67            final Dimension2D size = nativeGetNSImageSize(image);
68            return createImage(image, size.getWidth(), size.getHeight());
69        }
70
71        // 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
72        Image createImage(long image, double width, double height) {
73            if (image == 0) throw new Error("Unable to instantiate CImage with null native image reference.");
74            return createImageWithSize(image, width, height);
75        }
76
77        public Image createImageWithSize(final long image, final double width, final double height) {
78            final CImage img = new CImage(image);
79            img.resize(width, height);
80            return img.toImage();
81        }
82
83        // This is used to create a CImage that represents the icon of the given file.
84        public Image createImageOfFile(final String file, final int width, final int height) {
85            return createImage(nativeCreateNSImageOfFileFromLaunchServices(file), width, height);
86        }
87
88        public Image createImageFromFile(final String file, final double width, final double height) {
89            final long image = nativeCreateNSImageFromFileContents(file);
90            nativeSetNSImageSize(image, width, height);
91            return createImage(image, width, height);
92        }
93
94        public Image createSystemImageFromSelector(final String iconSelector, final int width, final int height) {
95            return createImage(nativeCreateNSImageFromIconSelector(getSelectorAsInt(iconSelector)), width, height);
96        }
97
98        public Image createImageFromName(final String name, final int width, final int height) {
99            return createImage(nativeCreateNSImageFromImageName(name), width, height);
100        }
101
102        public Image createImageFromName(final String name) {
103            return createImageUsingNativeSize(nativeCreateNSImageFromImageName(name));
104        }
105
106        private static int[] imageToArray(Image image, boolean prepareImage) {
107            if (image == null) return null;
108
109            if (prepareImage && !(image instanceof BufferedImage)) {
110                final MediaTracker mt = new MediaTracker(new Label());
111                final int id = 0;
112                mt.addImage(image, id);
113
114                try {
115                    mt.waitForID(id);
116                } catch (InterruptedException e) {
117                    return null;
118                }
119
120                if (mt.isErrorID(id)) {
121                    return null;
122                }
123            }
124
125            int w = image.getWidth(null);
126            int h = image.getHeight(null);
127
128            if (w < 0 || h < 0) {
129                return null;
130            }
131
132            BufferedImage bimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
133            Graphics2D g2 = bimg.createGraphics();
134            g2.setComposite(AlphaComposite.Src);
135            g2.drawImage(image, 0, 0, null);
136            g2.dispose();
137
138            return ((DataBufferInt)bimg.getRaster().getDataBuffer()).getData();
139        }
140
141        public CImage createFromImageImmediately(final Image image) {
142            int[]  buffer = imageToArray(image, false);
143
144            if (buffer == null) {
145                return null;
146            }
147
148            return new CImage(nativeCreateNSImageFromArray(buffer, image.getWidth(null),
149                                                           image.getHeight(null)));
150        }
151
152        public byte[] getPlatformImageBytes(final Image image) {
153            int[] buffer = imageToArray(image, false);
154
155            if (buffer == null) {
156                return null;
157            }
158
159            return nativeGetPlatformImageBytes(buffer, image.getWidth(null), image.getHeight(null));
160        }
161
162        /**
163         * Translates a byte array which contains platform-specific image data in the given format into an Image.
164         */
165        public Image createImageFromPlatformImageBytes(final byte[] buffer) {
166            return createImageUsingNativeSize(nativeCreateNSImageFromBytes(buffer));
167        }
168
169        // This is used to create a CImage from a Image
170        public CImage createFromImage(final Image image) {
171            if (image instanceof MultiResolutionImage) {
172                List<Image> resolutionVariants
173                        = ((MultiResolutionImage) image).getResolutionVariants();
174                return createFromImages(resolutionVariants);
175            }
176
177            int[] buffer = imageToArray(image, true);
178            if (buffer == null) {
179                return null;
180            }
181            return new CImage(nativeCreateNSImageFromArray(buffer, image.getWidth(null), image.getHeight(null)));
182        }
183
184        public CImage createFromImages(List<Image> images) {
185            if (images == null || images.isEmpty()) {
186                return null;
187            }
188
189            int num = images.size();
190
191            int[][] buffers = new int[num][];
192            int[] w = new int[num];
193            int[] h = new int[num];
194
195            num = 0;
196
197            for (Image img : images) {
198                buffers[num] = imageToArray(img, true);
199                if (buffers[num] == null) {
200                    // Unable to process the image
201                    continue;
202                }
203                w[num] = img.getWidth(null);
204                h[num] = img.getHeight(null);
205                num++;
206            }
207
208            if (num == 0) {
209                return null;
210            }
211
212            return new CImage(nativeCreateNSImageFromArrays(
213                        Arrays.copyOf(buffers, num),
214                        Arrays.copyOf(w, num),
215                        Arrays.copyOf(h, num)));
216        }
217
218        static int getSelectorAsInt(final String fromString) {
219            final byte[] b = fromString.getBytes();
220            final int len = Math.min(b.length, 4);
221            int result = 0;
222            for (int i = 0; i < len; i++) {
223                if (i > 0) result <<= 8;
224                result |= (b[i] & 0xff);
225            }
226            return result;
227        }
228    }
229
230    CImage(long nsImagePtr) {
231        super(nsImagePtr, true);
232    }
233
234    /** @return A MultiResolution image created from nsImagePtr, or null. */
235    private Image toImage() {
236        if (ptr == 0) return null;
237
238        final Dimension2D size = nativeGetNSImageSize(ptr);
239        final int w = (int)size.getWidth();
240        final int h = (int)size.getHeight();
241
242        Dimension2D[] sizes
243                = nativeGetNSImageRepresentationSizes(ptr,
244                        size.getWidth(), size.getHeight());
245
246        return sizes == null || sizes.length < 2 ?
247                new MultiResolutionCachedImage(w, h, (width, height)
248                        -> toImage(w, h, width, height))
249                : new MultiResolutionCachedImage(w, h, sizes, (width, height)
250                        -> toImage(w, h, width, height));
251    }
252
253    private BufferedImage toImage(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
254        final BufferedImage bimg = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_INT_ARGB_PRE);
255        final DataBufferInt dbi = (DataBufferInt)bimg.getRaster().getDataBuffer();
256        final int[] buffer = SunWritableRaster.stealData(dbi, 0);
257        nativeCopyNSImageIntoArray(ptr, buffer, srcWidth, srcHeight, dstWidth, dstHeight);
258        SunWritableRaster.markDirty(dbi);
259        return bimg;
260    }
261
262    /** If nsImagePtr != 0 then scale this NSImage. @return *this* */
263    CImage resize(final double w, final double h) {
264        if (ptr != 0) nativeSetNSImageSize(ptr, w, h);
265        return this;
266    }
267
268    void resizeRepresentations(double w, double h) {
269        if (ptr != 0) nativeResizeNSImageRepresentations(ptr, w, h);
270    }
271}
272