| /* 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; | |
| } | |
| } | |
| } |