image.java

来自「j2me设计的界面包」· Java 代码 · 共 722 行 · 第 1/2 页

JAVA
722
字号
/*
 * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.lwuit;

import com.sun.lwuit.geom.Dimension;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.lcdui.game.Sprite;
import java.lang.ref.WeakReference;
import java.util.Hashtable;

/**
 * Abstracts the underlying platform images allowing us to treat them as a uniform
 * object.
 * 
 * @author Chen Fishbein
 */
public class Image {
    
    private javax.microedition.lcdui.Image image;   
    private int transform;

    private boolean opaqueTested = false;
    private boolean opaque;
    private Graphics g;
    private Hashtable scaleCache;
    
    /** Creates a new instance of ImageImpl */
    Image(javax.microedition.lcdui.Image image) {
        this.image = image;
    }

    /** Creates a new instance of ImageImpl */
    Image(int[] imageArray, int w, int h) {
        this(javax.microedition.lcdui.Image.createRGBImage(imageArray, w, h, true));
    }
    
    /**
     * Returns a cached scaled image
     * 
     * @param size the size of the cached image
     * @return cached image
     */
    Image getCachedImage(Dimension size) {
        if(scaleCache != null) {
            WeakReference w = (WeakReference)scaleCache.get(size);
            if(w != null) {
                return (Image)w.get();
            }
        }
        return null;
    }
    
    /**
     * Returns a cached scaled image
     * 
     * @param size the size of the cached image
     * @return cached image
     */
    void cacheImage(Dimension size, Image i) {
        if(scaleCache == null) {
            scaleCache = new Hashtable();
        }
        WeakReference w = new WeakReference(i);
        scaleCache.put(size, w);
    }
    
    /**
     * Extracts a subimage from the given image allowing us to breakdown a single large image
     * into multiple smaller images in RAM, this actually creates a standalone version
     * of the image for use. 
     * 
     * @param width the width of internal images
     * @param height the height of internal images
     * @return An array of all the possible images that can be created from the source
     * @throws java.io.IOException
     */
    public Image subImage(int x, int y, int width, int height, boolean processAlpha)  {
        // we use the getRGB API rather than the mutable image API to allow translucency to
        // be maintained in the newly created image
        int[] arr = new int[width * height];
        image.getRGB(arr, 0, width, x, y, width, height);
        
        Image i = new Image(javax.microedition.lcdui.Image.createRGBImage(arr, width, height, processAlpha));
        i.opaque = opaque;
        i.opaqueTested = opaqueTested;
        return i;
    }
    
    /**
     * Returns an instance of this image rotated by the given number of degrees. By default 90 degree
     * angle divisions are supported, anything else is implementation dependent. This method assumes 
     * a square image. Notice that it is inefficient in the current implementation to rotate to
     * non-square angles, 
     * <p>E.g. rotating an image to 45, 90 and 135 degrees is inefficient. Use rotatate to 45, 90
     * and then rotate the 45 to another 90 degrees to achieve the same effect with less memory.
     * 
     * @param degrees A degree in right angle must be larer than 0 and up to 359 degrees
     * @return new image instance with the closest possible rotation
     */
    public Image rotate(int degrees) {
        // a square angle so we can use the "fast" algorithm... 
        int transform = 0;
        Image i;
        if(degrees % 90 == 0) {
            transform = fastRotate(degrees);
            i = new Image(image);
        } else {
            // rotate up to the point to a smaller than 90 degree angle then let the radian code do its magic...
            if(degrees > 90) {
                int deg = degrees - degrees % 90;
                transform = fastRotate(deg);
                degrees -= deg;
            }
            // can't use sprite rotation since it will lose transparency information...
            int width = image.getWidth();
            int height = image.getHeight();
            int[] arr = new int[width * height];
            int[] dest = new int[arr.length];
            image.getRGB(arr, 0, width, 0, 0, width, height);
            int centerX = width / 2;
            int centerY = height / 2;

            double radians = Math.toRadians(-degrees);
            double cosDeg = Math.cos(radians);
            double sinDeg = Math.sin(radians);
            for(int x = 0 ; x < width ; x++) {
                for(int y = 0 ; y < height ; y++) {
                    int x2 = round(cosDeg * (x - centerX) - sinDeg * (y - centerY) + centerX);
                    int y2 = round(sinDeg * (x - centerX) + cosDeg * (y - centerY) + centerY);
                    if(!(x2 < 0 || y2 < 0 || x2 >= width || y2 >= height)) {
                        int destOffset = x2 + y2 * width;
                        if(destOffset >= 0 && destOffset < dest.length) {
                            dest[x + y * width] = arr[destOffset];
                        }
                    }
                }
            }
            i = new Image(javax.microedition.lcdui.Image.createRGBImage(dest, width, height, true));
        }
        
        i.transform = transform;
        return i;
    }

