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

Subversion Repositories openrisc

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* CairoGraphics2D.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 gnu.classpath.Configuration;
42
 
43
import gnu.java.awt.ClasspathToolkit;
44
 
45
import java.awt.AWTPermission;
46
import java.awt.AlphaComposite;
47
import java.awt.BasicStroke;
48
import java.awt.Color;
49
import java.awt.Composite;
50
import java.awt.CompositeContext;
51
import java.awt.Font;
52
import java.awt.FontMetrics;
53
import java.awt.GradientPaint;
54
import java.awt.Graphics;
55
import java.awt.Graphics2D;
56
import java.awt.GraphicsConfiguration;
57
import java.awt.Image;
58
import java.awt.Paint;
59
import java.awt.PaintContext;
60
import java.awt.Point;
61
import java.awt.Polygon;
62
import java.awt.Rectangle;
63
import java.awt.RenderingHints;
64
import java.awt.Shape;
65
import java.awt.Stroke;
66
import java.awt.TexturePaint;
67
import java.awt.Toolkit;
68
import java.awt.font.FontRenderContext;
69
import java.awt.font.GlyphVector;
70
import java.awt.font.TextLayout;
71
import java.awt.geom.AffineTransform;
72
import java.awt.geom.Arc2D;
73
import java.awt.geom.Area;
74
import java.awt.geom.Ellipse2D;
75
import java.awt.geom.GeneralPath;
76
import java.awt.geom.Line2D;
77
import java.awt.geom.NoninvertibleTransformException;
78
import java.awt.geom.PathIterator;
79
import java.awt.geom.Point2D;
80
import java.awt.geom.Rectangle2D;
81
import java.awt.geom.RoundRectangle2D;
82
import java.awt.image.AffineTransformOp;
83
import java.awt.image.BufferedImage;
84
import java.awt.image.BufferedImageOp;
85
import java.awt.image.ColorModel;
86
import java.awt.image.DataBuffer;
87
import java.awt.image.DataBufferInt;
88
import java.awt.image.DirectColorModel;
89
import java.awt.image.ImageObserver;
90
import java.awt.image.ImageProducer;
91
import java.awt.image.ImagingOpException;
92
import java.awt.image.MultiPixelPackedSampleModel;
93
import java.awt.image.Raster;
94
import java.awt.image.RenderedImage;
95
import java.awt.image.SampleModel;
96
import java.awt.image.WritableRaster;
97
import java.awt.image.renderable.RenderContext;
98
import java.awt.image.renderable.RenderableImage;
99
import java.text.AttributedCharacterIterator;
100
import java.util.HashMap;
101
import java.util.Map;
102
 
103
/**
104
 * This is an abstract implementation of Graphics2D on Cairo.
105
 *
106
 * It should be subclassed for different Cairo contexts.
107
 *
108
 * Note for subclassers: Apart from the constructor (see comments below),
109
 * The following abstract methods must be implemented:
110
 *
111
 * Graphics create()
112
 * GraphicsConfiguration getDeviceConfiguration()
113
 * copyArea(int x, int y, int width, int height, int dx, int dy)
114
 *
115
 * Also, dispose() must be overloaded to free any native datastructures
116
 * used by subclass and in addition call super.dispose() to free the
117
 * native cairographics2d structure and cairo_t.
118
 *
119
 * @author Sven de Marothy
120
 */
121
public abstract class CairoGraphics2D extends Graphics2D
122
{
123
  static
124
  {
125
    if (true) // GCJ LOCAL
126
      {
127
        System.loadLibrary("gtkpeer");
128
      }
129
  }
130
 
131
  /**
132
   * Important: This is a pointer to the native cairographics2d structure
133
   *
134
   * DO NOT CHANGE WITHOUT CHANGING NATIVE CODE.
135
   */
136
  long nativePointer;
137
 
138
  // Drawing state variables
139
  /**
140
   * The current paint
141
   */
142
  Paint paint;
143
  boolean customPaint;
144
 
145
  /**
146
   * The current stroke
147
   */
148
  Stroke stroke;
149
 
150
  /*
151
   * Current foreground and background color.
152
   */
153
  Color fg, bg;
154
 
155
  /**
156
   * Current clip shape.
157
   */
158
  Shape clip;
159
 
160
  /**
161
   * Current transform.
162
   */
163
  AffineTransform transform;
164
 
165
  /**
166
   * Current font.
167
   */
168
  Font font;
169
 
170
  /**
171
   * The current compositing context, if any.
172
   */
173
  Composite comp;
174
  CompositeContext compCtx;
175
 
176
  /**
177
   * Rendering hint map.
178
   */
179
  private RenderingHints hints;
180
 
181
  /**
182
   * Status of the anti-alias flag in cairo.
183
   */
184
  private boolean antialias = false;
185
  private boolean ignoreAA = false;
186
 
187
  /**
188
   * Some operations (drawing rather than filling) require that their
189
   * coords be shifted to land on 0.5-pixel boundaries, in order to land on
190
   * "middle of pixel" coordinates and light up complete pixels.
191
   */
192
  protected boolean shiftDrawCalls = false;
193
 
194
  /**
195
   * Keep track if the first clip to be set, which is restored on setClip(null);
196
   */
197
  private boolean firstClip = true;
198
  private Shape originalClip;
199
 
200
  /**
201
   * Stroke used for 3DRects
202
   */
203
  private static BasicStroke draw3DRectStroke = new BasicStroke();
204
 
205
  static ColorModel rgb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
206
  static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF,
207
                                                  0xFF000000);
208
 
209
  /**
210
   * Native constants for interpolation methods.
211
   * Note, this corresponds to an enum in native/jni/gtk-peer/cairographics2d.h
212
   */
213
  public static final int INTERPOLATION_NEAREST         = 0,
214
                          INTERPOLATION_BILINEAR        = 1,
215
                          INTERPOLATION_BICUBIC         = 5,
216
                          ALPHA_INTERPOLATION_SPEED     = 2,
217
                          ALPHA_INTERPOLATION_QUALITY   = 3,
218
                          ALPHA_INTERPOLATION_DEFAULT   = 4;
219
  // TODO: Does ALPHA_INTERPOLATION really correspond to CAIRO_FILTER_FAST/BEST/GOOD?
220
 
221
  /**
222
   * Constructor does nothing.
223
   */
224
  public CairoGraphics2D()
225
  {
226
  }
227
 
228
  /**
229
   * Sets up the default values and allocates the native cairographics2d structure
230
   * @param cairo_t_pointer a native pointer to a cairo_t of the context.
231
   */
232
  public void setup(long cairo_t_pointer)
233
  {
234
    nativePointer = init(cairo_t_pointer);
235
    setRenderingHints(new RenderingHints(getDefaultHints()));
236
    setFont(new Font("SansSerif", Font.PLAIN, 12));
237
    setColor(Color.black);
238
    setBackground(Color.white);
239
    setPaint(Color.black);
240
    setStroke(new BasicStroke());
241
    setTransform(new AffineTransform());
242
    cairoSetAntialias(nativePointer, antialias);
243
  }
244
 
245
  /**
246
   * Same as above, but copies the state of another CairoGraphics2D.
247
   */
248
  public void copy(CairoGraphics2D g, long cairo_t_pointer)
249
  {
250
    nativePointer = init(cairo_t_pointer);
251
    paint = g.paint;
252
    stroke = g.stroke;
253
    setRenderingHints(g.hints);
254
 
255
    Color foreground;
256
 
257
    if (g.fg.getAlpha() != -1)
258
      foreground = new Color(g.fg.getRed(), g.fg.getGreen(), g.fg.getBlue(),
259
                             g.fg.getAlpha());
260
    else
261
      foreground = new Color(g.fg.getRGB());
262
 
263
    if (g.bg != null)
264
      {
265
        if (g.bg.getAlpha() != -1)
266
          bg = new Color(g.bg.getRed(), g.bg.getGreen(), g.bg.getBlue(),
267
                         g.bg.getAlpha());
268
        else
269
          bg = new Color(g.bg.getRGB());
270
      }
271
 
272
    firstClip = g.firstClip;
273
    originalClip = g.originalClip;
274
    clip = g.getClip();
275
 
276
    if (g.transform == null)
277
      transform = null;
278
    else
279
      transform = new AffineTransform(g.transform);
280
 
281
    setFont(g.font);
282
    setColor(foreground);
283
    setBackground(bg);
284
    setPaint(paint);
285
    setStroke(stroke);
286
    setTransformImpl(transform);
287
    setClip(clip);
288
    setComposite(comp);
289
 
290
    antialias = !g.antialias;
291
    setAntialias(g.antialias);
292
  }
293
 
294
  /**
295
   * Generic destructor - call the native dispose() method.
296
   */
297
  public void finalize()
298
  {
299
    dispose();
300
  }
301
 
302
  /**
303
   * Disposes the native cairographics2d structure, including the
304
   * cairo_t and any gradient stuff, if allocated.
305
   * Subclasses should of course overload and call this if
306
   * they have additional native structures.
307
   */
308
  public void dispose()
309
  {
310
    disposeNative(nativePointer);
311
    nativePointer = 0;
312
    if (compCtx != null)
313
      compCtx.dispose();
314
  }
315
 
316
  /**
317
   * Allocate the cairographics2d structure and set the cairo_t pointer in it.
318
   * @param pointer - a cairo_t pointer, casted to a long.
319
   */
320
  protected native long init(long pointer);
