OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [java/] [awt/] [java2d/] [AbstractGraphics2D.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* AbstractGraphics2D.java -- Abstract Graphics2D implementation
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
package gnu.java.awt.java2d;
39
 
40
import gnu.java.util.LRUCache;
41
 
42
import java.awt.AWTError;
43
import java.awt.AlphaComposite;
44
import java.awt.AWTPermission;
45
import java.awt.BasicStroke;
46
import java.awt.Color;
47
import java.awt.Composite;
48
import java.awt.CompositeContext;
49
import java.awt.Dimension;
50
import java.awt.Font;
51
import java.awt.FontMetrics;
52
import java.awt.Graphics;
53
import java.awt.Graphics2D;
54
import java.awt.Image;
55
import java.awt.Paint;
56
import java.awt.PaintContext;
57
import java.awt.Point;
58
import java.awt.Polygon;
59
import java.awt.Rectangle;
60
import java.awt.RenderingHints;
61
import java.awt.Shape;
62
import java.awt.Stroke;
63
import java.awt.Toolkit;
64
import java.awt.RenderingHints.Key;
65
import java.awt.font.FontRenderContext;
66
import java.awt.font.GlyphVector;
67
import java.awt.geom.AffineTransform;
68
import java.awt.geom.Arc2D;
69
import java.awt.geom.Area;
70
import java.awt.geom.Ellipse2D;
71
import java.awt.geom.GeneralPath;
72
import java.awt.geom.Line2D;
73
import java.awt.geom.NoninvertibleTransformException;
74
import java.awt.geom.RoundRectangle2D;
75
import java.awt.image.BufferedImage;
76
import java.awt.image.BufferedImageOp;
77
import java.awt.image.ColorModel;
78
import java.awt.image.DataBuffer;
79
import java.awt.image.FilteredImageSource;
80
import java.awt.image.ImageObserver;
81
import java.awt.image.ImageProducer;
82
import java.awt.image.Raster;
83
import java.awt.image.RenderedImage;
84
import java.awt.image.ReplicateScaleFilter;
85
import java.awt.image.SampleModel;
86
import java.awt.image.WritableRaster;
87
import java.awt.image.renderable.RenderableImage;
88
import java.text.AttributedCharacterIterator;
89
import java.util.Collections;
90
import java.util.HashMap;
91
import java.util.LinkedList;
92
import java.util.Map;
93
import java.util.WeakHashMap;
94
 
95
/**
96
 * This is a 100% Java implementation of the Java2D rendering pipeline. It is
97
 * meant as a base class for Graphics2D implementations.
98
 *
99
 * <h2>Backend interface</h2>
100
 * <p>
101
 * The backend must at the very least provide a Raster which the the rendering
102
 * pipeline can paint into. This must be implemented in
103
 * {@link #getDestinationRaster()}. For some backends that might be enough, like
104
 * when the target surface can be directly access via the raster (like in
105
 * BufferedImages). Other targets need some way to synchronize the raster with
106
 * the surface, which can be achieved by implementing the
107
 * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets
108
 * called after a chunk of data got painted into the raster.
109
 * </p>
110
 * <p>Alternativly the backend can provide a method for filling Shapes by
111
 * overriding the protected method fillShape(). This can be accomplished
112
 * by a polygon filling function of the backend. Keep in mind though that
113
 * Shapes can be quite complex (i.e. non-convex and containing holes, etc)
114
 * which is not supported by all polygon fillers. Also it must be noted
115
 * that fillShape() is expected to handle painting and compositing as well as
116
 * clipping and transformation. If your backend can't support this natively,
117
 * then you can fallback to the implementation in this class. You'll need
118
 * to provide a writable Raster then, see above.</p>
119
 * <p>Another alternative is to implement fillScanline() which only requires
120
 * the backend to be able to draw horizontal lines in device space,
121
 * which is usually very cheap.
122
 * The implementation should still handle painting and compositing,
123
 * but no more clipping and transformation is required by the backend.</p>
124
 * <p>The backend is free to provide implementations for the various raw*
125
 * methods for optimized AWT 1.1 style painting of some primitives. This should
126
 * accelerate painting of Swing greatly. When doing so, the backend must also
127
 * keep track of the clip and translation, probably by overriding
128
 * some clip and translate methods. Don't forget to message super in such a
129
 * case.</p>
130
 *
131
 * <h2>Acceleration options</h2>
132
 * <p>
133
 * The fact that it is
134
 * pure Java makes it a little slow. However, there are several ways of
135
 * accelerating the rendering pipeline:
136
 * <ol>
137
 * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em>
138
 *   The most important methods from the {@link java.awt.Graphics} class
139
 *   have a corresponding <code>raw*</code> method, which get called when
140
 *   several optimization conditions are fullfilled. These conditions are
141
 *   described below. Subclasses can override these methods and delegate
142
 *   it directly to a native backend.</li>
143
 * <li><em>Native PaintContexts and CompositeContext.</em> The implementations
144
 *   for the 3 PaintContexts and AlphaCompositeContext can be accelerated
145
 *   using native code. These have proved to two of the most performance
146
 *   critical points in the rendering pipeline and cannot really be done quickly
147
 *   in plain Java because they involve lots of shuffling around with large
148
 *   arrays. In fact, you really would want to let the graphics card to the
149
 *   work, they are made for this.</li>
150
 * <li>Provide an accelerated implementation for fillShape(). For instance,
151
 * OpenGL can fill shapes very efficiently. There are some considerations
152
 * to be made though, see above for details.</li>
153
 * </ol>
154
 * </p>
155
 *
156
 * @author Roman Kennke (kennke@aicas.com)
157
 */
158
public abstract class AbstractGraphics2D
159
  extends Graphics2D
160
  implements Cloneable, Pixelizer
161
{
162
  /**
163
   * Caches scaled versions of an image.
164
   *
165
   * @see #drawImage(Image, int, int, int, int, ImageObserver)
166
   */
167
  protected static final WeakHashMap<Image, HashMap<Dimension,Image>> imageCache =
168
    new WeakHashMap<Image, HashMap<Dimension, Image>>();
169
 
170
  /**
171
   * Wether we use anti aliasing for rendering text by default or not.
172
   */
173
  private static final boolean DEFAULT_TEXT_AA =
174
    Boolean.getBoolean("gnu.java2d.default_text_aa");
175
 
176
  /**
177
   * The default font to use on the graphics object.
178
   */
179
  private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12);
180
 
181
  /**
182
   * The size of the LRU cache used for caching GlyphVectors.
183
   */
184
  private static final int GV_CACHE_SIZE = 50;
185
 
186
  /**
187
   * Caches certain shapes to avoid massive creation of such Shapes in
188
   * the various draw* and fill* methods.
189
   */
190
  private static final ShapeCache shapeCache = new ShapeCache();
191
 
192
  /**
193
   * A pool of scanline converters. It is important to reuse scanline
194
   * converters because they keep their datastructures in place. We pool them
195
   * for use in multiple threads.
196
   */
197
  private static final LinkedList<ScanlineConverter> scanlineConverters =
198
    new LinkedList<ScanlineConverter>();
199
 
200
  /**
201
   * Caches glyph vectors for better drawing performance.
202
   */
203
  private static final Map<TextCacheKey,GlyphVector> gvCache =
204
    Collections.synchronizedMap(new LRUCache<TextCacheKey,GlyphVector>(GV_CACHE_SIZE));
205
 
206
  /**
207
   * This key is used to search in the gvCache without allocating a new
208
   * key each time.
209
   */
210
  private static final TextCacheKey searchTextKey = new TextCacheKey();
211
 
212
  /**
213
   * The transformation for this Graphics2D instance
214
   */
215
  protected AffineTransform transform;
216
 
217
  /**
218
   * The foreground.
219
   */
220
  private Paint paint;
221
 
222
  /**
223
   * The paint context during rendering.
224
   */
225
  private PaintContext paintContext = null;
226
 
227
  /**
228
   * The background.
229
   */
230
  private Color background = Color.WHITE;
231
 
232
  /**
233
   * Foreground color, as set by setColor.
234
   */
235
  private Color foreground = Color.BLACK;
236
  private boolean isForegroundColorNull = true;
237
 
238
  /**
239
   * The current font.
240
   */
241
  private Font font;
242
 
243
  /**
244
   * The current composite setting.
245
   */
246
  private Composite composite;
247
 
248
  /**
249
   * The current stroke setting.
250
   */
251
  private Stroke stroke;
252
 
253
  /**
254
   * The current clip. This clip is in user coordinate space.
255
   */
256
  private Shape clip;
257
 
258
  /**
259
   * The rendering hints.
260
   */
261
  private RenderingHints renderingHints;
262
 
263
  /**
264
   * The raster of the destination surface. This is where the painting is
265
   * performed.
266
   */
267
  private WritableRaster destinationRaster;
268
 
269
  /**
270
   * Indicates if certain graphics primitives can be rendered in an optimized
271
   * fashion. This will be the case if the following conditions are met:
272
   * - The transform may only be a translation, no rotation, shearing or
273
   *   scaling.
274
   * - The paint must be a solid color.
275
   * - The composite must be an AlphaComposite.SrcOver.
276
   * - The clip must be a Rectangle.
277
   * - The stroke must be a plain BasicStroke().
278
   *
279
   * These conditions represent the standard settings of a new
280
   * AbstractGraphics2D object and will be the most commonly used setting
281
   * in Swing rendering and should therefore be optimized as much as possible.
282
   */
283
  private boolean isOptimized = true;
284
 
285
  private static final BasicStroke STANDARD_STROKE = new BasicStroke();
286
 
287
  private static final HashMap<Key, Object> STANDARD_HINTS;
288
  static
289
    {
290
 
291
      HashMap<Key, Object> hints = new HashMap<Key, Object>();
292
      hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
293
                RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
294
      hints.put(RenderingHints.KEY_ANTIALIASING,
295
                RenderingHints.VALUE_ANTIALIAS_DEFAULT);
296
 
297
      STANDARD_HINTS = hints;
298
    }
299
 
300
  /**
301
   * Creates a new AbstractGraphics2D instance.
302
   */
303
  protected AbstractGraphics2D()
304
  {
305
    transform = new AffineTransform();
306
    background = Color.WHITE;
307
    composite = AlphaComposite.SrcOver;
308
    stroke = STANDARD_STROKE;
309
    renderingHints = new RenderingHints(STANDARD_HINTS);
310
  }
311
 
312
  /**
313
   * Draws the specified shape. The shape is passed through the current stroke
314
   * and is then forwarded to {@link #fillShape}.
315
   *
316
   * @param shape the shape to draw
317
   */
318
  public void draw(Shape shape)
319
  {
320
    // Stroke the shape.
321
    Shape strokedShape = stroke.createStrokedShape(shape);
322
    // Fill the stroked shape.
323
    fillShape(strokedShape, false);
324
  }
325
 
326
 
327
  /**
328
   * Draws the specified image and apply the transform for image space ->
329
   * user space conversion.
330
   *
331
   * This method is implemented to special case RenderableImages and
332
   * RenderedImages and delegate to
333
   * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
334
   * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
335
   * Other image types are not yet handled.
336
   *
337
   * @param image the image to be rendered
338
   * @param xform the transform from image space to user space
339
   * @param obs the image observer to be notified
340
   */
341
  public boolean drawImage(Image image, AffineTransform xform,
342
                           ImageObserver obs)
343
  {
344
    Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs),
345
                                             image.getHeight(obs));