    private int round(double d) {
        double f = Math.floor(d);
        double c = Math.ceil(d);
        if(c - d < d - f) {
            return (int)c;
        }
        return (int)f;
    }

    /**
     * Creates a new image instance with the alpha channel of opaque/translucent 
     * pixels within the image using the new alpha value. Transparent (alpha == 0)
     * pixels remain transparent. All other pixels will have the new alpha value.
     * 
     * @param alpha New value for the entire alpha channel
     * @return Translucent/Opaque image based on the alpha value and the pixels of 
     * this image
     */
    public Image modifyAlpha(byte alpha) {
        int w = image.getWidth();
        int h = image.getHeight();
        int size = w * h;
        int[] arr = new int[size];
        image.getRGB(arr, 0, w, 0, 0, w, h);
        int alphaInt = (((int)alpha) << 24) & 0xff000000;
        for(int iter = 0 ; iter < size ; iter++) {
            if((arr[iter] & 0xff000000) != 0) {
                arr[iter] = (arr[iter] & 0xffffff) | alphaInt;
            }
        }
        Image i = new Image(arr, w, h);
        i.opaqueTested = true;
        i.opaque = false;
        return i;
    }
    
    /**
     * Creates a new image instance with the alpha channel of opaque/translucent 
     * pixels within the image using the new alpha value. Transparent (alpha == 0)
     * pixels remain transparent. All other pixels will have the new alpha value.
     * 
     * @param alpha New value for the entire alpha channel
     * @param removeColor pixels matching this color are made transparent (alpha channel ignored)
     * @return Translucent/Opaque image based on the alpha value and the pixels of 
     * this image
     */
    public Image modifyAlpha(byte alpha, int removeColor) {
        removeColor = removeColor & 0xffffff;
        int w = image.getWidth();
        int h = image.getHeight();
        int size = w * h;
        int[] arr = new int[size];
        image.getRGB(arr, 0, w, 0, 0, w, h);
        int alphaInt = (((int)alpha) << 24) & 0xff000000;
        for(int iter = 0 ; iter < size ; iter++) {
            if((arr[iter] & 0xff000000) != 0) {
                arr[iter] = (arr[iter] & 0xffffff) | alphaInt;
                if(removeColor == (0xffffff & arr[iter])) {
                    arr[iter] = 0;
                }
            }   
        }
        Image i = new Image(arr, w, h);
        i.opaqueTested = true;
        i.opaque = false;
        return i;
    }