321
 
322
  /**
323
   * These are declared abstract as there may be context-specific issues.
324
   */
325
  public abstract Graphics create();
326
 
327
  public abstract GraphicsConfiguration getDeviceConfiguration();
328
 
329
  protected abstract void copyAreaImpl(int x, int y, int width, int height,
330
                                       int dx, int dy);
331
 
332
 
333
  /**
334
   * Find the bounds of this graphics context, in device space.
335
   *
336
   * @return the bounds in device-space
337
   */
338
  protected abstract Rectangle2D getRealBounds();
339
 
340
  ////// Native Methods ////////////////////////////////////////////////////
341
 
342
  /**
343
   * Dispose of allocate native resouces.
344
   */
345
  public native void disposeNative(long pointer);
346
 
347
  /**
348
   * Draw pixels as an RGBA int matrix
349
   * @param w - width
350
   * @param h - height
351
   * @param stride - stride of the array width
352
   * @param i2u - affine transform array
353
   */
354
  protected native void drawPixels(long pointer, int[] pixels, int w, int h,
355
                                 int stride, double[] i2u, double alpha,
356
                                 int interpolation);
357
 
358
  protected native void setGradient(long pointer, double x1, double y1,
359
                                  double x2, double y2,
360
                                  int r1, int g1, int b1, int a1, int r2,
361
                                  int g2, int b2, int a2, boolean cyclic);
362
 
363
  protected native void setPaintPixels(long pointer, int[] pixels, int w,
364
                                     int h, int stride, boolean repeat,
365
                                     int x, int y);
366
 
367
  /**
368
   * Set the current transform matrix
369
   */
370
  protected native void cairoSetMatrix(long pointer, double[] m);
371
 
372
  /**
373
   * Scaling method
374
   */
375
  protected native void cairoScale(long pointer, double x, double y);
376
 
377
  /**
378
   * Set the compositing operator
379
   */
380
  protected native void cairoSetOperator(long pointer, int cairoOperator);
381
 
382
  /**
383
   * Sets the current color in RGBA as a 0.0-1.0 double
384
   */
385
  protected native void cairoSetRGBAColor(long pointer, double red, double green,
386
                                        double blue, double alpha);
387
 
388
  /**
389
   * Sets the current winding rule in Cairo
390
   */
391
  protected native void cairoSetFillRule(long pointer, int cairoFillRule);
392
 
393
  /**
394
   * Set the line style, cap, join and miter limit.
395
   * Cap and join parameters are in the BasicStroke enumerations.
396
   */
397
  protected native void cairoSetLine(long pointer, double width, int cap,
398
                                   int join, double miterLimit);
399
 
400
  /**
401
   * Set the dash style
402
   */
403
  protected native void cairoSetDash(long pointer, double[] dashes, int ndash,
404
                                   double offset);
405
 
406
  /*
407
   * Draws a Glyph Vector
408
   */
409
  protected native void cairoDrawGlyphVector(long pointer, GdkFontPeer font,
410
                                   float x, float y, int n,
411
                                   int[] codes, float[] positions, long[] fontset);
412
 
413
  /**
414
   * Set the font in cairo.
415
   */
416
  protected native void cairoSetFont(long pointer, GdkFontPeer font);
417
 
418
  /**
419
   * Appends a rectangle to the current path
420
   */
421
  protected native void cairoRectangle(long pointer, double x, double y,
422
                                     double width, double height);
423
 
424
  /**
425
   * Appends an arc to the current path
426
   */
427
  protected native void cairoArc(long pointer, double x, double y,
428
                               double radius, double angle1, double angle2);
429
 
430
  /**
431
   * Save / restore a cairo path
432
   */
433
  protected native void cairoSave(long pointer);
434
  protected native void cairoRestore(long pointer);
435
 
436
  /**
437
   * New current path
438
   */
439
  protected native void cairoNewPath(long pointer);
440
 
441
  /**
442
   * Close current path
443
   */
444
  protected native void cairoClosePath(long pointer);
445
 
446
  /** moveTo */
447
  protected native void cairoMoveTo(long pointer, double x, double y);
448
 
449
  /** lineTo */
450
  protected native void cairoLineTo(long pointer, double x, double y);
451
 
452
  /** Cubic curve-to */
453
  protected native void cairoCurveTo(long pointer, double x1, double y1,
454
                                   double x2, double y2,
455
                                   double x3, double y3);
456
 
457
  /**
458
   * Stroke current path
459
   */
460
  protected native void cairoStroke(long pointer);
461
 
462
  /**
463
   * Fill current path
464
   */
465
  protected native void cairoFill(long pointer, double alpha);
466
 
467
  /**
468
   * Clip current path
469
   */
470
  protected native void cairoClip(long pointer);
471
 
472
  /**
473
   * Clear clip
474
   */
475
  protected native void cairoResetClip(long pointer);
476
 
477
  /**
478
   * Set antialias.
479
   */
480
  protected native void cairoSetAntialias(long pointer, boolean aa);
481
 
482
 
483
  ///////////////////////// TRANSFORMS ///////////////////////////////////
484
  /**
485
   * Set the current transform
486
   */
487
  public void setTransform(AffineTransform tx)
488
  {
489
    // Transform clip into target space using the old transform.
490
    updateClip(transform);
491
 
492
    // Update the native transform.
493
    setTransformImpl(tx);
494
 
495
    // Transform the clip back into user space using the inverse new transform.
496
    try
497
      {
498
        updateClip(transform.createInverse());
499
      }
500
    catch (NoninvertibleTransformException ex)
501
      {
502
        // TODO: How can we deal properly with this?
503
        ex.printStackTrace();
504
      }
505
 
506
    if (clip != null)
507
      setClip(clip);
508
  }
509
 
510
  private void setTransformImpl(AffineTransform tx)
511
  {
512
    transform = tx;
513
    if (transform != null)
514
      {
515
        double[] m = new double[6];
516
        transform.getMatrix(m);
517
        cairoSetMatrix(nativePointer, m);
518
      }
519
  }
520
 
521
  public void transform(AffineTransform tx)
522
  {
523
    if (transform == null)
524
      transform = new AffineTransform(tx);
525
    else
526
      transform.concatenate(tx);
527
 
528
    if (clip != null)
529
      {
530
        try
531
          {
532
            AffineTransform clipTransform = tx.createInverse();
533
            updateClip(clipTransform);
534
          }
535
        catch (NoninvertibleTransformException ex)
536
          {
537
            // TODO: How can we deal properly with this?
538
            ex.printStackTrace();
539
          }
540
      }
541
 
542
    setTransformImpl(transform);
543
  }
544
 
545
  public void rotate(double theta)
546
  {
547
    transform(AffineTransform.getRotateInstance(theta));
548
  }
549
 
550
  public void rotate(double theta, double x, double y)
551
  {
552
    transform(AffineTransform.getRotateInstance(theta, x, y));
553
  }
554
 
555
  public void scale(double sx, double sy)
556
  {
557
    transform(AffineTransform.getScaleInstance(sx, sy));
558
  }
559
 
560
  /**
561
   * Translate the system of the co-ordinates. As translation is a frequent
562
   * operation, it is done in an optimised way, unlike scaling and rotating.
563
   */
564
  public void translate(double tx, double ty)
565
  {
566
    if (transform != null)
567
      transform.translate(tx, ty);
568
    else
569
      transform = AffineTransform.getTranslateInstance(tx, ty);
570
 
571
    if (clip != null)
572
      {
573
        // FIXME: this should actuall try to transform the shape
574
        // rather than degrade to bounds.
575
        if (clip instanceof Rectangle2D)
576
          {
577
            Rectangle2D r = (Rectangle2D) clip;
578
            r.setRect(r.getX() - tx, r.getY() - ty, r.getWidth(),
579
                      r.getHeight());
580
          }
581
        else
582
          {
583
            AffineTransform clipTransform =
584
              AffineTransform.getTranslateInstance(-tx, -ty);
585
            updateClip(clipTransform);
586
          }
587
      }
588
 
589
    setTransformImpl(transform);
590
  }
591
 
592
  public void translate(int x, int y)
593
  {
594
    translate((double) x, (double) y);
595
  }
596
 
597
  public void shear(double shearX, double shearY)
598
  {
599
    transform(AffineTransform.getShearInstance(shearX, shearY));
600
  }
601
 
602
  ///////////////////////// DRAWING STATE ///////////////////////////////////
603
 
604
  public void clip(Shape s)
605
  {
606
    // Do not touch clip when s == null.
607
    if (s == null)
608
      {
609
        // The spec says this should clear the clip. The reference
610
        // implementation throws a NullPointerException instead. I think,
611
        // in this case we should conform to the specs, as it shouldn't
612
        // affect compatibility.
613
        setClip(null);
614
        return;
615
      }
616
 
617
    // If the current clip is still null, initialize it.
618
    if (clip == null)
619
      {
620
        clip = getRealBounds();
621
      }
622
 
623
    // This is so common, let's optimize this.
624
    if (clip instanceof Rectangle2D && s instanceof Rectangle2D)
625
      {
626
        Rectangle2D clipRect = (Rectangle2D) clip;
627
        Rectangle2D r = (Rectangle2D) s;
628
        Rectangle2D.intersect(clipRect, r, clipRect);
629
        setClip(clipRect);
630
      }
631
   else
632
     {
633
       Area current;
634
       if (clip instanceof Area)
635
         current = (Area) clip;
636
       else
637
         current = new Area(clip);
638
 
639
       Area intersect;
640
       if (s instanceof Area)
641
         intersect = (Area) s;
642
       else
643
         intersect = new Area(s);
644
 
645
       current.intersect(intersect);
646
       clip = current;
647
       // Call setClip so that the native side gets notified.
648
       setClip(clip);
649
     }
650
  }
