/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad 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. * * Nomad 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 for more details. * * You should have received a copy of the GNU General Public License * along with Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Created on May 2, 2006 */ package net.sf.nmedit.nmutils.graphics; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Component; import java.awt.Composite; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ImageObserver; import java.awt.image.PixelGrabber; import javax.swing.ImageIcon; public class GraphicsToolkit { public static ImageIcon renderColorIcon(Color c) { return new ImageIcon(renderColorImage(c, 16, 16, Color.BLACK)); } public static BufferedImage renderColorImage(Color c, int w, int h, Color border) { boolean opaque = c.getAlpha()==0xFF && (border==null || (border!= null && border.getAlpha()==0xFF)); BufferedImage img = new BufferedImage(w, h, opaque ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); Graphics2D g = img.createGraphics(); try { int fx, fy, fw, fh; if (border == null) { fx = 0; fy = 0; fw = w; fh = h; } else { fx = 1; fy = 1; fw = w-2; fh = h-2; g.setColor(border); g.drawRect(0, 0, w-1, h-1); } g.setColor(c); g.fillRect(fx, fy, fw, fh); } finally { g.dispose(); } return img; } public static BufferedImage getScaledImage(Image source, double f) { return getScaledImage(source, null, f); } public static BufferedImage getScaledImage(Image source, BufferedImage reuse, double f) { if (f<=0) throw new IllegalArgumentException("invalid factor specified"); final int iw = source.getWidth(null); final int ih = source.getHeight(null); final int sw = (int) (iw*f); final int sh = (int) (ih*f); final BufferedImage scaled; final int transparency = (source instanceof Transparency) ? ((Transparency)source).getTransparency() : Transparency.OPAQUE; Graphics2D g2 = null; if (reuse!=null && reuse.getWidth()==sw && reuse.getHeight()==sh && transparency == reuse.getTransparency()) { scaled = reuse; g2 = scaled.createGraphics(); clearRegion(g2, 0, 0, sw, sh); } else { scaled = createCompatibleBuffer(sw, sh, transparency); g2 = scaled.createGraphics(); } try { g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.scale(f, f); g2.drawImage(source, 0, 0, null); g2.dispose(); } finally { g2.dispose(); } return scaled; } /** * Returns <code>true</code> when the color is transparent, * thus when it has a alpha value not equal to <code>255</code> * (<code>0xFF</code>). * @param c the color * @return <code>true</code> when the color is transparent, */ public static boolean isTransparent(Color c) { return c.getAlpha() != 0xFF ; } public static boolean isTransparent(Color c1, Color c2) { return (((c1.getAlpha() & c2.getAlpha()) == 0xFF) ? false : true); } /* public static boolean isTransparent(Color ... colors) { for (int i=0;i<colors.length;i++) { if (colors[i].getAlpha() != 0xFF) return true; } return false; } */ public final static int getColorValue(Color c) { return getColorValue(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()); } public final static int getColorValue(int r, int g, int b) { return getColorValue(r, g, b, 0xFF); } public final static int getColorValue(int r, int g, int b, int a) { return (((int) a << 24)&0xFF000000) |(((int) r << 16)&0x00FF0000) |(((int) g << 8)&0x0000FF00) |( (int) b &0x000000FF); } /** * Computes the intersection between two rectangles. The result is * stored in the specified rectangle. * @param r * @param ax * @param ay * @param aw * @param ah */ public final static void intersect(Rectangle r, int ax, int ay, int aw, int ah) { intersect(r, r.x, r.y, r.width, r.height, ax, ay, aw, ah); } public final static void union(Rectangle r, int ax, int ay, int aw, int ah) { union(r, r.x, r.y, r.width, r.height, ax, ay, aw, ah); } public final static void union(Rectangle dest, int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) { if (aw<=0||ah<=0) { dest.x = bx; dest.y = by; dest.width = bw; dest.height = bh; } else if (bw<=0||bh<=0) { dest.x = ax; dest.y = ay; dest.width = aw; dest.height = ah; } else { int x1 = Math.min(ax, bx); int x2 = Math.max(ax + aw, bx + bw); int y1 = Math.min(ay, by); int y2 = Math.max(ay + ah, by + bh); dest.x = x1; dest.y = y1; dest.width = x2-x1; dest.height = y2-y1; } } /** * Computes the intersection of two rectangles <code>a</code> and <code>b</code>. * The intersection is stored in the rectangle <code>dest</code>. * @param dest The computed intersection * @param ax * @param ay * @param aw * @param ah * @param bx * @param by * @param bw * @param bh */ public final static void intersect(Rectangle dest, int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) { // modified and ripped from Rectangle#intersection(Rectangle) long ax2 = ax; ax2 += aw; long ay2 = ay; ay2 += ah; long bx2 = bx; bx2 += bw; long by2 = by; by2 += bh; if (ax < bx) ax = bx; if (ay < by) ay = by; if (ax2 > bx2) ax2 = bx2; if (ay2 > by2) ay2 = by2; ax2 -= ax; ay2 -= ay; // ax2,ay2 will never overflow (they will never be // larger than the smallest of the two source w,h) // they might underflow, though... if (ax2 < Integer.MIN_VALUE) ax2 = Integer.MIN_VALUE; if (ay2 < Integer.MIN_VALUE) ay2 = Integer.MIN_VALUE; // store the computed intersection dest.x = ax; dest.y = ay; dest.width = (int) ax2; dest.height = (int) ay2; } /** * Fills the specified area with the image. * * @param g the graphics object * @param tile the tile * @param iw width of the tile * @param ih height of the tile * @param dx translation of the tile relative to the origin (<0 means x>0 in the tile) * @param dy translation of the tile relative to the origin (<0 means y>0 in the tile) * @param x x coordinate of the filled area * @param y y coordinate of the filled area * @param w width of the filled area * @param h height of the filled area */ public static void paintTiles(Graphics g, Image tile, int iw, int ih, int dx, int dy, int x, int y, int w, int h) { if (iw<=0||ih<=0) throw new IllegalArgumentException("invalid image size"); // move (dx, dy) to the origin (x, y) dx -= x; dy -= y; Rectangle clip = new Rectangle(); g.getClipBounds(clip); /* Shape saveClip = g.getClip(); if (saveClip!=null) { Rectangle dummy = new Rectangle(); { // get clip bounds g.getClipBounds(dummy); // intersect with area intersect(dummy, dummy.x, dummy.y, dummy.width, dummy.height, x, y, w, h); // shrink area to intersection x = dummy.x; y = dummy.y; w = dummy.width; h = dummy.height; } } */ // intersect with area intersect(clip, clip.x, clip.y, clip.width, clip.height, x, y, w, h); // shrink area to intersection x = clip.x; y = clip.y; w = clip.width; h = clip.height; if (w<=0 || h<=0) return; // set clip rect to intersection with area g.clipRect(x,y,w,h); // distance from the painted (inner) area to the next larger area // in which the tiles fit int relx = (x+dx)%iw; int rely = (y+dy)%ih; if (relx<0) relx = iw-relx; if (rely<0) rely = ih-rely; x-=relx; y-=rely; w+=relx; h+=rely; // right, bottom int r = x+w-1; int b = y+h-1; // paint tiles int px, py; py = y; while (py<=b) { px = x; while (px<=r) { g.drawImage(tile, px, py, null); px+=iw; } py+=ih; } // restore clip // g.setClip(saveClip); } /** * Creates a image with the same size as the component using the components graphics configuration. * @see #createCompatibleBuffer(int, int, int, GraphicsConfiguration) */ public static BufferedImage createCompatibleBuffer(Component c, int transparency) { GraphicsConfiguration gc = c.getGraphicsConfiguration(); return createCompatibleBuffer(c.getWidth(), c.getHeight(), transparency, gc!=null?gc:getDefaultGraphicsConfiguration()); } /** * Creates a image using the screen graphics configuration. * @see #createCompatibleBuffer(int, int, int, GraphicsConfiguration) */ public static BufferedImage createCompatibleBuffer(Dimension size, int transparency) { return createCompatibleBuffer(size.width, size.height, transparency); } private static GraphicsConfiguration gc = GraphicsEnvironment .getLocalGraphicsEnvironment() .getDefaultScreenDevice() .getDefaultConfiguration(); public static GraphicsConfiguration getDefaultGraphicsConfiguration() { return gc; } /** * Creates a image using the screen graphics configuration. * @see #createCompatibleBuffer(int, int, int, GraphicsConfiguration) */ public static BufferedImage createCompatibleBuffer(int width, int height, int transparency) { return createCompatibleBuffer(width, height, transparency, getDefaultGraphicsConfiguration()); } /** * Creates a compatible image using the components graphics configuration. * @see #createCompatibleBuffer(int, int, int, GraphicsConfiguration) */ public static BufferedImage createCompatibleBuffer(Dimension size, int transparency, Component c) { return createCompatibleBuffer(size.width, size.height, transparency, c); } /** * Creates a compatible image using the components graphics configuration. * @see #createCompatibleBuffer(int, int, int, GraphicsConfiguration) */ public static BufferedImage createCompatibleBuffer(int width, int height, int transparency, Component c) { if (c==null || c.getGraphicsConfiguration()==null) { return createCompatibleBuffer(width, height, transparency); } return createCompatibleBuffer(width, height, transparency, c.getGraphicsConfiguration()); } /** * @see #createCompatibleBuffer(int, int, int, GraphicsConfiguration) */ public static BufferedImage createCompatibleBuffer(Dimension size, int transparency, GraphicsConfiguration gc) { return createCompatibleBuffer(size.width, size.height, transparency, gc); } /** * Creates a compatible image. * @param width with of image * @param height height of image * @param transparency transparency of image * @param gc graphics configuration * @return a image compatible to the graphics configuration */ public static BufferedImage createCompatibleBuffer(int width, int height, int transparency, GraphicsConfiguration gc) { return gc.createCompatibleImage(width, height, transparency); } /** * Returns true if the image has transparent pixels or the value * of alternativeResult if the transparency could not be determined. * * @param image The image to check for transparent pixels * @return true if the image has transparent pixels. */ public static boolean hasAlpha(Image image, boolean alternativeResult) { if (image instanceof BufferedImage) { BufferedImage bimage = (BufferedImage)image; return bimage.getColorModel().hasAlpha(); } PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); try { pg.grabPixels(); } catch (InterruptedException e) { // ups } if ((pg.getStatus() & ImageObserver.ABORT) != 0) { // return alternativeResult return alternativeResult; } // Get the image's color model ColorModel cm = pg.getColorModel(); return cm.hasAlpha(); } /** * Returns true if the image has transparent pixels or the value * of true if the transparency could not be determined. * * @param image The image to check for transparent pixels * @return true if the image has transparent pixels. */ public static boolean hasAlpha(Image image) { return hasAlpha(image, true); } /* public static void paintRegion(Graphics g, Image image, Rectangle srcReg, int dx, int dy) { if (srcReg != null) { paintRegion(g, image, srcReg.x, srcReg.y, srcReg.width, srcReg.height, dx, dy); } else { Rectangle clip = g.getClipBounds(); // use clip bounds if necessary if (clip==null) { g.drawImage(image, dx, dy, null); } else { // clip!=null // intersect clip with image bounds Rectangle ri = clip.intersection(new Rectangle(0, 0, image.getWidth(null), image.getHeight(null))); if (ri.isEmpty()){ // no painting necessary } else { // paint intersection of clip and image int r = ri.x+ri.width; int b = ri.y+ri.height; g.drawImage(image, dx+ri.x, dy+ri.y, dx+r, dy+b, ri.x, ri.y, r, b, null); } } } } public static void paintRegion(Graphics g, Image image, int sx, int sy, int sw, int sh, int dx, int dy) { Rectangle clip = g.getClipBounds(); // use clip bounds if necessary if (clip==null) { g.drawImage(image, dx, dy, dx+sw, dy+sh, sx, sy,sx+sw,sy+ sh, null); } else { // clip!=null Rectangle target = new Rectangle(0, 0, sw, sh); // move region to 0,0 Rectangle ri = clip.intersection(target); // intersect region and component if (!ri.isEmpty()) { int ix = sx+ri.x; int iy = sy+ri.y; g.drawImage(image, dx+ri.x, dy+ri.y, dx+ri.x+ri.width, dy+ri.y+ri.height, ix, iy, ix+ri.width, iy+ri.height, null); } else { // no painting necessary } } } */ public static Dimension getImageSize(Image image) { int iw = image.getWidth(null); int ih = image.getHeight(null); if (iw<=0||ih<=0) { ImageQuery.waitForDimensions(image); iw = image.getWidth(null); ih = image.getHeight(null); } return new Dimension(iw, ih); } public static void clearRegion(Graphics2D g2, int x, int y, int w, int h) { Composite c = g2.getComposite(); g2.setComposite(AlphaComposite.Clear); g2.fillRect(x, y, w, h); g2.setComposite(c); } /* public static BufferedImage getBufferedImage( Image image ) { if (image instanceof BufferedImage) return (BufferedImage) image; Dimension sz = getImageSize(image); int t = Transparency.OPAQUE; if (image instanceof Transparency) t = ((Transparency)image).getTransparency(); BufferedImage bi = createCompatibleBuffer(sz.width, sz.height, t); Graphics2D g2 = bi.createGraphics(); g2.drawImage(image, 0, 0, null); g2.dispose(); return bi; }*/ }