Canvas_Delegate.java revision 3054fe698a6be732e24128541a1db867ec606a59
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics;
18
19import com.android.layoutlib.bridge.Bridge;
20import com.android.layoutlib.bridge.impl.DelegateManager;
21import com.android.layoutlib.bridge.impl.Stack;
22
23import android.graphics.Paint_Delegate.FontInfo;
24import android.text.TextUtils;
25
26import java.awt.AlphaComposite;
27import java.awt.BasicStroke;
28import java.awt.Color;
29import java.awt.Composite;
30import java.awt.Graphics2D;
31import java.awt.Rectangle;
32import java.awt.RenderingHints;
33import java.awt.geom.AffineTransform;
34import java.awt.image.BufferedImage;
35import java.util.List;
36
37
38/**
39 * Delegate implementing the native methods of android.graphics.Canvas
40 *
41 * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
42 * by calls to methods of the same name in this delegate class.
43 *
44 * This class behaves like the original native implementation, but in Java, keeping previously
45 * native data into its own objects and mapping them to int that are sent back and forth between
46 * it and the original Canvas class.
47 *
48 * @see DelegateManager
49 *
50 */
51public class Canvas_Delegate {
52
53    // ---- delegate manager ----
54    private static final DelegateManager<Canvas_Delegate> sManager =
55            new DelegateManager<Canvas_Delegate>();
56
57    // ---- delegate helper data ----
58
59    // ---- delegate data ----
60    private BufferedImage mBufferedImage;
61    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
62
63    // ---- Public Helper methods ----
64
65    /**
66     * Returns the native delegate associated to a given {@link Canvas} object.
67     */
68    public static Canvas_Delegate getDelegate(Canvas canvas) {
69        return sManager.getDelegate(canvas.mNativeCanvas);
70    }
71
72    /**
73     * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
74     */
75    public static Canvas_Delegate getDelegate(int native_canvas) {
76        return sManager.getDelegate(native_canvas);
77    }
78
79    /**
80     * Returns the current {@link Graphics2D} used to draw.
81     */
82    public Graphics2D getGraphics2d() {
83        return mGraphicsStack.peek();
84    }
85
86    // ---- native methods ----
87
88    /*package*/ static boolean isOpaque(Canvas thisCanvas) {
89        // FIXME
90        throw new UnsupportedOperationException();
91    }
92
93    /*package*/ static int getWidth(Canvas thisCanvas) {
94        // get the delegate from the native int.
95        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
96        if (canvasDelegate == null) {
97            assert false;
98            return 0;
99        }
100
101        return canvasDelegate.mBufferedImage.getWidth();
102    }
103
104    /*package*/ static int getHeight(Canvas thisCanvas) {
105        // get the delegate from the native int.
106        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
107        if (canvasDelegate == null) {
108            assert false;
109            return 0;
110        }
111
112        return canvasDelegate.mBufferedImage.getHeight();
113    }
114
115    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
116        // get the delegate from the native int.
117        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
118        if (canvasDelegate == null) {
119            assert false;
120            return;
121        }
122
123        canvasDelegate.getGraphics2d().translate(dx, dy);
124    }
125
126    /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
127        // get the delegate from the native int.
128        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
129        if (canvasDelegate == null) {
130            assert false;
131            return;
132        }
133
134        canvasDelegate.getGraphics2d().rotate(Math.toRadians(degrees));
135    }
136
137    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
138        // get the delegate from the native int.
139        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
140        if (canvasDelegate == null) {
141            assert false;
142            return;
143        }
144
145        canvasDelegate.getGraphics2d().scale(sx, sy);
146    }
147
148    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
149        // get the delegate from the native int.
150        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
151        if (canvasDelegate == null) {
152            assert false;
153            return;
154        }
155
156        // get the current top graphics2D object.
157        Graphics2D g = canvasDelegate.getGraphics2d();
158
159        // get its current matrix
160        AffineTransform currentTx = g.getTransform();
161        // get the AffineTransform for the given skew.
162        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
163        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
164
165        // combine them so that the given matrix is applied after.
166        currentTx.preConcatenate(matrixTx);
167
168        // give it to the graphics2D as a new matrix replacing all previous transform
169        g.setTransform(currentTx);
170    }
171
172    /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
173        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
174    }
175
176    /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
177        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
178    }
179
180    /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
181            float bottom) {
182        // get the delegate from the native int.
183        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
184        if (canvasDelegate == null) {
185            assert false;
186            return false;
187        }
188
189        canvasDelegate.getGraphics2d().clipRect((int)left, (int)top, (int)(right-left),
190                (int)(bottom-top));
191        return true;
192    }
193
194    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
195            int bottom) {
196        // get the delegate from the native int.
197        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
198        if (canvasDelegate == null) {
199            assert false;
200            return false;
201        }
202
203        canvasDelegate.getGraphics2d().clipRect(left, top, right - left, bottom - top);
204        return true;
205    }
206
207    /*package*/ static int save(Canvas thisCanvas) {
208        // get the delegate from the native int.
209        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
210        if (canvasDelegate == null) {
211            assert false;
212            return 0;
213        }
214
215        // get the current save count
216        int count = canvasDelegate.mGraphicsStack.size();
217
218        // create a new graphics and add it to the stack
219        Graphics2D g = (Graphics2D)canvasDelegate.getGraphics2d().create();
220        canvasDelegate.mGraphicsStack.push(g);
221
222        // return the old save count
223        return count;
224
225    }
226
227    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
228        // FIXME implement save(flags)
229        return save(thisCanvas);
230    }
231
232    /*package*/ static void restore(Canvas thisCanvas) {
233        // get the delegate from the native int.
234        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
235        if (canvasDelegate == null) {
236            assert false;
237            return;
238        }
239
240        canvasDelegate.mGraphicsStack.pop();
241    }
242
243    /*package*/ static int getSaveCount(Canvas thisCanvas) {
244        // get the delegate from the native int.
245        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
246        if (canvasDelegate == null) {
247            assert false;
248            return 0;
249        }
250
251        return canvasDelegate.mGraphicsStack.size();
252    }
253
254    /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
255        // get the delegate from the native int.
256        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
257        if (canvasDelegate == null) {
258            assert false;
259            return;
260        }
261
262        while (canvasDelegate.mGraphicsStack.size() > saveCount) {
263            canvasDelegate.mGraphicsStack.pop();
264        }
265    }
266
267    /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
268            Paint paint) {
269        // FIXME
270        throw new UnsupportedOperationException();
271    }
272
273    /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
274        // FIXME
275        throw new UnsupportedOperationException();
276    }
277
278    /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count,
279            Paint paint) {
280        // FIXME
281        throw new UnsupportedOperationException();
282    }
283
284    /*package*/ static void freeCaches() {
285        // FIXME
286        throw new UnsupportedOperationException();
287    }
288
289    /*package*/ static int initRaster(int nativeBitmapOrZero) {
290        if (nativeBitmapOrZero > 0) {
291            // get the Bitmap from the int
292            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
293
294            // create a new Canvas_Delegate with the given bitmap and return its new native int.
295            Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate.getImage());
296
297            return sManager.addDelegate(newDelegate);
298        } else {
299            // create a new Canvas_Delegate and return its new native int.
300            Canvas_Delegate newDelegate = new Canvas_Delegate();
301
302            return sManager.addDelegate(newDelegate);
303        }
304    }
305
306    /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) {
307        // get the delegate from the native int.
308        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
309        if (canvasDelegate == null) {
310            assert false;
311            return;
312        }
313
314        // get the delegate from the native int.
315        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
316        if (bitmapDelegate == null) {
317            assert false;
318            return;
319        }
320
321        canvasDelegate.setBitmap(bitmapDelegate.getImage());
322    }
323
324    /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
325                                               int paint, int layerFlags) {
326        // FIXME
327        throw new UnsupportedOperationException();
328    }
329
330    /*package*/ static int native_saveLayer(int nativeCanvas, float l,
331                                               float t, float r, float b,
332                                               int paint, int layerFlags) {
333        // FIXME
334        throw new UnsupportedOperationException();
335    }
336
337    /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
338                                                    RectF bounds, int alpha,
339                                                    int layerFlags) {
340        // FIXME
341        throw new UnsupportedOperationException();
342    }
343
344    /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
345                                                    float t, float r, float b,
346                                                    int alpha, int layerFlags) {
347        // FIXME
348        throw new UnsupportedOperationException();
349    }
350
351
352    /*package*/ static void native_concat(int nCanvas, int nMatrix) {
353        // get the delegate from the native int.
354        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
355        if (canvasDelegate == null) {
356            assert false;
357            return;
358        }
359
360        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
361        if (matrixDelegate == null) {
362            assert false;
363            return;
364        }
365
366        // get the current top graphics2D object.
367        Graphics2D g = canvasDelegate.getGraphics2d();
368
369        // get its current matrix
370        AffineTransform currentTx = g.getTransform();
371        // get the AffineTransform of the given matrix
372        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
373
374        // combine them so that the given matrix is applied after.
375        currentTx.preConcatenate(matrixTx);
376
377        // give it to the graphics2D as a new matrix replacing all previous transform
378        g.setTransform(currentTx);
379    }
380
381    /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
382        // get the delegate from the native int.
383        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
384        if (canvasDelegate == null) {
385            assert false;
386        }
387
388        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
389        if (matrixDelegate == null) {
390            assert false;
391        }
392
393        // get the current top graphics2D object.
394        Graphics2D g = canvasDelegate.getGraphics2d();
395
396        // get the AffineTransform of the given matrix
397        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
398
399        // give it to the graphics2D as a new matrix replacing all previous transform
400        g.setTransform(matrixTx);
401
402        if (matrixDelegate.hasPerspective()) {
403            Bridge.getLog().warning(null,
404                    "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
405                    "supports affine transformations in the Layout Preview.");
406        }
407    }
408
409    /*package*/ static boolean native_clipRect(int nCanvas,
410                                                  float left, float top,
411                                                  float right, float bottom,
412                                                  int regionOp) {
413        // FIXME
414        throw new UnsupportedOperationException();
415    }
416
417    /*package*/ static boolean native_clipPath(int nativeCanvas,
418                                                  int nativePath,
419                                                  int regionOp) {
420        // FIXME
421        throw new UnsupportedOperationException();
422    }
423
424    /*package*/ static boolean native_clipRegion(int nativeCanvas,
425                                                    int nativeRegion,
426                                                    int regionOp) {
427        // FIXME
428        throw new UnsupportedOperationException();
429    }
430
431    /*package*/ static void nativeSetDrawFilter(int nativeCanvas,
432                                                   int nativeFilter) {
433        // FIXME
434        throw new UnsupportedOperationException();
435    }
436
437    /*package*/ static boolean native_getClipBounds(int nativeCanvas,
438                                                       Rect bounds) {
439        // get the delegate from the native int.
440        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
441        if (canvasDelegate == null) {
442            assert false;
443            return false;
444        }
445
446        Rectangle rect = canvasDelegate.getGraphics2d().getClipBounds();
447        if (rect != null) {
448            bounds.left = rect.x;
449            bounds.top = rect.y;
450            bounds.right = rect.x + rect.width;
451            bounds.bottom = rect.y + rect.height;
452            return true;
453        }
454        return false;
455    }
456
457    /*package*/ static void native_getCTM(int canvas, int matrix) {
458        // FIXME
459        throw new UnsupportedOperationException();
460    }
461
462    /*package*/ static boolean native_quickReject(int nativeCanvas,
463                                                     RectF rect,
464                                                     int native_edgeType) {
465        // FIXME properly implement quickReject
466        return false;
467    }
468
469    /*package*/ static boolean native_quickReject(int nativeCanvas,
470                                                     int path,
471                                                     int native_edgeType) {
472        // FIXME properly implement quickReject
473        return false;
474    }
475
476    /*package*/ static boolean native_quickReject(int nativeCanvas,
477                                                     float left, float top,
478                                                     float right, float bottom,
479                                                     int native_edgeType) {
480        // FIXME properly implement quickReject
481        return false;
482    }
483
484    /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) {
485        native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
486                PorterDuff.Mode.SRC_OVER.nativeInt);
487
488    }
489
490    /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) {
491        native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
492                PorterDuff.Mode.SRC_OVER.nativeInt);
493    }
494
495    /*package*/ static void native_drawColor(int nativeCanvas, int color) {
496        native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
497    }
498
499    /*package*/ static void native_drawColor(int nativeCanvas, int color, int mode) {
500        // get the delegate from the native int.
501        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
502        if (canvasDelegate == null) {
503            assert false;
504            return;
505        }
506
507        // get a new graphics context.
508        Graphics2D graphics = (Graphics2D)canvasDelegate.getGraphics2d().create();
509        try {
510            // reset its transform just in case
511            graphics.setTransform(new AffineTransform());
512
513            // set the color
514            graphics.setColor(new Color(color, true /*alpha*/));
515
516            setModeInGraphics(graphics, mode);
517
518            graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(),
519                    canvasDelegate.mBufferedImage.getHeight());
520        } finally {
521            // dispose Graphics2D object
522            graphics.dispose();
523        }
524    }
525
526    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
527        // FIXME
528        throw new UnsupportedOperationException();
529    }
530
531    /*package*/ static void native_drawLine(int nativeCanvas, float startX,
532                                               float startY, float stopX,
533                                               float stopY, int paint) {
534        // get the delegate from the native int.
535        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
536        if (canvasDelegate == null) {
537            assert false;
538            return;
539        }
540
541        // get the delegate from the native int.
542        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
543        if (paintDelegate == null) {
544            assert false;
545            return;
546        }
547
548        // get a Graphics2D object configured with the drawing parameters.
549        Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
550
551        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
552
553        // dispose Graphics2D object
554        g.dispose();
555    }
556
557    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
558                                               int paint) {
559        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
560    }
561
562    /*package*/ static void native_drawRect(int nativeCanvas, float left,
563                                               float top, float right,
564                                               float bottom, int paint) {
565        // get the delegate from the native int.
566        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
567        if (canvasDelegate == null) {
568            assert false;
569            return;
570        }
571
572        // get the delegate from the native int.
573        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
574        if (paintDelegate == null) {
575            assert false;
576            return;
577        }
578
579        if (right > left && bottom > top) {
580            // get a Graphics2D object configured with the drawing parameters.
581            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
582
583            int style = paintDelegate.getStyle();
584
585            // draw
586            if (style == Paint.Style.FILL.nativeInt ||
587                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
588                g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
589            }
590
591            if (style == Paint.Style.STROKE.nativeInt ||
592                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
593                g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
594            }
595
596            // dispose Graphics2D object
597            g.dispose();
598        }
599    }
600
601    /*package*/ static void native_drawOval(int nativeCanvas, RectF oval,
602                                               int paint) {
603        // get the delegate from the native int.
604        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
605        if (canvasDelegate == null) {
606            assert false;
607            return;
608        }
609
610        // get the delegate from the native int.
611        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
612        if (paintDelegate == null) {
613            assert false;
614            return;
615        }
616
617        if (oval.right > oval.left && oval.bottom > oval.top) {
618            // get a Graphics2D object configured with the drawing parameters.
619            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
620
621            int style = paintDelegate.getStyle();
622
623            // draw
624            if (style == Paint.Style.FILL.nativeInt ||
625                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
626                g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
627            }
628
629            if (style == Paint.Style.STROKE.nativeInt ||
630                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
631                g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
632            }
633
634            // dispose Graphics2D object
635            g.dispose();
636        }
637    }
638
639    /*package*/ static void native_drawCircle(int nativeCanvas, float cx,
640                                                 float cy, float radius,
641                                                 int paint) {
642        native_drawOval(nativeCanvas,
643                new RectF(cx - radius, cy - radius, radius*2, radius*2),
644                paint);
645    }
646
647    /*package*/ static void native_drawArc(int nativeCanvas, RectF oval,
648                                              float startAngle, float sweep,
649                                              boolean useCenter, int paint) {
650        // FIXME
651        throw new UnsupportedOperationException();
652    }
653
654    /*package*/ static void native_drawRoundRect(int nativeCanvas,
655                                                    RectF rect, float rx,
656                                                    float ry, int paint) {
657        // get the delegate from the native int.
658        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
659        if (canvasDelegate == null) {
660            assert false;
661            return;
662        }
663
664        // get the delegate from the native int.
665        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
666        if (paintDelegate == null) {
667            assert false;
668            return;
669        }
670
671        if (rect.right > rect.left && rect.bottom > rect.top) {
672            // get a Graphics2D object configured with the drawing parameters.
673            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
674
675            int style = paintDelegate.getStyle();
676
677            // draw
678            if (style == Paint.Style.FILL.nativeInt ||
679                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
680                g.fillRoundRect(
681                        (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
682                        (int)rx, (int)ry);
683            }
684
685            if (style == Paint.Style.STROKE.nativeInt ||
686                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
687                g.drawRoundRect(
688                        (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
689                        (int)rx, (int)ry);
690            }
691
692            // dispose Graphics2D object
693            g.dispose();
694        }
695    }
696
697    /*package*/ static void native_drawPath(int nativeCanvas, int path,
698                                               int paint) {
699        // FIXME
700        throw new UnsupportedOperationException();
701    }
702
703    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
704                                                 float left, float top,
705                                                 int nativePaintOrZero,
706                                                 int canvasDensity,
707                                                 int screenDensity,
708                                                 int bitmapDensity) {
709        // FIXME
710        throw new UnsupportedOperationException();
711    }
712
713    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
714                                                 Rect src, RectF dst,
715                                                 int nativePaintOrZero,
716                                                 int screenDensity,
717                                                 int bitmapDensity) {
718        // get the delegate from the native int.
719        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
720        if (bitmapDelegate == null) {
721            assert false;
722            return;
723        }
724
725        BufferedImage image = bitmapDelegate.getImage();
726
727        if (src == null) {
728            drawBitmap(nativeCanvas, image, nativePaintOrZero,
729                    0, 0, image.getWidth(), image.getHeight(),
730                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
731        } else {
732            drawBitmap(nativeCanvas, image, nativePaintOrZero,
733                    src.left, src.top, src.width(), src.height(),
734                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
735        }
736    }
737
738    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
739                                                 Rect src, Rect dst,
740                                                 int nativePaintOrZero,
741                                                 int screenDensity,
742                                                 int bitmapDensity) {
743        // get the delegate from the native int.
744        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
745        if (bitmapDelegate == null) {
746            assert false;
747            return;
748        }
749
750        BufferedImage image = bitmapDelegate.getImage();
751
752        if (src == null) {
753            drawBitmap(nativeCanvas, image, nativePaintOrZero,
754                    0, 0, image.getWidth(), image.getHeight(),
755                    dst.left, dst.top, dst.right, dst.bottom);
756        } else {
757            drawBitmap(nativeCanvas, image, nativePaintOrZero,
758                    src.left, src.top, src.width(), src.height(),
759                    dst.left, dst.top, dst.right, dst.bottom);
760        }
761    }
762
763    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
764                                                int offset, int stride, float x,
765                                                 float y, int width, int height,
766                                                 boolean hasAlpha,
767                                                 int nativePaintOrZero) {
768        // FIXME
769        throw new UnsupportedOperationException();
770    }
771
772    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
773                                                      int nMatrix, int nPaint) {
774        // FIXME
775        throw new UnsupportedOperationException();
776    }
777
778    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
779                                                    int meshWidth, int meshHeight,
780                                                    float[] verts, int vertOffset,
781                                                    int[] colors, int colorOffset, int nPaint) {
782        // FIXME
783        throw new UnsupportedOperationException();
784    }
785
786    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
787                   float[] verts, int vertOffset, float[] texs, int texOffset,
788                   int[] colors, int colorOffset, short[] indices,
789                   int indexOffset, int indexCount, int nPaint) {
790        // FIXME
791        throw new UnsupportedOperationException();
792    }
793
794    /*package*/ static void native_drawText(int nativeCanvas, char[] text,
795                                               int index, int count, float x,
796                                               float y, int flags, int paint) {
797        // WARNING: the logic in this method is similar to Paint.measureText.
798        // Any change to this method should be reflected in Paint.measureText
799
800        // get the delegate from the native int.
801        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
802        if (canvasDelegate == null) {
803            assert false;
804            return;
805        }
806
807        // get the delegate from the native int.
808        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
809        if (paintDelegate == null) {
810            assert false;
811            return;
812        }
813
814        Graphics2D g = (Graphics2D) canvasDelegate.getCustomGraphics(paintDelegate);
815        try {
816            // Paint.TextAlign indicates how the text is positioned relative to X.
817            // LEFT is the default and there's nothing to do.
818            if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
819                float m = paintDelegate.measureText(text, index, count);
820                if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
821                    x -= m / 2;
822                } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
823                    x -= m;
824                }
825            }
826
827            List<FontInfo> fonts = paintDelegate.getFonts();
828
829            if (fonts.size() > 0) {
830                FontInfo mainFont = fonts.get(0);
831                int i = index;
832                int lastIndex = index + count;
833                while (i < lastIndex) {
834                    // always start with the main font.
835                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
836                    if (upTo == -1) {
837                        // draw all the rest and exit.
838                        g.setFont(mainFont.mFont);
839                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
840                        return;
841                    } else if (upTo > 0) {
842                        // draw what's possible
843                        g.setFont(mainFont.mFont);
844                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
845
846                        // compute the width that was drawn to increase x
847                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
848
849                        // move index to the first non displayed char.
850                        i = upTo;
851
852                        // don't call continue at this point. Since it is certain the main font
853                        // cannot display the font a index upTo (now ==i), we move on to the
854                        // fallback fonts directly.
855                    }
856
857                    // no char supported, attempt to read the next char(s) with the
858                    // fallback font. In this case we only test the first character
859                    // and then go back to test with the main font.
860                    // Special test for 2-char characters.
861                    boolean foundFont = false;
862                    for (int f = 1 ; f < fonts.size() ; f++) {
863                        FontInfo fontInfo = fonts.get(f);
864
865                        // need to check that the font can display the character. We test
866                        // differently if the char is a high surrogate.
867                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
868                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
869                        if (upTo == -1) {
870                            // draw that char
871                            g.setFont(fontInfo.mFont);
872                            g.drawChars(text, i, charCount, (int)x, (int)y);
873
874                            // update x
875                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
876
877                            // update the index in the text, and move on
878                            i += charCount;
879                            foundFont = true;
880                            break;
881
882                        }
883                    }
884
885                    // in case no font can display the char, display it with the main font.
886                    // (it'll put a square probably)
887                    if (foundFont == false) {
888                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
889
890                        g.setFont(mainFont.mFont);
891                        g.drawChars(text, i, charCount, (int)x, (int)y);
892
893                        // measure it to advance x
894                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
895
896                        // and move to the next chars.
897                        i += charCount;
898                    }
899                }
900            }
901        } finally {
902            g.dispose();
903        }
904    }
905
906    /*package*/ static void native_drawText(int nativeCanvas, String text,
907                                               int start, int end, float x,
908                                               float y, int flags, int paint) {
909        int count = end - start;
910        char[] buffer = TemporaryBuffer.obtain(count);
911        TextUtils.getChars(text, start, end, buffer, 0);
912
913        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
914    }
915
916    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
917            int start, int end, int contextStart, int contextEnd,
918            float x, float y, int flags, int paint) {
919        int count = end - start;
920        char[] buffer = TemporaryBuffer.obtain(count);
921        TextUtils.getChars(text, start, end, buffer, 0);
922
923        native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint);
924    }
925
926    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
927            int start, int count, int contextStart, int contextCount,
928            float x, float y, int flags, int paint) {
929        native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint);
930    }
931
932    /*package*/ static void native_drawPosText(int nativeCanvas,
933                                                  char[] text, int index,
934                                                  int count, float[] pos,
935                                                  int paint) {
936        // FIXME
937        throw new UnsupportedOperationException();
938    }
939
940    /*package*/ static void native_drawPosText(int nativeCanvas,
941                                                  String text, float[] pos,
942                                                  int paint) {
943        // FIXME
944        throw new UnsupportedOperationException();
945    }
946
947    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
948                                                     char[] text, int index,
949                                                     int count, int path,
950                                                     float hOffset,
951                                                     float vOffset, int bidiFlags,
952                                                     int paint) {
953        // FIXME
954        throw new UnsupportedOperationException();
955    }
956
957    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
958                                                     String text, int path,
959                                                     float hOffset,
960                                                     float vOffset,
961                                                     int flags, int paint) {
962        // FIXME
963        throw new UnsupportedOperationException();
964    }
965
966    /*package*/ static void native_drawPicture(int nativeCanvas,
967                                                  int nativePicture) {
968        // FIXME
969        throw new UnsupportedOperationException();
970    }
971
972    /*package*/ static void finalizer(int nativeCanvas) {
973        // get the delegate from the native int so that it can be disposed.
974        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
975        if (canvasDelegate == null) {
976            assert false;
977            return;
978        }
979
980        canvasDelegate.dispose();
981
982        // remove it from the manager.
983        sManager.removeDelegate(nativeCanvas);
984    }
985
986    // ---- Private delegate/helper methods ----
987
988    private Canvas_Delegate(BufferedImage image) {
989        setBitmap(image);
990    }
991
992    private Canvas_Delegate() {
993    }
994
995    /**
996     * Disposes of the {@link Graphics2D} stack.
997     */
998    private void dispose() {
999        while (mGraphicsStack.size() > 0) {
1000            mGraphicsStack.pop().dispose();
1001        }
1002    }
1003
1004    private void setBitmap(BufferedImage image) {
1005        mBufferedImage = image;
1006        mGraphicsStack.push(mBufferedImage.createGraphics());
1007    }
1008
1009    /**
1010     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
1011     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
1012     */
1013    /*package*/ Graphics2D getCustomGraphics(Paint_Delegate paint) {
1014        // make new one
1015        Graphics2D g = getGraphics2d();
1016        g = (Graphics2D)g.create();
1017
1018        // configure it
1019
1020        if (paint.isAntiAliased()) {
1021            g.setRenderingHint(
1022                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
1023            g.setRenderingHint(
1024                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1025        }
1026
1027        boolean useColorPaint = true;
1028
1029        // get the shader first, as it'll replace the color if it can be used it.
1030        Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader());
1031        if (shaderDelegate != null) {
1032            java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
1033            if (shaderPaint != null) {
1034                g.setPaint(shaderPaint);
1035                useColorPaint = false;
1036            } else {
1037                Bridge.getLog().warning(null,
1038                        String.format(
1039                            "Shader '%1$s' is not supported in the Layout Preview.",
1040                            shaderDelegate.getClass().getCanonicalName()));
1041            }
1042        }
1043
1044        if (useColorPaint) {
1045            g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
1046        }
1047
1048        int style = paint.getStyle();
1049        if (style == Paint.Style.STROKE.nativeInt ||
1050                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
1051
1052            PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(
1053                    paint.getPathEffect());
1054
1055            if (effectDelegate instanceof DashPathEffect_Delegate) {
1056                DashPathEffect_Delegate dpe = (DashPathEffect_Delegate)effectDelegate;
1057                g.setStroke(new BasicStroke(
1058                        paint.getStrokeWidth(),
1059                        paint.getJavaCap(),
1060                        paint.getJavaJoin(),
1061                        paint.getStrokeMiter(),
1062                        dpe.getIntervals(),
1063                        dpe.getPhase()));
1064            } else {
1065                g.setStroke(new BasicStroke(
1066                        paint.getStrokeWidth(),
1067                        paint.getJavaCap(),
1068                        paint.getJavaJoin(),
1069                        paint.getStrokeMiter()));
1070            }
1071        }
1072
1073        Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
1074        if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
1075            int mode = ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
1076
1077            setModeInGraphics(g, mode);
1078        } else {
1079            // default mode is src_over
1080            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
1081
1082            // if xfermode wasn't null, then it's something we don't support. log it.
1083            if (xfermodeDelegate != null) {
1084                Bridge.getLog().warning(null,
1085                        String.format(
1086                            "Xfermode '%1$s' is not supported in the Layout Preview.",
1087                            xfermodeDelegate.getClass().getCanonicalName()));
1088            }
1089        }
1090
1091        return g;
1092    }
1093
1094    private static void setModeInGraphics(Graphics2D g, int mode) {
1095        for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
1096            if (m.nativeInt == mode) {
1097                setModeInGraphics(g, m);
1098                return;
1099            }
1100        }
1101    }
1102
1103    private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode) {
1104        switch (mode) {
1105            case CLEAR:
1106                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f /*alpha*/));
1107                break;
1108            case DARKEN:
1109                break;
1110            case DST:
1111                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, 1.0f /*alpha*/));
1112                break;
1113            case DST_ATOP:
1114                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1.0f /*alpha*/));
1115                break;
1116            case DST_IN:
1117                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0f /*alpha*/));
1118                break;
1119            case DST_OUT:
1120                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, 1.0f /*alpha*/));
1121                break;
1122            case DST_OVER:
1123                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f /*alpha*/));
1124                break;
1125            case LIGHTEN:
1126                break;
1127            case MULTIPLY:
1128                break;
1129            case SCREEN:
1130                break;
1131            case SRC:
1132                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f /*alpha*/));
1133                break;
1134            case SRC_ATOP:
1135                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f /*alpha*/));
1136                break;
1137            case SRC_IN:
1138                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f /*alpha*/));
1139                break;
1140            case SRC_OUT:
1141                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, 1.0f /*alpha*/));
1142                break;
1143            case SRC_OVER:
1144                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f /*alpha*/));
1145                break;
1146            case XOR:
1147                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1.0f /*alpha*/));
1148                break;
1149        }
1150    }
1151
1152
1153    private static void drawBitmap(
1154            int nativeCanvas,
1155            BufferedImage image,
1156            int nativePaintOrZero,
1157            int sleft, int stop, int sright, int sbottom,
1158            int dleft, int dtop, int dright, int dbottom) {
1159        // get the delegate from the native int.
1160        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1161        if (canvasDelegate == null) {
1162            assert false;
1163            return;
1164        }
1165
1166        // get the delegate from the native int.
1167        Paint_Delegate paintDelegate = null;
1168        if (nativePaintOrZero > 0) {
1169            paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1170            if (paintDelegate == null) {
1171                assert false;
1172                return;
1173            }
1174        }
1175
1176        drawBitmap(canvasDelegate, image, paintDelegate,
1177                sleft, stop, sright, sbottom,
1178                dleft, dtop, dright, dbottom);
1179    }
1180
1181    private static void drawBitmap(
1182            Canvas_Delegate canvasDelegate,
1183            BufferedImage image,
1184            Paint_Delegate paintDelegate,
1185            int sleft, int stop, int sright, int sbottom,
1186            int dleft, int dtop, int dright, int dbottom) {
1187
1188        Graphics2D g = canvasDelegate.getGraphics2d();
1189
1190        Composite c = null;
1191
1192        if (paintDelegate != null) {
1193            if (paintDelegate.isFilterBitmap()) {
1194                g = (Graphics2D)g.create();
1195                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1196                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1197            }
1198        }
1199
1200        g.drawImage(image, dleft, dtop, dright, dbottom,
1201                sleft, stop, sright, sbottom, null);
1202
1203        if (paintDelegate != null) {
1204            if (paintDelegate.isFilterBitmap()) {
1205                g.dispose();
1206            }
1207            if (c != null) {
1208                g.setComposite(c);
1209            }
1210        }
1211    }
1212
1213}
1214
1215