651
 
652
  public Paint getPaint()
653
  {
654
    return paint;
655
  }
656
 
657
  public AffineTransform getTransform()
658
  {
659
    return (AffineTransform) transform.clone();
660
  }
661
 
662
  public void setPaint(Paint p)
663
  {
664
    if (p == null)
665
      return;
666
 
667
    paint = p;
668
    if (paint instanceof Color)
669
      {
670
        setColor((Color) paint);
671
        customPaint = false;
672
      }
673
 
674
    else if (paint instanceof TexturePaint)
675
      {
676
        TexturePaint tp = (TexturePaint) paint;
677
        BufferedImage img = tp.getImage();
678
 
679
        // map the image to the anchor rectangle
680
        int width = (int) tp.getAnchorRect().getWidth();
681
        int height = (int) tp.getAnchorRect().getHeight();
682
 
683
        double scaleX = width / (double) img.getWidth();
684
        double scaleY = height / (double) img.getHeight();
685
 
686
        AffineTransform at = new AffineTransform(scaleX, 0, 0, scaleY, 0, 0);
687
        AffineTransformOp op = new AffineTransformOp(at, getRenderingHints());
688
        BufferedImage texture = op.filter(img, null);
689
        int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width);
690
        setPaintPixels(nativePointer, pixels, width, height, width, true, 0, 0);
691
        customPaint = false;
692
      }
693
 
694
    else if (paint instanceof GradientPaint)
695
      {
696
        GradientPaint gp = (GradientPaint) paint;
697
        Point2D p1 = gp.getPoint1();
698
        Point2D p2 = gp.getPoint2();
699
        Color c1 = gp.getColor1();
700
        Color c2 = gp.getColor2();
701
        setGradient(nativePointer, p1.getX(), p1.getY(), p2.getX(), p2.getY(),
702
                    c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha(),
703
                    c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha(),
704
                    gp.isCyclic());
705
        customPaint = false;
706
      }
707
    else
708
      {
709
        customPaint = true;
710
      }
711
  }
712
 
713
  /**
714
   * Sets a custom paint
715
   *
716
   * @param bounds the bounding box, in user space
717
   */
718
  protected void setCustomPaint(Rectangle bounds)
719
  {
720
    if (paint instanceof Color || paint instanceof TexturePaint
721
        || paint instanceof GradientPaint)
722
      return;
723
 
724
    int userX = bounds.x;
725
    int userY = bounds.y;
726
    int userWidth = bounds.width;
727
    int userHeight = bounds.height;
728
 
729
    // Find bounds in device space
730
    Rectangle2D bounds2D = getTransformedBounds(bounds, transform);
731
    int deviceX = (int)bounds2D.getX();
732
    int deviceY = (int)bounds2D.getY();
733
    int deviceWidth = (int)Math.ceil(bounds2D.getWidth());
734
    int deviceHeight = (int)Math.ceil(bounds2D.getHeight());
735
 
736
    // Get raster of the paint background
737
    PaintContext pc = paint.createContext(CairoSurface.cairoColorModel,
738
                                          new Rectangle(deviceX, deviceY,
739
                                                        deviceWidth,
740
                                                        deviceHeight),
741
                                          bounds,
742
                                          transform, hints);
743
 
744
    Raster raster = pc.getRaster(deviceX, deviceY, deviceWidth,
745
                                 deviceHeight);
746
 
747
    // Clear the transform matrix in Cairo, since the raster returned by the
748
    // PaintContext is already in device-space
749
    AffineTransform oldTx = new AffineTransform(transform);
750
    setTransformImpl(new AffineTransform());
751
 
752
    // Set pixels in cairo, aligning the top-left of the background image
753
    // to the top-left corner in device space
754
    if (pc.getColorModel().equals(CairoSurface.cairoColorModel)
755
        && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT)
756
      {
757
        // Use a fast copy if the paint context can uses a Cairo-compatible
758
        // color model
759
        setPaintPixels(nativePointer,
760
                       (int[])raster.getDataElements(0, 0, deviceWidth,
761
                                                     deviceHeight, null),
762
                       deviceWidth, deviceHeight, deviceWidth, false,
763
                       deviceX, deviceY);
764
      }
765
 
766
    else if (pc.getColorModel().equals(CairoSurface.cairoCM_opaque)
767
            && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT)
768
      {
769
        // We can also optimize if the context uses a similar color model
770
        // but without an alpha channel; we just add the alpha
771
        int[] pixels = (int[])raster.getDataElements(0, 0, deviceWidth,
772
                                                     deviceHeight, null);
773
 
774
        for (int i = 0; i < pixels.length; i++)
775
          pixels[i] = 0xff000000 | (pixels[i] & 0x00ffffff);
776
 
777
        setPaintPixels(nativePointer, pixels, deviceWidth, deviceHeight,
778
                       deviceWidth, false, deviceX, deviceY);
779
      }
780
 
781
    else
782
      {
783
        // Fall back on wrapping the raster in a BufferedImage, and
784
        // use BufferedImage.getRGB() to do color-model conversion
785
        WritableRaster wr = Raster.createWritableRaster(raster.getSampleModel(),
786
                                                        new Point(raster.getMinX(),
787
                                                                  raster.getMinY()));
788
        wr.setRect(raster);
789
 
790
        BufferedImage img2 = new BufferedImage(pc.getColorModel(), wr,
791
                                               pc.getColorModel().isAlphaPremultiplied(),
792
                                               null);
793
 
794
        setPaintPixels(nativePointer,
795
                       img2.getRGB(0, 0, deviceWidth, deviceHeight, null, 0,
796
                                   deviceWidth),
797
                       deviceWidth, deviceHeight, deviceWidth, false,
798
                       deviceX, deviceY);
799
      }
800
 
801
    // Restore transform
802
    setTransformImpl(oldTx);
803
  }
804
 
805
  public Stroke getStroke()
806
  {
807
    return stroke;
808
  }
809
 
810
  public void setStroke(Stroke st)
811
  {
812
    stroke = st;
813
    if (stroke instanceof BasicStroke)
814
      {
815
        BasicStroke bs = (BasicStroke) stroke;
816
        cairoSetLine(nativePointer, bs.getLineWidth(), bs.getEndCap(),
817
                     bs.getLineJoin(), bs.getMiterLimit());
818
 
819
        float[] dashes = bs.getDashArray();
820
        if (dashes != null)
821
          {
822
            double[] double_dashes = new double[dashes.length];
823
            for (int i = 0; i < dashes.length; i++)
824
              double_dashes[i] = dashes[i];
825
 
826
            cairoSetDash(nativePointer, double_dashes, double_dashes.length,
827
                         (double) bs.getDashPhase());
828
          }
829
        else
830
          cairoSetDash(nativePointer, new double[0], 0, 0.0);
831
      }
832
  }
833
 
834
  /**
835
   * Utility method to find the bounds of a shape, including the stroke width.
836
   *
837
   * @param s the shape
838
   * @return the bounds of the shape, including stroke width
839
   */
840
  protected Rectangle findStrokedBounds(Shape s)
841
  {
842
    Rectangle r = s.getBounds();
843
 
844
    if (stroke instanceof BasicStroke)
845
      {
846
        int strokeWidth = (int)Math.ceil(((BasicStroke)stroke).getLineWidth());
847
        r.x -= strokeWidth / 2;
848
        r.y -= strokeWidth / 2;
849
        r.height += strokeWidth;
850
        r.width += strokeWidth;
851
      }
852
    else
853
      {
854
        Shape s2 = stroke.createStrokedShape(s);
855
        r = s2.getBounds();
856
      }
857
 
858
    return r;
859
  }
860
 
861
  public void setPaintMode()
862
  {
863
    setComposite(AlphaComposite.SrcOver);
864
  }
865
 
866
  public void setXORMode(Color c)
867
  {
868
    // FIXME: implement
869
  }
870
 
871
  public void setColor(Color c)
872
  {
873
    if (c == null)
874
      c = Color.BLACK;
875
 
876
    fg = c;
877
    paint = c;
878
    updateColor();
879
  }
880
 
881
  /**
882
   * Set the current fg value as the cairo color.
883
   */
884
  void updateColor()
885
  {
886
    if (fg == null)
887
      fg = Color.BLACK;
888
 
889
    cairoSetRGBAColor(nativePointer, fg.getRed() / 255.0,
890
                      fg.getGreen() / 255.0,fg.getBlue() / 255.0,
891
                      fg.getAlpha() / 255.0);
892
  }
893
 
894
  public Color getColor()
895
  {
896
    return fg;
897
  }
898
 
899
  public void clipRect(int x, int y, int width, int height)
900
  {
901
    if (clip == null)
902
      setClip(new Rectangle(x, y, width, height));
903
    else if (clip instanceof Rectangle)
904
      {
905
        computeIntersection(x, y, width, height, (Rectangle) clip);
906
        setClip(clip);
907
      }
908
    else
909
      clip(new Rectangle(x, y, width, height));
910
  }
911
 
912
  public Shape getClip()