346
    return drawImageImpl(image, xform, obs, areaOfInterest);
347
  }
348
 
349
  /**
350
   * Draws the specified image and apply the transform for image space ->
351
   * user space conversion. This method only draw the part of the image
352
   * specified by <code>areaOfInterest</code>.
353
   *
354
   * This method is implemented to special case RenderableImages and
355
   * RenderedImages and delegate to
356
   * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
357
   * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
358
   * Other image types are not yet handled.
359
   *
360
   * @param image the image to be rendered
361
   * @param xform the transform from image space to user space
362
   * @param obs the image observer to be notified
363
   * @param areaOfInterest the area in image space that is rendered
364
   */
365
  private boolean drawImageImpl(Image image, AffineTransform xform,
366
                             ImageObserver obs, Rectangle areaOfInterest)
367
  {
368
    boolean ret;
369
    if (image == null)
370
      {
371
        ret = true;
372
      }
373
    else if (image instanceof RenderedImage)
374
      {
375
        // FIXME: Handle the ImageObserver.
376
        drawRenderedImageImpl((RenderedImage) image, xform, areaOfInterest);
377
        ret = true;
378
      }
379
    else if (image instanceof RenderableImage)
380
      {
381
        // FIXME: Handle the ImageObserver.
382
        drawRenderableImageImpl((RenderableImage) image, xform, areaOfInterest);
383
        ret = true;
384
      }
385
    else
386
      {
387
        // FIXME: Implement rendering of other Image types.
388
        ret = false;
389
      }
390
    return ret;
391
  }
392
 
393
  /**
394
   * Renders a BufferedImage and applies the specified BufferedImageOp before
395
   * to filter the BufferedImage somehow. The resulting BufferedImage is then
396
   * passed on to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
397
   * to perform the final rendering.
398
   *
399
   * @param image the source buffered image
400
   * @param op the filter to apply to the buffered image before rendering
401
   * @param x the x coordinate to render the image to
402
   * @param y the y coordinate to render the image to
403
   */
404
  public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
405
  {
406
    BufferedImage filtered =
407
      op.createCompatibleDestImage(image, image.getColorModel());
408
    AffineTransform t = new AffineTransform();
409
    t.translate(x, y);
410
    drawRenderedImage(filtered, t);
411
  }
412
 
413
  /**
414
   * Renders the specified image to the destination raster. The specified
415
   * transform is used to convert the image into user space. The transform
416
   * of this AbstractGraphics2D object is used to transform from user space
417
   * to device space.
418
   *
419
   * The rendering is performed using the scanline algorithm that performs the
420
   * rendering of other shapes and a custom Paint implementation, that supplies
421
   * the pixel values of the rendered image.
422
   *
423
   * @param image the image to render to the destination raster
424
   * @param xform the transform from image space to user space
425
   */
426
  public void drawRenderedImage(RenderedImage image, AffineTransform xform)
427
  {
428
    Rectangle areaOfInterest = new Rectangle(image.getMinX(),
429
                                             image.getHeight(),
430
                                             image.getWidth(),
431
                                             image.getHeight());
432
    drawRenderedImageImpl(image, xform, areaOfInterest);
433
  }
434
 
435
  /**
436
   * Renders the specified image to the destination raster. The specified
437
   * transform is used to convert the image into user space. The transform
438
   * of this AbstractGraphics2D object is used to transform from user space
439
   * to device space. Only the area specified by <code>areaOfInterest</code>
440
   * is finally rendered to the target.
441
   *
442
   * The rendering is performed using the scanline algorithm that performs the
443
   * rendering of other shapes and a custom Paint implementation, that supplies
444
   * the pixel values of the rendered image.
445
   *
446
   * @param image the image to render to the destination raster
447
   * @param xform the transform from image space to user space
448
   */
449
  private void drawRenderedImageImpl(RenderedImage image,
450
                                     AffineTransform xform,
451
                                     Rectangle areaOfInterest)
452
  {
453
    // First we compute the transformation. This is made up of 3 parts:
454
    // 1. The areaOfInterest -> image space transform.
455
    // 2. The image space -> user space transform.
456
    // 3. The user space -> device space transform.
457
    AffineTransform t = new AffineTransform();
458
    t.translate(- areaOfInterest.x - image.getMinX(),
459
                - areaOfInterest.y - image.getMinY());
460
    t.concatenate(xform);
461
    t.concatenate(transform);
462
    AffineTransform it = null;
463
    try
464
      {
465
        it = t.createInverse();
466
      }
467
    catch (NoninvertibleTransformException ex)
468
      {
469
        // Ignore -- we return if the transform is not invertible.
470
      }
471
    if (it != null)
472
      {
473
        // Transform the area of interest into user space.
474
        GeneralPath aoi = new GeneralPath(areaOfInterest);
475
        aoi.transform(xform);
476
        // Render the shape using the standard renderer, but with a temporary
477
        // ImagePaint.
478
        ImagePaint p = new ImagePaint(image, it);
479
        Paint savedPaint = paint;
480
        try
481
          {
482
            paint = p;
483
            fillShape(aoi, false);
484
          }
485
        finally
486
          {
487
            paint = savedPaint;
488
          }
489
      }
490
  }
491
 
492
  /**
493
   * Renders a renderable image. This produces a RenderedImage, which is
494
   * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
495
   * to perform the final rendering.
496
   *
497
   * @param image the renderable image to be rendered
498
   * @param xform the transform from image space to user space
499
   */
500
  public void drawRenderableImage(RenderableImage image, AffineTransform xform)
501
  {
502
    Rectangle areaOfInterest = new Rectangle((int) image.getMinX(),
503
                                             (int) image.getHeight(),
504
                                             (int) image.getWidth(),
505
                                             (int) image.getHeight());
506
    drawRenderableImageImpl(image, xform, areaOfInterest);
507
 
508
  }
509
 
510
  /**
511
   * Renders a renderable image. This produces a RenderedImage, which is
512
   * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
513
   * to perform the final rendering. Only the area of the image specified
514
   * by <code>areaOfInterest</code> is rendered.
515
   *
516
   * @param image the renderable image to be rendered
517
   * @param xform the transform from image space to user space
518
   */
