javauis/lcdui_qt/src/javax/microedition/lcdui/Image.java
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:27:20 +0300
changeset 21 2a9601315dfc
child 23 98ccebc37403
permissions -rw-r--r--
Revision: v2.1.22 Kit: 201018

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
package javax.microedition.lcdui;

import java.io.*;

import javax.microedition.lcdui.game.Sprite;

import org.eclipse.swt.graphics.Internal_GfxPackageSupport;

/**
 * Class containing graphical data of image.<br>
 * <br>
 * Image path handling is not implemented correctly, for example "standard"
 * LCDUI accepts path "/TestMIDlets/images/100x100.png" but corresponding path
 * with this image-class must be "/100x100.png".
 */
public class Image {

    private static Image createImageReturnValue;

    private org.eclipse.swt.graphics.Image eswtImage;
    private int eswtImageWidth;
    private int eswtImageHeight;

    private Graphics imageGraphics;
    private boolean mutable;

    private com.nokia.mj.impl.rt.support.Finalizer finalizer;

    /**
     * Constructor.
     */
    Image(org.eclipse.swt.graphics.Image eswtImage, boolean isMutable) {
        this.eswtImage = eswtImage;
        this.mutable = isMutable;
        eswtImageWidth = eswtImage.getBounds().width;
        eswtImageHeight = eswtImage.getBounds().height;
        finalizer = ((finalizer != null) ? finalizer
                : new com.nokia.mj.impl.rt.support.Finalizer() {
                    public void finalizeImpl() {
                        if (finalizer != null) {
                            finalizer = null;
                            if (!ESWTUIThreadRunner.isDisposed()) {
                                dispose();
                            }
                        }
                    }
                });
        ESWTUIThreadRunner.update(getClass().getName(), 1);
    }