913
  {
914
    if (clip == null)
915
      return null;
916
    else if (clip instanceof Rectangle2D)
917
      return clip.getBounds2D(); //getClipInDevSpace();
918
    else
919
      {
920
        GeneralPath p = new GeneralPath();
921
        PathIterator pi = clip.getPathIterator(null);
922
        p.append(pi, false);
923
        return p;
924
      }
925
  }
926
 
927
  public Rectangle getClipBounds()
928
  {
929
    if (clip == null)
930
      return null;
931
    else
932
      return clip.getBounds();
933
  }
934
 
935
  protected Rectangle2D getClipInDevSpace()
936
  {
937
    Rectangle2D uclip = clip.getBounds2D();
938
    if (transform == null)
939
      return uclip;
940
    else
941
      return getTransformedBounds(clip.getBounds2D(), transform);
942
  }
943
 
944
  public void setClip(int x, int y, int width, int height)
945
  {
946
    if( width < 0 || height < 0 )
947
      return;
948
 
949
    setClip(new Rectangle2D.Double(x, y, width, height));
950
  }
951
 
952
  public void setClip(Shape s)
953
  {
954
    // The first time the clip is set, save it as the original clip
955
    // to reset to on s == null. We can rely on this being non-null
956
    // because the constructor in subclasses is expected to set the
957
    // initial clip properly.
958
    if( firstClip )
959
      {
960
        originalClip = s;
961
        firstClip = false;
962
      }
963
 
964
    clip = s;
965
    cairoResetClip(nativePointer);
966
 
967
    if (clip != null)
968
      {
969
        cairoNewPath(nativePointer);
970
        if (clip instanceof Rectangle2D)
971
          {
972
            Rectangle2D r = (Rectangle2D) clip;
973
            cairoRectangle(nativePointer, r.getX(), r.getY(), r.getWidth(),
974
                           r.getHeight());
975
          }
976
        else
977
          walkPath(clip.getPathIterator(null), false);
978
 
979
        cairoClip(nativePointer);
980
      }
981
  }
982
 
983
  public void setBackground(Color c)
984
  {
985
    if (c == null)
986
      c = Color.WHITE;
987
    bg = c;
988
  }
989
 
990
  public Color getBackground()
991
  {
992
    return bg;
993
  }
994
 
995
  /**
996
   * Return the current composite.
997
   */
998
  public Composite getComposite()
999
  {
1000
    if (comp == null)
1001
      return AlphaComposite.SrcOver;
1002
    else
1003
      return comp;
1004
  }
1005
 
1006
  /**
1007
   * Sets the current composite context.
1008
   */
1009
  public void setComposite(Composite comp)
1010
  {
1011
    if (this.comp == comp)
1012
      return;
1013
 
1014
    this.comp = comp;
1015
    if (compCtx != null)
1016
      compCtx.dispose();
1017
    compCtx = null;
1018
 
1019
    if (comp instanceof AlphaComposite)
1020
      {
1021
        AlphaComposite a = (AlphaComposite) comp;
1022
        cairoSetOperator(nativePointer, a.getRule());
1023
      }
1024
 
1025
    else
1026
      {
1027
        cairoSetOperator(nativePointer, AlphaComposite.SRC_OVER);
1028
 
1029
        if (comp != null)
1030
          {
1031
            // FIXME: this check is only required "if this Graphics2D
1032
            // context is drawing to a Component on the display screen".
1033
            SecurityManager sm = System.getSecurityManager();
1034
            if (sm != null)
1035
              sm.checkPermission(new AWTPermission("readDisplayPixels"));
1036
 
1037
            compCtx = comp.createContext(getBufferCM(), getNativeCM(), hints);
1038
          }
1039
      }
1040
  }
1041
 
1042
  /**
1043
   * Returns the Colour Model describing the native, raw image data for this
1044
   * specific peer.
1045
   *
1046
   * @return ColorModel the ColorModel of native data in this peer
1047
   */
1048
  protected abstract ColorModel getNativeCM();
1049
 
1050
  /**
1051
   * Returns the Color Model describing the buffer that this peer uses
1052
   * for custom composites.
1053
   *
1054
   * @return ColorModel the ColorModel of the composite buffer in this peer.
1055
   */
1056
  protected ColorModel getBufferCM()
1057
  {
1058
    // This may be overridden by some subclasses
1059
    return getNativeCM();
1060
  }
1061
 
1062
  ///////////////////////// DRAWING PRIMITIVES ///////////////////////////////////
1063
 
1064
  public void draw(Shape s)
1065
  {
1066
    if ((stroke != null && ! (stroke instanceof BasicStroke))
1067
        || (comp instanceof AlphaComposite && ((AlphaComposite) comp).getAlpha() != 1.0))
1068
      {
1069
        // Cairo doesn't support stroking with alpha, so we create the stroked
1070
        // shape and fill with alpha instead
1071
        fill(stroke.createStrokedShape(s));
1072
        return;
1073
      }
1074
 
1075
    if (customPaint)
1076
      {
1077
        Rectangle r = findStrokedBounds(s);
1078
        setCustomPaint(r);
1079
      }
1080
 
1081
    setAntialias(!hints.get(RenderingHints.KEY_ANTIALIASING)
1082
                       .equals(RenderingHints.VALUE_ANTIALIAS_OFF));
1083
    createPath(s, true);
1084
    cairoStroke(nativePointer);
1085
  }
1086
 
1087
  public void fill(Shape s)
1088
  {
1089
    createPath(s, false);
1090
 
1091
    if (customPaint)
1092
      setCustomPaint(s.getBounds());
1093
 
1094
    setAntialias(!hints.get(RenderingHints.KEY_ANTIALIASING)
1095
                       .equals(RenderingHints.VALUE_ANTIALIAS_OFF));
1096
    double alpha = 1.0;
1097
    if (comp instanceof AlphaComposite)
1098
      alpha = ((AlphaComposite) comp).getAlpha();
1099
    cairoFill(nativePointer, alpha);
1100
  }
1101
 
1102
  private void createPath(Shape s, boolean isDraw)
1103
  {
1104
    cairoNewPath(nativePointer);
1105
 
1106
    // Optimize rectangles, since there is a direct Cairo function
1107
    if (s instanceof Rectangle2D)
1108
      {
1109
        Rectangle2D r = (Rectangle2D) s;
1110
 
1111
        // Pixels need to be shifted in draw operations to ensure that they
1112
        // light up entire pixels, but we also need to make sure the rectangle
1113
        // does not get distorted by this shifting operation
1114
        double x = shiftX(r.getX(),shiftDrawCalls && isDraw);
1115
        double y = shiftY(r.getY(), shiftDrawCalls && isDraw);
1116
        double w = Math.round(r.getWidth());
1117
        double h = Math.round(r.getHeight());
1118
        cairoRectangle(nativePointer, x, y, w, h);
1119
      }
1120
 
1121
    // Lines are easy too
1122
    else if (s instanceof Line2D)
1123
      {
1124
        Line2D l = (Line2D) s;
1125
        cairoMoveTo(nativePointer, shiftX(l.getX1(), shiftDrawCalls && isDraw),
1126
                  shiftY(l.getY1(), shiftDrawCalls && isDraw));
1127
        cairoLineTo(nativePointer, shiftX(l.getX2(), shiftDrawCalls && isDraw),
1128
                  shiftY(l.getY2(), shiftDrawCalls && isDraw));
1129
      }
1130
 
1131
    // We can optimize ellipses too; however we don't bother optimizing arcs:
1132
    // the iterator is fast enough (an ellipse requires 5 steps using the
1133
    // iterator, while most arcs are only 2-3)
1134
    else if (s instanceof Ellipse2D)
1135
      {
1136
        Ellipse2D e = (Ellipse2D) s;
1137
 
1138
        double radius = Math.min(e.getHeight(), e.getWidth()) / 2;
1139
 
1140
        // Cairo only draws circular shapes, but we can use a stretch to make
1141
        // them into ellipses
1142
        double xscale = 1, yscale = 1;
1143
        if (e.getHeight() != e.getWidth())
1144
          {
1145
            cairoSave(nativePointer);
1146
 
1147
            if (e.getHeight() < e.getWidth())
1148
              xscale = e.getWidth() / (radius * 2);
1149
            else
1150
              yscale = e.getHeight() / (radius * 2);
1151
 
1152
            if (xscale != 1 || yscale != 1)
1153
              cairoScale(nativePointer, xscale, yscale);
1154
          }
1155
 
1156
        cairoArc(nativePointer,
1157
                 shiftX(e.getCenterX() / xscale, shiftDrawCalls && isDraw),
1158
                 shiftY(e.getCenterY() / yscale, shiftDrawCalls && isDraw),
1159
                 radius, 0, Math.PI * 2);
1160
 
1161
        if (xscale != 1 || yscale != 1)
1162
          cairoRestore(nativePointer);
1163
      }
1164
 
1165
    // All other shapes are broken down and drawn in steps using the
1166
    // PathIterator
1167
    else
1168
      walkPath(s.getPathIterator(null), shiftDrawCalls && isDraw);
1169
  }
1170
 
1171
  /**
1172
   * Note that the rest of the drawing methods go via fill() or draw() for the drawing,
1173
   * although subclasses may with to overload these methods where context-specific
1174
   * optimizations are possible (e.g. bitmaps and fillRect(int, int, int, int)
1175
   */
1176
 
1177
  public void clearRect(int x, int y, int width, int height)
