/**
* Copyright (c) 2006-2008 MiniMe. Code released under The MIT/X Window System
* License. Full license text can be found in license.txt
*/
package minime;
import javax.microedition.lcdui.Graphics;
import minime.core.ResourceManager;
/**
* The Image class is used to hold graphical image data. It can create image by
* image id, or a source javax.microedition.lcdui.Image instance,or image uri.If
* create image by image id, it will get image data from image resource file
* using ResourceManager file.
*
* @author aaa
*
*/
public class Image //extends Resource
{
private javax.microedition.lcdui.Image nativeImage;
int rscId ;
int majorColor = -1;
static final Logger LOG = Logger.getLogger( "minime.Image" );
/**
* Creates an image object from a source image.
*
* @param image
*/
public Image(javax.microedition.lcdui.Image image)
{
nativeImage = image;
rscId = -1 ;
}
/**
* Create an instance image, it accesses ResourceManager to get the image
* data from resource file(.bin) based on image resource id.
*
* @param imRscId
* the image resource id
*/
public Image(int imRscId)
{
rscId = imRscId ;
nativeImage = null;
}
/**
* Create am instance image empty with a specific size.
* @param width of the Image
* @param height of the Image
*/
public Image(int width,int height)
{
nativeImage = javax.microedition.lcdui.Image.createImage(width, height);
rscId = -1 ;
}
/**
* Create am instance image empty with from a byte array
* @param imageData the array of image data in a supported image format
* @param imageOffset the offset of the start of the data in the array
* @param imageLength the length of the data in the array
*/
public Image(byte[] imageData, int imageOffset, int imageLength)
{
nativeImage = javax.microedition.lcdui.Image.createImage(imageData, imageOffset, imageLength);
rscId = -1 ;
}
public Image()
{
}
/**
* Checks if the native image is null.
*
* @return true if the image is not null, false otherwise
*/
public boolean realized()
{
return nativeImage != null;
}
/**
* Purges the native image to null.
*/
public void purge()
{
nativeImage = null;
}
public int getRscId()
{
return rscId;
}
public void setRscId(int rscId)
{
this.rscId=rscId;
}
/**
* Gets the height of the native image in pixels. The value returned must
* reflect the actual height of the image when rendered.
*
* @return height of the image
*/
public int getHeight()
{
if (nativeImage == null && !realize())
{
return -1;
}
return nativeImage.getHeight();
}
/**
* Gets the width of the native image in pixels. The value returned must
* reflect the actual width of the image when rendered.
*
* @return width of the image
*/
public int getWidth()
{
if (nativeImage == null && !realize())
{
return -1;
}
return nativeImage.getWidth();
}
/**
* Draws the image at x,y coordinates.The anchor point for positioning the
* image is Graphics.TOP | Graphics.LEFT
*
* @param gc
* the Graphics object to be used for rendering image
* @param x
* the x coordinate of the anchor point
* @param y
* the y coordinate of the anchor point
*/
public void draw(Graphics gc, int x, int y)
{
if (nativeImage == null && !realize())
{
return;
}
gc.drawImage(nativeImage, x, y, Graphics.TOP | Graphics.LEFT);
}
/**
* Renders a series of RGB+transparency values in a specified region
*
* @param gc
* the Graphics object to be used for rendering image
* @param x
* the x coordinate of the anchor point
* @param y
* the y coordinate of the anchor point
* @param alf
* the value of alpha
*/
public void drawAlf(Graphics gc, int x, int y, int alf)
{
if (nativeImage == null && !realize())
{
return;
}
int argb[] = new int[nativeImage.getWidth() * nativeImage.getHeight()];
nativeImage.getRGB(argb, 0, nativeImage.getWidth(), 0, 0, nativeImage.getWidth(), nativeImage.getHeight());
for (int i = 0; i < argb.length; i++)
{
argb[i] = (alf << 24) | (argb[i] & 0x00FFFFFF);
}
gc.drawRGB(argb, 0, nativeImage.getWidth(), x, y, nativeImage.getWidth(), nativeImage.getHeight(), true);
}
/**
* Copies a region of the native image to a location within the destination.
*
* @param gc
* the Graphics object to be used for rendering image
* @param srcX
* the x coordinate of the upper left corner of the region within
* the native image to copy
* @param srcY
* the Y coordinate of the upper left corner of the region within
* the native image to copy
* @param srcW
* the width of the region to copy
* @param srcH
* the height of the region to copy
* @param x
* the x coordinate of the upper left corner of the destination
* drawing area
* @param y
* the y coordinate of the upper left corner of the destination
* drawing area
*/
public void draw(Graphics gc, int srcX, int srcY, int srcW, int srcH, int x, int y)
{
if (nativeImage == null && !realize())
{
return;
}
gc.drawRegion(nativeImage, srcX, srcY, srcW, srcH, 0, x, y, Graphics.TOP | Graphics.LEFT);
}
/**
* Creates native image instance(javax.microedition.lcdui.Image)
*
* @return false if failed. true if successful.
*/
private boolean realize()
{
if(rscId!=-1)
nativeImage = ResourceManager.getImage(rscId);
return (null!=nativeImage) ;
}
public javax.microedition.lcdui.Image getNativeImage() {
if (nativeImage == null && !realize())
{
return null;
}
return nativeImage;
}
public Image scaleBresenham(int newWidth, int newHeight, boolean bgTrans)
{
if (nativeImage == null && !realize())
{
return null;
}
if(newWidth == 0 || newHeight ==0 )
return null;
int[] rawInput = new int[nativeImage.getHeight() * nativeImage.getWidth()];
nativeImage.getRGB(rawInput, 0, nativeImage.getWidth(), 0, 0, nativeImage.getWidth(), nativeImage.getHeight());
int[] rawOutput = new int[newWidth*newHeight];
// YD compensates for the x loop by subtracting the width back out
int YD = (nativeImage.getHeight() / newHeight) * nativeImage.getWidth() - nativeImage.getWidth();
int YR = nativeImage.getHeight() % newHeight;
int XD = nativeImage.getWidth() / newWidth;
int XR = nativeImage.getWidth() % newWidth;
int outOffset= 0;
int inOffset= 0;
for (int y= newHeight, YE= 0; y > 0; y--) {
for (int x= newWidth, XE= 0; x > 0; x--) {
rawOutput[outOffset++]= rawInput[inOffset];
inOffset+=XD;
XE+=XR;
if (XE >= newWidth) {
XE-= newWidth;
inOffset++;
}
}
inOffset+= YD;
YE+= YR;
if (YE >= newHeight) {
YE -= newHeight;
inOffset+=nativeImage.getWidth();
}
}
return new Image(javax.microedition.lcdui.Image.createRGBImage(rawOutput, newWidth, newHeight, bgTrans));
}
public Image scaleBresenhamSelf(int newWidth, int newHeight, boolean bgTrans)
{
if (nativeImage == null && !realize())
{
return null;
}
if(newWidth == 0 || newHeight ==0 )
return null;
int[] rawInput = new int[nativeImage.getHeight() * nativeImage.getWidth()];
nativeImage.getRGB(rawInput, 0, nativeImage.getWidth(), 0, 0, nativeImage.getWidth(), nativeImage.getHeight());
int nativeHeight=nativeImage.getHeight();
int nativeWidth=nativeImage.getWidth();
Image newImage=null;
int[] rawOutput = new int[newWidth*newHeight];
// YD compensates for the x loop by subtracting the width back out
int YD = (nativeHeight / newHeight) * nativeWidth - nativeWidth;
int YR = nativeHeight % newHeight;
int XD = nativeWidth / newWidth;
int XR = nativeWidth % newWidth;
int outOffset= 0;
int inOffset= 0;
for (int y= newHeight, YE= 0; y > 0; y--) {
for (int x= newWidth, XE= 0; x > 0; x--) {
rawOutput[outOffset++]= rawInput[inOffset];
inOffset+=XD;
XE+=XR;
if (XE >= newWidth) {
XE-= newWidth;
inOffset++;
}
}
inOffset+= YD;
YE+= YR;
if (YE >= newHeight) {
YE -= newHeight;
inOffset+=nativeWidth;
}
}
rawInput=null;
newImage=new Image(javax.microedition.lcdui.Image.createRGBImage(rawOutput, newWidth, newHeight, bgTrans));
rawOutput=null;
return newImage;
}
public Image scaleBilinear(int newWidth, int newHeight) {
int orgWidth = nativeImage.getWidth();
int orgHeight = nativeImage.getHeight();
int orgLength = orgWidth * orgHeight;
int orgMax = orgLength - 1;
int[] rawInput = new int[orgLength];
nativeImage.getRGB(rawInput, 0, orgWidth, 0, 0, orgWidth, orgHeight);
int newLength = newWidth * newHeight;
int[] rawOutput = new int[newLength];
int yd = (orgHeight / newHeight - 1) * orgWidth;
int yr = orgHeight % newHeight;
int xd = orgWidth / newWidth;
int xr = orgWidth % newWidth;
int outOffset = 0;
int inOffset = 0;
// Whole pile of non array variables for the loop.
int pixelA, pixelB, pixelC, pixelD;
int xo, yo;
int weightA, weightB, weightC, weightD;
int redA, redB, redC, redD;
int greenA, greenB, greenC, greenD;
int blueA, blueB, blueC, blueD;
int red, green, blue;
for (int y = newHeight, ye = 0; y > 0; y--) {
for (int x = newWidth, xe = 0; x > 0; x--) {
// Set source pixels.
pixelA = inOffset;
pixelB = pixelA + 1;
pixelC = pixelA + orgWidth;
pixelD = pixelC + 1;
// Get pixel values from array for speed, avoiding overflow.
pixelA = rawInput[pixelA];
pixelB = pixelB > orgMax ? pixelA : rawInput[pixelB];
pixelC = pixelC > orgMax ? pixelA : rawInput[pixelC];
pixelD = pixelD > orgMax ? pixelB : rawInput[pixelD];
// Calculate pixel weights from error values xe & ye.
xo = (xe << 8) / newWidth;
yo = (ye << 8) / newHeight;
weightD = xo * yo;
weightC = (yo << 8) - weightD;
weightB = (xo << 8) - weightD;
weightA = 0x10000 - weightB - weightC - weightD;
// Isolate colour channels.
redA = pixelA >> 16;
redB = pixelB >> 16;
redC = pixelC >> 16;
redD = pixelD >> 16;
greenA = pixelA & 0x00FF00;
greenB = pixelB & 0x00FF00;
greenC = pixelC & 0x00FF00;
greenD = pixelD & 0x00FF00;
blueA = pixelA & 0x0000FF;
blueB = pixelB & 0x0000FF;
blueC = pixelC & 0x0000FF;
blueD = pixelD & 0x0000FF;
// Calculate new pixels colour and mask.
red = 0x00FF0000 & (redA * weightA + redB * weightB + redC * weightC + redD * weightD);
green = 0xFF000000 & (greenA * weightA + greenB * weightB + greenC * weightC + greenD * weightD);
blue = 0x00FF0000 & (blueA * weightA + blueB * weightB + blueC * weightC + blueD * weightD);
// Store pixel in output buffer and increment offset.
rawOutput[outOffset++] = red + (((green | blue) >> 16));
// Increment input by x delta.
inOffset += xd;
// Correct if we have a roll over error.
xe += xr;
if (xe >= newWidth) {
xe -= newWidth;
inOffset++;
}
}
// Increment input by y delta.
inOffset += yd;
// Correct if we have a roll over error.
ye += yr;
if (ye >= newHeight) {
ye -= newHeight;
inOffset += orgWidth;
}
}
return new Image(javax.microedition.lcdui.Image.createRGBImage(rawOutput, newWidth, newHeight, false));
}
public Image zoomImage(int desW, int desH,
boolean isBackgroundTrans, boolean isTrans) {
if (nativeImage == null && !realize())
{
return null;
}
Image desImg = null;
int srcW = nativeImage.getWidth(); // source image width
int srcH = nativeImage.getHeight(); // source image height
int[] srcBuf = new int[srcW * srcH]; // source image pixel
nativeImage.getRGB(srcBuf, 0, srcW, 0, 0, srcW, srcH);
// compute interpolation table
int[] tabY = new int[desH];
int[] tabX = new int[desW];
int sb = 0;
int db = 0;
int tems = 0;
int temd = 0;
int distance = srcH > desH ? srcH : desH;
for (int i = 0; i <= distance; i++) { /* vertical direction */
tabY[db] = sb;
tems += srcH;
temd += desH;
if (tems > distance) {
tems -= distance;
sb++;
}
if (temd > distance) {
temd -= distance;
db++;
}
}
sb = 0;
db = 0;
tems = 0;
temd = 0;
distance = srcW > desW ? srcW : desW;
for (int i = 0; i <= distance; i++) { /* horizontal direction */
tabX[db] = (short) sb;
tems += srcW;
temd += desW;
if (tems > distance) {
tems -= distance;
sb++;
}
if (temd > distance) {
temd -= distance;
db++;
}
}
// set transparence
// if(isTrans){
// int a= 100;//set the transparence of pixel 100
// for(int i=0;i<srcBuf.length;i++){
// if(srcBuf[i]==0x00FFFFFF)continue;
// srcBuf[i]=(a<<24) | (srcBuf[i] & 0x00FFFFFF);// modify the highest 2
// value
// }
// }
// formation enlarge and shorten buffer pixel
int[] desBuf = new int[desW * desH];
int dx = 0;
int dy = 0;
int sy = 0;
int oldy = -1;
for (int i = 0; i < desH; i++) {
if (oldy == tabY[i]) {
System.arraycopy(desBuf, dy - desW, desBuf, dy, desW);
} else {
dx = 0;
for (int j = 0; j < desW; j++) {
desBuf[dy + dx] = srcBuf[sy + tabX[j]];
dx++;
}
sy += (tabY[i] - oldy) * srcW;
}
oldy = tabY[i];
dy += desW;
}
if (isTrans) {
// int a= 100;//set the transparence of pixel 100
for (int i = 0; i < desBuf.length; i++) {
if (desBuf[i] == 0x00FFFFFF)
continue;
int alpha = ((desBuf[i] & 0xff000000) >>> 24) == 0 ? 0 : 100;
desBuf[i] = ((alpha + 1) << 24) | (desBuf[i] & 0x00FFFFFF);// modify
// the
// highest
// 2
// value
}
}
if (isBackgroundTrans) {
desImg = new Image(javax.microedition.lcdui.Image.createRGBImage(desBuf, desW, desH, true));
} else {
desImg = new Image(javax.microedition.lcdui.Image.createRGBImage(desBuf, desW, desH, false));
}
return desImg;
}
/**
* get the major color of the image, you can consider major color as the average color of an icon.
* @return
*/
public int getMajorColor() {
if(majorColor != -1)
return majorColor;
// LOG.error("getMajor color");
javax.microedition.lcdui.Image image=getNativeImage();
// LOG.error("image="+image);
int width=image.getWidth();
int height=image.getHeight();
int []color=new int[width*height];
image.getRGB(color, 0, width, 0, 0, width,height);
// clearAlpha(color);
// LOG.error("color length:"+color.length);
// majorColor = getAverageColor(color);
majorColor = color[25*width+25];
return majorColor;
}
private void clearAlpha(int[] color)
{
for(int i=0; i<color.length; i++)
{
color[i]=color[i] & 0x00FFFFFF;
}
}
private int getAverageColor(int [] color)
{
// LOG.error("getAverageColor");
int average = average(color);
// LOG.error("average="+average);
color = filter(color, average, 100);
average = average(color);
// color = filter(color, average, 100);
// average = average(color);
// LOG.error("after filter average="+average);
return average;
}
/**
* filter the color, remove the ones which more than distance.
* @param color
* @param average
* @param distance
* @return
*/
private int[] filter(int [] color, int average, int distance)
{
int [] temp = new int[color.length];
int index=0;
for(int i=0; i<color.length; i++)
{
double dist = distance(color[i], average);
if( dist < distance){
LOG.debug("dist="+dist);
temp[index++] = color[i];
}
}
if(index == 0)
return new int[1];
int [] ret = new int[index-1];
for(int i=0; i<index-1; i++)
{
ret[i] = temp[i];
}
return temp;
}
/**
* calculate the average of the color array.
* @param color
* @return
*/
private int average(int [] color)
{
if(color == null)
throw new IllegalStateException("color array can't be null");
int sum_a = 0;
int sum_r = 0;
int sum_g = 0;
int sum_b = 0;
for(int i=0; i<color.length; i++)
{
sum_a += getA(color[i]);
sum_r += getR(color[i]);
sum_g += getG(color[i]);
sum_b += getB(color[i]);
}
int ave = ((sum_a/color.length)<<24)+((sum_r/color.length)<<16) + ((sum_g/color.length)<<8) + (sum_b/color.length);
return ave;
}
/**
* calculate the distance of two colors
* @param color1
* @param color2
* @return
*/
private double distance(int color1, int color2)
{
int s = square(getA(color1)-getA(color2))+
square(getR(color1)-getR(color2))+
square(getG(color1)-getG(color2))+
square(getB(color1)-getB(color2));
s = s/4;
return Math.sqrt(s);
}
private double distance2(int color1, int color2)
{
int r = Math.abs(getR(color1)-getR(color2));
int g = Math.abs(getG(color1)-getG(color2));
int b = Math.abs(getB(color1)-getB(color2));
int d = Math.max(Math.max(r, g), b);
return d;
}
private int getA(int color)
{
return ( color & 0xff000000)>>>24;
}
private int getR(int color)
{
return ( color & 0xff0000)>>>16;
}
private int getG(int color)
{
return ( color & 0xff00)>>>8;
}
private int getB(int color)
{
return ( color & 0xff);
}
private int square(int x)
{
return x*x;
}
/**
* Access Graphics class to be able to draw in the Image
* @return Graphics class of the image
*/
public javax.microedition.lcdui.Graphics getGraphics()
{
return nativeImage.getGraphics();
}
/**
* Check if this image is mutable.
* Mutable images can be modified by rendering to them through a
* Graphics object obtained from the getGraphics() method of this object.
* @return true if mutable false otherwise
*/
public boolean isMutable()
{
return nativeImage.isMutable();
}
public static void main(String[] args) {
// int[] color={769 ,3 ,3};
//// Color c = new Color();
// int ave = c.getAverageColor(color);
// System.out.print("ave="+ave);
// System.out.println("G="+c.getG(769));
}
}
|