519
  private void drawRenderableImageImpl(RenderableImage image,
520
                                       AffineTransform xform,
521
                                       Rectangle areaOfInterest)
522
  {
523
    // TODO: Maybe make more clever usage of a RenderContext here.
524
    RenderedImage rendered = image.createDefaultRendering();
525
    drawRenderedImageImpl(rendered, xform, areaOfInterest);
526
  }
527
 
528
  /**
529
   * Draws the specified string at the specified location.
530
   *
531
   * @param text the string to draw
532
   * @param x the x location, relative to the bounding rectangle of the text
533
   * @param y the y location, relative to the bounding rectangle of the text
534
   */
535
  public void drawString(String text, int x, int y)
536
  {
537
    GlyphVector gv;
538
    synchronized (searchTextKey)
539
      {
540
        TextCacheKey tck = searchTextKey;
541
        FontRenderContext frc = getFontRenderContext();
542
        tck.setString(text);
543
        tck.setFont(font);
544
        tck.setFontRenderContext(frc);
545
        if (gvCache.containsKey(tck))
546
          {
547
            gv = gvCache.get(tck);
548
          }
549
        else
550
          {
551
            gv = font.createGlyphVector(frc, text.toCharArray());
552
            gvCache.put(new TextCacheKey(text, font, frc), gv);
553
          }
554
      }
555
    drawGlyphVector(gv, x, y);
556
  }
557
 
558
  /**
559
   * Draws the specified string at the specified location.
560
   *
561
   * @param text the string to draw
562
   * @param x the x location, relative to the bounding rectangle of the text
563
   * @param y the y location, relative to the bounding rectangle of the text
564
   */
565
  public void drawString(String text, float x, float y)
566
  {
567
    FontRenderContext ctx = getFontRenderContext();
568
    GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
569
    drawGlyphVector(gv, x, y);
570
  }
571
 
572
  /**
573
   * Draws the specified string (as AttributedCharacterIterator) at the
574
   * specified location.
575
   *
576
   * @param iterator the string to draw
577
   * @param x the x location, relative to the bounding rectangle of the text
578
   * @param y the y location, relative to the bounding rectangle of the text
579
   */
580
  public void drawString(AttributedCharacterIterator iterator, int x, int y)
581
  {
582
    FontRenderContext ctx = getFontRenderContext();
583
    GlyphVector gv = font.createGlyphVector(ctx, iterator);
584
    drawGlyphVector(gv, x, y);
585
  }
586
 
587
  /**
588
   * Draws the specified string (as AttributedCharacterIterator) at the
589
   * specified location.
590
   *
591
   * @param iterator the string to draw
592
   * @param x the x location, relative to the bounding rectangle of the text
593
   * @param y the y location, relative to the bounding rectangle of the text
594
   */
595
  public void drawString(AttributedCharacterIterator iterator, float x, float y)
596
  {
597
    FontRenderContext ctx = getFontRenderContext();
598
    GlyphVector gv = font.createGlyphVector(ctx, iterator);
599
    drawGlyphVector(gv, x, y);
600
  }
601
 
602
  /**
603
   * Fills the specified shape with the current foreground.
604
   *
605
   * @param shape the shape to fill
606
   */
607
  public void fill(Shape shape)
608
  {
609
    fillShape(shape, false);
610
  }
611
 
612
  public boolean hit(Rectangle rect, Shape text, boolean onStroke)
613
  {
614
    // FIXME: Implement this.
615
    throw new UnsupportedOperationException("Not yet implemented");
616
  }
617
 
618
  /**
619
   * Sets the composite.
620
   *
621
   * @param comp the composite to set
622
   */
623
  public void setComposite(Composite comp)
624
  {
625
    if (! (comp instanceof AlphaComposite))
626
      {
627
        // FIXME: this check is only required "if this Graphics2D
628
        // context is drawing to a Component on the display screen".
629
        SecurityManager sm = System.getSecurityManager();
630
        if (sm != null)
631
          sm.checkPermission(new AWTPermission("readDisplayPixels"));
632
      }
633
 
634
    composite = comp;
635
    if (! (comp.equals(AlphaComposite.SrcOver)))
636
      isOptimized = false;
637
    else
638
      updateOptimization();
639
  }
640
 
641
  /**
642
   * Sets the current foreground.
643
   *
644
   * @param p the foreground to set.
645
   */
646
  public void setPaint(Paint p)
647
  {
648
    if (p != null)
649
      {
650
        paint = p;
651
 
652
        if (! (paint instanceof Color))
653
          {
654
            isOptimized = false;
655
          }
656
        else
657
          {
658
            this.foreground = (Color) paint;
659
            isForegroundColorNull = false;
660
            updateOptimization();
661
          }
662
      }
663
    else
664
      {
665
        this.foreground = Color.BLACK;
666
        isForegroundColorNull = true;
667
      }
668
 
669
    // free resources if needed, then put the paint context to null
670
    if (this.paintContext != null)
671
      this.paintContext.dispose();
672
 
673
    this.paintContext = null;
674
  }
675
 
676
  /**
677
   * Sets the stroke for this graphics object.
678
   *
679
   * @param s the stroke to set
680
   */
681
  public void setStroke(Stroke s)
682
  {
683
    stroke = s;
684
    if (! stroke.equals(new BasicStroke()))
685
      isOptimized = false;
686
    else
687
      updateOptimization();
688
  }
689
 
690
  /**
691
   * Sets the specified rendering hint.
692
   *
693
   * @param hintKey the key of the rendering hint
694
   * @param hintValue the value
695
   */
696
  public void setRenderingHint(Key hintKey, Object hintValue)
697
  {
698
    renderingHints.put(hintKey, hintValue);
699
  }
700
 
701
  /**
702
   * Returns the rendering hint for the specified key.
703
   *
704
   * @param hintKey the rendering hint key
705
   *
706
   * @return the rendering hint for the specified key
707
   */
708
  public Object getRenderingHint(Key hintKey)
709
  {
710
    return renderingHints.get(hintKey);
711
  }
712
 
713
  /**
714
   * Sets the specified rendering hints.
715
   *
716
   * @param hints the rendering hints to set
717
   */
718
  public void setRenderingHints(Map hints)
719
  {
720
    renderingHints.clear();
721
    renderingHints.putAll(hints);
722
  }
723
 
724
  /**
725
   * Adds the specified rendering hints.
726
   *
727
   * @param hints the rendering hints to add
728
   */
729
  public void addRenderingHints(Map hints)
730
  {
731
    renderingHints.putAll(hints);
732
  }
733
 
734
  /**
735
   * Returns the current rendering hints.
736
   *
737
   * @return the current rendering hints
738
   */
739
  public RenderingHints getRenderingHints()
740
  {
741
    return (RenderingHints) renderingHints.clone();
742
  }
743
 
744
  /**
745
   * Translates the coordinate system by (x, y).
746
   *
747
   * @param x the translation X coordinate
748
   * @param y the translation Y coordinate
749
   */
750
  public void translate(int x, int y)
751
  {
752
    transform.translate(x, y);
753
 
754
    // Update the clip. We special-case rectangular clips here, because they
755
    // are so common (e.g. in Swing).
756
    if (clip != null)
757
      {
758
        if (clip instanceof Rectangle)
759
          {
760
            Rectangle r = (Rectangle) clip;
761
            r.x -= x;
762
            r.y -= y;
763
            setClip(r);
764
          }
765
        else
766
          {
767
            AffineTransform clipTransform = new AffineTransform();
768
            clipTransform.translate(-x, -y);
769
            updateClip(clipTransform);
770
          }
771
      }
772
  }
773
 
774
  /**
775
   * Translates the coordinate system by (tx, ty).
776
   *
777
   * @param tx the translation X coordinate
778
   * @param ty the translation Y coordinate
779
   */
780
  public void translate(double tx, double ty)
781
  {
782
    transform.translate(tx, ty);
783
 
784
    // Update the clip. We special-case rectangular clips here, because they
785
    // are so common (e.g. in Swing).
786
    if (clip != null)
787
      {
788
        if (clip instanceof Rectangle)
789
          {
790
            Rectangle r = (Rectangle) clip;
791
            r.x -= tx;
792
            r.y -= ty;
793
          }
794
        else
795
          {
796
            AffineTransform clipTransform = new AffineTransform();
797
            clipTransform.translate(-tx, -ty);
798
            updateClip(clipTransform);
799
          }
800
      }
801
  }
802
 
803
  /**
804
   * Rotates the coordinate system by <code>theta</code> degrees.
805
   *
806
   * @param theta the angle be which to rotate the coordinate system
807
   */
808
  public void rotate(double theta)
