/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An image that only keeps the binary data of the source file used to load it
* in permanent memory. This allows the bitmap to get collected while the binary
* data remains, a weak reference is used for caching.
*
* @author Shai Almog
*/
public class EncodedImage extends Image {
private byte[] imageData;
private int width = -1;
private int height = -1;
private boolean opaqueChecked = false;
private boolean opaque = false;
private Object cache;
private Image hardCache;
private boolean locked;
private EncodedImage(byte[] imageData) {
super(null);
this.imageData = imageData;
}
/**
* Allows subclasses to create more advanced variations of this class that
* lazily store the data in an arbitrary location.
*
* @param width -1 if unknown ideally the width/height should be known in advance
* @param height -1 if unknown ideally the width/height should be known in advance
*/
protected EncodedImage(int width, int height) {
super(null);
this.width = width;
this.height = height;
}
/**
* A subclass might choose to load asynchroniously and reset the cache when the image is ready.
*/
protected void resetCache() {
cache = null;
}
/**
* Returns the byte array data backing the image allowing the image to be stored
* and discarded completely from RAM.
*
* @return byte array used to create the image, e.g. encoded PNG, JPEG etc.
*/
public byte[] getImageData() {
return imageData;
}
/**
* Creates an image from the given byte array
*
* @param data the data of the image
* @return newly created encoded image
*/
public static EncodedImage create(byte[] data) {
if(data == null) {
throw new NullPointerException();
}
return new EncodedImage(data);
}
/**
* Creates an image from the input stream
*
* @param i the input stream
* @return newly created encoded image
* @throws java.io.IOException if thrown by the input stream
*/
public static EncodedImage create(InputStream i) throws IOException {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int size = i.read(buffer);
while(size > -1) {
bo.write(buffer, 0, size);
size = i.read(buffer);
}
bo.close();
return new EncodedImage(bo.toByteArray());
}
private Image getInternalImpl() {
if(hardCache != null) {
return hardCache;
}
Image i = getInternal();
if(locked) {
hardCache = i;
}
return i;
}
/**
* Returns the actual image represented by the encoded image, this image will
* be cached in a weak/soft reference internally. This method is useful to detect
* when the system actually created an image instance. You shouldn't invoke this
* method manually!
*
* @return drawable image instance
*/
protected Image getInternal() {
if(cache != null) {
Image i = (Image)Display.getInstance().extractHardRef(cache);
if(i != null) {
return i;
}
}
Image i;
try {
byte[] b = getImageData();
i = Image.createImage(b, 0, b.length);
} catch(Exception err) {
err.printStackTrace();
i = Image.createImage(5, 5);
}
cache = Display.getInstance().createSoftWeakRef(i);
return i;
}
/**
* @inheritDoc
*/
public void lock() {
locked = true;
if(cache != null) {
hardCache = (Image)Display.getInstance().extractHardRef(cache);
}
}
/**
* @inheritDoc
*/
public void unlock() {
if(hardCache != null) {
if(cache == null || Display.getInstance().extractHardRef(cache) == null) {
cache = Display.getInstance().createSoftWeakRef(hardCache);
}
}
locked = false;
}
/**
* Creates an image from the input stream
*
* @param i the resource
* @return newly created encoded image
* @throws java.io.IOException if thrown by the input stream
*/
public static EncodedImage create(String i) throws IOException {
return create(Display.getInstance().getResourceAsStream(EncodedImage.class, i));
}
/**
* @inheritDoc
*/
public Image subImage(int x, int y, int width, int height, boolean processAlpha) {
return getInternalImpl().subImage(x, y, width, height, processAlpha);
}
/**
* @inheritDoc
*/
public Image rotate(int degrees) {
return getInternalImpl().rotate(degrees);
}
/**
* @inheritDoc
*/
public Image modifyAlpha(byte alpha) {
return getInternalImpl().modifyAlpha(alpha);
}
/**
* @inheritDoc
*/
public Image modifyAlpha(byte alpha, int removeColor) {
return getInternalImpl().modifyAlpha(alpha, removeColor);
}
/**
* @inheritDoc
*/
public Graphics getGraphics() {
return null;
}
/**
* @inheritDoc
*/
public int getWidth() {
if(width > -1) {
return width;
}
width = getInternalImpl().getWidth();
return width;
}
/**
* @inheritDoc
*/
public int getHeight() {
if(height > -1) {
return height;
}
height = getInternalImpl().getHeight();
return height;
}
/**
* @inheritDoc
*/
protected void drawImage(Graphics g, Object nativeGraphics, int x, int y) {
getInternalImpl().drawImage(g, nativeGraphics, x, y);
}
/**
* @inheritDoc
*/
protected void drawImage(Graphics g, Object nativeGraphics, int x, int y, int w, int h) {
getInternalImpl().drawImage(g, nativeGraphics, x, y, w, h);
}
/**
* @inheritDoc
*/
void getRGB(int[] rgbData,
int offset,
int x,
int y,
int width,
int height) {
getInternalImpl().getRGB(rgbData, offset, x, y, width, height);
}
/**
* @inheritDoc
*/
public void toRGB(RGBImage image,
int destX,
int destY,
int x,
int y,
int width,
int height) {
getInternalImpl().toRGB(image, destX, destY, x, y, width, height);
}
/**
* @inheritDoc
*/
public Image scaledWidth(int width) {
return getInternalImpl().scaledWidth(width);
}
/**
* @inheritDoc
*/
public Image scaledHeight(int height) {
return getInternalImpl().scaledHeight(height);
}
/**
* @inheritDoc
*/
public Image scaledSmallerRatio(int width, int height) {
return getInternalImpl().scaledSmallerRatio(width, height);
}
/**
* @inheritDoc
*/
public Image scaled(int width, int height) {
return getInternalImpl().scaled(width, height);
}
/**
* @inheritDoc
*/
public void scale(int width, int height) {
getInternalImpl().scale(width, height);
}
/**
* @inheritDoc
*/
public boolean isAnimation() {
return false;
}
/**
* @inheritDoc
*/
public boolean isOpaque() {
if(opaqueChecked) {
return opaque;
}
opaque = getInternalImpl().isOpaque();
return opaque;
}
}