Source for gnu.java.awt.BitwiseXORComposite

   1: /* BitwiseXORComposite.java -- Composite for emulating old-style XOR.
   2:    Copyright (C) 2003, 2004  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.awt;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Composite;
  43: import java.awt.CompositeContext;
  44: import java.awt.Rectangle;
  45: import java.awt.RenderingHints;
  46: import java.awt.image.ColorModel;
  47: import java.awt.image.DataBuffer;
  48: import java.awt.image.Raster;
  49: import java.awt.image.WritableRaster;
  50: 
  51: 
  52: /**
  53:  * A composite for emulating traditional bitwise XOR of pixel values.
  54:  * Please note that this composite does <i>not</i> implement the Porter-Duff
  55:  * XOR operator, but an exclusive or of overlapping subpixel regions.
  56:  *
  57:  * <p><img src="doc-files/BitwiseXORComposite-1.png" width="545"
  58:  * height="138" alt="A screen shot of BitwiseXORComposite in action"
  59:  * />
  60:  *
  61:  * <p>The above screen shot shows the result of applying six different
  62:  * BitwiseXORComposites. They were constructed with the colors
  63:  * white, blue, black, orange, green, and brown, respectively. Each
  64:  * composite was used to paint a fully white rectangle on top of the
  65:  * blue bar in the background.
  66:  *
  67:  * <p>The purpose of this composite is to support the {@link
  68:  * Graphics#setXORMode(Color)} method in composite-aware graphics
  69:  * implementations. Applications typically would use
  70:  * <code>setXORMode</code> for drawing &#x201c;highlights&#x201d; such
  71:  * as text selections or cursors by inverting colors temporarily and
  72:  * then inverting them back.
  73:  *
  74:  * <p>A concrete <code>Graphics</code> implementation may contain
  75:  * the following code:
  76:  *
  77:  * <p><pre> public void setXORMode(Color xorColor)
  78:  * {
  79:  *   setComposite(new gnu.java.awt.BitwiseXORComposite(xorColor));
  80:  * }
  81:  *
  82:  * public void setPaintMode()
  83:  * {
  84:  *   setComposite(java.awt.AlphaComposite.SrcOver);
  85:  * }</pre>
  86:  *
  87:  * @author Graydon Hoare (graydon@redhat.com)
  88:  * @author Sascha Brawer (brawer@dandelis.ch)
  89:  */
  90: public class BitwiseXORComposite
  91:   implements Composite
  92: {
  93:   /**
  94:    * The color whose RGB value is xor-ed with the values of each
  95:    * pixel.
  96:    */
  97:   protected Color xorColor;
  98: 
  99: 
 100:   /**
 101:    * Constructs a new composite for xor-ing the pixel value.
 102:    *
 103:    * @param xorColor the color whose pixel value will be bitwise
 104:    * xor-ed with the source and destination pixels.
 105:    */
 106:   public BitwiseXORComposite(Color xorColor)
 107:   {
 108:     this.xorColor = xorColor;
 109:   }
 110: 
 111: 
 112:   /**
 113:    * Creates a context object for performing the compositing
 114:    * operation. Several contexts may co-exist for one composite; each
 115:    * context may simultaneously be called from concurrent threads.
 116:    *
 117:    * @param srcColorModel the color model of the source.
 118:    * @param dstColorModel the color model of the destination.
 119:    * @param hints hints for choosing between rendering alternatives.
 120:    */
 121:   public CompositeContext createContext(ColorModel srcColorModel,
 122:                                         ColorModel dstColorModel,
 123:                                         RenderingHints hints)
 124:   {
 125:     if (IntContext.isSupported(srcColorModel, dstColorModel, hints))
 126:       return new IntContext(srcColorModel, xorColor);
 127: 
 128:     return new GeneralContext(srcColorModel, dstColorModel, xorColor);
 129:   }
 130: 
 131: 
 132:   /**
 133:    * A fallback CompositeContext that performs bitwise XOR of pixel
 134:    * values with the pixel value of the specified <code>xorColor</code>.
 135:    *
 136:    * <p>Applying this CompositeContext on a 1024x1024 BufferedImage of
 137:    * <code>TYPE_INT_RGB</code> took 611 ms on a lightly loaded 2.4 GHz
 138:    * Intel Pentium 4 CPU running Sun J2SE 1.4.1_01 on GNU/Linux
 139:    * 2.4.20. The timing is the average of ten runs on the same
 140:    * BufferedImage. Since the measurements were taken with {@link
 141:    * System#currentTimeMillis()}, they are rather inaccurate.
 142:    *
 143:    * @author Graydon Hoare (graydon@redhat.com)
 144:    */
 145:   private static class GeneralContext
 146:     implements CompositeContext
 147:   {
 148:     ColorModel srcColorModel;
 149:     ColorModel dstColorModel;
 150:     Color xorColor;
 151: 
 152:     public GeneralContext(ColorModel srcColorModel,
 153:                           ColorModel dstColorModel,
 154:                           Color xorColor)
 155:     {
 156:       this.srcColorModel = srcColorModel;
 157:       this.dstColorModel = dstColorModel;
 158:       this.xorColor = xorColor;
 159:     }
 160: 
 161: 
 162:     public void compose(Raster src, Raster dstIn, WritableRaster dstOut)
 163:     {
 164:       Rectangle srcRect = src.getBounds();
 165:       Rectangle dstInRect = dstIn.getBounds();
 166:       Rectangle dstOutRect = dstOut.getBounds();
 167: 
 168:       int xp = xorColor.getRGB();
 169:       int w = Math.min(Math.min(srcRect.width, dstOutRect.width),
 170:                        dstInRect.width);
 171:       int h = Math.min(Math.min(srcRect.height, dstOutRect.height),
 172:                        dstInRect.height);
 173: 
 174:       Object srcPix = null, dstPix = null, rpPix = null;
 175: 
 176:       // Re-using the rpPix object saved 1-2% of execution time in
 177:       // the 1024x1024 pixel benchmark.
 178: 
 179:       for (int y = 0; y < h; y++)
 180:       {
 181:         for (int x = 0; x < w; x++)
 182:         {
 183:           srcPix = src.getDataElements(x + srcRect.x, y + srcRect.y, srcPix);
 184:           dstPix = dstIn.getDataElements(x + dstInRect.x, y + dstInRect.y,
 185:                                          dstPix);
 186:           int sp = srcColorModel.getRGB(srcPix);
 187:           int dp = dstColorModel.getRGB(dstPix);
 188:           int rp = sp ^ xp ^ dp;
 189:           dstOut.setDataElements(x + dstOutRect.x, y + dstOutRect.y,
 190:                                  dstColorModel.getDataElements(rp, rpPix));
 191:         }
 192:       }
 193:     }
 194: 
 195: 
 196:     /**
 197:      * Disposes any cached resources. The default implementation does
 198:      * nothing because no resources are cached.
 199:      */
 200:     public void dispose()
 201:     {
 202:     }
 203:   }
 204: 
 205: 
 206:   /**
 207:    * An optimized CompositeContext that performs bitwise XOR of
 208:    * <code>int</code> pixel values with the pixel value of a specified
 209:    * <code>xorColor</code>.  This CompositeContext working only for
 210:    * rasters whose transfer format is {@link DataBuffer#TYPE_INT}.
 211:    *
 212:    * <p>Applying this CompositeContext on a 1024x1024 BufferedImage of
 213:    * <code>TYPE_INT_RGB</code> took 69 ms on a lightly loaded 2.4 GHz
 214:    * Intel Pentium 4 CPU running Sun J2SE 1.4.1_01 on GNU/Linux
 215:    * 2.4.20. The timing is the average of ten runs on the same
 216:    * BufferedImage. Since the measurements were taken with {@link
 217:    * System#currentTimeMillis()}, they are rather inaccurate.
 218:    *
 219:    * @author Sascha Brawer (brawer@dandelis.ch)
 220:    */
 221:   private static class IntContext
 222:     extends GeneralContext
 223:   {
 224:     public IntContext(ColorModel colorModel, Color xorColor)
 225:     {
 226:       super(colorModel, colorModel, xorColor);
 227:     }
 228: 
 229: 
 230:     public void compose(Raster src, Raster dstIn,
 231:                         WritableRaster dstOut)
 232:     {
 233:       int aX, bX, dstX, aY, bY, dstY, width, height;
 234:       int xorPixel;
 235:       int[] srcLine, dstLine;
 236: 
 237:       aX = src.getMinX();
 238:       aY = src.getMinY();
 239:       bX = dstIn.getMinX();
 240:       bY = dstIn.getMinY();
 241:       dstX = dstOut.getMinX();
 242:       dstY = dstOut.getMinY();
 243:       width = Math.min(Math.min(src.getWidth(), dstIn.getWidth()),
 244:                        dstOut.getWidth());
 245:       height = Math.min(Math.min(src.getHeight(), dstIn.getHeight()),
 246:                         dstOut.getHeight());
 247:       if ((width < 1) || (height < 1))
 248:         return;
 249: 
 250:       srcLine = new int[width];
 251:       dstLine = new int[width];
 252: 
 253:       /* We need an int[] array with at least one element here;
 254:        * srcLine is as good as any other.
 255:        */
 256:       srcColorModel.getDataElements(this.xorColor.getRGB(), srcLine);
 257:       xorPixel = srcLine[0];
 258: 
 259:       for (int y = 0; y < height; y++)
 260:       {
 261:         src.getDataElements(aX, y + aY, width, 1, srcLine);
 262:         dstIn.getDataElements(bX, y + bY, width, 1, dstLine);
 263: 
 264:         for (int x = 0; x < width; x++)
 265:           dstLine[x] ^= srcLine[x] ^ xorPixel;
 266: 
 267:         dstOut.setDataElements(dstX, y + dstY, width, 1, dstLine);
 268:       }
 269:     }
 270: 
 271: 
 272:     /**
 273:      * Determines whether an instance of this CompositeContext would
 274:      * be able to process the specified color models.
 275:      */
 276:     public static boolean isSupported(ColorModel srcColorModel,
 277:                                       ColorModel dstColorModel,
 278:                                       RenderingHints hints)
 279:     {
 280:       // FIXME: It would be good if someone could review these checks.
 281:       // They probably need to be more restrictive.
 282: 
 283:       int transferType;
 284: 
 285:       transferType = srcColorModel.getTransferType();
 286:       if (transferType != dstColorModel.getTransferType())
 287:         return false;
 288: 
 289:       if (transferType != DataBuffer.TYPE_INT)
 290:         return false;
 291: 
 292:       return true;
 293:     }
 294:   }
 295: }