809
  {
810
    transform.rotate(theta);
811
    if (clip != null)
812
      {
813
        AffineTransform clipTransform = new AffineTransform();
814
        clipTransform.rotate(-theta);
815
        updateClip(clipTransform);
816
      }
817
    updateOptimization();
818
  }
819
 
820
  /**
821
   * Rotates the coordinate system by <code>theta</code> around the point
822
   * (x, y).
823
   *
824
   * @param theta the angle by which to rotate the coordinate system
825
   * @param x the point around which to rotate, X coordinate
826
   * @param y the point around which to rotate, Y coordinate
827
   */
828
  public void rotate(double theta, double x, double y)
829
  {
830
    transform.rotate(theta, x, y);
831
    if (clip != null)
832
      {
833
        AffineTransform clipTransform = new AffineTransform();
834
        clipTransform.rotate(-theta, x, y);
835
        updateClip(clipTransform);
836
      }
837
    updateOptimization();
838
  }
839
 
840
  /**
841
   * Scales the coordinate system by the factors <code>scaleX</code> and
842
   * <code>scaleY</code>.
843
   *
844
   * @param scaleX the factor by which to scale the X axis
845
   * @param scaleY the factor by which to scale the Y axis
846
   */
847
  public void scale(double scaleX, double scaleY)
848
  {
849
    transform.scale(scaleX, scaleY);
850
    if (clip != null)
851
      {
852
        AffineTransform clipTransform = new AffineTransform();
853
        clipTransform.scale(1 / scaleX, 1 / scaleY);
854
        updateClip(clipTransform);
855
      }
856
    updateOptimization();
857
  }
858
 
859
  /**
860
   * Shears the coordinate system by <code>shearX</code> and
861
   * <code>shearY</code>.
862
   *
863
   * @param shearX the X shearing
864
   * @param shearY the Y shearing
865
   */
866
  public void shear(double shearX, double shearY)
867
  {
868
    transform.shear(shearX, shearY);
869
    if (clip != null)
870
      {
871
        AffineTransform clipTransform = new AffineTransform();
872
        clipTransform.shear(-shearX, -shearY);
873
        updateClip(clipTransform);
874
      }
875
    updateOptimization();
876
  }
877
 
878
  /**
879
   * Transforms the coordinate system using the specified transform
880
   * <code>t</code>.
881
   *
882
   * @param t the transform
883
   */
884
  public void transform(AffineTransform t)
885
  {
886
    transform.concatenate(t);
887
    try
888
      {
889
        AffineTransform clipTransform = t.createInverse();
890
        updateClip(clipTransform);
891
      }
892
    catch (NoninvertibleTransformException ex)
893
      {
894
        // TODO: How can we deal properly with this?
895
        ex.printStackTrace();
896
      }
897
    updateOptimization();
898
  }
899
 
900
  /**
901
   * Sets the transformation for this Graphics object.
902
   *
903
   * @param t the transformation to set
904
   */
905
  public void setTransform(AffineTransform t)
906
  {
907
    // Transform clip into target space using the old transform.
908
    updateClip(transform);
909
    transform.setTransform(t);
910
    // Transform the clip back into user space using the inverse new transform.
911
    try
912
      {
913
        updateClip(transform.createInverse());
914
      }
915
    catch (NoninvertibleTransformException ex)
916
      {
917
        // TODO: How can we deal properly with this?
918
        ex.printStackTrace();
919
      }
920
    updateOptimization();
921
  }
922
 
923
  /**
924
   * Returns the transformation of this coordinate system.
925
   *
926
   * @return the transformation of this coordinate system
927
   */
928
  public AffineTransform getTransform()
929
  {
930
    return (AffineTransform) transform.clone();
931
  }
932
 
933
  /**
934
   * Returns the current foreground.
935
   *
936
   * @return the current foreground
937
   */
938
  public Paint getPaint()
939
  {
940
    return paint;
941
  }
942
 
943
 
944
  /**
945
   * Returns the current composite.
946
   *
947
   * @return the current composite
948
   */
949
  public Composite getComposite()
950
  {
951
    return composite;
952
  }
953
 
954
  /**
955
   * Sets the current background.
956
   *
957
   * @param color the background to set.
958
   */
959
  public void setBackground(Color color)
960
  {
961
    background = color;
962
  }
963
 
964
  /**
965
   * Returns the current background.
966
   *
967
   * @return the current background
968
   */
969
  public Color getBackground()
970
  {
971
    return background;
972
  }
973
 
974
  /**
975
   * Returns the current stroke.
976
   *
977
   * @return the current stroke
978
   */
979
  public Stroke getStroke()
980
  {
981
    return stroke;
982
  }
983
 
984
  /**
985
   * Intersects the clip of this graphics object with the specified clip.
986
   *
987
   * @param s the clip with which the current clip should be intersected
988
   */
989
  public void clip(Shape s)
990
  {
991
    // Initialize clip if not already present.
992
    if (clip == null)
993
      setClip(s);
994
 
995
    // This is so common, let's optimize this.
996
    else if (clip instanceof Rectangle && s instanceof Rectangle)
997
      {
998
        Rectangle clipRect = (Rectangle) clip;
999
        Rectangle r = (Rectangle) s;
1000
        computeIntersection(r.x, r.y, r.width, r.height, clipRect);
1001
        // Call setClip so that subclasses get notified.
1002
        setClip(clipRect);
1003
      }
1004
   else
1005
     {
1006
       Area current;
1007
       if (clip instanceof Area)
1008
         current = (Area) clip;
1009
       else
1010
         current = new Area(clip);
1011
 
1012
       Area intersect;
1013
       if (s instanceof Area)
1014
         intersect = (Area) s;
1015
       else
1016
         intersect = new Area(s);
1017
 
1018
       current.intersect(intersect);
1019
       clip = current;
1020
       isOptimized = false;
1021
       // Call setClip so that subclasses get notified.
1022
       setClip(clip);
1023
     }
1024
  }
1025
 
1026
  public FontRenderContext getFontRenderContext()
1027
  {
1028
    // Protect our own transform from beeing modified.
1029
    AffineTransform tf = new AffineTransform(transform);
1030
    // TODO: Determine antialias and fractionalmetrics parameters correctly.
1031
    return new FontRenderContext(tf, false, true);
1032
  }
1033
 
1034
  /**
1035
   * Draws the specified glyph vector at the specified location.
1036
   *
1037
   * @param gv the glyph vector to draw
1038
   * @param x the location, x coordinate
1039
   * @param y the location, y coordinate
1040
   */
1041
  public void drawGlyphVector(GlyphVector gv, float x, float y)
1042
  {
1043
    translate(x, y);
1044
    fillShape(gv.getOutline(), true);
1045
    translate(-x, -y);
1046
  }
1047
 
1048
  /**
1049
   * Creates a copy of this graphics object.
1050
   *
1051
   * @return a copy of this graphics object
1052
   */
1053
  public Graphics create()
1054
  {
1055
    AbstractGraphics2D copy = (AbstractGraphics2D) clone();
1056
    return copy;
1057
  }
1058
 
1059
  /**
1060
   * Creates and returns a copy of this Graphics object. This should
1061
   * be overridden by subclasses if additional state must be handled when
1062
   * cloning. This is called by {@link #create()}.
1063
   *
1064
   * @return a copy of this Graphics object
1065
   */
1066
  protected Object clone()
1067
  {
1068
    try
1069
      {
1070
        AbstractGraphics2D copy = (AbstractGraphics2D) super.clone();
1071
        // Copy the clip. If it's a Rectangle, preserve that for optimization.
1072
        if (clip instanceof Rectangle)
1073
          copy.clip = new Rectangle((Rectangle) clip);
1074
        else if (clip != null)
1075
          copy.clip = new GeneralPath(clip);
1076
        else
1077
          copy.clip = null;
1078
 
1079
        copy.renderingHints = new RenderingHints(null);
1080
        copy.renderingHints.putAll(renderingHints);
1081
        copy.transform = new AffineTransform(transform);
1082
        // The remaining state is inmmutable and doesn't need to be copied.
1083
        return copy;
1084
      }
1085
    catch (CloneNotSupportedException ex)
1086
      {
1087
        AWTError err = new AWTError("Unexpected exception while cloning");
1088
        err.initCause(ex);
1089
        throw err;
1090
      }
1091
  }
1092
 
1093
  /**
1094
   * Returns the current foreground.
1095
   */
1096
  public Color getColor()
1097
  {
1098
    if (isForegroundColorNull)
1099
      return null;
1100
 
1101
    return this.foreground;
1102
  }
1103
 
1104
  /**
1105
   * Sets the current foreground.
1106
   *
1107
   * @param color the foreground to set
1108
   */
1109
  public void setColor(Color color)
1110
  {
1111
    this.setPaint(color);
1112
  }
1113
 
1114
  public void setPaintMode()