    /**
     * Called by finalizer when Image is ready to free its resources.
     */
    void dispose() {
        ESWTUIThreadRunner.update(getClass().getName(), -1);
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                if (eswtImage != null) {
                    eswtImage.dispose();
                    eswtImage = null;
                }
            }
        });
    }

    /**
     * Return eSWT Image object.
     */
    org.eclipse.swt.graphics.Image getESWTImage() {
        return eswtImage;
    }

    /**
     * Return eSWT Image object.
     *
     * @param img Image
     * @return eSWT Image
     */
    static org.eclipse.swt.graphics.Image getESWTImage(Image img) {
        if (img != null) {
            return img.eswtImage;
        }
        return null;
    }

    /**
     * Creates new mutable image where every pixel is white.
     *
     * @param w The width of the image to be created.
     * @param h The height of the image to be created.
     * @return Image created.
     * @throws IllegalArgumentException if width or height are zero or less.
     */
    public static Image createImage(int w, int h) {
        if (w <= 0 || h <= 0) {
            throw new IllegalArgumentException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_DIMENSIONS);
        }

        final int width = w;
        final int height = h;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.graphics.Image eswtImg =
                    eswtCreateImage(width, height);

                if (eswtImg != null) {
                    createImageReturnValue = new Image(eswtImg, true);
                }
                else {
                    createImageReturnValue = null;
                    throw new OutOfMemoryError();
                }
            }
        });
        return createImageReturnValue;
    }

    /**
     * Creates an immutable image with the specified size from the existing
     * image source.
     *
     * @param srcImage the source of image.
     * @param width the width of the new image.
     * @param height the height of the new image.
     * @return the resized Image.
     * @throws NullPointerException if aImage is null.
     */
    static Image createImage(Image srcImage, int width, int height) {
        if (srcImage == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_IS_NULL);
        }
        final Image finalImage = srcImage;
        final int finalWidth = width;
        final int finalHeight = height;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.graphics.Image eswtImg =
                    eswtCreateImage(finalImage.eswtImage, finalWidth, finalHeight);

                if (eswtImg != null) {
                    createImageReturnValue = new Image(eswtImg, false);
                }
                else {
                    createImageReturnValue = null;
                    throw new OutOfMemoryError();
                }
            }
        });
        return createImageReturnValue;
    }

    /**
     * Creates immutable image from existing image source.
     *
     * @param srcImage The source of image.
     * @return Image created.
     * @throws NullPointerException if source image is null.
     */
    public static Image createImage(Image srcImage) {
        if (srcImage == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_IS_NULL);
        }

        if (!srcImage.isMutable()) {
            return srcImage;
        }

        final Image finalImage = srcImage;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.graphics.Image eswtImg =
                    eswtCreateImage(finalImage.eswtImage);

                if (eswtImg != null) {
                    createImageReturnValue = new Image(eswtImg, false);
                }
                else {
                    createImageReturnValue = null;
                    throw new OutOfMemoryError();
                }
            }
        });
        return createImageReturnValue;
    }

    /**
     * Creates an immutable image from named resource.
     *
     * @param name The name of the resource.
     * @return Image created.
     * @throws NullPointerException if name is null.
     * @throws IOException If resource doesn't exist, data cannot be loaded or
     *             data is invalid.
     */
    public static Image createImage(String name) throws IOException {
        if (name == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_FILE_NAME_IS_NULL);
        }

        String fileName = name;
        if (!fileName.startsWith("/")) {
            fileName = "/" + fileName;
        }

        InputStream stream = Image.class.getResourceAsStream(fileName);
        if (stream == null) {
            throw new IOException(MsgRepository.IMAGE_EXCEPTION_IO_ERROR);
        }
        Image image = createImage(stream);
        try {
            stream.close();
        }
        catch (IOException e) {
            // ignore
        }
        return image;
    }

    /**
     * Creates immutable image from stream. Thread's execution is blocked until
     * this method is finished.
     *
     * @param stream The stream.
     * @return Image created.
     * @throws NullPointerException if stream is null.
     * @throws java.io.IOException If I/O error occurs or data from stream is
     *             invalid.
     */
    public static Image createImage(InputStream stream) throws java.io.IOException {
        if (stream == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_FILE_STREAM_IS_NULL);
        }
        final InputStream finalStream = stream;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.graphics.Image eswtImg = eswtCreateImage(finalStream);

                if (eswtImg != null) {
                    createImageReturnValue = new Image(eswtImg, false);
                }
                else {
                    createImageReturnValue = null;
                }
            }
        });
        if (createImageReturnValue == null) {
            throw new IOException(MsgRepository.IMAGE_EXCEPTION_IO_ERROR);
        }
        return createImageReturnValue;
    }

    /**
     * Creates new immutable image from image data provided.
     *
     * @param imgData Image data.
     * @param imgOffset Start offset in image data.
     * @param imgLength Length of data used.
     * @return Image created.
     * @throws ArrayIndexOutOfBoundsException if imgOffset and imgLength
     *             specifies invalid range.
     * @throws NullPointerException if imgData is null.
     * @throws IllegalArgumentException if imgData content is invalid.
     */
    public static Image createImage(byte[] imgData, int imgOffset, int imgLength) {
        if (imgData == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_DATA_IS_NULL);
        }
        if (imgOffset + imgLength > imgData.length) {
            throw new ArrayIndexOutOfBoundsException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_BOUNDS);
        }
        if (imgOffset < 0 || imgLength < 0) {
            throw new ArrayIndexOutOfBoundsException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_BOUNDS);
        }

        InputStream inputStream = new ByteArrayInputStream(imgData, imgOffset, imgLength);
        try {
            createImageReturnValue = createImage(inputStream);
        }
        catch (IOException e) {
            throw new IllegalArgumentException(
                        MsgRepository.IMAGE_EXCEPTION_INVALID_DATA);
        }
        finally {
            try {
                inputStream.close();
            }
            catch (IOException e) {
                // ignore
            }
        }
        return createImageReturnValue;
    }

    /**
     * Creates an immutable image from specified region of existing source image
     * with specified transform.
     *
     * @param srcImage Source image.
     * @param x Region's x.
     * @param y Region's y.
     * @param w Regions width.
     * @param h Regions height.
     * @param transform Transform, one of the transforms specified in
     *            Sprite-class.
     * @return Image created.
     * @throws NullPointerException if img is null.
     * @throws IllegalArgumentException if Region specified exceeds the source
     *             image or if w or h is zero or less or if transform is not one
     *             defined in Sprite-class.
     */
    public static Image createImage(Image srcImage,
                                    int x,
                                    int y,
                                    int w,
                                    int h,
                                    int transform) {
        if (srcImage == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_IS_NULL);
        }
        if (w <= 0 || h <= 0) {
            throw new IllegalArgumentException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_DIMENSIONS);
        }
        if (!validateTransform(transform)) {
            throw new IllegalArgumentException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_TRANSFORM);
        }
        if (!validateRegion(srcImage.getWidth(), srcImage.getHeight(), x, y, w, h)) {
            throw new IllegalArgumentException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_REGION);
        }

        final Image fImage = srcImage;
        final int fx = x;
        final int fy = y;
        final int fw = w;
        final int fh = h;
        final int fTransform = transform;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.internal.qt.graphics.Image srcCgImage =
                    Internal_GfxPackageSupport.getImage(fImage.getESWTImage());

                org.eclipse.swt.internal.qt.graphics.Image cgImage =
                    new org.eclipse.swt.internal.qt.graphics.Image(
                            srcCgImage, fx, fy, fw, fh);

                cgImage.transform(getCgTransformValue(fTransform));

                org.eclipse.swt.graphics.Image eswtImg =
                    Internal_GfxPackageSupport.new_Image(
                            ESWTUIThreadRunner.getInstance().getDisplay(),
                            cgImage);

                if (eswtImg != null) {
                    createImageReturnValue = new Image(eswtImg, false);
                }
                else {
                    createImageReturnValue = null;
                    throw new OutOfMemoryError();
                }
            }
        });
        return createImageReturnValue;
    }

    /**
     * Validates that a specified region is fully located within the image
     * bounds. Method is package-private, used by Graphics.drawRegion as well.
     */
    static boolean validateRegion(int srcWidth, int srcHeight, int x, int y,
            int w, int h) {
        return x >= 0 && y >= 0 && w <= srcWidth && h <= srcHeight;
    }

    /**
     * Validates if transform has a valid value. Method is package-private, used
     * by Graphics.drawRegion as well.
     */
    static boolean validateTransform(int transform) {
        return transform == Sprite.TRANS_NONE
            || transform == Sprite.TRANS_MIRROR
            || transform == Sprite.TRANS_MIRROR_ROT90
            || transform == Sprite.TRANS_MIRROR_ROT180
            || transform == Sprite.TRANS_MIRROR_ROT270
            || transform == Sprite.TRANS_ROT90
            || transform == Sprite.TRANS_ROT180
            || transform == Sprite.TRANS_ROT270;
    }

    /**
     *  Maps LCDUI transform constants to Common Graphics. Method is package-private, used
     *  by Graphics.drawRegion as well.
     */
    static int getCgTransformValue(int transform) {
        int retVal = org.eclipse.swt.internal.qt.graphics.Image.TRANS_NONE;
        switch (transform) {
            case Sprite.TRANS_NONE:
                retVal = org.eclipse.swt.internal.qt.graphics.Image.TRANS_NONE;
                break;
            case Sprite.TRANS_ROT90:
                retVal = org.eclipse.swt.internal.qt.graphics.Image.TRANS_ROT90;
                break;
            case Sprite.TRANS_ROT180:
                retVal =
                    org.eclipse.swt.internal.qt.graphics.Image.TRANS_ROT180;
                break;
            case Sprite.TRANS_ROT270:
                retVal =
                    org.eclipse.swt.internal.qt.graphics.Image.TRANS_ROT270;
                break;
            case Sprite.TRANS_MIRROR:
                retVal =
                    org.eclipse.swt.internal.qt.graphics.Image.TRANS_MIRROR;
                break;
            case Sprite.TRANS_MIRROR_ROT90:
                retVal = org.eclipse.swt.internal.qt.graphics.
                                            Image.TRANS_MIRROR_ROT90;
                break;
            case Sprite.TRANS_MIRROR_ROT180:
                retVal = org.eclipse.swt.internal.qt.graphics.
                                            Image.TRANS_MIRROR_ROT180;
                break;
            case Sprite.TRANS_MIRROR_ROT270:
                retVal = org.eclipse.swt.internal.qt.graphics.
                                            Image.TRANS_MIRROR_ROT270;
                break;
            default:
                break;
        }
        return retVal;
    }

    /**
     * Creates eSWT image as a copy of existing image.
     *
     * @param image - image to be copied.
     * @return new eSWT image.
     */
    private static org.eclipse.swt.graphics.Image eswtCreateImage(
                                    org.eclipse.swt.graphics.Image srcImage) {
        org.eclipse.swt.graphics.Image ret = null;
        try {
            org.eclipse.swt.internal.qt.graphics.Image cgImage =
                Internal_GfxPackageSupport.getImage(srcImage);

            org.eclipse.swt.internal.qt.graphics.Image cgImage2 =
                new org.eclipse.swt.internal.qt.graphics.Image(cgImage);

            ret = Internal_GfxPackageSupport.new_Image(
                    ESWTUIThreadRunner.getInstance().getDisplay(), cgImage2);
        }
        catch (IllegalArgumentException ex) {
            // Device is null or there's no current device.
            ret = null;
        }
        catch (org.eclipse.swt.SWTException ex) {
            ret = null;
        }
        catch (org.eclipse.swt.SWTError err) {
            ret = null;
        }
        return ret;
    }

    /**
     * Creates eSWT image of the specified size from the given image.
     */
    static org.eclipse.swt.graphics.Image eswtCreateImage(
            org.eclipse.swt.graphics.Image image, int newWidth, int newHeight) {
        org.eclipse.swt.graphics.Image ret = null;
        try {
            ret = new org.eclipse.swt.graphics.Image(
                    ESWTUIThreadRunner.getInstance().getDisplay(),
                    image.getImageData().scaledTo(newWidth, newHeight));
        }
        catch (IllegalArgumentException ex) {
            // Device is null or there's no current device.
            ret = null;
        }
        catch (org.eclipse.swt.SWTException ex) {
            ret = null;
        }
        catch (org.eclipse.swt.SWTError err) {
            ret = null;
        }
        return ret;
    }

    /**
     * Creates eSWT image of the specified size.
     */
    static org.eclipse.swt.graphics.Image eswtCreateImage(int width, int height) {
        org.eclipse.swt.graphics.Image ret = null;
        try {
            ret = new org.eclipse.swt.graphics.Image(
                    ESWTUIThreadRunner.getInstance().getDisplay(), width, height);
        }
        catch (IllegalArgumentException ex) {
            // Device is null or there's no current device.
            ret = null;
        }
        catch (org.eclipse.swt.SWTException ex) {
            ret = null;
        }
        catch (org.eclipse.swt.SWTError err) {
            ret = null;
        }
        return ret;
    }

    /**
     * Creates and eSWT image out of the InputStream.
     */
    static org.eclipse.swt.graphics.Image eswtCreateImage(InputStream inputStream) {
        org.eclipse.swt.graphics.Image ret = null;
        try {
            ret = new org.eclipse.swt.graphics.Image(
                    ESWTUIThreadRunner.getInstance().getDisplay(), inputStream);
        }
        catch (IllegalArgumentException ex) {
            // Device is null or there's no current device.
            ret = null;
        }
        catch (org.eclipse.swt.SWTException ex) {
            ret = null;
        }
        catch (org.eclipse.swt.SWTError err) {
            ret = null;
        }
        return ret;
    }

    /**
     * Creates new image from specified RGB array data.
     *
     * @param rgbData Pixel data.
     * @param width Width of the image to be created.
     * @param height Height of the image to be created.
     * @param processAlpha true if there's alpha channel in image data.
     *            Otherwise method assumes that all pixels are fully opaque.
     * @return Image created.
     * @throws NullPointerException if rgb is null.
     * @throws IllegalArgumentException if w or h is zero or less.
     * @throws ArrayIndexOutOfBoundsException if the w and h parameters doesn't
     *             match the length of the data array provided.
     */
    public static Image createRGBImage(int[] rgbData,
                                       int width,
                                       int height,
                                       boolean processAlpha) {

        if (rgbData == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_DATA_IS_NULL);
        }
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_DIMENSIONS);
        }
        if (width * height > rgbData.length) {
            throw new ArrayIndexOutOfBoundsException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_BOUNDS);
        }

        final int[] rgb = rgbData;
        final int w = width;
        final int h = height;
        final boolean alpha = processAlpha;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.internal.qt.graphics.Image cgImage =
                    new org.eclipse.swt.internal.qt.graphics.Image(rgb, w, h, alpha);

                org.eclipse.swt.graphics.Image eswtImg =
                    Internal_GfxPackageSupport.new_Image(
                            ESWTUIThreadRunner.getInstance().getDisplay(), cgImage);

                if (eswtImg != null) {
                    createImageReturnValue = new Image(eswtImg, false);
                }
                else {
                    createImageReturnValue = null;
                    throw new OutOfMemoryError();
                }
            }
        });
        return createImageReturnValue;
    }

    /**
     * Returns ARGB data of image region to an int[] array.
     *
     * @param rgbData - array to be filled with ARGB data.
     * @param offset - first index in the array to store the data.
     * @param length - relative distance in the array between corresponding
     *            pixels of consecutive rows.
     * @param xPos - x-coordinate of the top-left corner of the region.
     * @param yPos - y-coordinate of the top-left corner of the region.
     * @param width - width of the region.
     * @param height - height of the region.
     */
    public void getRGB(int[] rgbData,
                       int offset,
                       int length,
                       int xPos,
                       int yPos,
                       int width,
                       int height) {

        if (rgbData == null) {
            throw new NullPointerException(
                    MsgRepository.IMAGE_EXCEPTION_DATA_IS_NULL);
        }
        if (!validateRegion(this.getWidth(), this.getHeight(),
             xPos, yPos, width, height)) {
            throw new IllegalArgumentException(
                                MsgRepository.IMAGE_EXCEPTION_INVALID_BOUNDS);
        }
        if (Math.abs(length) < width) {
            throw new IllegalArgumentException(
                    MsgRepository.IMAGE_EXCEPTION_INVALID_SCANLENGTH);
        }

        final int[] localRgbData = rgbData;
        final int localOffset = offset;
        final int localLength = length;
        final int localX = xPos;
        final int localY = yPos;
        final int localW = width;
        final int localH = height;
        ESWTUIThreadRunner.safeSyncExec(new Runnable() {
            public void run() {
                org.eclipse.swt.internal.qt.graphics.Image cgImage = Internal_GfxPackageSupport.getImage(eswtImage);
                cgImage.getRGB(localRgbData, localOffset, localLength,
                        localX, localY, localW, localH);
            }
        });
    }

    /**
     * Creates a new Graphics-objects that is able to draw to this image.
     *
     * @return New Graphics-object.
     * @throws IllegalStateException if image is immutable.
     */
    public Graphics getGraphics() {
        if (mutable) {
            if (imageGraphics == null) {
                ESWTUIThreadRunner.safeSyncExec(new Runnable() {
                    public void run() {
                        // instantiate the Graphics object
                        imageGraphics = new Graphics();
                        // bind the GC to the Image target
                        imageGraphics.eswtSetParentImage(Image.this);
                    }
                });
            }
            return imageGraphics;
        }
        throw new IllegalStateException(MsgRepository.IMAGE_EXCEPTION_IMMUTABLE);
    }

    /**
     * Gets width of the image.
     *
     * @return Image width.
     */
    public int getWidth() {
        return eswtImageWidth;
    }

    /**
     * Gets height of the image.
     *
     * @return Image height.
     */
    public int getHeight() {
        return eswtImageHeight;
    }

    /**
     * Check is it possible to modify this image.
     *
     * @return true, if image is mutable.
     */
    public boolean isMutable() {
        return mutable;
    }

}