1178
  {
1179
    if (bg != null)
1180
      cairoSetRGBAColor(nativePointer, bg.getRed() / 255.0,
1181
                        bg.getGreen() / 255.0, bg.getBlue() / 255.0,
1182
                        bg.getAlpha() / 255.0);
1183
 
1184
    Composite oldcomp = comp;
1185
    setComposite(AlphaComposite.Src);
1186
    fillRect(x, y, width, height);
1187
 
1188
    setComposite(oldcomp);
1189
    updateColor();
1190
  }
1191
 
1192
  public void draw3DRect(int x, int y, int width, int height, boolean raised)
1193
  {
1194
    Stroke tmp = stroke;
1195
    setStroke(draw3DRectStroke);
1196
    super.draw3DRect(x, y, width, height, raised);
1197
    setStroke(tmp);
1198
  }
1199
 
1200
  public void drawArc(int x, int y, int width, int height, int startAngle,
1201
                      int arcAngle)
1202
  {
1203
    draw(new Arc2D.Double((double) x, (double) y, (double) width,
1204
                          (double) height, (double) startAngle,
1205
                          (double) arcAngle, Arc2D.OPEN));
1206
  }
1207
 
1208
  public void drawLine(int x1, int y1, int x2, int y2)
1209
  {
1210
    // The coordinates being pairwise identical means one wants
1211
    // to draw a single pixel. This is emulated by drawing
1212
    // a one pixel sized rectangle.
1213
    if (x1 == x2 && y1 == y2)
1214
      fill(new Rectangle(x1, y1, 1, 1));
1215
    else
1216
      draw(new Line2D.Double(x1, y1, x2, y2));
1217
  }
1218
 
1219
  public void drawRect(int x, int y, int width, int height)
1220
  {
1221
    draw(new Rectangle(x, y, width, height));
1222
  }
1223
 
1224
  public void fillArc(int x, int y, int width, int height, int startAngle,
1225
                      int arcAngle)
1226
  {
1227
    fill(new Arc2D.Double((double) x, (double) y, (double) width,
1228
                          (double) height, (double) startAngle,
1229
                          (double) arcAngle, Arc2D.PIE));
1230
  }
1231
 
1232
  public void fillRect(int x, int y, int width, int height)
1233
  {
1234
    fill (new Rectangle(x, y, width, height));
1235
  }
1236
 
1237
  public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
1238
  {
1239
    fill(new Polygon(xPoints, yPoints, nPoints));
1240
  }
1241
 
1242
  public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
1243
  {
1244
    draw(new Polygon(xPoints, yPoints, nPoints));
1245
  }
1246
 
1247
  public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
1248
  {
1249
    for (int i = 1; i < nPoints; i++)
1250
      draw(new Line2D.Double(xPoints[i - 1], yPoints[i - 1],
1251
                             xPoints[i], yPoints[i]));
1252
  }
1253
 
1254
  public void drawOval(int x, int y, int width, int height)
1255
  {
1256
    drawArc(x, y, width, height, 0, 360);
1257
  }
1258
 
1259
  public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1260
                            int arcHeight)
1261
  {
1262
    draw(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight));
1263
  }
1264
 
1265
  public void fillOval(int x, int y, int width, int height)
1266
  {
1267
    fillArc(x, y, width, height, 0, 360);
1268
  }
1269
 
1270
  public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1271
                            int arcHeight)
1272
  {
1273
    fill(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight));
1274
  }
1275
 
1276
  /**
1277
   * CopyArea - performs clipping to the native surface as a convenience
1278
   * (requires getRealBounds). Then calls copyAreaImpl.
1279
   */
1280
  public void copyArea(int ox, int oy, int owidth, int oheight,
1281
                       int odx, int ody)
1282
  {
1283
    // FIXME: does this handle a rotation transform properly?
1284
    // (the width/height might not be correct)
1285
    Point2D pos = transform.transform(new Point2D.Double(ox, oy),
1286
                                      (Point2D) null);
1287
    Point2D dim = transform.transform(new Point2D.Double(ox + owidth,
1288
                                                         oy + oheight),
1289
                                      (Point2D) null);
1290
    Point2D p2 = transform.transform(new Point2D.Double(ox + odx, oy + ody),
1291
                                     (Point2D) null);
1292
    int x = (int)pos.getX();
1293
    int y = (int)pos.getY();
1294
    int width = (int)(dim.getX() - pos.getX());
1295
    int height = (int)(dim.getY() - pos.getY());
1296
    int dx = (int)(p2.getX() - pos.getX());
1297
    int dy = (int)(p2.getY() - pos.getY());
1298
 
1299
    Rectangle2D r = getRealBounds();
1300
 
1301
    if( width <= 0 || height <= 0 )
1302
      return;
1303
    // Return if outside the surface
1304
    if( x + dx > r.getWidth() || y + dy > r.getHeight() )
1305
      return;
1306
 
1307
    if( x + dx + width < r.getX() || y + dy + height < r.getY() )
1308
      return;
1309
 
1310
    // Clip edges if necessary
1311
    if( x + dx < r.getX() ) // left
1312
      {
1313
        width = x + dx + width;
1314
        x = (int)r.getX() - dx;
1315
      }
1316
 
1317
    if( y + dy < r.getY() ) // top
1318
      {
1319
        height = y + dy + height;
1320
        y = (int)r.getY() - dy;
1321
      }
1322
 
1323
    if( x + dx + width >= r.getWidth() ) // right
1324
      width = (int)r.getWidth() - dx - x;
1325
 
1326
    if( y + dy + height >= r.getHeight() ) // bottom
1327
      height = (int)r.getHeight() - dy - y;
1328
 
1329
    copyAreaImpl(x, y, width, height, dx, dy);
1330
  }
1331
 
1332
  ///////////////////////// RENDERING HINTS ///////////////////////////////////
1333
 
1334
  public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
1335
  {
1336
    hints.put(hintKey, hintValue);
1337
 
1338
    shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1339
      || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1340
  }
1341
 
1342
  public Object getRenderingHint(RenderingHints.Key hintKey)
1343
  {
1344
    return hints.get(hintKey);
1345
  }
1346
 
1347
  public void setRenderingHints(Map<?,?> hints)
1348
  {
1349
    this.hints = new RenderingHints(getDefaultHints());
1350
    this.hints.putAll(hints);
1351
 
1352
    shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1353
      || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1354
 
1355
    if (compCtx != null)
1356
      {
1357
        compCtx.dispose();
1358
        compCtx = comp.createContext(getNativeCM(), getNativeCM(), this.hints);
1359
      }
1360
  }
1361
 
1362
  public void addRenderingHints(Map hints)
1363
  {
1364
    this.hints.putAll(hints);
1365
  }
1366
 
1367
  public RenderingHints getRenderingHints()
1368
  {
1369
    return hints;
1370
  }
1371
 
1372
  private int getInterpolation()
1373
  {
1374
    if (this.hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1375
      return INTERPOLATION_NEAREST;
1376
 
1377
    else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1378
      return INTERPOLATION_BILINEAR;
1379
 
1380
    else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC))
1381
      return INTERPOLATION_BICUBIC;
1382
 
1383
    else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1384
      return ALPHA_INTERPOLATION_SPEED;
1385
 
1386
    else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1387
      return ALPHA_INTERPOLATION_QUALITY;
1388
 
1389
    else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1390
      return ALPHA_INTERPOLATION_DEFAULT;
1391
 
1392
    // Do bilinear interpolation as default
1393
    return INTERPOLATION_BILINEAR;
1394
  }
1395
 
1396
  /**
1397
   * Set antialias if needed.  If the ignoreAA flag is set, this method will
1398
   * return without doing anything.
1399
   *
1400
   * @param needAA RenderingHints.VALUE_ANTIALIAS_ON or RenderingHints.VALUE_ANTIALIAS_OFF
1401
   */
1402
  private void setAntialias(boolean needAA)
1403
  {
1404
    if (ignoreAA)
1405
      return;
1406
 
1407
    if (needAA != antialias)
1408
      {
1409
        antialias = !antialias;
1410
        cairoSetAntialias(nativePointer, antialias);
1411
      }
1412
  }
1413
 
1414
  ///////////////////////// IMAGE. METHODS ///////////////////////////////////
1415
 
1416
  protected boolean drawImage(Image img, AffineTransform xform,
1417
                            Color bgcolor, ImageObserver obs)
