package at.jta.componentX;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
import java.awt.RenderingHints;
import java.awt.Color;
import java.awt.image.WritableRaster;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Hashtable;
/********************************************************************************************************************************
*
* <p>Title: Methode </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2007 - Code is under GPL</p>
*
* <p>Company: JTA</p>
*
* @author Taschek Jörg
* @version 1.0 Erstversion 12.02.07 XPainter kann entweder einen gradient (farbverlauf) darstellen oder ein Bild laden
*******************************************************************************************************************************/
public class XPainter
implements Paint, PaintContext
{
private Color gradients[] = null;
private WritableRaster raster = null;
private int startX = 0, startY = 0, offsetY = 0, offsetX = 0;
private int iMyWidth = -1, iMyHeight = -1;
private boolean gradientMode = false, horizontalgradient = false;
private Image img = null;
private static final boolean logging = false;
private double rotation = -1;
private int TRANSPARENCY = 0;
private int ALPHA_LEVEL = 255;
private Hashtable htRasters = new Hashtable();
private boolean bFitImage = false;
private static int storeCount = 0; //Startup no backup Mode is used
/******************************************************************************************************************************
* Kontruktor mit einem Color Array für den Farbverlauf
* @param colors Color[]
*****************************************************************************************************************************/
public XPainter(Color[] colors)
{
this.gradients = colors;
this.gradientMode = true;
}
/******************************************************************************************************************************
* Konstruktor mit ImageIcon
* @param icon ImageIcon
*****************************************************************************************************************************/
public XPainter(ImageIcon icon)
{
this.gradientMode = false;
this.img = icon.getImage();
}
/******************************************************************************************************************************
* Konstruktor mit Image
* @param image Image
*****************************************************************************************************************************/
public XPainter(Image image)
{
this.gradientMode = false;
this.img = new ImageIcon(image).getImage(); //be sure that image is loaded
}
/*****************************************************************************************************************************
*
****************************************************************************************************************************/
public void dispose()
{
}
/*****************************************************************************************************************************
* Gibt das ColorModel zurück um einen Raster zu erstellen
* @return ColorModel
****************************************************************************************************************************/
public ColorModel getColorModel()
{
return ColorModel.getRGBdefault();
}
/******************************************************************************************************************************
* Methode holt den Raster für die jeweiligen Koordinaten (also von einem Teilausschnitt vom Bild)
* @param x int
* @param y int
* @param w int
* @param h int
* @return Raster
*****************************************************************************************************************************/
public Raster getRaster(int x, int y, int w, int h)
{
return calculate(x,y, w,h);
}
/*****************************************************************************************************************************
* Methode gibt vom Hauptraster den jeweiligen kleinen Raster zurück
* @param x int
* @param y int
* @param w int
* @param h int
* @return Raster
****************************************************************************************************************************/
private Raster calculate(int x, int y, int w, int h)
{
if(logging)
System.out.println("X: " + x + " Y: " + y + " W: " + w + " H: " + h + " SX: " + startX + " SY: " + startY + " OX: " + offsetX + " OY: " + offsetY);
WritableRaster ret = getColorModel().createCompatibleWritableRaster(w,h);
double ar[] = new double[w * h * 4] ;
int iY = y -startY;
// if(iY < 0)
iY = y - offsetY;
if(iMyHeight != -1)
iY += startY;
if(iY < 0)
iY = 0; //lieber auf 0 setzen bevor ne Exception produziert wird
int iX = x - startX;
// if(iX < 0)
iX = x - offsetX;
if(iMyWidth != -1)
iX += startX; //wieder dazuzählen da es ein generelles Bild ist
if(iX < 0)
iX = 0; //0 bevor exception kommt
if(iX + w > raster.getWidth()) //Überprüfung ob die Abfrage nicht größer ist, als der Raster
w = raster.getWidth() - iX;
if(iY + h > raster.getHeight())
h = raster.getHeight() - iY;
if(logging)
System.out.println("GETTING FROM IX: " + iX + " IY: " + iY + " " + raster.getBounds());
try{
String _raster = null;
if(storeCount > 0 && htRasters != null) //raster merken
{
_raster = new StringBuffer().append(iX).append(iY).append(w).append(h).toString();
Object o = htRasters.get(_raster);
if (o != null)
return(Raster) o;
}
raster.getPixels(iX, iY, w, h, ar); //holt die Pixel aus dem Hauptraster
ret.setPixels(0,0,w,h,ar); //fügt sie in den kleinen rater ein
if(storeCount > 0 && htRasters != null && _raster != null)
_put(ret,_raster);
}
catch(Exception ex)
{
System.out.println("CALC_RASTER_EXCEPTION X: " + x + " Y: " + y + " W: " + w + " H: " + h + " SX: " + startX + " SY: " +
startY + " OX: " + offsetX + " OY: " + offsetY + "\nRaster: " + raster.getBounds());
}
return ret;
}
/*****************************************************************************************************************************
* Methode speichert einen Raster ab
* @param raster Raster
* @param _raster String
****************************************************************************************************************************/
private void _put(Raster raster, String _raster)
{
if(htRasters.size() >= storeCount) //falles es die Anzahl der zu speichernden Raster übersteigt
{
htRasters.remove(htRasters.keys().nextElement()); //löschen
_put(raster,_raster);
return;
}
htRasters.put(_raster,raster);
}
/*****************************************************************************************************************************
* Methode setzt die Anzahl der zu speicherenden Rastereinträge
* @param aAmount int
***************************************************************************************************************************/
public static void setRasterStoreCount(int aAmount)
{
storeCount = aAmount;
}
/****************************************************************************************************************************
* Methode gibt die Anzahl der zu speicherneden Raster einträge zurück
* @return int
***************************************************************************************************************************/
public static int getRasterStoreCount()
{
return storeCount;
}
/******************************************************************************************************************************
* Methode setzt das Rastermodel auf eine gewisse Gesamtgröße dadurch wird die einzelne Größe ignoriert
* @param width int
* @param height int
*****************************************************************************************************************************/
public void setDimension(int width, int height)
{
this.iMyWidth = width;
this.iMyHeight = height;
_initModel(null); //erstellt das Model neu
}
/******************************************************************************************************************************
* Mehtode setzt die Dimension des Effektes
* @param dim Dimension
*****************************************************************************************************************************/
public void setDimension(java.awt.Dimension dim)
{
if(dim != null)
setDimension(dim.width, dim.height);
}
/******************************************************************************************************************************
* Methode gibt die Dimension zurück
* @return Dimension
*****************************************************************************************************************************/
public java.awt.Dimension getDimension()
{
if(this.iMyWidth != -1 && this.iMyHeight != -1)
return new java.awt.Dimension(this.iMyWidth, this.iMyHeight);
return null;
}
/******************************************************************************************************************************
* Methode setzt die eigene Dimension wieder zurück
*****************************************************************************************************************************/
public void resetDimension()
{
this.iMyWidth = -1;
this.iMyHeight = -1;
}
/******************************************************************************************************************************
* Methode setzt ob ein Bild wiederholt gezeichnet werden soll (also so breit wie die breite ist und nicht so breit wie das Bild
* selber ist) oder wenn das Bild größer ist als die Breite, dass das Bild automatisch kleiner skaliert wird
* @param aValue boolean
*****************************************************************************************************************************/
public void setImageRenderToFit(boolean aValue)
{
this.bFitImage = aValue;
raster = null; //neu erstellen
}
/******************************************************************************************************************************
* Methode gibt zurück ob ein Bild (falls überhaupt ein Bild übergeben wurde) immer wieder nebeneinander gezeichnet wird,
* wenn die Bildbreite kleiner ist als der Context (default = false) und falls das Bild größer ist als die Breite ob das
* Bild automatisch skaliert wird
* @return boolean
****************************************************************************************************************************/
public boolean isImageRenderToFit()
{
return bFitImage;
}
/******************************************************************************************************************************
* Methode setzt den veritkal Malmodus
* @param aValue boolean
*****************************************************************************************************************************/
public void setHorizontalGradient(boolean aValue)
{
this.horizontalgradient = aValue;
raster = null; //to make new model
}
/******************************************************************************************************************************
* Methode gibt zurück ob es auch vertikal gezeichnet wird
* @return boolean
*****************************************************************************************************************************/
public boolean isHorizontalGradient()
{
return this.horizontalgradient;
}
/*****************************************************************************************************************************
* Methode gibt den Rotationsgrad zurück
* @return double
****************************************************************************************************************************/
public double getRotation()
{
return this.rotation;
}
/*****************************************************************************************************************************
* Methode setzt den Rotationsgrad für den Picture Mode
* @param d double
****************************************************************************************************************************/
public void setRotation(double d)
{
if(d < 0)
throw new RuntimeException("No valid rotation! Rotation musst be greator zero! (set to 0 if no rotation)");
this.rotation = d;
raster = null; //to make new model
}
/*****************************************************************************************************************************
* Methode gibt den Context zurück, welcher für das malen zuständig ist
* @param cm ColorModel
* @param deviceBounds Rectangle
* @param userBounds Rectangle2D
* @param xform AffineTransform
* @param hints RenderingHints
* @return PaintContext
****************************************************************************************************************************/
public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints)
{
offsetX = deviceBounds.x;
offsetY = deviceBounds.y;
startX = userBounds.getBounds().x;
startY = userBounds.getBounds().y;
if(!checkModel(deviceBounds)) //überprüft die Bounds
_initModel(deviceBounds); //erstellt das Model
return this;
}
/*****************************************************************************************************************************
* Methode überprüft ob ein neues Model angelegt werden muss
* @param bounds Rectangle
* @return boolean
***************************************************************************************************************************/
private boolean checkModel(Rectangle bounds)
{
if(raster != null)
{
//wenn die 2 Größen verändert worden sind
if(iMyWidth != -1 && iMyHeight != -1)
{
//überprüfen ob es gleich ist, wenn ja nix neues erzeugen
if(raster.getWidth() == iMyWidth && raster.getHeight() == iMyHeight)
return true;
else //ansonsten neu erzeugen
return false;
}
//wenn die Bounds gleich sind, braucht man kein neues Model erstellen!
if(raster.getWidth() == bounds.width && raster.getHeight() == bounds.height)
return true;
}
return false;
}
/******************************************************************************************************************************
* Methode erstellt das gesamte Rastermodel mit dem Farbverlauf
* @param bounds Rectangle
*****************************************************************************************************************************/
private void _initModel(Rectangle bounds)
{
if(htRasters != null)
htRasters.clear();
htRasters = null;
htRasters = new Hashtable();
int width = iMyWidth, height = iMyHeight;
if(bounds != null && width == -1 && height == -1)
{
width = bounds.width;
height = bounds.height;
}
if(gradientMode)
{
if(horizontalgradient)
createHorizontalGradientModel(width, height);
else
createGradientModel(width, height);
}
else
createPictureModel(width, height);
}
/******************************************************************************************************************************
* Methode setzt das Picture Model
* @param width int
* @param height int
*****************************************************************************************************************************/
private void createPictureModel(int width, int height)
{
Image tmp = img;
//wenn eine Rotation eingeschalten ist
if(rotation > 0)
{
int size = Math.max(img.getHeight(null), img.getWidth(null));
if(logging)
System.out.println(width + " " + height + " " + size);
BufferedImage bimg = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D)bimg.createGraphics();
AffineTransform aft = new AffineTransform();
aft.rotate ( Math.toRadians( 180 ), width / 2 , height / 2);
g2.setTransform(aft);
g2.drawImage(tmp, 0, 0, null);
tmp = bimg; //weißt es zu
}
if(bFitImage) //falls das Bild zu groß ist, wird es runterscaliert
{
if (width < tmp.getWidth(null))
{
tmp = tmp.getScaledInstance(width, -1, Image.SCALE_SMOOTH);
tmp = new ImageIcon(tmp).getImage(); //be sure that image is loaded
}
if (height < tmp.getHeight(null))
{
tmp = tmp.getScaledInstance( -1, height, Image.SCALE_SMOOTH);
tmp = new ImageIcon(tmp).getImage(); //be sure that image is loaded in imageicon (fuck IMAGE!!!)
}
}
BufferedImage bimg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = bimg.createGraphics();
if(bFitImage) //wenn es automatisch skaliert werden soll
{
int iWidth = tmp.getWidth(null);
int iHeight = tmp.getHeight(null);
//nur wenn es entweder niedriger oder zu schmal ist, die Bilder nochmals nebeneinander malen
if(width > iWidth || height > iHeight)
{
for(int x = 0; x <= width; x += iWidth)
{
for(int y = 0; y <= height; y += iHeight)
{
g.drawImage(tmp, x, y, null);
}
}
}
}
else
g.drawImage(tmp,0,0,null); //nur einmal draufmalen
this.raster = bimg.getRaster();
}
/******************************************************************************************************************************
* Methode erstellt das GradientModel
* @param width int
* @param height int
*****************************************************************************************************************************/
private void createGradientModel(int width, int height)
{
raster = getColorModel().createCompatibleWritableRaster(width, height);
int iwidth_sep = width / (gradients.length - 1);
float fwidth = width / ( (gradients.length - 1) * 1.0f);
int x = 0;
for (int u = 0; u != gradients.length - 1; u++)
{
float rc = (gradients[u + 1].getRed() - gradients[u].getRed()) / fwidth;
float gc = (gradients[u + 1].getGreen() - gradients[u].getGreen()) / fwidth;
float bc = (gradients[u + 1].getBlue() - gradients[u].getBlue()) / fwidth;
float red = gradients[u].getRed(), green = gradients[u].getGreen(), blue = gradients[u].getBlue();
for (int i = 0; i != iwidth_sep; i++,x++)
{
float rgba[] = new float[] { red, green, blue, ALPHA_LEVEL};
for (int y = 0; y != height; y++)
{
try
{
raster.setPixel(x, y, rgba);
}
catch (Exception ex)
{
System.out.println("GEN_RASTER_EXCEPTION X: " + x + " Y: " + y + " I: " + i + " W: " + width + " H: "
+ height + "\nU: " + u + " Length: " + gradients.length);
}
}
red += rc;
green += gc;
blue += bc;
}
}
}
/******************************************************************************************************************************
* Methode erstellt das horziontal gradient Model
* @param width int
* @param height int
*****************************************************************************************************************************/
private void createHorizontalGradientModel(int width, int height)
{
raster = getColorModel().createCompatibleWritableRaster(width, height);
int iheight_sep = height / (gradients.length - 1);
float fheight = height / ( (gradients.length - 1) * 1.0f);
int y = 0;
for (int u = 0; u != gradients.length - 1; u++)
{
float rc = (gradients[u + 1].getRed() - gradients[u].getRed()) / fheight;
float gc = (gradients[u + 1].getGreen() - gradients[u].getGreen()) / fheight;
float bc = (gradients[u + 1].getBlue() - gradients[u].getBlue()) / fheight;
float red = gradients[u].getRed(), green = gradients[u].getGreen(), blue = gradients[u].getBlue();
for (int i = 0; i != iheight_sep; i++,y++)
{
float rgba[] = new float[] { red, green, blue, ALPHA_LEVEL};
for (int x = 0; x != width; x++)
{
try
{
raster.setPixel(x, y, rgba);
}
catch (Exception ex)
{
System.out.println("GEN_RASTER_EXCEPTION X: " + x + " Y: " + y + " I: " + i + " W: " + width + " H: "
+ height + "\nU: " + u + " Length: " + gradients.length);
}
}
red += rc;
green += gc;
blue += bc;
}
}
}
/******************************************************************************************************************************
*
* @param TRANSPARENCY int
*****************************************************************************************************************************/
public void setTransparency(int TRANSPARENCY)
{
this.TRANSPARENCY = TRANSPARENCY;
}
/*****************************************************************************************************************************
* Methode setzt das Alpha Level welches bei jedem Pixel beim GradientModel gesetzt wird
* @param alpha int
****************************************************************************************************************************/
public void setAlphaLevel(int alpha)
{
this.ALPHA_LEVEL = alpha;
}
/*****************************************************************************************************************************
* Methode gibt den Alpha Wert zurück, welches bei jedem Pixel übergeben wird (zumindest beim Gradient)
* @return int
****************************************************************************************************************************/
public int getAlphaLevel()
{
return ALPHA_LEVEL;
}
/******************************************************************************************************************************
* Methode gibt zurück ob etwas transparent oder nicht ist
* @return int
****************************************************************************************************************************/
public int getTransparency()
{
return TRANSPARENCY;
}
}