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

   1: /* CairoSurfaceGraphics.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.GraphicsEnvironment;
  48: import java.awt.Image;
  49: import java.awt.Rectangle;
  50: import java.awt.Shape;
  51: import java.awt.Toolkit;
  52: import java.awt.font.GlyphVector;
  53: import java.awt.geom.AffineTransform;
  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.RenderedImage;
  60: import java.util.Hashtable;
  61: 
  62: /**
  63:  * Implementation of Graphics2D on a Cairo surface.
  64:  */
  65: public class CairoSurfaceGraphics extends CairoGraphics2D
  66: {
  67:   protected CairoSurface surface;
  68:   private BufferedImage buffer;
  69:   private long cairo_t;
  70: 
  71:   /**
  72:    * Create a graphics context from a cairo surface
  73:    */
  74:   public CairoSurfaceGraphics(CairoSurface surface)
  75:   {
  76:     this.surface = surface;
  77:     cairo_t = surface.newCairoContext();
  78:     setup( cairo_t );
  79:     setClip(0, 0, surface.width, surface.height);
  80:   }
  81: 
  82:   /**
  83:    * Creates another context from a surface.
  84:    * Used by create().
  85:    */
  86:   private CairoSurfaceGraphics(CairoSurfaceGraphics copyFrom)
  87:   {
  88:     surface = copyFrom.surface;
  89:     cairo_t = surface.newCairoContext();
  90:     copy( copyFrom, cairo_t );
  91:   }
  92: 
  93:   public Graphics create()
  94:   {
  95:     return new CairoSurfaceGraphics(this);
  96:   }
  97: 
  98:   public GraphicsConfiguration getDeviceConfiguration()
  99:   {
 100:     return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
 101:   }
 102: 
 103:   protected Rectangle2D getRealBounds()
 104:   {
 105:     return new Rectangle2D.Double(0.0, 0.0, surface.width, surface.height);
 106:   }
 107: 
 108:   public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy)
 109:   {
 110:     surface.copyAreaNative(x, y, width, height, dx, dy, surface.width);
 111:   }
 112: 
 113:   /**
 114:    * Overloaded methods that do actual drawing need to account for custom
 115:    * composites
 116:    */
 117:   public void draw(Shape s)
 118:   {
 119:     if (!surface.sharedBuffer)
 120:       surface.syncJavaToNative(surface.surfacePointer, surface.getData());
 121: 
 122:     // Find total bounds of shape
 123:     Rectangle r = findStrokedBounds(s);
 124:     if (shiftDrawCalls)
 125:       {
 126:         r.width++;
 127:         r.height++;
 128:       }
 129: 
 130:     // Do the drawing
 131:     if (comp == null || comp instanceof AlphaComposite)
 132:       super.draw(s);
 133: 
 134:     else
 135:       {
 136:         createBuffer();
 137: 
 138:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 139:         g2d.setStroke(this.getStroke());
 140:         g2d.setColor(this.getColor());
 141:         g2d.setTransform(transform);
 142:         g2d.draw(s);
 143: 
 144:         drawComposite(r.getBounds2D(), null);
 145:       }
 146: 
 147:     if (!surface.sharedBuffer)
 148:       surface.syncNativeToJava(surface.surfacePointer, surface.getData());
 149:   }
 150: 
 151:   public void fill(Shape s)
 152:   {
 153:     if (!surface.sharedBuffer)
 154:       surface.syncJavaToNative(surface.surfacePointer, surface.getData());
 155: 
 156:     if (comp == null || comp instanceof AlphaComposite)
 157:       super.fill(s);
 158: 
 159:     else
 160:       {
 161:         createBuffer();
 162: 
 163:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 164:         g2d.setPaint(this.getPaint());
 165:         g2d.setColor(this.getColor());
 166:         g2d.setTransform(transform);
 167:         g2d.fill(s);
 168: 
 169:         drawComposite(s.getBounds2D(), null);
 170:       }
 171: 
 172:     if (!surface.sharedBuffer)
 173:       surface.syncNativeToJava(surface.surfacePointer, surface.getData());
 174:   }
 175: 
 176:   public void drawRenderedImage(RenderedImage image, AffineTransform xform)
 177:   {
 178:     if (!surface.sharedBuffer)
 179:       surface.syncJavaToNative(surface.surfacePointer, surface.getData());
 180: 
 181:     if (comp == null || comp instanceof AlphaComposite)
 182:       super.drawRenderedImage(image, xform);
 183: 
 184:     else
 185:       {
 186:         createBuffer();
 187: 
 188:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 189:         g2d.setRenderingHints(this.getRenderingHints());
 190:         g2d.setTransform(transform);
 191:         g2d.drawRenderedImage(image, xform);
 192: 
 193:         drawComposite(buffer.getRaster().getBounds(), null);
 194:       }
 195: 
 196:     if (!surface.sharedBuffer)
 197:       surface.syncNativeToJava(surface.surfacePointer, surface.getData());
 198:   }
 199: 
 200:   protected boolean drawImage(Image img, AffineTransform xform,
 201:                               Color bgcolor, ImageObserver obs)
 202:   {
 203:     if (!surface.sharedBuffer)
 204:       surface.syncJavaToNative(surface.surfacePointer, surface.getData());
 205: 
 206:     boolean ret;
 207:     if (comp == null || comp instanceof AlphaComposite)
 208:       ret = super.drawImage(img, xform, bgcolor, obs);
 209: 
 210:     else
 211:       {
 212:         // Get buffered image of source
 213:         if( !(img instanceof BufferedImage) )
 214:           {
 215:             ImageProducer source = img.getSource();
 216:             if (source == null)
 217:               return false;
 218:             img = Toolkit.getDefaultToolkit().createImage(source);
 219:           }
 220:         BufferedImage bImg = (BufferedImage) img;
 221: 
 222:         // Find translated bounds
 223:         Rectangle2D bounds = new Rectangle(bImg.getMinX(), bImg.getMinY(),
 224:                                            bImg.getWidth(), bImg.getHeight());
 225:         if (xform != null)
 226:           bounds = getTransformedBounds(bounds, xform);
 227: 
 228:         // Create buffer and draw image
 229:         createBuffer();
 230: 
 231:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 232:         g2d.setRenderingHints(this.getRenderingHints());
 233:         g2d.drawImage(img, xform, obs);
 234: 
 235:         // Perform compositing
 236:         ret = drawComposite(bounds, obs);
 237:       }
 238: 
 239:     if (!surface.sharedBuffer)
 240:       surface.syncNativeToJava(surface.surfacePointer, surface.getData());
 241: 
 242:     return ret;
 243:   }
 244: 
 245:   public void drawGlyphVector(GlyphVector gv, float x, float y)
 246:   {
 247:     if (!surface.sharedBuffer)
 248:       surface.syncJavaToNative(surface.surfacePointer, surface.getData());
 249: 
 250:     if (comp == null || comp instanceof AlphaComposite)
 251:       super.drawGlyphVector(gv, x, y);
 252: 
 253:     else
 254:       {
 255:         createBuffer();
 256: 
 257:         Graphics2D g2d = (Graphics2D)buffer.getGraphics();
 258:         g2d.setPaint(this.getPaint());
 259:         g2d.setStroke(this.getStroke());
 260:         g2d.drawGlyphVector(gv, x, y);
 261: 
 262:         Rectangle2D bounds = gv.getLogicalBounds();
 263:         bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(),
 264:                                         bounds.getWidth(), bounds.getHeight());
 265:         drawComposite(bounds, null);
 266:       }
 267: 
 268:     if (!surface.sharedBuffer)
 269:       surface.syncNativeToJava(surface.surfacePointer, surface.getData());
 270:   }
 271: 
 272:   private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
 273:   {
 274:     // Find bounds in device space
 275:     bounds = getTransformedBounds(bounds, transform);
 276: 
 277:     // Clip bounds by the stored clip, and by the internal buffer
 278:     Rectangle2D devClip = this.getClipInDevSpace();
 279:     Rectangle2D.intersect(bounds, devClip, bounds);
 280:     devClip = new Rectangle(buffer.getMinX(), buffer.getMinY(),
 281:                             buffer.getWidth(), buffer.getHeight());
 282:     Rectangle2D.intersect(bounds, devClip, bounds);
 283: 
 284:     // Round bounds as needed, but be careful in our rounding
 285:     // (otherwise it may leave unpainted stripes)
 286:     double x = bounds.getX();
 287:     double y = bounds.getY();
 288:     double maxX = x + bounds.getWidth();
 289:     double maxY = y + bounds.getHeight();
 290:     x = Math.round(x);
 291:     y = Math.round(y);
 292:     bounds.setRect(x, y, Math.round(maxX - x), Math.round(maxY - y));
 293: 
 294:     // Find subimage of internal buffer for updating
 295:     BufferedImage buffer2 = buffer;
 296:     if (!bounds.equals(buffer2.getRaster().getBounds()))
 297:       buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(),
 298:                                     (int)bounds.getWidth(),
 299:                                     (int)bounds.getHeight());
 300: 
 301:     // Find subimage of main image for updating
 302:     BufferedImage current = CairoSurface.getBufferedImage(surface);
 303:     current = current.getSubimage((int)bounds.getX(), (int)bounds.getY(),
 304:                                   (int)bounds.getWidth(),
 305:                                   (int)bounds.getHeight());
 306: 
 307:     // Perform actual composite operation
 308:     compCtx.compose(buffer2.getRaster(), current.getRaster(),
 309:                     buffer2.getRaster());
 310: 
 311:     // Set cairo's composite to direct SRC, since we've already done our own
 312:     // compositing
 313:     Composite oldcomp = comp;
 314:     setComposite(AlphaComposite.Src);
 315: 
 316:     // This MUST call directly into the "action" method in CairoGraphics2D,
 317:     // not one of the wrappers, to ensure that the composite isn't processed
 318:     // more than once!
 319:     boolean rv = super.drawImage(buffer2,
 320:                                  AffineTransform.getTranslateInstance(bounds.getX(),
 321:                                                                       bounds.getY()),
 322:                                  null, null);
 323:     setComposite(oldcomp);
 324:     updateColor();
 325:     return rv;
 326:   }
 327: 
 328:   private void createBuffer()
 329:   {
 330:     if (buffer == null)
 331:       {
 332:         buffer = new BufferedImage(getBufferCM(),
 333:                                    surface.createCompatibleWritableRaster(),
 334:                                    getBufferCM().isAlphaPremultiplied(),
 335:                                    new Hashtable());
 336:       }
 337:     else
 338:       {
 339:         Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
 340: 
 341:         g2d.setBackground(new Color(0,0,0,0));
 342:         g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
 343:       }
 344:   }
 345: 
 346:   protected ColorModel getNativeCM()
 347:   {
 348:     return CairoSurface.cairoCM_pre;
 349:   }
 350: 
 351:   protected ColorModel getBufferCM()
 352:   {
 353:     return CairoSurface.cairoColorModel;
 354:   }
 355: }