| /* BitwiseXORComposite.java -- Composite for emulating old-style XOR. |
| Copyright (C) 2003, 2004 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath 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, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.java.awt; |
| |
| import java.awt.Color; |
| import java.awt.Composite; |
| import java.awt.CompositeContext; |
| import java.awt.Rectangle; |
| import java.awt.RenderingHints; |
| import java.awt.image.ColorModel; |
| import java.awt.image.DataBuffer; |
| import java.awt.image.Raster; |
| import java.awt.image.WritableRaster; |
| |
| |
| /** |
| * A composite for emulating traditional bitwise XOR of pixel values. |
| * Please note that this composite does <i>not</i> implement the Porter-Duff |
| * XOR operator, but an exclusive or of overlapping subpixel regions. |
| * |
| * <p><img src="doc-files/BitwiseXORComposite-1.png" width="545" |
| * height="138" alt="A screen shot of BitwiseXORComposite in action" |
| * /> |
| * |
| * <p>The above screen shot shows the result of applying six different |
| * BitwiseXORComposites. They were constructed with the colors |
| * white, blue, black, orange, green, and brown, respectively. Each |
| * composite was used to paint a fully white rectangle on top of the |
| * blue bar in the background. |
| * |
| * <p>The purpose of this composite is to support the {@link |
| * Graphics#setXORMode(Color)} method in composite-aware graphics |
| * implementations. Applications typically would use |
| * <code>setXORMode</code> for drawing “highlights” such |
| * as text selections or cursors by inverting colors temporarily and |
| * then inverting them back. |
| * |
| * <p>A concrete <code>Graphics</code> implementation may contain |
| * the following code: |
| * |
| * <p><pre> public void setXORMode(Color xorColor) |
| * { |
| * setComposite(new gnu.java.awt.BitwiseXORComposite(xorColor)); |
| * } |
| * |
| * public void setPaintMode() |
| * { |
| * setComposite(java.awt.AlphaComposite.SrcOver); |
| * }</pre> |
| * |
| * @author Graydon Hoare (graydon@redhat.com) |
| * @author Sascha Brawer (brawer@dandelis.ch) |
| */ |
| public class BitwiseXORComposite |
| implements Composite |
| { |
| /** |
| * The color whose RGB value is xor-ed with the values of each |
| * pixel. |
| */ |
| protected Color xorColor; |
| |
| |
| /** |
| * Constructs a new composite for xor-ing the pixel value. |
| * |
| * @param xorColor the color whose pixel value will be bitwise |
| * xor-ed with the source and destination pixels. |
| */ |
| public BitwiseXORComposite(Color xorColor) |
| { |
| this.xorColor = xorColor; |
| } |
| |
| |
| /** |
| * Creates a context object for performing the compositing |
| * operation. Several contexts may co-exist for one composite; each |
| * context may simultaneously be called from concurrent threads. |
| * |
| * @param srcColorModel the color model of the source. |
| * @param dstColorModel the color model of the destination. |
| * @param hints hints for choosing between rendering alternatives. |
| */ |
| public CompositeContext createContext(ColorModel srcColorModel, |
| ColorModel dstColorModel, |
| RenderingHints hints) |
| { |
| if (IntContext.isSupported(srcColorModel, dstColorModel, hints)) |
| return new IntContext(srcColorModel, xorColor); |
| |
| return new GeneralContext(srcColorModel, dstColorModel, xorColor); |
| } |
| |
| |
| /** |
| * A fallback CompositeContext that performs bitwise XOR of pixel |
| * values with the pixel value of the specified <code>xorColor</code>. |
| * |
| * <p>Applying this CompositeContext on a 1024x1024 BufferedImage of |
| * <code>TYPE_INT_RGB</code> took 611 ms on a lightly loaded 2.4 GHz |
| * Intel Pentium 4 CPU running Sun J2SE 1.4.1_01 on GNU/Linux |
| * 2.4.20. The timing is the average of ten runs on the same |
| * BufferedImage. Since the measurements were taken with {@link |
| * System#currentTimeMillis()}, they are rather inaccurate. |
| * |
| * @author Graydon Hoare (graydon@redhat.com) |
| */ |
| private static class GeneralContext |
| implements CompositeContext |
| { |
| ColorModel srcColorModel; |
| ColorModel dstColorModel; |
| Color xorColor; |
| |
| public GeneralContext(ColorModel srcColorModel, |
| ColorModel dstColorModel, |
| Color xorColor) |
| { |
| this.srcColorModel = srcColorModel; |
| this.dstColorModel = dstColorModel; |
| this.xorColor = xorColor; |
| } |
| |
| |
| public void compose(Raster src, Raster dstIn, WritableRaster dstOut) |
| { |
| Rectangle srcRect = src.getBounds(); |
| Rectangle dstInRect = dstIn.getBounds(); |
| Rectangle dstOutRect = dstOut.getBounds(); |
| |
| int xp = xorColor.getRGB(); |
| int w = Math.min(Math.min(srcRect.width, dstOutRect.width), |
| dstInRect.width); |
| int h = Math.min(Math.min(srcRect.height, dstOutRect.height), |
| dstInRect.height); |
| |
| Object srcPix = null, dstPix = null, rpPix = null; |
| |
| // Re-using the rpPix object saved 1-2% of execution time in |
| // the 1024x1024 pixel benchmark. |
| |
| for (int y = 0; y < h; y++) |
| { |
| for (int x = 0; x < w; x++) |
| { |
| srcPix = src.getDataElements(x + srcRect.x, y + srcRect.y, srcPix); |
| dstPix = dstIn.getDataElements(x + dstInRect.x, y + dstInRect.y, |
| dstPix); |
| int sp = srcColorModel.getRGB(srcPix); |
| int dp = dstColorModel.getRGB(dstPix); |
| int rp = sp ^ xp ^ dp; |
| dstOut.setDataElements(x + dstOutRect.x, y + dstOutRect.y, |
| dstColorModel.getDataElements(rp, rpPix)); |
| } |
| } |
| } |
| |
| |
| /** |
| * Disposes any cached resources. The default implementation does |
| * nothing because no resources are cached. |
| */ |
| public void dispose() |
| { |
| } |
| } |
| |
| |
| /** |
| * An optimized CompositeContext that performs bitwise XOR of |
| * <code>int</code> pixel values with the pixel value of a specified |
| * <code>xorColor</code>. This CompositeContext working only for |
| * rasters whose transfer format is {@link DataBuffer#TYPE_INT}. |
| * |
| * <p>Applying this CompositeContext on a 1024x1024 BufferedImage of |
| * <code>TYPE_INT_RGB</code> took 69 ms on a lightly loaded 2.4 GHz |
| * Intel Pentium 4 CPU running Sun J2SE 1.4.1_01 on GNU/Linux |
| * 2.4.20. The timing is the average of ten runs on the same |
| * BufferedImage. Since the measurements were taken with {@link |
| * System#currentTimeMillis()}, they are rather inaccurate. |
| * |
| * @author Sascha Brawer (brawer@dandelis.ch) |
| */ |
| private static class IntContext |
| extends GeneralContext |
| { |
| public IntContext(ColorModel colorModel, Color xorColor) |
| { |
| super(colorModel, colorModel, xorColor); |
| } |
| |
| |
| public void compose(Raster src, Raster dstIn, |
| WritableRaster dstOut) |
| { |
| int aX, bX, dstX, aY, bY, dstY, width, height; |
| int xorPixel; |
| int[] srcLine, dstLine; |
| |
| aX = src.getMinX(); |
| aY = src.getMinY(); |
| bX = dstIn.getMinX(); |
| bY = dstIn.getMinY(); |
| dstX = dstOut.getMinX(); |
| dstY = dstOut.getMinY(); |
| width = Math.min(Math.min(src.getWidth(), dstIn.getWidth()), |
| dstOut.getWidth()); |
| height = Math.min(Math.min(src.getHeight(), dstIn.getHeight()), |
| dstOut.getHeight()); |
| if ((width < 1) || (height < 1)) |
| return; |
| |
| srcLine = new int[width]; |
| dstLine = new int[width]; |
| |
| /* We need an int[] array with at least one element here; |
| * srcLine is as good as any other. |
| */ |
| srcColorModel.getDataElements(this.xorColor.getRGB(), srcLine); |
| xorPixel = srcLine[0]; |
| |
| for (int y = 0; y < height; y++) |
| { |
| src.getDataElements(aX, y + aY, width, 1, srcLine); |
| dstIn.getDataElements(bX, y + bY, width, 1, dstLine); |
| |
| for (int x = 0; x < width; x++) |
| dstLine[x] ^= srcLine[x] ^ xorPixel; |
| |
| dstOut.setDataElements(dstX, y + dstY, width, 1, dstLine); |
| } |
| } |
| |
| |
| /** |
| * Determines whether an instance of this CompositeContext would |
| * be able to process the specified color models. |
| */ |
| public static boolean isSupported(ColorModel srcColorModel, |
| ColorModel dstColorModel, |
| RenderingHints hints) |
| { |
| // FIXME: It would be good if someone could review these checks. |
| // They probably need to be more restrictive. |
| |
| int transferType; |
| |
| transferType = srcColorModel.getTransferType(); |
| if (transferType != dstColorModel.getTransferType()) |
| return false; |
| |
| if (transferType != DataBuffer.TYPE_INT) |
| return false; |
| |
| return true; |
| } |
| } |
| } |