Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.embed.swt;
027
028import java.nio.ByteBuffer;
029import java.nio.IntBuffer;
030import java.security.AccessController;
031import java.security.PrivilegedExceptionAction;
032
033import org.eclipse.swt.SWT;
034import org.eclipse.swt.graphics.ImageData;
035import org.eclipse.swt.graphics.PaletteData;
036import org.eclipse.swt.graphics.RGB;
037
038import java.lang.reflect.Method;
039import java.lang.reflect.Field;
040
041import javafx.scene.image.PixelFormat;
042import javafx.scene.image.PixelReader;
043import javafx.scene.image.PixelWriter;
044import javafx.scene.image.WritableImage;
045import javafx.scene.image.Image;
046import javafx.scene.image.WritablePixelFormat;
047
048/**
049 * This class provides utility methods for converting data types between
050 * SWT and JavaFX formats.
051 */
052public class SWTFXUtils {
053    private SWTFXUtils() {} // no instances
054
055    /**
056     * Snapshots the specified {@link ImageData} and stores a copy of
057     * its pixels into a JavaFX {@link Image} object, creating a new
058     * object if needed.
059     * The returned {@code Image} will be a static snapshot of the state
060     * of the pixels in the {@code ImageData} at the time the method
061     * completes.  Further changes to the {@code ImageData} will not
062     * be reflected in the {@code Image}.
063     * <p>
064     * The optional JavaFX {@link WritableImage} parameter may be reused
065     * to store the copy of the pixels.
066     * A new {@code Image} will be created if the supplied object is null,
067     * is too small or of a type which the image pixels cannot be easily
068     * converted into.
069     * 
070     * @param imageData the {@code ImageData} object to be converted
071     * @param image an optional {@code WritableImage} object that can be
072     *        used to store the returned pixel data
073     * @return an {@code Image} object representing a snapshot of the
074     *         current pixels in the {@code ImageData}, or null if 
075     *         the {@code Image} is not readable.
076     */
077    public static WritableImage toFXImage(ImageData imageData, 
078                                          WritableImage image) {
079        byte[] data = convertImage(imageData);
080        if (data == null) return null;
081        int width = imageData.width;
082        int height = imageData.height;
083        if (image != null) {
084            int iw = (int) image.getWidth();
085            int ih = (int) image.getHeight();
086            if (iw < width || ih < height) {
087                image = null;
088            } else if (width < iw || height < ih) {
089                int empty[] = new int[iw];
090                PixelWriter pw = image.getPixelWriter();
091                PixelFormat<IntBuffer> pf = PixelFormat.getIntArgbPreInstance();
092                if (width < iw) {
093                    pw.setPixels(width, 0, iw-width, height, pf, empty, 0, 0);
094                }
095                if (height < ih) {
096                    pw.setPixels(0, height, iw, ih-height, pf, empty, 0, 0);
097                }
098            }
099        }
100        if (image == null) {
101            image = new WritableImage(width, height);
102        }
103        PixelWriter pw = image.getPixelWriter();
104        int scan = width * 4;
105        PixelFormat<ByteBuffer> pf = PixelFormat.getByteBgraInstance();
106        pw.setPixels(0, 0, width, height, pf, data, 0, scan);
107        return image;
108    }
109
110    /**
111     * Snapshots the specified JavaFX {@link Image} object and stores a
112     * copy of its pixels into a new {@link ImageData} object.
113     * The method will only convert a JavaFX {@code Image} that is readable
114     * as per the conditions on the
115     * {@link Image#getPixelReader() Image.getPixelReader()}
116     * method.
117     * If the {@code Image} is not readable, as determined by its
118     * {@code getPixelReader()} method, then this method will return null.
119     * If the {@code Image} is a writable, or other dynamic image, then
120     * the {@code ImageData} will only be set to the current state of
121     * the pixels in the image as determined by its {@link PixelReader}.
122     * Further changes to the pixels of the {@code Image} will not be
123     * reflected in the returned {@code ImageData}.
124     * <p>
125     * The optional {@code ImageData} parameter may be reused to store
126     * the copy of the pixels.
127     * A new {@code ImageData} will be created if the supplied object
128     * is null, is too small or of a type which the image pixels cannot
129     * be easily converted into.
130     * 
131     * @param image the JavaFX {@code Image} to be converted
132     * @param imageData an optional {@code ImageData} object that may be
133     *        used to store the returned pixel data
134     * @return a {@code ImageData} containing a snapshot of the JavaFX
135     *         {@code Image}, or null if the {@code Image} is not readable.
136     */
137    public static ImageData fromFXImage(Image image, ImageData imageData) {
138        PixelReader pr = image.getPixelReader();
139        if (pr == null) {
140            return null;
141        }
142        int width = (int) image.getWidth();
143        int height = (int) image.getHeight();
144        int bpr = width * 4;
145        int dataSize = bpr * height;
146        byte[] buffer = new byte[dataSize];
147        WritablePixelFormat<ByteBuffer> pf = PixelFormat.getByteBgraInstance();
148        pr.getPixels(0, 0, width, height, pf, buffer, 0, bpr);
149        byte[] alphaData = new byte[width * height];
150        for (int y = 0, offset = 0, alphaOffset = 0; y < height; y++) {
151            for (int x = 0; x < width; x++, offset += 4) {
152                byte alpha = buffer[offset+3];
153                buffer[offset+3] = 0;
154                alphaData[alphaOffset++] = alpha;
155            }
156        }
157        PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
158        imageData = new ImageData(width, height, 32, palette, 4, buffer);
159        imageData.alphaData = alphaData;
160        return imageData;
161    }
162
163    private static int blitSrc;
164    private static boolean blitSrcCache;
165    private static int BLIT_SRC() throws Exception {
166        if (!blitSrcCache) {
167            blitSrc = readValue("BLIT_SRC");
168            blitSrcCache = true;
169        }
170        return blitSrc;
171    }
172
173    private static int alphaOpaque;
174    private static boolean alphaOpaqueCache;
175    private static int ALPHA_OPAQUE() throws Exception {
176        if (!alphaOpaqueCache) {
177            alphaOpaque = readValue("ALPHA_OPAQUE");
178            alphaOpaqueCache = true;
179        }
180        return alphaOpaque;
181    }
182
183    private static int msbFirst;
184    private static boolean msbFirstCache;
185    private static int MSB_FIRST() throws Exception {
186        if (!msbFirstCache) {
187            msbFirst = readValue("MSB_FIRST");
188            msbFirstCache = true;
189        }
190        return msbFirst;
191    }
192
193    private static int readValue(final String name) throws Exception {
194        final Class<?> clazz = ImageData.class;
195        return AccessController.doPrivileged(
196            new PrivilegedExceptionAction<Integer>() {
197                public Integer run() throws Exception {
198                    Field field = clazz.getDeclaredField(name);
199                    field.setAccessible(true);
200                    return field.getInt(clazz);
201                }
202            });
203    }
204
205    private static Method blitDirect;
206    private static void blit(int op,
207            byte[] srcData, int srcDepth, int srcStride, int srcOrder,
208            int srcX, int srcY, int srcWidth, int srcHeight,
209            int srcRedMask, int srcGreenMask, int srcBlueMask,
210            int alphaMode, byte[] alphaData, int alphaStride, 
211            int alphaX, int alphaY,
212            byte[] destData, int destDepth, int destStride, int destOrder,
213            int destX, int destY, int destWidth, int destHeight,
214            int destRedMask, int destGreenMask, int destBlueMask,
215            boolean flipX, boolean flipY) throws Exception {
216        final Class<?> clazz = ImageData.class;
217        if (blitDirect == null) {
218            Class<?> I = Integer.TYPE, B = Boolean.TYPE, BA = byte[].class;
219            final Class<?>[] argClasses = {I, 
220                    BA, I, I, I,
221                    I, I, I, I,
222                    I, I, I,
223                    I, BA, I, I, I,
224                    BA, I, I, I,
225                    I, I, I, I,
226                    I, I, I, B, B};
227            blitDirect = AccessController.doPrivileged(
228                new PrivilegedExceptionAction<Method>() {
229                    public Method run() throws Exception {
230                        Method method = clazz.
231                            getDeclaredMethod("blit", argClasses);
232                        method.setAccessible(true);
233                        return method;
234                    }
235                });
236        }
237        if (blitDirect != null) {
238            blitDirect.invoke(clazz, op,
239                    srcData, srcDepth, srcStride, srcOrder,
240                    srcX, srcY, srcWidth, srcHeight,
241                    srcRedMask, srcGreenMask, srcBlueMask,
242                    alphaMode, alphaData, alphaStride, alphaX, alphaY,
243                    destData, destDepth, destStride, destOrder,
244                    destX, destY, destWidth, destHeight,
245                    destRedMask, destGreenMask, destBlueMask,
246                    flipX, flipY);
247        }
248    }
249
250    private static Method blitPalette;
251    private static void blit(int op,
252        byte[] srcData, int srcDepth, int srcStride, int srcOrder,
253        int srcX, int srcY, int srcWidth, int srcHeight,
254        byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
255        int alphaMode, byte[] alphaData, int alphaStride,
256        int alphaX, int alphaY,
257        byte[] destData, int destDepth, int destStride, int destOrder,
258        int destX, int destY, int destWidth, int destHeight,
259        int destRedMask, int destGreenMask, int destBlueMask,
260        boolean flipX, boolean flipY) throws Exception {
261        final Class<?> clazz = ImageData.class;
262        if (blitPalette == null) {
263            Class<?> I = Integer.TYPE, B = Boolean.TYPE, BA = byte[].class;
264            final Class<?>[] argClasses = {I, 
265                    BA, I, I, I,
266                    I, I, I, I,
267                    BA, BA, BA,
268                    I, BA, I, I, I,
269                    BA, I, I, I,
270                    I, I, I, I,
271                    I, I, I, B, B};
272            blitPalette = AccessController.doPrivileged(
273                new PrivilegedExceptionAction<Method>() {
274                    public Method run() throws Exception {
275                        Method method = clazz.
276                            getDeclaredMethod("blit", argClasses);
277                        method.setAccessible(true);
278                        return method;
279                    }
280                });
281        }
282        if (blitPalette != null) {
283            blitPalette.invoke(clazz, op,
284                    srcData, srcDepth, srcStride, srcOrder,
285                    srcX, srcY, srcWidth, srcHeight,
286                    srcReds, srcGreens, srcBlues,
287                    alphaMode, alphaData, alphaStride, alphaX, alphaY,
288                    destData, destDepth, destStride, destOrder,
289                    destX, destY, destWidth, destHeight,
290                    destRedMask, destGreenMask, destBlueMask,
291                    flipX, flipY);
292        }
293    }
294    
295    private static Method getByteOrderMethod;
296    private static int getByteOrder(ImageData image) throws Exception {
297        final Class<?> clazz = ImageData.class;
298        if (getByteOrderMethod != null) {
299            getByteOrderMethod = AccessController.doPrivileged(
300                new PrivilegedExceptionAction<Method>() {
301                    public Method run() throws Exception {
302                        Method method = clazz.getDeclaredMethod("getByteOrder");
303                        method.setAccessible(true);
304                        return method;
305                    }
306                });
307        }
308        if (getByteOrderMethod != null) {
309            return (Integer)getByteOrderMethod.invoke(image);
310        }
311        return MSB_FIRST();
312    }
313
314    private static byte[] convertImage(ImageData image) {
315        byte[] buffer = null;
316        try {
317            PaletteData palette = image.palette;
318            if (!(((image.depth == 1 || image.depth == 2 || 
319                    image.depth == 4 || image.depth == 8) && 
320                    !palette.isDirect) ||
321                    ((image.depth == 8) || (image.depth == 16 || 
322                    image.depth == 24 || image.depth == 32) 
323                    && palette.isDirect))) {
324                    return null;
325            }
326
327            final int BLIT_SRC = BLIT_SRC();
328            final int ALPHA_OPAQUE = ALPHA_OPAQUE();
329            final int MSB_FIRST = MSB_FIRST();
330
331            int width = image.width;
332            int height = image.height;
333            int byteOrder = getByteOrder(image);
334            int ao = 3;
335            int redMask = 0xFF00;
336            int greenMask = 0xFF0000;
337            int blueMask = 0xFF000000;
338            int dataSize = width * height * 4;
339            int bpr = width * 4;
340            buffer = new byte[dataSize];
341
342            if (palette.isDirect) {
343                blit(BLIT_SRC,
344                    image.data, image.depth, image.bytesPerLine, byteOrder, 
345                    0, 0, width, height, 
346                    palette.redMask, palette.greenMask, palette.blueMask,
347                    ALPHA_OPAQUE, null, 0, 0, 0, 
348                    buffer, 32, bpr, MSB_FIRST, 0, 0, width, height, 
349                    redMask, greenMask, blueMask,
350                    false, false);
351            } else {
352                RGB[] rgbs = palette.getRGBs();
353                int length = rgbs.length;
354                byte[] srcReds = new byte[length];
355                byte[] srcGreens = new byte[length];
356                byte[] srcBlues = new byte[length];
357                for (int i = 0; i < rgbs.length; i++) {
358                    RGB rgb = rgbs[i];
359                    if (rgb == null) continue;
360                    srcReds[i] = (byte)rgb.red;
361                    srcGreens[i] = (byte)rgb.green;
362                    srcBlues[i] = (byte)rgb.blue;
363                }
364                blit(BLIT_SRC,
365                    image.data, image.depth, image.bytesPerLine, byteOrder, 
366                    0, 0, width, height, srcReds, srcGreens, srcBlues,
367                    ALPHA_OPAQUE, null, 0, 0, 0,
368                    buffer, 32, bpr, MSB_FIRST, 0, 0, width, height, 
369                    redMask, greenMask, blueMask,
370                    false, false);
371            }
372
373            /* Initialize transparency */
374            int transparency = image.getTransparencyType();
375            boolean hasAlpha = transparency != SWT.TRANSPARENCY_NONE;
376            if (transparency == SWT.TRANSPARENCY_MASK || 
377                image.transparentPixel != -1) {
378                ImageData maskImage = image.getTransparencyMask();
379                byte[] maskData = maskImage.data;
380                int maskBpl = maskImage.bytesPerLine;
381                int offset = 0, maskOffset = 0;
382                for (int y = 0; y<height; y++) {
383                    for (int x = 0; x<width; x++) {
384                        int m = maskData[maskOffset + (x >> 3)];
385                        int v = 1 << (7 - (x & 0x7));
386                        buffer[offset + ao] = (m & v) != 0 ? (byte)0xff : 0;
387                        offset += 4;
388                    }
389                    maskOffset += maskBpl;
390                }
391            } else {
392                if (image.alpha != -1) {
393                    hasAlpha = true;
394                    int alpha = image.alpha;
395                    byte a = (byte)alpha;
396                    for (int offset=0; offset<buffer.length; offset+=4) {
397                        buffer[offset + ao] = a;
398                    }
399                } else if (image.alphaData != null) {
400                    hasAlpha = true;
401                    byte[] alphaData = new byte[image.alphaData.length];
402                    System.arraycopy(image.alphaData, 0,
403                                     alphaData, 0, alphaData.length);
404                    int offset = 0, alphaOffset = 0;
405                    for (int y = 0; y<height; y++) {
406                        for (int x = 0; x<width; x++) {
407                            buffer[offset + ao] = alphaData[alphaOffset];
408                            offset += 4;
409                            alphaOffset += 1;
410                        }
411                    }
412                }
413            }
414            if (!hasAlpha) {
415                for (int offset=0; offset<buffer.length; offset+=4) {
416                    buffer[offset + ao] = (byte)0xFF;
417                }
418            }
419        } catch (Exception e) {
420            return null;
421        }
422        return buffer;
423    }
424}