RotatableImageIcon.java

/*
 *   An ImageIcon class with rotation.
 *
 *   Hans Liss <Hans@Liss.pp.se> 2006
 *
 *   ... with a grateful nod to Karl Lager for his illuminating
 *   text "A Fast Algorithm For Rotating Bitmaps".
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version
 *   2 of the License, or (at your option) any later version.
 *
 */
 
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.net.*;
 
public class RotatableImageIcon extends ImageIcon {
    private boolean isInitialized=false;
    private Graphics ibG=null;
    private BufferedImage iconBuffer=null;
    private int iw;
    private int ih;
    private int nw;
    private int nh;
    private int maxx;
    private int minx;
    private int maxy;
    private int miny;
    private WritableRaster iconRaster;
    private int[] iconPixels;
    private BufferedImage iconBuffer2=null;
    private WritableRaster iconRaster2;
    private int[] iconPixels2;
    private ImageIcon rotatedIcon=null;
    RotatableImageIcon(byte[] imageData ) {
	super(imageData);
	RI_Initialize();
    }
 
    RotatableImageIcon(byte[] imageData, String description) {
	super(imageData, description);
	RI_Initialize();
    }
 
    RotatableImageIcon(Image image) {
	super(image);
	RI_Initialize();
    }
 
    RotatableImageIcon(Image image, String description) {
	super(image, description);
	RI_Initialize();
    }
 
    RotatableImageIcon(String filename) {
	super(filename);
	RI_Initialize();
    }
 
    RotatableImageIcon(String filename, String description) {
	super(filename, description);
	RI_Initialize();
    }
 
    RotatableImageIcon(URL location) {
	super(location);
	RI_Initialize();
    }
 
    RotatableImageIcon(URL location, String description) {
	super(location, description);
	RI_Initialize();
    }
 
    private void RI_Initialize() {
	Image iconI=getImage();
	ImageObserver io=getImageObserver();
	int iconW=iconI.getWidth(io);
	int iconH=iconI.getHeight(io);
	iconBuffer=new BufferedImage(iconW, iconH, BufferedImage.TYPE_INT_ARGB);
	ibG=iconBuffer.getGraphics();
	paintIcon((Component)io, ibG, 0, 0);
	iw=getIconWidth();
	ih=getIconHeight();
	nw=(int)(Math.sqrt(iw*iw + ih*ih)+0.5) & ~1;
	nh=nw;
	maxx=(int)(nw/2+0.5);
	minx=-maxx;
	maxy=(int)(nh/2+0.5);
	miny=-maxy;
	iconBuffer2=new BufferedImage(nw, nh, BufferedImage.TYPE_INT_ARGB);
	rotatedIcon=new ImageIcon(iconBuffer2);
	iconRaster = iconBuffer.getRaster();
	iconPixels = ( (DataBufferInt) iconRaster.getDataBuffer()).getData();
	iconRaster2 = iconBuffer2.getRaster();
	iconPixels2 = ( (DataBufferInt) iconRaster2.getDataBuffer()).getData();
	isInitialized=true;
    }
 
    /*
      Karl Lager's algorithm with some extra oomph in the form of pure integer
      arithmetic (and only one multiplication) inside the loop.
 
      This also makes use of the direct raster access functionality of the
      BufferedImage class. See RI_Initialize().
      A hint: don't ever use setRGB() for more than isolated pixels, unless
      you absolutely have to.
     */
 
    public ImageIcon rotatedIcon(double angle) {
	if (!isInitialized) {
	    RI_Initialize();
	}
	double cosT=Math.cos(angle);
	double sinT=Math.sin(angle);
	double xpstart=(double)miny*sinT + (double)minx*cosT + 0.5 + iw/2;
	double ypstart=(double)miny*cosT - (double)minx*sinT + 0.5 + ih/2;
	int offsbits=16;
	int numscale=(1<<offsbits);
	int ixps=(int)(xpstart * numscale);
	int iyps=(int)(ypstart * numscale);
	int ist=(int)(sinT * numscale);
	int ict=(int)(cosT * numscale);
	int ixp, iyp, xp, yp;
 
	int oxp=0, oyp=0;
	int val=0;
	int line, col;
	int linemax=nh*nw;
 
	for (line=0; line < linemax; line += nw) {
	    ixp=ixps; iyp=iyps;
	    for (col=0; col<nw; col++) {
		xp=ixp >> offsbits;
		yp=iyp >> offsbits;
		if (xp>=0 && xp<iw && yp>=0 && yp<ih) {
		    /* Avoid unnecessary accesses to the same pixel */
		    if (xp != oxp || yp != oyp) {
			val=iconPixels[xp + yp * iw];
			oxp=xp; oyp=yp;
		    }
		} else val=0xffffff; // Note: this is actually transparent!
		iconPixels2[line + col] = val;
		ixp += ict;
		iyp -= ist;
	    }
	    ixps += ist;
	    iyps += ict;
	}
	return rotatedIcon;
    }
}