1418
  {
1419
    if (img == null)
1420
      return false;
1421
 
1422
    if (xform == null)
1423
      xform = new AffineTransform();
1424
 
1425
    // In this case, xform is an AffineTransform that transforms bounding
1426
    // box of the specified image from image space to user space. However
1427
    // when we pass this transform to cairo, cairo will use this transform
1428
    // to map "user coordinates" to "pixel" coordinates, which is the
1429
    // other way around. Therefore to get the "user -> pixel" transform
1430
    // that cairo wants from "image -> user" transform that we currently
1431
    // have, we will need to invert the transformation matrix.
1432
    AffineTransform invertedXform;
1433
 
1434
    try
1435
      {
1436
        invertedXform = xform.createInverse();
1437
      }
1438
    catch (NoninvertibleTransformException e)
1439
      {
1440
        throw new ImagingOpException("Unable to invert transform "
1441
                                     + xform.toString());
1442
      }
1443
 
1444
    // Unrecognized image - convert to a BufferedImage
1445
    // Note - this can get us in trouble when the gdk lock is re-acquired.
1446
    // for example by VolatileImage. See ComponentGraphics for how we work
1447
    // around this.
1448
    img = AsyncImage.realImage(img, obs);
1449
    if( !(img instanceof BufferedImage) )
1450
      {
1451
        ImageProducer source = img.getSource();
1452
        if (source == null)
1453
          return false;
1454
        img = Toolkit.getDefaultToolkit().createImage(source);
1455
      }
1456
 
1457
    BufferedImage b = (BufferedImage) img;
1458
    Raster raster;
1459
    double[] i2u = new double[6];
1460
    int width = b.getWidth();
1461
    int height = b.getHeight();
1462
 
1463
    // If this BufferedImage has a BufferedImageGraphics object,
1464
    // use the cached CairoSurface that BIG is drawing onto
1465
 
1466
    if( BufferedImageGraphics.bufferedImages.get( b ) != null )
1467
      raster = BufferedImageGraphics.bufferedImages.get( b );
1468
    else
1469
      raster = b.getRaster();
1470
 
1471
    invertedXform.getMatrix(i2u);
1472
 
1473
    double alpha = 1.0;
1474
    if (comp instanceof AlphaComposite)
1475
      alpha = ((AlphaComposite) comp).getAlpha();
1476
 
1477
    if(raster instanceof CairoSurface
1478
        && ((CairoSurface)raster).sharedBuffer == true)
1479
      {
1480
        drawCairoSurface((CairoSurface)raster, xform, alpha, getInterpolation());
1481
        updateColor();
1482
        return true;
1483
      }
1484
 
1485
    if( bgcolor != null )
1486
      {
1487
        Color oldColor = bg;
1488
        setBackground(bgcolor);
1489
 
1490
        Rectangle2D bounds = new Rectangle2D.Double(0, 0, width, height);
1491
        bounds = getTransformedBounds(bounds, xform);
1492
 
1493
        clearRect((int)bounds.getX(), (int)bounds.getY(),
1494
                  (int)bounds.getWidth(), (int)bounds.getHeight());
1495
 
1496
        setBackground(oldColor);
1497
      }
1498
 
1499
    int[] pixels = b.getRGB(0, 0, width, height, null, 0, width);
1500
    // FIXME: The above method returns data in the standard ARGB colorspace,
1501
    // meaning data should NOT be alpha pre-multiplied; however Cairo expects
1502
    // data to be premultiplied.
1503
 
1504
    cairoSave(nativePointer);
1505
    Rectangle2D bounds = new Rectangle2D.Double(0, 0, width, height);
1506
    bounds = getTransformedBounds(bounds, xform);
1507
    cairoRectangle(nativePointer, bounds.getX(), bounds.getY(),
1508
                   bounds.getWidth(), bounds.getHeight());
1509
    cairoClip(nativePointer);
1510
 
1511
    drawPixels(nativePointer, pixels, width, height, width, i2u, alpha,
1512
               getInterpolation());
1513
 
1514
    cairoRestore(nativePointer);
1515
 
1516
    // Cairo seems to lose the current color which must be restored.
1517
    updateColor();
1518
    return true;
1519
  }
1520
 
1521
  public void drawRenderedImage(RenderedImage image, AffineTransform xform)
1522
  {
1523
    drawRaster(image.getColorModel(), image.getData(), xform, null);
1524
  }
1525
 
1526
  public void drawRenderableImage(RenderableImage image, AffineTransform xform)
1527
  {
1528
    drawRenderedImage(image.createRendering(new RenderContext(xform)), xform);
1529
  }
1530
 
1531
  public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
1532
  {
1533
    return drawImage(img, xform, null, obs);
1534
  }
1535
 
1536
  public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
1537
  {
1538
    Image filtered = image;
1539
    if (op != null)
1540
      filtered = op.filter(image, null);
1541
    drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), null, null);
1542
  }
1543
 
1544
  public boolean drawImage(Image img, int x, int y, ImageObserver observer)
1545
  {
1546
    return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), null,
1547
                     observer);
1548
  }
1549
 
1550
  public boolean drawImage(Image img, int x, int y, Color bgcolor,
1551
                           ImageObserver observer)
1552
  {
1553
    return drawImage(img, x, y, img.getWidth(observer),
1554
                     img.getHeight(observer), bgcolor, observer);
1555
  }
1556
 
1557
  public boolean drawImage(Image img, int x, int y, int width, int height,
1558
                           Color bgcolor, ImageObserver observer)
1559
  {
1560
    double scaleX = width / (double) img.getWidth(observer);
1561
    double scaleY = height / (double) img.getHeight(observer);
1562
    if( scaleX == 0 || scaleY == 0 )
1563
      return true;
1564
 
1565
    return drawImage(img, new AffineTransform(scaleX, 0f, 0f, scaleY, x, y),
1566
                     bgcolor, observer);
1567
  }
1568
 
1569
  public boolean drawImage(Image img, int x, int y, int width, int height,
1570
                           ImageObserver observer)
1571
  {
1572
    return drawImage(img, x, y, width, height, null, observer);
1573
  }
1574
 
1575
  public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1576
                           int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1577
                           ImageObserver observer)
1578
  {
1579
    if (img == null)
1580
      return false;
1581
 
1582
    int sourceWidth = sx2 - sx1;
1583
    int sourceHeight = sy2 - sy1;
1584
 
1585
    int destWidth = dx2 - dx1;
1586
    int destHeight = dy2 - dy1;
1587
 
1588
    if(destWidth == 0 || destHeight == 0 || sourceWidth == 0 ||
1589
       sourceHeight == 0)
1590
      return true;
1591
 
1592
    double scaleX = destWidth / (double) sourceWidth;
1593
    double scaleY = destHeight / (double) sourceHeight;
1594
 
1595
    // FIXME: Avoid using an AT if possible here - it's at least twice as slow.
1596
 
1597
    Shape oldClip = getClip();
1598
    int cx, cy, cw, ch;
1599
    if( dx1 < dx2 )
1600
      { cx = dx1; cw = dx2 - dx1; }
1601
    else
1602
      { cx = dx2; cw = dx1 - dx2; }
1603
    if( dy1 < dy2 )
1604
      { cy = dy1; ch = dy2 - dy1; }
1605
    else
1606
      { cy = dy2; ch = dy1 - dy2; }
1607
 
1608
    clipRect( cx, cy, cw, ch );
1609
 
1610
    AffineTransform tx = new AffineTransform();
1611
    tx.translate( dx1 - sx1*scaleX, dy1 - sy1*scaleY );
1612
    tx.scale( scaleX, scaleY );
1613
 
1614
    boolean retval = drawImage(img, tx, bgcolor, observer);
1615
    setClip( oldClip );
1616
    return retval;
1617
  }
1618
 
1619
  public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1620
                           int sx1, int sy1, int sx2, int sy2,
1621
                           ImageObserver observer)
1622
  {
1623
    return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer);
1624
  }
1625
 
1626
  /**
1627
   * Optimized method for drawing a CairoSurface onto this graphics context.
1628
   *
1629
   * @param surface The surface to draw.
1630
   * @param tx The transformation matrix (cannot be null).
1631
   * @param alpha The alpha value to paint with ( 0 <= alpha <= 1).
1632
   * @param interpolation The interpolation type.
1633
   */
1634
  protected void drawCairoSurface(CairoSurface surface, AffineTransform tx,
1635
                                  double alpha, int interpolation)
1636
  {
1637
    // Find offset required if this surface is a sub-raster, and append offset
1638
    // to transformation.
1639
    if (surface.getSampleModelTranslateX() != 0
1640
        || surface.getSampleModelTranslateY() != 0)
1641
      {
1642
        Point2D origin = new Point2D.Double(0, 0);
1643
        Point2D offset = new Point2D.Double(surface.getSampleModelTranslateX(),
1644
                                            surface.getSampleModelTranslateY());
1645
 
1646
        tx.transform(origin, origin);
1647
        tx.transform(offset, offset);
1648
 
1649
        tx.translate(offset.getX() - origin.getX(),
1650
                     offset.getY() - origin.getY());
1651
      }
1652
 
1653
    // Find dimensions of this surface relative to the root parent surface
1654
    Rectangle bounds = new Rectangle(-surface.getSampleModelTranslateX(),
1655
                                     -surface.getSampleModelTranslateY(),
1656
                                     surface.width, surface.height);
1657
 
1658
    // Clip to the translated image
1659
    //   We use direct cairo methods to avoid the overhead of maintaining a
1660
    //   java copy of the clip, since we will be reverting it immediately
1661
    //   after drawing
1662
    Shape newBounds = tx.createTransformedShape(bounds);
1663
    cairoSave(nativePointer);
1664
    walkPath(newBounds.getPathIterator(null), false);
1665
    cairoClip(nativePointer);
1666
 
1667
    // Draw the surface
1668
    try
1669
    {
1670
      double[] i2u = new double[6];
1671
      tx.createInverse().getMatrix(i2u);
1672
      surface.nativeDrawSurface(surface.surfacePointer, nativePointer, i2u,
1673
                                alpha, interpolation);
1674
    }
1675
    catch (NoninvertibleTransformException ex)
1676
    {
1677
      // This should never happen(?), so we don't need to do anything here.
1678
      ;
1679
    }
1680
 
1681
    // Restore clip
1682
    cairoRestore(nativePointer);
1683
  }
1684
 
1685
 