1115
  {
1116
    // FIXME: Implement this.
1117
    throw new UnsupportedOperationException("Not yet implemented");
1118
  }
1119
 
1120
  public void setXORMode(Color color)
1121
  {
1122
    // FIXME: Implement this.
1123
    throw new UnsupportedOperationException("Not yet implemented");
1124
  }
1125
 
1126
  /**
1127
   * Returns the current font.
1128
   *
1129
   * @return the current font
1130
   */
1131
  public Font getFont()
1132
  {
1133
    return font;
1134
  }
1135
 
1136
  /**
1137
   * Sets the font on this graphics object. When <code>f == null</code>, the
1138
   * current setting is not changed.
1139
   *
1140
   * @param f the font to set
1141
   */
1142
  public void setFont(Font f)
1143
  {
1144
    if (f != null)
1145
      font = f;
1146
  }
1147
 
1148
  /**
1149
   * Returns the font metrics for the specified font.
1150
   *
1151
   * @param font the font for which to fetch the font metrics
1152
   *
1153
   * @return the font metrics for the specified font
1154
   */
1155
  public FontMetrics getFontMetrics(Font font)
1156
  {
1157
    return Toolkit.getDefaultToolkit().getFontMetrics(font);
1158
  }
1159
 
1160
  /**
1161
   * Returns the bounds of the current clip.
1162
   *
1163
   * @return the bounds of the current clip
1164
   */
1165
  public Rectangle getClipBounds()
1166
  {
1167
    Rectangle b = null;
1168
    if (clip != null)
1169
      b = clip.getBounds();
1170
    return b;
1171
  }
1172
 
1173
  /**
1174
   * Intersects the current clipping region with the specified rectangle.
1175
   *
1176
   * @param x the x coordinate of the rectangle
1177
   * @param y the y coordinate of the rectangle
1178
   * @param width the width of the rectangle
1179
   * @param height the height of the rectangle
1180
   */
1181
  public void clipRect(int x, int y, int width, int height)
1182
  {
1183
    clip(new Rectangle(x, y, width, height));
1184
  }
1185
 
1186
  /**
1187
   * Sets the clip to the specified rectangle.
1188
   *
1189
   * @param x the x coordinate of the clip rectangle
1190
   * @param y the y coordinate of the clip rectangle
1191
   * @param width the width of the clip rectangle
1192
   * @param height the height of the clip rectangle
1193
   */
1194
  public void setClip(int x, int y, int width, int height)
1195
  {
1196
    setClip(new Rectangle(x, y, width, height));
1197
  }
1198
 
1199
  /**
1200
   * Returns the current clip.
1201
   *
1202
   * @return the current clip
1203
   */
1204
  public Shape getClip()
1205
  {
1206
    return clip;
1207
  }
1208
 
1209
  /**
1210
   * Sets the current clipping area to <code>clip</code>.
1211
   *
1212
   * @param c the clip to set
1213
   */
1214
  public void setClip(Shape c)
1215
  {
1216
    clip = c;
1217
    if (! (clip instanceof Rectangle))
1218
      isOptimized = false;
1219
    else
1220
      updateOptimization();
1221
  }
1222
 
1223
  public void copyArea(int x, int y, int width, int height, int dx, int dy)
1224
  {
1225
    if (isOptimized)
1226
      rawCopyArea(x, y, width, height, dx, dy);
1227
    else
1228
      copyAreaImpl(x, y, width, height, dx, dy);
1229
  }
1230
 
1231
  /**
1232
   * Draws a line from (x1, y1) to (x2, y2).
1233
   *
1234
   * This implementation transforms the coordinates and forwards the call to
1235
   * {@link #rawDrawLine}.
1236
   */
1237
  public void drawLine(int x1, int y1, int x2, int y2)
1238
  {
1239
    if (isOptimized)
1240
      {
1241
        int tx = (int) transform.getTranslateX();
1242
        int ty = (int) transform.getTranslateY();
1243
        rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty);
1244
      }
1245
    else
1246
      {
1247
        ShapeCache sc = shapeCache;
1248
        if (sc.line == null)
1249
          sc.line = new Line2D.Float();
1250
        sc.line.setLine(x1, y1, x2, y2);
1251
        draw(sc.line);
1252
      }
1253
  }
1254
 
1255
  public void drawRect(int x, int y, int w, int h)
1256
  {
1257
    if (isOptimized)
1258
      {
1259
        int tx = (int) transform.getTranslateX();
1260
        int ty = (int) transform.getTranslateY();
1261
        rawDrawRect(x + tx, y + ty, w, h);
1262
      }
1263
    else
1264
      {
1265
        ShapeCache sc = shapeCache;
1266
        if (sc.rect == null)
1267
          sc.rect = new Rectangle();
1268
        sc.rect.setBounds(x, y, w, h);
1269
        draw(sc.rect);
1270
      }
1271
  }
1272
 
1273
  /**
1274
   * Fills a rectangle with the current paint.
1275
   *
1276
   * @param x the upper left corner, X coordinate
1277
   * @param y the upper left corner, Y coordinate
1278
   * @param width the width of the rectangle
1279
   * @param height the height of the rectangle
1280
   */
1281
  public void fillRect(int x, int y, int width, int height)
1282
  {
1283
    if (isOptimized)
1284
      {
1285
        rawFillRect(x + (int) transform.getTranslateX(),
1286
                    y + (int) transform.getTranslateY(), width, height);
1287
      }
1288
    else
1289
      {
1290
        ShapeCache sc = shapeCache;
1291
        if (sc.rect == null)
1292
          sc.rect = new Rectangle();
1293
        sc.rect.setBounds(x, y, width, height);
1294
        fill(sc.rect);
1295
      }
1296
  }
1297
 
1298
  /**
1299
   * Fills a rectangle with the current background color.
1300
   *
1301
   * This implementation temporarily sets the foreground color to the
1302
   * background and forwards the call to {@link #fillRect(int, int, int, int)}.
1303
   *
1304
   * @param x the upper left corner, X coordinate
1305
   * @param y the upper left corner, Y coordinate
1306
   * @param width the width of the rectangle
1307
   * @param height the height of the rectangle
1308
   */
1309
  public void clearRect(int x, int y, int width, int height)
1310
  {
1311
    if (isOptimized)
1312
      rawClearRect(x, y, width, height);
1313
    else
1314
      {
1315
        Paint savedForeground = getPaint();
1316
        setPaint(getBackground());
1317
        fillRect(x, y, width, height);
1318
        setPaint(savedForeground);
1319
      }
1320
  }
1321
 
1322
  /**
1323
   * Draws a rounded rectangle.
1324
   *
1325
   * @param x the x coordinate of the rectangle
1326
   * @param y the y coordinate of the rectangle
1327
   * @param width the width of the rectangle
1328
   * @param height the height of the rectangle
1329
   * @param arcWidth the width of the arcs
1330
   * @param arcHeight the height of the arcs
1331
   */
1332
  public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1333
                            int arcHeight)
1334
  {
1335
    ShapeCache sc = shapeCache;
1336
    if (sc.roundRect == null)
1337
      sc.roundRect = new RoundRectangle2D.Float();
1338
    sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1339
    draw(sc.roundRect);
1340
  }
1341
 
1342
  /**
1343
   * Fills a rounded rectangle.
1344
   *
1345
   * @param x the x coordinate of the rectangle
1346
   * @param y the y coordinate of the rectangle
1347
   * @param width the width of the rectangle
1348
   * @param height the height of the rectangle
1349
   * @param arcWidth the width of the arcs
1350
   * @param arcHeight the height of the arcs
1351
   */
1352
  public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1353
                            int arcHeight)
1354
  {
1355
    ShapeCache sc = shapeCache;
1356
    if (sc.roundRect == null)
1357
      sc.roundRect = new RoundRectangle2D.Float();
1358
    sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1359
    fill(sc.roundRect);
1360
  }
1361
 
1362
  /**
1363
   * Draws the outline of an oval.
1364
   *
1365
   * @param x the upper left corner of the bounding rectangle of the ellipse
1366
   * @param y the upper left corner of the bounding rectangle of the ellipse
1367
   * @param width the width of the ellipse
1368
   * @param height the height of the ellipse
1369
   */
1370
  public void drawOval(int x, int y, int width, int height)
1371
  {
1372
    ShapeCache sc = shapeCache;
1373
    if (sc.ellipse == null)
1374
      sc.ellipse = new Ellipse2D.Float();
1375
    sc.ellipse.setFrame(x, y, width, height);
1376
    draw(sc.ellipse);
1377
  }
1378
 
1379
  /**
1380
   * Fills an oval.
1381
   *
1382
   * @param x the upper left corner of the bounding rectangle of the ellipse
1383
   * @param y the upper left corner of the bounding rectangle of the ellipse
1384
   * @param width the width of the ellipse
1385
   * @param height the height of the ellipse
1386
   */
