Source for gnu.java.awt.peer.gtk.VolatileImageGraphics

   1: /* VolatileImageGraphics.java
   2:    Copyright (C) 2006 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.peer.gtk;
  40: 
  41: import java.awt.AlphaComposite;
  42: import java.awt.Color;
  43: import java.awt.Composite;
  44: import java.awt.Graphics;
  45: import java.awt.Graphics2D;
  46: import java.awt.GraphicsConfiguration;
  47: import java.awt.Image;
  48: import java.awt.Point;
  49: import java.awt.Shape;
  50: import java.awt.Toolkit;
  51: import java.awt.font.GlyphVector;
  52: import java.awt.geom.AffineTransform;
  53: import java.awt.geom.Point2D;
  54: import java.awt.geom.Rectangle2D;
  55: import java.awt.image.BufferedImage;
  56: import java.awt.image.ColorModel;
  57: import java.awt.image.ImageObserver;
  58: import java.awt.image.ImageProducer;
  59: import java.awt.image.Raster;
  60: import java.awt.image.WritableRaster;
  61: import java.util.Hashtable;
  62: 
  63: public class VolatileImageGraphics extends ComponentGraphics
  64: {
  65:   private GtkVolatileImage owner;
  66:   private BufferedImage buffer;
  67: 
  68:   public VolatileImageGraphics(GtkVolatileImage img)
  69:   {
  70:     this.owner = img;
  71:     cairo_t = initFromVolatile( owner.nativePointer );
  72:     setup( cairo_t );
  73:   }
  74: 
  75:   private VolatileImageGraphics(VolatileImageGraphics copy)
  76:   {
  77:     this.owner = copy.owner;
  78:     cairo_t = initFromVolatile(owner.nativePointer);
  79:     copy( copy, cairo_t );
  80:   }
  81: 
  82:   public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy)
  83:   {
  84:     owner.copyArea(x, y, width, height, dx, dy);
  85:   }
  86: 
  87:   public GraphicsConfiguration getDeviceConfiguration()
  88:   {
  89:     GraphicsConfiguration conf;
  90:     if (owner.component != null)
  91:       {
  92:         conf = owner.component.getGraphicsConfiguration();
  93:       }
  94:     else
  95:       {
  96:         return java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
  97:           .getDefaultScreenDevice().getDefaultConfiguration();
  98:       }
  99:     return conf;
 100:   }
 101: 
 102:   public Graphics create()
 103:   {
 104:     return new VolatileImageGraphics( this );
 105:   }
 106: 
 107:   public void draw(Shape s)
 108:   {
 109:     if (comp == null || comp instanceof AlphaComposite)
 110:       super.draw(s);
 111: 
 112:     // Custom composite
 113:     else
 114:       {
 115:         // Draw operation to temporary buffer
 116:         createBuffer();
 117: 
 118:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 119:         g2d.setColor(this.getColor());
 120:         g2d.setStroke(this.getStroke());
 121:         g2d.draw(s);
 122: 
 123:         drawComposite(s.getBounds2D(), null);
 124:       }
 125:   }
 126: 
 127:   public void fill(Shape s)
 128:   {
 129:     if (comp == null || comp instanceof AlphaComposite)
 130:       super.fill(s);
 131: 
 132:     // Custom composite
 133:     else
 134:       {
 135:         // Draw operation to temporary buffer
 136:         createBuffer();
 137: 
 138:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 139:         g2d.setPaint(this.getPaint());
 140:         g2d.setColor(this.getColor());
 141:         g2d.fill(s);
 142: 
 143:         drawComposite(s.getBounds2D(), null);
 144:       }
 145:   }
 146: 
 147:   public void drawGlyphVector(GlyphVector gv, float x, float y)
 148:   {
 149:     if (comp == null || comp instanceof AlphaComposite)
 150:       super.drawGlyphVector(gv, x, y);
 151: 
 152:     // Custom composite
 153:     else
 154:       {
 155:         // Draw operation to temporary buffer
 156:         createBuffer();
 157: 
 158:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 159: 
 160:         g2d.setPaint(this.getPaint());
 161:         g2d.setColor(this.getColor());
 162:         g2d.drawGlyphVector(gv, x, y);
 163: 
 164:         Rectangle2D bounds = gv.getLogicalBounds();
 165:         bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(),
 166:                                         bounds.getWidth(), bounds.getHeight());
 167:         drawComposite(bounds, null);
 168:       }
 169:   }
 170: 
 171:   protected boolean drawImage(Image img, AffineTransform xform,
 172:                               Color bgcolor, ImageObserver obs)
 173:     {
 174:       if (comp == null || comp instanceof AlphaComposite)
 175:         return super.drawImage(img, xform, bgcolor, obs);
 176: 
 177:       // Custom composite
 178:       else
 179:         {
 180:           // Get buffered image of source
 181:           if( !(img instanceof BufferedImage) )
 182:             {
 183:               ImageProducer source = img.getSource();
 184:               if (source == null)
 185:                 return false;
 186:               img = Toolkit.getDefaultToolkit().createImage(source);
 187:             }
 188:           BufferedImage bImg = (BufferedImage) img;
 189: 
 190:           // Find dimensions of translation
 191:           Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY());
 192:           Point2D pt = new Point2D.Double(bImg.getWidth(), bImg.getHeight());
 193:           if (xform != null)
 194:             {
 195:               origin = xform.transform(origin, origin);
 196:               pt = xform.transform(pt, pt);
 197:             }
 198: 
 199:           // Create buffer and draw image
 200:           createBuffer();
 201: 
 202:           Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 203:           g2d.setRenderingHints(this.getRenderingHints());
 204:           g2d.drawImage(img, xform, obs);
 205: 
 206:           // Perform compositing from buffer to screen
 207:           return drawComposite(new Rectangle2D.Double((int)origin.getX(),
 208:                                                       (int)origin.getY(),
 209:                                                       (int)pt.getX(),
 210:                                                       (int)pt.getY()),
 211:                                obs);
 212:         }
 213:     }
 214: 
 215:   public boolean drawImage(Image img, int x, int y, ImageObserver observer)
 216:   {
 217:     if (img instanceof GtkVolatileImage
 218:         && (comp == null || comp instanceof AlphaComposite))
 219:       {
 220:         owner.drawVolatile( ((GtkVolatileImage)img).nativePointer,
 221:                             x, y,
 222:                             ((GtkVolatileImage)img).width,
 223:                             ((GtkVolatileImage)img).height );
 224:         return true;
 225:       }
 226:     return super.drawImage( img, x, y, observer );
 227:   }
 228: 
 229:   public boolean drawImage(Image img, int x, int y, int width, int height,
 230:                            ImageObserver observer)
 231:   {
 232:     if ((img instanceof GtkVolatileImage)
 233:         && (comp == null || comp instanceof AlphaComposite))
 234:       {
 235:         owner.drawVolatile( ((GtkVolatileImage)img).nativePointer,
 236:                             x, y, width, height );
 237:         return true;
 238:       }
 239:     return super.drawImage( img, x, y, width, height, observer );
 240:   }
 241: 
 242:   protected Rectangle2D getRealBounds()
 243:   {
 244:     return new Rectangle2D.Double(0, 0, owner.width, owner.height);
 245:   }
 246: 
 247:   private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
 248:   {
 249:     // Clip source to visible areas that need updating
 250:     Rectangle2D clip = this.getClipBounds();
 251:     Rectangle2D.intersect(bounds, clip, bounds);
 252: 
 253:     BufferedImage buffer2 = buffer;
 254:     if (!bounds.equals(buffer2.getRaster().getBounds()))
 255:       buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(),
 256:                                     (int)bounds.getWidth(),
 257:                                     (int)bounds.getHeight());
 258: 
 259:     // Get current on-screen pixels (destination) and clip to bounds
 260:     BufferedImage current = owner.getSnapshot();
 261: 
 262:     double[] points = new double[] {bounds.getX(), bounds.getY(),
 263:                                     bounds.getMaxX(), bounds.getMaxY()};
 264:     transform.transform(points, 0, points, 0, 2);
 265: 
 266:     Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1],
 267:                                                       points[2] - points[0],
 268:                                                       points[3] - points[1]);
 269:     Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds);
 270: 
 271:     current = current.getSubimage((int)deviceBounds.getX(),
 272:                                   (int)deviceBounds.getY(),
 273:                                   (int)deviceBounds.getWidth(),
 274:                                   (int)deviceBounds.getHeight());
 275: 
 276:     // Perform actual composite operation
 277:     compCtx.compose(buffer2.getRaster(), current.getRaster(),
 278:                     buffer2.getRaster());
 279: 
 280:     // This MUST call directly into the "action" method in CairoGraphics2D,
 281:     // not one of the wrappers, to ensure that the composite isn't processed
 282:     // more than once!
 283:     Composite oldComp = comp;           // so that ComponentGraphics doesn't
 284:     comp = null;                        // process the composite again
 285:     boolean rv = super.drawImage(buffer2,
 286:                            AffineTransform.getTranslateInstance(bounds.getX(),
 287:                                                                 bounds.getY()),
 288:                            null, null);
 289:     comp = oldComp;
 290: 
 291:     return rv;
 292:   }
 293: 
 294:   private void createBuffer()
 295:   {
 296:     if (buffer == null)
 297:       {
 298:         WritableRaster rst;
 299:         rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(owner.width,
 300:                                                                   owner.height),
 301:                                           new Point(0,0));
 302: 
 303:         buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst,
 304:                                    GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(),
 305:                                    new Hashtable());
 306:       }
 307:     else
 308:       {
 309:         Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
 310: 
 311:         g2d.setBackground(new Color(0,0,0,0));
 312:         g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
 313:       }
 314:   }
 315: 
 316:   protected ColorModel getNativeCM()
 317:   {
 318:     // We should really return GtkVolatileImage.gdkColorModel ,
 319:     // but CairoGraphics2D doesn't handle alpha premultiplication properly (see
 320:     // the fixme in drawImage) so we use the naive Cairo model instead to trick
 321:     // the compositing context.
 322:     // Because getNativeCM() == getBufferCM() for this peer, it doesn't break.
 323:     return CairoSurface.cairoCM_pre;
 324:   }
 325: }