1686
  ///////////////////////// TEXT METHODS ////////////////////////////////////
1687
 
1688
  public void drawString(String str, float x, float y)
1689
  {
1690
    if (str == null || str.length() == 0)
1691
      return;
1692
    GdkFontPeer fontPeer = (GdkFontPeer) font.getPeer();
1693
    TextLayout tl = (TextLayout) fontPeer.textLayoutCache.get(str);
1694
    if (tl == null)
1695
      {
1696
        tl = new TextLayout( str, getFont(), getFontRenderContext() );
1697
        fontPeer.textLayoutCache.put(str, tl);
1698
      }
1699
 
1700
    // Set antialias to text_antialiasing, and set the ignoreAA flag so that
1701
    // the setting doesn't get overridden in a draw() or fill() call.
1702
    setAntialias(!hints.get(RenderingHints.KEY_TEXT_ANTIALIASING)
1703
                       .equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
1704
    ignoreAA = true;
1705
 
1706
    tl.draw(this, x, y);
1707
    ignoreAA = false;
1708
  }
1709
 
1710
  public void drawString(String str, int x, int y)
1711
  {
1712
    drawString (str, (float) x, (float) y);
1713
  }
1714
 
1715
  public void drawString(AttributedCharacterIterator ci, int x, int y)
1716
  {
1717
    drawString (ci, (float) x, (float) y);
1718
  }
1719
 
1720
  public void drawGlyphVector(GlyphVector gv, float x, float y)
1721
  {
1722
    double alpha = 1.0;
1723
 
1724
    if( gv.getNumGlyphs() <= 0 )
1725
      return;
1726
 
1727
    if (customPaint)
1728
      setCustomPaint(gv.getOutline().getBounds());
1729
 
1730
    if (comp instanceof AlphaComposite)
1731
      alpha = ((AlphaComposite) comp).getAlpha();
1732
 
1733
    setAntialias(!hints.get(RenderingHints.KEY_TEXT_ANTIALIASING)
1734
                       .equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
1735
    ignoreAA = true;
1736
 
1737
    if (gv instanceof FreetypeGlyphVector && alpha == 1.0
1738
        && !((FreetypeGlyphVector)gv).hasTransforms())
1739
      {
1740
        int n = gv.getNumGlyphs ();
1741
        int[] codes = gv.getGlyphCodes (0, n, null);
1742
        long[] fontset = ((FreetypeGlyphVector)gv).getGlyphFonts (0, n, null);
1743
        float[] positions = gv.getGlyphPositions (0, n, null);
1744
 
1745
        setFont (gv.getFont ());
1746
        GdkFontPeer fontPeer = (GdkFontPeer) font.getPeer();
1747
        synchronized (fontPeer)
1748
          {
1749
            cairoDrawGlyphVector(nativePointer, fontPeer,
1750
                                 x, y, n, codes, positions, fontset);
1751
          }
1752
      }
1753
    else
1754
      {
1755
        translate(x, y);
1756
        fill(gv.getOutline());
1757
        translate(-x, -y);
1758
      }
1759
 
1760
    ignoreAA = false;
1761
  }
1762
 
1763
  public void drawString(AttributedCharacterIterator ci, float x, float y)
1764
  {
1765
    GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), ci);
1766
    drawGlyphVector(gv, x, y);
1767
  }
1768
 
1769
  /**
1770
   * Should perhaps be contexct dependent, but this is left for now as an
1771
   * overloadable default implementation.
1772
   */
1773
  public FontRenderContext getFontRenderContext()
1774
  {
1775
    return new FontRenderContext(transform, true, true);
1776
  }
1777
 
1778
  // Until such time as pango is happy to talk directly to cairo, we
1779
  // actually need to redirect some calls from the GtkFontPeer and
1780
  // GtkFontMetrics into the drawing kit and ask cairo ourselves.
1781
 
1782
  public FontMetrics getFontMetrics()
1783
  {
1784
    return getFontMetrics(getFont());
1785
  }
1786
 
1787
  public FontMetrics getFontMetrics(Font f)
1788
  {
1789
    return ((GdkFontPeer) f.getPeer()).getFontMetrics(f);
1790
  }
1791
 
1792
  public void setFont(Font f)
1793
  {
1794
    // Sun's JDK does not throw NPEs, instead it leaves the current setting
1795
    // unchanged. So do we.
1796
    if (f == null)
1797
      return;
1798
 
1799
    if (f.getPeer() instanceof GdkFontPeer)
1800
      font = f;
1801
    else
1802
      font =
1803
        ((ClasspathToolkit)(Toolkit.getDefaultToolkit()))
1804
        .getFont(f.getName(), f.getAttributes());
1805
 
1806
    GdkFontPeer fontpeer = (GdkFontPeer) getFont().getPeer();
1807
    synchronized (fontpeer)
1808
      {
1809
        cairoSetFont(nativePointer, fontpeer);
1810
      }
1811
  }
1812
 
1813
  public Font getFont()
1814
  {
1815
    if (font == null)
1816
      return new Font("SansSerif", Font.PLAIN, 12);
1817
    return font;
1818
  }
1819
 
1820
  /////////////////////// MISC. PUBLIC METHODS /////////////////////////////////
1821
 
1822
  public boolean hit(Rectangle rect, Shape s, boolean onStroke)
1823
  {
1824
    if( onStroke )
1825
      {
1826
        Shape stroked = stroke.createStrokedShape( s );
1827
        return stroked.intersects( (double)rect.x, (double)rect.y,
1828
                                   (double)rect.width, (double)rect.height );
1829
      }
1830
    return s.intersects( (double)rect.x, (double)rect.y,
1831
                         (double)rect.width, (double)rect.height );
1832
  }
1833
 
1834
  public String toString()
1835
  {
1836
    return  (getClass().getName()
1837
             + "[font=" + getFont().toString()
1838
             + ",color=" + fg.toString()
1839
             + "]");
1840
  }
1841
 
1842
  ///////////////////////// PRIVATE METHODS ///////////////////////////////////
1843
 
1844
  /**
1845
   * All the drawImage() methods eventually get delegated here if the image
1846
   * is not a Cairo surface.
1847
   *
1848
   * @param bgcolor - if non-null draws the background color before
1849
   * drawing the image.
1850
   */
1851
  private boolean drawRaster(ColorModel cm, Raster r,
1852
                             AffineTransform imageToUser, Color bgcolor)
1853
  {
1854
    if (r == null)
1855
      return false;
1856
 
1857
    SampleModel sm = r.getSampleModel();
1858
    DataBuffer db = r.getDataBuffer();
1859
 
1860
    if (db == null || sm == null)
1861
      return false;
1862
 
1863
    if (cm == null)
1864
      cm = ColorModel.getRGBdefault();
1865
 
1866
    double[] i2u = new double[6];
1867
    if (imageToUser != null)
1868
      imageToUser.getMatrix(i2u);
1869
    else
1870
      {
1871
        i2u[0] = 1;
1872
        i2u[1] = 0;
1873
        i2u[2] = 0;
1874
        i2u[3] = 1;
1875
        i2u[4] = 0;
1876
        i2u[5] = 0;
1877
      }
1878
 
1879
    int[] pixels = findSimpleIntegerArray(cm, r);
1880
 
1881
    if (pixels == null)
1882
      {
1883
        // FIXME: I don't think this code will work correctly with a non-RGB
1884
        // MultiPixelPackedSampleModel. Although this entire method should
1885
        // probably be rewritten to better utilize Cairo's different supported
1886
        // data formats.
1887
        if (sm instanceof MultiPixelPackedSampleModel)
1888
          {
1889
            pixels = r.getPixels(0, 0, r.getWidth(), r.getHeight(), pixels);
1890
            for (int i = 0; i < pixels.length; i++)
1891
              pixels[i] = cm.getRGB(pixels[i]);
1892
          }
1893
        else
1894
          {
1895
            pixels = new int[r.getWidth() * r.getHeight()];
1896
            for (int i = 0; i < pixels.length; i++)
1897
              pixels[i] = cm.getRGB(db.getElem(i));
1898
          }
1899
      }
1900
 
1901
    // Change all transparent pixels in the image to the specified bgcolor,
1902
    // or (if there's no alpha) fill in an alpha channel so that it paints
1903
    // correctly.
1904
    if (cm.hasAlpha())
1905
      {
1906
        if (bgcolor != null && cm.hasAlpha())
1907
          for (int i = 0; i < pixels.length; i++)
1908
            {
1909
              if (cm.getAlpha(pixels[i]) == 0)
1910
                pixels[i] = bgcolor.getRGB();
1911
            }
1912
      }
1913
    else
1914
      for (int i = 0; i < pixels.length; i++)
1915
        pixels[i] |= 0xFF000000;
1916
 
1917
    double alpha = 1.0;
1918
    if (comp instanceof AlphaComposite)
1919
      alpha = ((AlphaComposite) comp).getAlpha();
1920
 
1921
    drawPixels(nativePointer, pixels, r.getWidth(), r.getHeight(),
1922
               r.getWidth(), i2u, alpha, getInterpolation());
1923
 
1924
    // Cairo seems to lose the current color which must be restored.
1925
    updateColor();
1926
 
1927
    return true;
1928
  }
1929
 
1930
  /**
1931
   * Shifts an x-coordinate by 0.5 in device space.
1932
   */
1933
  private double shiftX(double coord, boolean doShift)