1387
  public void fillOval(int x, int y, int width, int height)
1388
  {
1389
    ShapeCache sc = shapeCache;
1390
    if (sc.ellipse == null)
1391
      sc.ellipse = new Ellipse2D.Float();
1392
    sc.ellipse.setFrame(x, y, width, height);
1393
    fill(sc.ellipse);
1394
  }
1395
 
1396
  /**
1397
   * Draws an arc.
1398
   */
1399
  public void drawArc(int x, int y, int width, int height, int arcStart,
1400
                      int arcAngle)
1401
  {
1402
    ShapeCache sc = shapeCache;
1403
    if (sc.arc == null)
1404
      sc.arc = new Arc2D.Float();
1405
    sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN);
1406
    draw(sc.arc);
1407
  }
1408
 
1409
  /**
1410
   * Fills an arc.
1411
   */
1412
  public void fillArc(int x, int y, int width, int height, int arcStart,
1413
                      int arcAngle)
1414
  {
1415
    ShapeCache sc = shapeCache;
1416
    if (sc.arc == null)
1417
      sc.arc = new Arc2D.Float();
1418
    sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE);
1419
    draw(sc.arc);
1420
  }
1421
 
1422
  public void drawPolyline(int[] xPoints, int[] yPoints, int npoints)
1423
  {
1424
    ShapeCache sc = shapeCache;
1425
    if (sc.polyline == null)
1426
      sc.polyline = new GeneralPath();
1427
    GeneralPath p = sc.polyline;
1428
    p.reset();
1429
    if (npoints > 0)
1430
      p.moveTo(xPoints[0], yPoints[0]);
1431
    for (int i = 1; i < npoints; i++)
1432
      p.lineTo(xPoints[i], yPoints[i]);
1433
    fill(p);
1434
  }
1435
 
1436
  /**
1437
   * Draws the outline of a polygon.
1438
   */
1439
  public void drawPolygon(int[] xPoints, int[] yPoints, int npoints)
1440
  {
1441
    ShapeCache sc = shapeCache;
1442
    if (sc.polygon == null)
1443
      sc.polygon = new Polygon();
1444
    sc.polygon.reset();
1445
    sc.polygon.xpoints = xPoints;
1446
    sc.polygon.ypoints = yPoints;
1447
    sc.polygon.npoints = npoints;
1448
    draw(sc.polygon);
1449
  }
1450
 
1451
  /**
1452
   * Fills the outline of a polygon.
1453
   */
1454
  public void fillPolygon(int[] xPoints, int[] yPoints, int npoints)
1455
  {
1456
    ShapeCache sc = shapeCache;
1457
    if (sc.polygon == null)
1458
      sc.polygon = new Polygon();
1459
    sc.polygon.reset();
1460
    sc.polygon.xpoints = xPoints;
1461
    sc.polygon.ypoints = yPoints;
1462
    sc.polygon.npoints = npoints;
1463
    fill(sc.polygon);
1464
  }
1465
 
1466
  /**
1467
   * Draws the specified image at the specified location. This forwards
1468
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1469
   *
1470
   * @param image the image to render
1471
   * @param x the x location to render to
1472
   * @param y the y location to render to
1473
   * @param observer the image observer to receive notification
1474
   */
1475
  public boolean drawImage(Image image, int x, int y, ImageObserver observer)
1476
  {
1477
    boolean ret;
1478
    if (isOptimized)
1479
      {
1480
        ret = rawDrawImage(image, x + (int) transform.getTranslateX(),
1481
                           y + (int) transform.getTranslateY(), observer);
1482
      }
1483
    else
1484
      {
1485
        AffineTransform t = new AffineTransform();
1486
        t.translate(x, y);
1487
        ret = drawImage(image, t, observer);
1488
      }
1489
    return ret;
1490
  }
1491
 
1492
  /**
1493
   * Draws the specified image at the specified location. The image
1494
   * is scaled to the specified width and height. This forwards
1495
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1496
   *
1497
   * @param image the image to render
1498
   * @param x the x location to render to
1499
   * @param y the y location to render to
1500
   * @param width the target width of the image
1501
   * @param height the target height of the image
1502
   * @param observer the image observer to receive notification
1503
   */
1504
  public boolean drawImage(Image image, int x, int y, int width, int height,
1505
                           ImageObserver observer)
1506
  {
1507
    AffineTransform t = new AffineTransform();
1508
    int imWidth = image.getWidth(observer);
1509
    int imHeight = image.getHeight(observer);
1510
    if (imWidth == width && imHeight == height)
1511
      {
1512
        // No need to scale, fall back to non-scaling loops.
1513
        return drawImage(image, x, y, observer);
1514
      }
1515
    else
1516
      {
1517
        Image scaled = prepareImage(image, width, height);
1518
        // Ideally, this should notify the observer about the scaling progress.
1519
        return drawImage(scaled, x, y, observer);
1520
      }
1521
  }
1522
 
1523
  /**
1524
   * Draws the specified image at the specified location. This forwards
1525
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1526
   *
1527
   * @param image the image to render
1528
   * @param x the x location to render to
1529
   * @param y the y location to render to
1530
   * @param bgcolor the background color to use for transparent pixels
1531
   * @param observer the image observer to receive notification
1532
   */
1533
  public boolean drawImage(Image image, int x, int y, Color bgcolor,
1534
                           ImageObserver observer)
1535
  {
1536
    AffineTransform t = new AffineTransform();
1537
    t.translate(x, y);
1538
    // TODO: Somehow implement the background option.
1539
    return drawImage(image, t, observer);
1540
  }
1541
 
1542
  /**
1543
   * Draws the specified image at the specified location. The image
1544
   * is scaled to the specified width and height. This forwards
1545
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1546
   *
1547
   * @param image the image to render
1548
   * @param x the x location to render to
1549
   * @param y the y location to render to
1550
   * @param width the target width of the image
1551
   * @param height the target height of the image
1552
   * @param bgcolor the background color to use for transparent pixels
1553
   * @param observer the image observer to receive notification
1554
   */
1555
  public boolean drawImage(Image image, int x, int y, int width, int height,
1556
                           Color bgcolor, ImageObserver observer)
1557
  {
1558
    AffineTransform t = new AffineTransform();
1559
    t.translate(x, y);
1560
    double scaleX = (double) image.getWidth(observer) / (double) width;
1561
    double scaleY = (double) image.getHeight(observer) / (double) height;
1562
    t.scale(scaleX, scaleY);
1563
    // TODO: Somehow implement the background option.
1564
    return drawImage(image, t, observer);
1565
  }
1566
 
1567
  /**
1568
   * Draws an image fragment to a rectangular area of the target.
1569
   *
1570
   * @param image the image to render
1571
   * @param dx1 the first corner of the destination rectangle
1572
   * @param dy1 the first corner of the destination rectangle
1573
   * @param dx2 the second corner of the destination rectangle
1574
   * @param dy2 the second corner of the destination rectangle
1575
   * @param sx1 the first corner of the source rectangle
1576
   * @param sy1 the first corner of the source rectangle
1577
   * @param sx2 the second corner of the source rectangle
1578
   * @param sy2 the second corner of the source rectangle
1579
   * @param observer the image observer to be notified
1580
   */
1581
  public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1582
                           int sx1, int sy1, int sx2, int sy2,
1583
                           ImageObserver observer)
1584
  {
1585
    int sx = Math.min(sx1, sx1);
1586
    int sy = Math.min(sy1, sy2);
1587
    int sw = Math.abs(sx1 - sx2);
1588
    int sh = Math.abs(sy1 - sy2);
1589
    int dx = Math.min(dx1, dx1);
1590
    int dy = Math.min(dy1, dy2);
1591
    int dw = Math.abs(dx1 - dx2);
1592
    int dh = Math.abs(dy1 - dy2);
1593
 
1594
    AffineTransform t = new AffineTransform();
1595
    t.translate(sx - dx, sy - dy);
1596
    double scaleX = (double) sw / (double) dw;
1597
    double scaleY = (double) sh / (double) dh;
1598
    t.scale(scaleX, scaleY);
1599
    Rectangle areaOfInterest = new Rectangle(sx, sy, sw, sh);
1600
    return drawImageImpl(image, t, observer, areaOfInterest);
1601
  }
1602
 
1603
  /**
1604
   * Draws an image fragment to a rectangular area of the target.
1605
   *
1606
   * @param image the image to render
1607
   * @param dx1 the first corner of the destination rectangle
1608
   * @param dy1 the first corner of the destination rectangle
1609
   * @param dx2 the second corner of the destination rectangle
1610
   * @param dy2 the second corner of the destination rectangle
1611
   * @param sx1 the first corner of the source rectangle
1612
   * @param sy1 the first corner of the source rectangle
1613
   * @param sx2 the second corner of the source rectangle
1614
   * @param sy2 the second corner of the source rectangle
1615
   * @param bgcolor the background color to use for transparent pixels
1616
   * @param observer the image observer to be notified
1617
   */