    /**
     * Returns a mirror instance of this image
     * 
     * @return a mirror instance of this image
     * @deprecated this method is no longer supported due to issues when mixing it with
     * other methods such as rotate. Future versions of the API will provide an alternative
     */
    public Image mirror() {
        Image i = new Image(image);
        if(transform != 0) {
            switch(transform) {
                case Sprite.TRANS_MIRROR:
                    i.transform = 0;
                    break;
                case Sprite.TRANS_MIRROR_ROT180:
                    i.transform = Sprite.TRANS_ROT180;
                    break;
                case Sprite.TRANS_MIRROR_ROT270:
                    i.transform = Sprite.TRANS_ROT270;
                    break;
                case Sprite.TRANS_MIRROR_ROT90:
                    i.transform = Sprite.TRANS_ROT90;
                    break;
                case Sprite.TRANS_NONE:
                    i.transform = Sprite.TRANS_MIRROR;
                    break;
                case Sprite.TRANS_ROT180:
                    i.transform = Sprite.TRANS_MIRROR_ROT180;
                    break;
                case Sprite.TRANS_ROT270:
                    i.transform = Sprite.TRANS_MIRROR_ROT270;
                    break;
                case Sprite.TRANS_ROT90:
                    i.transform = Sprite.TRANS_MIRROR_ROT90;
                    break;
                default:
                    throw new IllegalStateException("Image could not be mirrored, error code: " + transform);
            }
        } else {
            i.transform = transform;
        }
        return i;
    }
    
    /**
     * Rotates the given image array fast assuming this is a "right" angle 
     * divides by 90 degrees
     */
    private int fastRotate(int degrees) {
        // if we have a preexisting rotation...
        switch(transform) {
            case javax.microedition.lcdui.game.Sprite.TRANS_ROT90:
                degrees += 90;
                break;
            case javax.microedition.lcdui.game.Sprite.TRANS_ROT180:
                degrees += 180;
                break;
            case javax.microedition.lcdui.game.Sprite.TRANS_ROT270:
                degrees += 270;
                break;
        }
        
        switch((degrees % 360) / 90) {
        case 0:
            return 0;
            
        // 90 degree angle
        case 1:
            return javax.microedition.lcdui.game.Sprite.TRANS_ROT90;

        // 180 degree angle
        case 2:
            return javax.microedition.lcdui.game.Sprite.TRANS_ROT180;
            
        // 270 degree angle
        case 3:
            return javax.microedition.lcdui.game.Sprite.TRANS_ROT270;

        default:
            throw new IllegalArgumentException("Fast rotate can't handle degrees = " + degrees);
        }
    }
    
    /**
     * Converts a newly loaded masked image into a translucent image. A masked image is an image file
     * of even height that is two separate opaque images the top part is the actual image containing 
     * the opaque content. The bottom part is a mask that would be ovelayed as the alpha channel of 
     * the top part of the image. The red channel of the bottom part would just be assigned to the alpha
     * portion of the top part thus allowing an image to contain translucency while being portable to
     * different devices.
     * 
     * @return Translucent image instance
     * @deprecated translucency masks are no longer supported or necessary, use resources for portability
     * and unification
     */
    public Image extractTranslucentMask() {
        int w = image.getWidth();
        int h = image.getHeight();
        int[] arr = new int[w * h];
        image.getRGB(arr, 0, w, 0, 0, w, h);
        int offset = h * w / 2;
        for(int iter = 0 ; iter < offset ; iter++) {
            // extract the red component from the bottom portion of the image
            int alpha = arr[iter + offset] & 0xff0000;
            
            // shift the alpha 8 bits to the left
            // apply the alpha to the top portion of the image
            arr[iter] = (arr[iter] & 0xffffff) | (alpha << 8);
        }
        return new Image(javax.microedition.lcdui.Image.createRGBImage(arr, w, h / 2, true));
    }
    
    /**
     * creates an image from the given path based on MIDP's createImage(path)
     * 
     * @param path 
     * @throws java.io.IOException 
     * @return newly created image object
     */
    public static Image createImage(String path) throws IOException {
        try {
            return new Image(javax.microedition.lcdui.Image.createImage(path));
        } catch(OutOfMemoryError err) {
            // Images have a major bug on many phones where they sometimes throw 
            // an OOM with no reason. A system.gc followed by the same call over
            // solves the problem. This has something to do with the fact that 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?