1934
  {
1935
    if (doShift)
1936
      {
1937
        double shift = 0.5;
1938
        if (!transform.isIdentity())
1939
          shift /= transform.getScaleX();
1940
        return (coord + shift);
1941
      }
1942
    else
1943
      return coord;
1944
  }
1945
 
1946
  /**
1947
   * Shifts a y-coordinate by 0.5 in device space.
1948
   */
1949
  private double shiftY(double coord, boolean doShift)
1950
  {
1951
    if (doShift)
1952
      {
1953
        double shift = 0.5;
1954
        if (!transform.isIdentity())
1955
          shift /= transform.getScaleY();
1956
        return (coord + shift);
1957
      }
1958
    else
1959
      return coord;
1960
  }
1961
 
1962
  /**
1963
   * Adds a pathIterator to the current Cairo path, also sets the cairo winding rule.
1964
   */
1965
  private void walkPath(PathIterator p, boolean doShift)
1966
  {
1967
    double x = 0;
1968
    double y = 0;
1969
    double[] coords = new double[6];
1970
 
1971
    cairoSetFillRule(nativePointer, p.getWindingRule());
1972
    for (; ! p.isDone(); p.next())
1973
      {
1974
        int seg = p.currentSegment(coords);
1975
        switch (seg)
1976
          {
1977
          case PathIterator.SEG_MOVETO:
1978
            x = shiftX(coords[0], doShift);
1979
            y = shiftY(coords[1], doShift);
1980
            cairoMoveTo(nativePointer, x, y);
1981
            break;
1982
          case PathIterator.SEG_LINETO:
1983
            x = shiftX(coords[0], doShift);
1984
            y = shiftY(coords[1], doShift);
1985
            cairoLineTo(nativePointer, x, y);
1986
            break;
1987
          case PathIterator.SEG_QUADTO:
1988
            // splitting a quadratic bezier into a cubic:
1989
            // see: http://pfaedit.sourceforge.net/bezier.html
1990
            double x1 = x + (2.0 / 3.0) * (shiftX(coords[0], doShift) - x);
1991
            double y1 = y + (2.0 / 3.0) * (shiftY(coords[1], doShift) - y);
1992
 
1993
            double x2 = x1 + (1.0 / 3.0) * (shiftX(coords[2], doShift) - x);
1994
            double y2 = y1 + (1.0 / 3.0) * (shiftY(coords[3], doShift) - y);
1995
 
1996
            x = shiftX(coords[2], doShift);
1997
            y = shiftY(coords[3], doShift);
1998
            cairoCurveTo(nativePointer, x1, y1, x2, y2, x, y);
1999
            break;
2000
          case PathIterator.SEG_CUBICTO:
2001
            x = shiftX(coords[4], doShift);
2002
            y = shiftY(coords[5], doShift);
2003
            cairoCurveTo(nativePointer, shiftX(coords[0], doShift),
2004
                         shiftY(coords[1], doShift),
2005
                         shiftX(coords[2], doShift),
2006
                         shiftY(coords[3], doShift), x, y);
2007
            break;
2008
          case PathIterator.SEG_CLOSE:
2009
            cairoClosePath(nativePointer);
2010
            break;
2011
          }
2012
      }
2013
  }
2014
 
2015
  /**
2016
   * Used by setRenderingHints()
2017
   */
2018
  private Map<RenderingHints.Key, Object> getDefaultHints()
2019
  {
2020
    HashMap<RenderingHints.Key, Object> defaultHints =
2021
      new HashMap<RenderingHints.Key, Object>();
2022
 
2023
    defaultHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
2024
                     RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
2025
 
2026
    defaultHints.put(RenderingHints.KEY_STROKE_CONTROL,
2027
                     RenderingHints.VALUE_STROKE_DEFAULT);
2028
 
2029
    defaultHints.put(RenderingHints.KEY_FRACTIONALMETRICS,
2030
                     RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
2031
 
2032
    defaultHints.put(RenderingHints.KEY_ANTIALIASING,
2033
                     RenderingHints.VALUE_ANTIALIAS_OFF);
2034
 
2035
    defaultHints.put(RenderingHints.KEY_RENDERING,
2036
                     RenderingHints.VALUE_RENDER_DEFAULT);
2037
 
2038
    return defaultHints;
2039
  }
2040
 
2041
  /**
2042
   * Used by drawRaster and GdkPixbufDecoder
2043
   */
2044
  public static int[] findSimpleIntegerArray (ColorModel cm, Raster raster)
2045
  {
2046
    if (cm == null || raster == null)
2047
      return null;
2048
 
2049
    if (! cm.getColorSpace().isCS_sRGB())
2050
      return null;
2051
 
2052
    if (! (cm instanceof DirectColorModel))
2053
      return null;
2054
 
2055
    DirectColorModel dcm = (DirectColorModel) cm;
2056
 
2057
    if (dcm.getRedMask() != 0x00FF0000 || dcm.getGreenMask() != 0x0000FF00
2058
        || dcm.getBlueMask() != 0x000000FF)
2059
      return null;
2060
 
2061
    if (! (raster instanceof WritableRaster))
2062
      return null;
2063
 
2064
    if (raster.getSampleModel().getDataType() != DataBuffer.TYPE_INT)
2065
      return null;
2066
 
2067
    if (! (raster.getDataBuffer() instanceof DataBufferInt))
2068
      return null;
2069
 
2070
    DataBufferInt db = (DataBufferInt) raster.getDataBuffer();
2071
 
2072
    if (db.getNumBanks() != 1)
2073
      return null;
2074
 
2075
    // Finally, we have determined that this is a single bank, [A]RGB-int
2076
    // buffer in sRGB space. It's worth checking all this, because it means
2077
    // that cairo can paint directly into the data buffer, which is very
2078
    // fast compared to all the normal copying and converting.
2079
 
2080
    return db.getData();
2081
  }
2082
 
2083
  /**
2084
   * Helper method to transform the clip. This is called by the various
2085
   * transformation-manipulation methods to update the clip (which is in
2086
   * userspace) accordingly.
2087
   *
2088
   * The transform usually is the inverse transform that was applied to the
2089
   * graphics object.
2090
   *
2091
   * @param t the transform to apply to the clip
2092
   */
2093
  private void updateClip(AffineTransform t)
2094
  {
2095
    if (clip == null)
2096
      return;
2097
 
2098
    // If the clip is a rectangle, and the transformation preserves the shape
2099
    // (translate/stretch only), then keep the clip as a rectangle
2100
    double[] matrix = new double[4];
2101
    t.getMatrix(matrix);
2102
    if (clip instanceof Rectangle2D && matrix[1] == 0 && matrix[2] == 0)
2103
      {
2104
        Rectangle2D rect = (Rectangle2D)clip;
2105
        double[] origin = new double[] {rect.getX(), rect.getY()};
2106
        double[] dimensions = new double[] {rect.getWidth(), rect.getHeight()};
2107
        t.transform(origin, 0, origin, 0, 1);
2108
        t.deltaTransform(dimensions, 0, dimensions, 0, 1);
2109
        rect.setRect(origin[0], origin[1], dimensions[0], dimensions[1]);
2110
      }
2111
    else
2112
      {
2113
        if (! (clip instanceof GeneralPath))
2114
          clip = new GeneralPath(clip);
2115
 
2116
        GeneralPath p = (GeneralPath) clip;
2117
        p.transform(t);
2118
      }
2119
  }
2120
 
2121
  private static Rectangle computeIntersection(int x, int y, int w, int h,
2122
                                               Rectangle rect)
2123
  {
2124
    int x2 = rect.x;
2125
    int y2 = rect.y;
2126
    int w2 = rect.width;
2127
    int h2 = rect.height;
2128
 
2129
    int dx = (x > x2) ? x : x2;
2130
    int dy = (y > y2) ? y : y2;
2131
    int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
2132
    int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
2133
 
2134
    if (dw >= 0 && dh >= 0)
2135
      rect.setBounds(dx, dy, dw, dh);
2136
    else
2137
      rect.setBounds(0, 0, 0, 0);
2138
 
2139
    return rect;
2140
  }
2141
 
2142
  static Rectangle2D getTransformedBounds(Rectangle2D bounds, AffineTransform tx)
2143
  {
2144
    double x1 = bounds.getX();
2145
    double x2 = bounds.getX() + bounds.getWidth();
2146
    double x3 = x1;
2147
    double x4 = x2;
2148
    double y1 = bounds.getY();
2149
    double y2 = y1;
2150
    double y3 = bounds.getY() + bounds.getHeight();
2151
    double y4 = y3;
2152
 
2153
    double[] points = new double[] {x1, y1, x2, y2, x3, y3, x4, y4};
2154
    tx.transform(points, 0, points, 0, 4);
2155
 
2156
    double minX = points[0];
2157
    double maxX = minX;
2158
    double minY = points[1];
2159
    double maxY = minY;
2160
    for (int i = 0; i < 8; i++)
2161
      {
2162
        if (points[i] < minX)
2163
          minX = points[i];
2164
        if (points[i] > maxX)
2165
          maxX = points[i];
2166
        i++;
2167
 
2168
        if (points[i] < minY)
2169
          minY = points[i];
2170
        if (points[i] > maxY)
2171
          maxY = points[i];
2172
      }
2173
 
2174
    return new Rectangle2D.Double(minX, minY, (maxX - minX), (maxY - minY));
2175
  }
2176
}

powered by: WebSVN 2.1.0

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