1618
  public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1619
                           int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1620
                           ImageObserver observer)
1621
  {
1622
    // FIXME: Do something with bgcolor.
1623
    return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
1624
  }
1625
 
1626
  /**
1627
   * Disposes this graphics object.
1628
   */
1629
  public void dispose()
1630
  {
1631
    // Nothing special to do here.
1632
  }
1633
 
1634
  /**
1635
   * Fills the specified shape. Override this if your backend can efficiently
1636
   * fill shapes. This is possible on many systems via a polygon fill
1637
   * method or something similar. But keep in mind that Shapes can be quite
1638
   * complex (non-convex, with holes etc), which is not necessarily supported
1639
   * by all polygon fillers. Also note that you must perform clipping
1640
   * before filling the shape.
1641
   *
1642
   * @param s the shape to fill
1643
   * @param isFont <code>true</code> if the shape is a font outline
1644
   */
1645
  protected void fillShape(Shape s, boolean isFont)
1646
  {
1647
    // Determine if we need to antialias stuff.
1648
    boolean antialias = false;
1649
    if (isFont)
1650
      {
1651
        Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
1652
        // We default to antialiasing for text rendering.
1653
        antialias = v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON
1654
                    || (v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT
1655
                         && DEFAULT_TEXT_AA);
1656
      }
1657
    else
1658
      {
1659
        Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING);
1660
        antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
1661
      }
1662
    ScanlineConverter sc = getScanlineConverter();
1663
    int resolution = 0;
1664
    int yRes = 0;
1665
    if (antialias)
1666
      {
1667
        // Adjust resolution according to rendering hints.
1668
        resolution = 2;
1669
        yRes = 4;
1670
      }
1671
    sc.renderShape(this, s, clip, transform, resolution, yRes, renderingHints);
1672
    freeScanlineConverter(sc);
1673
  }
1674
 
1675
  /**
1676
   * Returns the color model of this Graphics object.
1677
   *
1678
   * @return the color model of this Graphics object
1679
   */
1680
  protected abstract ColorModel getColorModel();
1681
 
1682
  /**
1683
   * Returns the bounds of the target.
1684
   *
1685
   * @return the bounds of the target
1686
   */
1687
  protected abstract Rectangle getDeviceBounds();
1688
 
1689
  /**
1690
   * Draws a line in optimization mode. The implementation should respect the
1691
   * clip and translation. It can assume that the clip is a rectangle and that
1692
   * the transform is only a translating transform.
1693
   *
1694
   * @param x0 the starting point, X coordinate
1695
   * @param y0 the starting point, Y coordinate
1696
   * @param x1 the end point, X coordinate
1697
   * @param y1 the end point, Y coordinate
1698
   */
1699
  protected void rawDrawLine(int x0, int y0, int x1, int y1)
1700
  {
1701
    ShapeCache sc = shapeCache;
1702
    if (sc.line == null)
1703
      sc.line = new Line2D.Float();
1704
    sc.line.setLine(x0, y0, x1, y1);
1705
    draw(sc.line);
1706
  }
1707
 
1708
  protected void rawDrawRect(int x, int y, int w, int h)
1709
  {
1710
    ShapeCache sc = shapeCache;
1711
    if (sc.rect == null)
1712
      sc.rect = new Rectangle();
1713
    sc.rect.setBounds(x, y, w, h);
1714
    draw(sc.rect);
1715
  }
1716
 
1717
  /**
1718
   * Clears a rectangle in optimization mode. The implementation should respect the
1719
   * clip and translation. It can assume that the clip is a rectangle and that
1720
   * the transform is only a translating transform.
1721
   *
1722
   * @param x the upper left corner, X coordinate
1723
   * @param y the upper left corner, Y coordinate
1724
   * @param w the width
1725
   * @param h the height
1726
   */
1727
  protected void rawClearRect(int x, int y, int w, int h)
1728
  {
1729
    Paint savedForeground = getPaint();
1730
    setPaint(getBackground());
1731
    rawFillRect(x, y, w, h);
1732
    setPaint(savedForeground);
1733
  }
1734
 
1735
  /**
1736
   * Fills a rectangle in optimization mode. The implementation should respect
1737
   * the clip but can assume that it is a rectangle.
1738
   *
1739
   * @param x the upper left corner, X coordinate
1740
   * @param y the upper left corner, Y coordinate
1741
   * @param w the width
1742
   * @param h the height
1743
   */
1744
  protected void rawFillRect(int x, int y, int w, int h)
1745
  {
1746
    ShapeCache sc = shapeCache;
1747
    if (sc.rect == null)
1748
      sc.rect = new Rectangle();
1749
    sc.rect.setBounds(x, y, w, h);
1750
    fill(sc.rect);
1751
  }
1752
 
1753
  /**
1754
   * Draws an image in optimization mode. The implementation should respect
1755
   * the clip but can assume that it is a rectangle.
1756
   *
1757
   * @param image the image to be painted
1758
   * @param x the location, X coordinate
1759
   * @param y the location, Y coordinate
1760
   * @param obs the image observer to be notified
1761
   *
1762
   * @return <code>true</code> when the image is painted completely,
1763
   *         <code>false</code> if it is still rendered
1764
   */
1765
  protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
1766
  {
1767
    AffineTransform t = new AffineTransform();
1768
    t.translate(x, y);
1769
    return drawImage(image, t, obs);
1770
  }
1771
 
1772
  /**
1773
   * Copies a rectangular region to another location.
1774
   *
1775
   * @param x the upper left corner, X coordinate
1776
   * @param y the upper left corner, Y coordinate
1777
   * @param w the width
1778
   * @param h the height
1779
   * @param dx
1780
   * @param dy
1781
   */
1782
  protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy)
1783
  {
1784
    copyAreaImpl(x, y, w, h, dx, dy);
1785
  }
1786
 
1787
  // Private implementation methods.
1788
 
1789
  /**
1790
   * Copies a rectangular area of the target raster to a different location.
1791
   */
1792
  private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy)
1793
  {
1794
    // FIXME: Implement this properly.
1795
    throw new UnsupportedOperationException("Not implemented yet.");
1796
  }
1797
 
1798
  /**
1799
   * Paints a scanline between x0 and x1. Override this when your backend
1800
   * can efficiently draw/fill horizontal lines.
1801
   *
1802
   * @param x0 the left offset
1803
   * @param x1 the right offset
1804
   * @param y the scanline
1805
   */
1806
  public void renderScanline(int y, ScanlineCoverage c)
1807
  {
1808
    PaintContext pCtx = getPaintContext();
1809
 
1810
    int x0 = c.getMinX();
1811
    int x1 = c.getMaxX();
1812
    Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1);
1813
 
1814
    // Do the anti aliasing thing.
1815
    float coverageAlpha = 0;
1816
    float maxCoverage = c.getMaxCoverage();
1817
    ColorModel cm = pCtx.getColorModel();
1818
    DataBuffer db = paintRaster.getDataBuffer();
1819
    Point loc = new Point(paintRaster.getMinX(), paintRaster.getMinY());
1820
    SampleModel sm = paintRaster.getSampleModel();
1821
    WritableRaster writeRaster = Raster.createWritableRaster(sm, db, loc);
1822
    WritableRaster alphaRaster = cm.getAlphaRaster(writeRaster);
1823
    int pixel;
1824
    ScanlineCoverage.Iterator iter = c.iterate();
1825
    while (iter.hasNext())
1826
      {
1827
        ScanlineCoverage.Range range = iter.next();
1828
        coverageAlpha = range.getCoverage() / maxCoverage;
1829
        if (coverageAlpha < 1.0)
1830
          {
1831
            for (int x = range.getXPos(); x < range.getXPosEnd(); x++)
1832
              {
1833
                pixel = alphaRaster.getSample(x, y, 0);
1834
                pixel = (int) (pixel * coverageAlpha);
1835
                alphaRaster.setSample(x, y, 0, pixel);
1836
              }
1837
          }
1838
      }
1839
    ColorModel paintColorModel = pCtx.getColorModel();
1840
    CompositeContext cCtx = composite.createContext(paintColorModel,
1841
                                                    getColorModel(),
1842
                                                    renderingHints);
1843
    WritableRaster raster = getDestinationRaster();
1844
    WritableRaster targetChild = raster.createWritableTranslatedChild(-x0, -y);
1845
 
1846
    cCtx.compose(paintRaster, targetChild, targetChild);
1847
    updateRaster(raster, x0, y, x1 - x0, 1);
1848
    cCtx.dispose();
1849
  }
1850
 
