/*
Copyright (c) 2004 Joseph Gleason
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Current versions of this and other code can be downloaded at:
http://gleason.cc/
*/
package cc.glsn;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Point;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.Stack;
/** A swing component that holds a bunch of images at various locations and layers */
public class FancyImage extends javax.swing.JComponent
{
/**
* Comment for serialVersionUID
*/
private static final long serialVersionUID = 1L;
int Width;
int Height;
BufferedImage Fin;
Color Background;
Color TransColor;
TreeSet Images;
boolean RedrawEnabled;
public FancyImage()
{
this(100,100,java.awt.Color.BLACK);
}
/** */
public FancyImage(int W, int H, Color Back)
{
TransColor=Color.WHITE;
Background=Back;
setSize(W,H);
Images=new TreeSet();
RedrawEnabled=true;
}
public synchronized void setRedrawEnabled(boolean r)
{
RedrawEnabled=r;
if (r==true)
{
setRe(0,0,Width,Height);
}
}
private void setRe(int X, int Y, int W, int H)
{
if (!RedrawEnabled) return;
if (X<0) X=0;
if (Y<0) Y=0;
if (W<0) W=0;
if (H<0) H=0;
if (X>Width) X=Width;
if (Y>Height) Y=Height;
if (X+W>Width) W=Width-X;
if (Y+H>Height) H=Height-Y;
repaint(new Rectangle(X,Y,W,H));
}
/** Resize the fancyimage to new size */
public synchronized void setSize(int W,int H)
{
if ((W!=Width) || (H!=Height))
{
Width=W;
Height=H;
Fin=new BufferedImage(W,H,BufferedImage.TYPE_4BYTE_ABGR);
setRe(0,0,W,H);
}
}
/** Remove all images */
public synchronized void clearImages()
{
Images.clear();
setRe(0,0,Width,Height);
}
/** removes all images in the specified layer range inclusive.
Returns number of images removed */
public synchronized int clearLayer(int low, int high)
{
int rc=0;
for(Iterator I=Images.iterator(); I.hasNext(); )
{
ImageData ID=(ImageData)I.next();
if ((ID.Layer >= low) && (ID.Layer <= high))
{
I.remove(); rc++;
setRe(ID.x,ID.y,ID.Im.getWidth(),ID.Im.getHeight());
}
}
return rc;
}
/** removes all images at specified layer.
Returns number of images removed */
public synchronized int clearLayer(int n)
{
return clearLayer(n,n);
}
/** should be done more efficently later */
public synchronized void remove(String Name)
{
for(Iterator I=Images.iterator(); I.hasNext(); )
{
ImageData ID=(ImageData)I.next();
if (ID.Name.equals(Name))
{
I.remove();
setRe(ID.x,ID.y,ID.Im.getWidth(),ID.Im.getHeight());
}
}
}
/** Add image at layer and name. Any preexisting images with same layer and name will
be replaced.
x and y are the coordinates of the top left pixel of the new image relative to the entire fancy image */
public synchronized void addImage(int Layer, String Name, BufferedImage Im, int x, int y)
{
ImageData ID=new ImageData(Layer,Name,Im,x,y);
SortedSet SS=Images.tailSet(ID);
if (!SS.isEmpty())
{
ImageData OldID=(ImageData)SS.first();
if ((OldID!=null) && (OldID.equals(ID)))
{
Images.remove(OldID);
setRe(OldID.x,OldID.y,OldID.Im.getWidth(),OldID.Im.getHeight());
}
}
Images.add(ID);
setRe(x,y,Im.getWidth(),Im.getHeight());
}
public void addImageCenter(int Layer, String Name, BufferedImage Im, Point P)
{
Point Q=new Point(P.x-Im.getWidth()/2,P.y-Im.getHeight()/2);
addImage(Layer,Name,Im,Q);
}
public void addImage(int Layer, String Name, BufferedImage Im, Point P)
{
addImage(Layer,Name,Im,P.x,P.y);
}
/** Make every image with layer>=MinLayer into a solid image named "simp" at layer MinLayer */
public synchronized void simplify(int MinLayer)
{
BufferedImage BI=new BufferedImage(Width,Height,BufferedImage.TYPE_4BYTE_ABGR);
Rectangle R=new Rectangle(0,0,Width,Height);
Graphics G=BI.getGraphics();
G.setColor(Background);
G.fillRect(0,0,Width,Height);
Stack RemoveList=new Stack();
for(Iterator I=Images.iterator(); I.hasNext(); )
{
ImageData ID=(ImageData)I.next();
if (ID.Layer>=MinLayer)
{
ID.draw(BI,R);
RemoveList.push(ID);
}
}
while(!RemoveList.empty())
{
Images.remove(RemoveList.pop());
}
addImage(MinLayer,"simp",BI,new Point(0,0));
}
private synchronized void redraw(java.awt.Rectangle R)
{
Graphics G=Fin.getGraphics();
G.setColor(Background);
G.fillRect(R.x,R.y,R.width,R.height);
Iterator I=Images.iterator();
while(I.hasNext())
{
ImageData ID=(ImageData)I.next();
ID.draw(Fin,R);
}
}
/** Called by java VM when rebuild of images is needed as a result of
damage or repaint being called */
protected synchronized void paintComponent(Graphics g)
{
Rectangle R=g.getClip().getBounds();
//System.out.println("Clip:" + g.getClip());
if (R.x > Width) return;
if (R.y > Height) return;
if (R.x + R.width > Width)
{
R.width=Width-R.x;
}
if (R.y + R.height > Height)
{
R.height=Height-R.y;
}
redraw(R);
g.drawImage(Fin.getSubimage(R.x,R.y,R.width,R.height),R.x,R.y,R.width,R.height,null);
}
/** Used by some layout managers */
public Dimension getMaximumSize()
{
return getPreferredSize();
}
/** Used by some layout managers */
public Dimension getMinimumSize()
{
return getPreferredSize();
}
/** Used by some layout managers */
public Dimension getPreferredSize()
{
return new Dimension(Width,Height);
}
/** Used for swing optimization */
public boolean isOpaque()
{
return true;
}
private class ImageData implements Comparable
{
int Layer;
String Name;
BufferedImage Im;
int x;
int y;
public ImageData(int Layer2, String Name2, BufferedImage Im2, int x2, int y2)
{
Layer=Layer2;
Name=Name2;
Im=Im2;
x=x2;
y=y2;
}
private void draw(BufferedImage Fin,Rectangle R)
{
int ReMinX=R.x;
int ReMinY=R.y;
int ReMaxX=R.x+R.width;
int ReMaxY=R.y+R.height;
int TransRGB=TransColor.getRGB();
int imax=Im.getWidth();
if (x+imax > ReMaxX) imax=ReMaxX-x;
int jmax=Im.getHeight();
if (y+jmax > ReMaxY) jmax=ReMaxY-y;
int imin=ReMinX-x;
if (imin<0) imin=0;
int jmin=ReMinY-y;
if (jmin<0) jmin=0;
for(int i=imin; iO2.Layer) return -1;
if (Layer