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 */ CairoSurfaceGraphics(CairoSurface surface)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 */ CairoSurfaceGraphics(CairoSurfaceGraphics copyFrom)86 private CairoSurfaceGraphics(CairoSurfaceGraphics copyFrom) 87 { 88 surface = copyFrom.surface; 89 cairo_t = surface.newCairoContext(); 90 copy( copyFrom, cairo_t ); 91 } 92 create()93 public Graphics create() 94 { 95 return new CairoSurfaceGraphics(this); 96 } 97 getDeviceConfiguration()98 public GraphicsConfiguration getDeviceConfiguration() 99 { 100 return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); 101 } 102 getRealBounds()103 protected Rectangle2D getRealBounds() 104 { 105 return new Rectangle2D.Double(0.0, 0.0, surface.width, surface.height); 106 } 107 copyAreaImpl(int x, int y, int width, int height, int dx, int dy)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 */ draw(Shape s)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 fill(Shape s)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 drawRenderedImage(RenderedImage image, AffineTransform xform)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 drawImage(Image img, AffineTransform xform, Color bgcolor, ImageObserver obs)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 drawGlyphVector(GlyphVector gv, float x, float y)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 drawComposite(Rectangle2D bounds, ImageObserver observer)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 createBuffer()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 getNativeCM()346 protected ColorModel getNativeCM() 347 { 348 return CairoSurface.cairoCM_pre; 349 } 350 getBufferCM()351 protected ColorModel getBufferCM() 352 { 353 return CairoSurface.cairoColorModel; 354 } 355 } 356