1851
 
1852
  /**
1853
   * Initializes this graphics object. This must be called by subclasses in
1854
   * order to correctly initialize the state of this object.
1855
   */
1856
  protected void init()
1857
  {
1858
    setPaint(Color.BLACK);
1859
    setFont(FONT);
1860
    isOptimized = true;
1861
  }
1862
 
1863
  /**
1864
   * Returns a WritableRaster that is used by this class to perform the
1865
   * rendering in. It is not necessary that the target surface immediately
1866
   * reflects changes in the raster. Updates to the raster are notified via
1867
   * {@link #updateRaster}.
1868
   *
1869
   * @return the destination raster
1870
   */
1871
  protected WritableRaster getDestinationRaster()
1872
  {
1873
    // TODO: Ideally we would fetch the xdrawable's surface pixels for
1874
    // initialization of the raster.
1875
    Rectangle db = getDeviceBounds();
1876
    if (destinationRaster == null)
1877
      {
1878
        int[] bandMasks = new int[]{ 0xFF0000, 0xFF00, 0xFF };
1879
        destinationRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT,
1880
                                                      db.width, db.height,
1881
                                                      bandMasks, null);
1882
        // Initialize raster with white.
1883
        int x0 = destinationRaster.getMinX();
1884
        int x1 = destinationRaster.getWidth() + x0;
1885
        int y0 = destinationRaster.getMinY();
1886
        int y1 = destinationRaster.getHeight() + y0;
1887
        int numBands = destinationRaster.getNumBands();
1888
        for (int y = y0; y < y1; y++)
1889
          {
1890
            for (int x = x0; x < x1; x++)
1891
              {
1892
                for (int b = 0; b < numBands; b++)
1893
                  destinationRaster.setSample(x, y, b, 255);
1894
              }
1895
          }
1896
      }
1897
    return destinationRaster;
1898
  }
1899
 
1900
  /**
1901
   * Notifies the backend that the raster has changed in the specified
1902
   * rectangular area. The raster that is provided in this method is always
1903
   * the same as the one returned in {@link #getDestinationRaster}.
1904
   * Backends that reflect changes to this raster directly don't need to do
1905
   * anything here.
1906
   *
1907
   * @param raster the updated raster, identical to the raster returned
1908
   *        by {@link #getDestinationRaster()}
1909
   * @param x the upper left corner of the updated region, X coordinate
1910
   * @param y the upper lef corner of the updated region, Y coordinate
1911
   * @param w the width of the updated region
1912
   * @param h the height of the updated region
1913
   */
1914
  protected void updateRaster(Raster raster, int x, int y, int w, int h)
1915
  {
1916
    // Nothing to do here. Backends that need to update their surface
1917
    // to reflect the change should override this method.
1918
  }
1919
 
1920
  // Some helper methods.
1921
 
1922
  /**
1923
   * Helper method to check and update the optimization conditions.
1924
   */
1925
  private void updateOptimization()
1926
  {
1927
    int transformType = transform.getType();
1928
    boolean optimizedTransform = false;
1929
    if (transformType == AffineTransform.TYPE_IDENTITY
1930
        || transformType == AffineTransform.TYPE_TRANSLATION)
1931
      optimizedTransform = true;
1932
 
1933
    boolean optimizedClip = (clip == null || clip instanceof Rectangle);
1934
    isOptimized = optimizedClip
1935
                  && optimizedTransform && paint instanceof Color
1936
                  && composite == AlphaComposite.SrcOver
1937
                  && stroke.equals(new BasicStroke());
1938
  }
1939
 
1940
  /**
1941
   * Calculates the intersection of two rectangles. The result is stored
1942
   * in <code>rect</code>. This is basically the same
1943
   * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1944
   * create new Rectangle instances. The tradeoff is that you loose any data in
1945
   * <code>rect</code>.
1946
   *
1947
   * @param x upper-left x coodinate of first rectangle
1948
   * @param y upper-left y coodinate of first rectangle
1949
   * @param w width of first rectangle
1950
   * @param h height of first rectangle
1951
   * @param rect a Rectangle object of the second rectangle
1952
   *
1953
   * @throws NullPointerException if rect is null
1954
   *
1955
   * @return a rectangle corresponding to the intersection of the
1956
   *         two rectangles. An empty rectangle is returned if the rectangles
1957
   *         do not overlap
1958
   */
1959
  private static Rectangle computeIntersection(int x, int y, int w, int h,
1960
                                               Rectangle rect)
1961
  {
1962
    int x2 = rect.x;
1963
    int y2 = rect.y;
1964
    int w2 = rect.width;
1965
    int h2 = rect.height;
1966
 
1967
    int dx = (x > x2) ? x : x2;
1968
    int dy = (y > y2) ? y : y2;
1969
    int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1970
    int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1971
 
1972
    if (dw >= 0 && dh >= 0)
1973
      rect.setBounds(dx, dy, dw, dh);
1974
    else
1975
      rect.setBounds(0, 0, 0, 0);
1976
 
1977
    return rect;
1978
  }
1979
 
1980
  /**
1981
   * Helper method to transform the clip. This is called by the various
1982
   * transformation-manipulation methods to update the clip (which is in
1983
   * userspace) accordingly.
1984
   *
1985
   * The transform usually is the inverse transform that was applied to the
1986
   * graphics object.
1987
   *
1988
   * @param t the transform to apply to the clip
1989
   */
1990
  private void updateClip(AffineTransform t)
1991
  {
1992
    if (! (clip instanceof GeneralPath))
1993
      clip = new GeneralPath(clip);
1994
 
1995
    GeneralPath p = (GeneralPath) clip;
1996
    p.transform(t);
1997
  }
1998
 
1999
  /**
2000
   * Returns a free scanline converter from the pool.
2001
   *
2002
   * @return a scanline converter
2003
   */
2004
  private ScanlineConverter getScanlineConverter()
2005
  {
2006
    synchronized (scanlineConverters)
2007
      {
2008
        ScanlineConverter sc;
2009
        if (scanlineConverters.size() > 0)
2010
          {
2011
            sc = scanlineConverters.removeFirst();
2012
          }
2013
        else
2014
          {
2015
            sc = new ScanlineConverter();
2016
          }
2017
        return sc;
2018
      }
2019
  }
2020
 
2021
  /**
2022
   * Puts a scanline converter back in the pool.
2023
   *
2024
   * @param sc
2025
   */
2026
  private void freeScanlineConverter(ScanlineConverter sc)
2027
  {
2028
    synchronized (scanlineConverters)
2029
      {
2030
        scanlineConverters.addLast(sc);
2031
      }
2032
  }
2033
 
2034
  private PaintContext getPaintContext()
2035
  {
2036
    if (this.paintContext == null)
2037
      {
2038
        this.paintContext =
2039
          this.foreground.createContext(getColorModel(),
2040
                                        getDeviceBounds(),
2041
                                        getClipBounds(),
2042
                                        getTransform(),
2043
                                        getRenderingHints());
2044
      }
2045
 
2046
    return this.paintContext;
2047
  }
2048
 
2049
  /**
2050
   * Scales an image to the specified width and height. This should also
2051
   * be used to implement
2052
   * {@link Toolkit#prepareImage(Image, int, int, ImageObserver)}.
2053
   * This uses {@link Toolkit#createImage(ImageProducer)} to create the actual
2054
   * image.
2055
   *
2056
   * @param image the image to prepare
2057
   * @param w the width
2058
   * @param h the height
2059
   *
2060
   * @return the scaled image
2061
   */
2062
  public static Image prepareImage(Image image, int w, int h)
2063
  {
2064
    // Try to find cached scaled image.
2065
    HashMap<Dimension,Image> scaledTable = imageCache.get(image);
2066
    Dimension size = new Dimension(w, h);
2067
    Image scaled = null;
2068
    if (scaledTable != null)
2069
      {
2070
        scaled = scaledTable.get(size);
2071
      }
2072
    if (scaled == null)
2073
      {
2074
        // No cached scaled image. Start scaling image now.
2075
        ImageProducer source = image.getSource();
2076
        ReplicateScaleFilter scaler = new ReplicateScaleFilter(w, h);
2077
        FilteredImageSource filteredSource =
2078
          new FilteredImageSource(source, scaler);
2079
        // Ideally, this should asynchronously scale the image.
2080
        Image scaledImage =
2081
          Toolkit.getDefaultToolkit().createImage(filteredSource);
2082
        scaled = scaledImage;
2083
        // Put scaled image in cache.
2084
        if (scaledTable == null)
2085
          {
2086
            scaledTable = new HashMap<Dimension,Image>();
2087
            imageCache.put(image, scaledTable);
2088
          }
2089
        scaledTable.put(size, scaledImage);
2090
      }
2091
    return scaled;
2092
  }
2093
 
2094
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.