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 
17 package android.graphics;
18 
19 import com.android.ide.common.rendering.api.LayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.impl.DelegateManager;
22 import com.android.layoutlib.bridge.impl.GcSnapshot;
23 import com.android.layoutlib.bridge.impl.PorterDuffUtility;
24 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
25 
26 import android.graphics.Bitmap.Config;
27 import android.text.TextUtils;
28 
29 import java.awt.Color;
30 import java.awt.Composite;
31 import java.awt.Graphics2D;
32 import java.awt.Rectangle;
33 import java.awt.RenderingHints;
34 import java.awt.Shape;
35 import java.awt.geom.AffineTransform;
36 import java.awt.geom.Arc2D;
37 import java.awt.image.BufferedImage;
38 
39 
40 /**
41  * Delegate implementing the native methods of android.graphics.Canvas
42  *
43  * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
44  * by calls to methods of the same name in this delegate class.
45  *
46  * This class behaves like the original native implementation, but in Java, keeping previously
47  * native data into its own objects and mapping them to int that are sent back and forth between
48  * it and the original Canvas class.
49  *
50  * @see DelegateManager
51  *
52  */
53 public final class Canvas_Delegate {
54 
55     // ---- delegate manager ----
56     private static final DelegateManager<Canvas_Delegate> sManager =
57             new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
58 
59 
60     // ---- delegate helper data ----
61 
62     private final static boolean[] sBoolOut = new boolean[1];
63 
64 
65     // ---- delegate data ----
66     private Bitmap_Delegate mBitmap;
67     private GcSnapshot mSnapshot;
68 
69     private DrawFilter_Delegate mDrawFilter = null;
70 
71 
72     // ---- Public Helper methods ----
73 
74     /**
75      * Returns the native delegate associated to a given {@link Canvas} object.
76      */
getDelegate(Canvas canvas)77     public static Canvas_Delegate getDelegate(Canvas canvas) {
78         return sManager.getDelegate(canvas.getNativeCanvasWrapper());
79     }
80 
81     /**
82      * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
83      */
getDelegate(long native_canvas)84     public static Canvas_Delegate getDelegate(long native_canvas) {
85         return sManager.getDelegate(native_canvas);
86     }
87 
88     /**
89      * Returns the current {@link Graphics2D} used to draw.
90      */
getSnapshot()91     public GcSnapshot getSnapshot() {
92         return mSnapshot;
93     }
94 
95     /**
96      * Returns the {@link DrawFilter} delegate or null if none have been set.
97      *
98      * @return the delegate or null.
99      */
getDrawFilter()100     public DrawFilter_Delegate getDrawFilter() {
101         return mDrawFilter;
102     }
103 
104     // ---- native methods ----
105 
106     @LayoutlibDelegate
freeCaches()107     /*package*/ static void freeCaches() {
108         // nothing to be done here.
109     }
110 
111     @LayoutlibDelegate
freeTextLayoutCaches()112     /*package*/ static void freeTextLayoutCaches() {
113         // nothing to be done here yet.
114     }
115 
116     @LayoutlibDelegate
initRaster(long nativeBitmapOrZero)117     /*package*/ static long initRaster(long nativeBitmapOrZero) {
118         if (nativeBitmapOrZero > 0) {
119             // get the Bitmap from the int
120             Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
121 
122             // create a new Canvas_Delegate with the given bitmap and return its new native int.
123             Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
124 
125             return sManager.addNewDelegate(newDelegate);
126         }
127 
128         // create a new Canvas_Delegate and return its new native int.
129         Canvas_Delegate newDelegate = new Canvas_Delegate();
130 
131         return sManager.addNewDelegate(newDelegate);
132     }
133 
134     @LayoutlibDelegate
135     /*package*/
native_setBitmap(long canvas, long bitmap, boolean copyState)136     static void native_setBitmap(long canvas, long bitmap, boolean copyState) {
137         Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
138         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
139         if (canvasDelegate == null || bitmapDelegate==null) {
140             return;
141         }
142         canvasDelegate.mBitmap = bitmapDelegate;
143         canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
144     }
145 
146     @LayoutlibDelegate
native_isOpaque(long nativeCanvas)147     /*package*/ static boolean native_isOpaque(long nativeCanvas) {
148         // get the delegate from the native int.
149         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
150         if (canvasDelegate == null) {
151             return false;
152         }
153 
154         return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
155     }
156 
157     @LayoutlibDelegate
native_getWidth(long nativeCanvas)158     /*package*/ static int native_getWidth(long nativeCanvas) {
159         // get the delegate from the native int.
160         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
161         if (canvasDelegate == null) {
162             return 0;
163         }
164 
165         return canvasDelegate.mBitmap.getImage().getWidth();
166     }
167 
168     @LayoutlibDelegate
native_getHeight(long nativeCanvas)169     /*package*/ static int native_getHeight(long nativeCanvas) {
170         // get the delegate from the native int.
171         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
172         if (canvasDelegate == null) {
173             return 0;
174         }
175 
176         return canvasDelegate.mBitmap.getImage().getHeight();
177     }
178 
179     @LayoutlibDelegate
native_save(long nativeCanvas, int saveFlags)180     /*package*/ static int native_save(long nativeCanvas, int saveFlags) {
181         // get the delegate from the native int.
182         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
183         if (canvasDelegate == null) {
184             return 0;
185         }
186 
187         return canvasDelegate.save(saveFlags);
188     }
189 
190     @LayoutlibDelegate
native_saveLayer(long nativeCanvas, float l, float t, float r, float b, long paint, int layerFlags)191     /*package*/ static int native_saveLayer(long nativeCanvas, float l,
192                                                float t, float r, float b,
193                                                long paint, int layerFlags) {
194         // get the delegate from the native int.
195         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
196         if (canvasDelegate == null) {
197             return 0;
198         }
199 
200         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
201         if (paintDelegate == null) {
202             return 0;
203         }
204 
205         return canvasDelegate.saveLayer(new RectF(l, t, r, b),
206                 paintDelegate, layerFlags);
207     }
208 
209     @LayoutlibDelegate
native_saveLayerAlpha(long nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags)210     /*package*/ static int native_saveLayerAlpha(long nativeCanvas, float l,
211                                                     float t, float r, float b,
212                                                     int alpha, int layerFlags) {
213         // get the delegate from the native int.
214         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
215         if (canvasDelegate == null) {
216             return 0;
217         }
218 
219         return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
220     }
221 
222     @LayoutlibDelegate
native_restore(long nativeCanvas)223     /*package*/ static void native_restore(long nativeCanvas) {
224         // get the delegate from the native int.
225         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
226         if (canvasDelegate == null) {
227             return;
228         }
229 
230         canvasDelegate.restore();
231     }
232 
233     @LayoutlibDelegate
native_restoreToCount(long nativeCanvas, int saveCount)234     /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount) {
235         // get the delegate from the native int.
236         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
237         if (canvasDelegate == null) {
238             return;
239         }
240 
241         canvasDelegate.restoreTo(saveCount);
242     }
243 
244     @LayoutlibDelegate
native_getSaveCount(long nativeCanvas)245     /*package*/ static int native_getSaveCount(long nativeCanvas) {
246         // get the delegate from the native int.
247         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
248         if (canvasDelegate == null) {
249             return 0;
250         }
251 
252         return canvasDelegate.getSnapshot().size();
253     }
254 
255     @LayoutlibDelegate
native_translate(long nativeCanvas, float dx, float dy)256    /*package*/ static void native_translate(long nativeCanvas, float dx, float dy) {
257         // get the delegate from the native int.
258         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
259         if (canvasDelegate == null) {
260             return;
261         }
262 
263         canvasDelegate.getSnapshot().translate(dx, dy);
264     }
265 
266     @LayoutlibDelegate
native_scale(long nativeCanvas, float sx, float sy)267        /*package*/ static void native_scale(long nativeCanvas, float sx, float sy) {
268             // get the delegate from the native int.
269             Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
270             if (canvasDelegate == null) {
271                 return;
272             }
273 
274             canvasDelegate.getSnapshot().scale(sx, sy);
275         }
276 
277     @LayoutlibDelegate
native_rotate(long nativeCanvas, float degrees)278     /*package*/ static void native_rotate(long nativeCanvas, float degrees) {
279         // get the delegate from the native int.
280         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
281         if (canvasDelegate == null) {
282             return;
283         }
284 
285         canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
286     }
287 
288     @LayoutlibDelegate
native_skew(long nativeCanvas, float kx, float ky)289    /*package*/ static void native_skew(long nativeCanvas, float kx, float ky) {
290         // get the delegate from the native int.
291         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
292         if (canvasDelegate == null) {
293             return;
294         }
295 
296         // get the current top graphics2D object.
297         GcSnapshot g = canvasDelegate.getSnapshot();
298 
299         // get its current matrix
300         AffineTransform currentTx = g.getTransform();
301         // get the AffineTransform for the given skew.
302         float[] mtx = Matrix_Delegate.getSkew(kx, ky);
303         AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
304 
305         // combine them so that the given matrix is applied after.
306         currentTx.preConcatenate(matrixTx);
307 
308         // give it to the graphics2D as a new matrix replacing all previous transform
309         g.setTransform(currentTx);
310     }
311 
312     @LayoutlibDelegate
native_concat(long nCanvas, long nMatrix)313     /*package*/ static void native_concat(long nCanvas, long nMatrix) {
314         // get the delegate from the native int.
315         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
316         if (canvasDelegate == null) {
317             return;
318         }
319 
320         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
321         if (matrixDelegate == null) {
322             return;
323         }
324 
325         // get the current top graphics2D object.
326         GcSnapshot snapshot = canvasDelegate.getSnapshot();
327 
328         // get its current matrix
329         AffineTransform currentTx = snapshot.getTransform();
330         // get the AffineTransform of the given matrix
331         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
332 
333         // combine them so that the given matrix is applied after.
334         currentTx.concatenate(matrixTx);
335 
336         // give it to the graphics2D as a new matrix replacing all previous transform
337         snapshot.setTransform(currentTx);
338     }
339 
340     @LayoutlibDelegate
native_setMatrix(long nCanvas, long nMatrix)341     /*package*/ static void native_setMatrix(long nCanvas, long nMatrix) {
342         // get the delegate from the native int.
343         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
344         if (canvasDelegate == null) {
345             return;
346         }
347 
348         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
349         if (matrixDelegate == null) {
350             return;
351         }
352 
353         // get the current top graphics2D object.
354         GcSnapshot snapshot = canvasDelegate.getSnapshot();
355 
356         // get the AffineTransform of the given matrix
357         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
358 
359         // give it to the graphics2D as a new matrix replacing all previous transform
360         snapshot.setTransform(matrixTx);
361 
362         if (matrixDelegate.hasPerspective()) {
363             assert false;
364             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
365                     "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
366                     "supports affine transformations.", null, null /*data*/);
367         }
368     }
369 
370     @LayoutlibDelegate
native_clipRect(long nCanvas, float left, float top, float right, float bottom, int regionOp)371     /*package*/ static boolean native_clipRect(long nCanvas,
372                                                   float left, float top,
373                                                   float right, float bottom,
374                                                   int regionOp) {
375         // get the delegate from the native int.
376         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
377         if (canvasDelegate == null) {
378             return false;
379         }
380 
381         return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
382     }
383 
384     @LayoutlibDelegate
native_clipPath(long nativeCanvas, long nativePath, int regionOp)385     /*package*/ static boolean native_clipPath(long nativeCanvas,
386                                                   long nativePath,
387                                                   int regionOp) {
388         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
389         if (canvasDelegate == null) {
390             return true;
391         }
392 
393         Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
394         if (pathDelegate == null) {
395             return true;
396         }
397 
398         return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
399     }
400 
401     @LayoutlibDelegate
native_clipRegion(long nativeCanvas, long nativeRegion, int regionOp)402     /*package*/ static boolean native_clipRegion(long nativeCanvas,
403                                                     long nativeRegion,
404                                                     int regionOp) {
405         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
406         if (canvasDelegate == null) {
407             return true;
408         }
409 
410         Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
411         if (region == null) {
412             return true;
413         }
414 
415         return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
416     }
417 
418     @LayoutlibDelegate
nativeSetDrawFilter(long nativeCanvas, long nativeFilter)419     /*package*/ static void nativeSetDrawFilter(long nativeCanvas, long nativeFilter) {
420         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
421         if (canvasDelegate == null) {
422             return;
423         }
424 
425         canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
426 
427         if (canvasDelegate.mDrawFilter != null &&
428                 canvasDelegate.mDrawFilter.isSupported() == false) {
429             Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
430                     canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
431         }
432     }
433 
434     @LayoutlibDelegate
native_getClipBounds(long nativeCanvas, Rect bounds)435     /*package*/ static boolean native_getClipBounds(long nativeCanvas,
436                                                        Rect bounds) {
437         // get the delegate from the native int.
438         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
439         if (canvasDelegate == null) {
440             return false;
441         }
442 
443         Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
444         if (rect != null && rect.isEmpty() == false) {
445             bounds.left = rect.x;
446             bounds.top = rect.y;
447             bounds.right = rect.x + rect.width;
448             bounds.bottom = rect.y + rect.height;
449             return true;
450         }
451 
452         return false;
453     }
454 
455     @LayoutlibDelegate
native_getCTM(long canvas, long matrix)456     /*package*/ static void native_getCTM(long canvas, long matrix) {
457         // get the delegate from the native int.
458         Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
459         if (canvasDelegate == null) {
460             return;
461         }
462 
463         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
464         if (matrixDelegate == null) {
465             return;
466         }
467 
468         AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
469         matrixDelegate.set(Matrix_Delegate.makeValues(transform));
470     }
471 
472     @LayoutlibDelegate
native_quickReject(long nativeCanvas, long path)473     /*package*/ static boolean native_quickReject(long nativeCanvas, long path) {
474         // FIXME properly implement quickReject
475         return false;
476     }
477 
478     @LayoutlibDelegate
native_quickReject(long nativeCanvas, float left, float top, float right, float bottom)479     /*package*/ static boolean native_quickReject(long nativeCanvas,
480                                                      float left, float top,
481                                                      float right, float bottom) {
482         // FIXME properly implement quickReject
483         return false;
484     }
485 
486     @LayoutlibDelegate
native_drawColor(long nativeCanvas, final int color, final int mode)487     /*package*/ static void native_drawColor(long nativeCanvas, final int color, final int mode) {
488         // get the delegate from the native int.
489         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
490         if (canvasDelegate == null) {
491             return;
492         }
493 
494         final int w = canvasDelegate.mBitmap.getImage().getWidth();
495         final int h = canvasDelegate.mBitmap.getImage().getHeight();
496         draw(nativeCanvas, new GcSnapshot.Drawable() {
497 
498             @Override
499             public void draw(Graphics2D graphics, Paint_Delegate paint) {
500                 // reset its transform just in case
501                 graphics.setTransform(new AffineTransform());
502 
503                 // set the color
504                 graphics.setColor(new Color(color, true /*alpha*/));
505 
506                 Composite composite = PorterDuffUtility.getComposite(
507                         PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
508                 if (composite != null) {
509                     graphics.setComposite(composite);
510                 }
511 
512                 graphics.fillRect(0, 0, w, h);
513             }
514         });
515     }
516 
517     @LayoutlibDelegate
native_drawPaint(long nativeCanvas, long paint)518     /*package*/ static void native_drawPaint(long nativeCanvas, long paint) {
519         // FIXME
520         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
521                 "Canvas.drawPaint is not supported.", null, null /*data*/);
522     }
523 
524     @LayoutlibDelegate
native_drawPoint(long nativeCanvas, float x, float y, long nativePaint)525     /*package*/ static void native_drawPoint(long nativeCanvas, float x, float y,
526             long nativePaint) {
527         // FIXME
528         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
529                 "Canvas.drawPoint is not supported.", null, null /*data*/);
530     }
531 
532     @LayoutlibDelegate
native_drawPoints(long nativeCanvas, float[] pts, int offset, int count, long nativePaint)533     /*package*/ static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count,
534             long nativePaint) {
535         // FIXME
536         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
537                 "Canvas.drawPoint is not supported.", null, null /*data*/);
538     }
539 
540     @LayoutlibDelegate
native_drawLine(long nativeCanvas, final float startX, final float startY, final float stopX, final float stopY, long paint)541     /*package*/ static void native_drawLine(long nativeCanvas,
542             final float startX, final float startY, final float stopX, final float stopY,
543             long paint) {
544         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
545                 new GcSnapshot.Drawable() {
546                     @Override
547                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
548                         graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
549                     }
550         });
551     }
552 
553     @LayoutlibDelegate
native_drawLines(long nativeCanvas, final float[] pts, final int offset, final int count, long nativePaint)554     /*package*/ static void native_drawLines(long nativeCanvas,
555             final float[] pts, final int offset, final int count,
556             long nativePaint) {
557         draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
558                 false /*forceSrcMode*/, new GcSnapshot.Drawable() {
559                     @Override
560                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
561                         for (int i = 0; i < count; i += 4) {
562                             graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
563                                     (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
564                         }
565                     }
566                 });
567     }
568 
569     @LayoutlibDelegate
native_drawRect(long nativeCanvas, final float left, final float top, final float right, final float bottom, long paint)570     /*package*/ static void native_drawRect(long nativeCanvas,
571             final float left, final float top, final float right, final float bottom, long paint) {
572 
573         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
574                 new GcSnapshot.Drawable() {
575                     @Override
576                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
577                         int style = paintDelegate.getStyle();
578 
579                         // draw
580                         if (style == Paint.Style.FILL.nativeInt ||
581                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
582                             graphics.fillRect((int)left, (int)top,
583                                     (int)(right-left), (int)(bottom-top));
584                         }
585 
586                         if (style == Paint.Style.STROKE.nativeInt ||
587                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
588                             graphics.drawRect((int)left, (int)top,
589                                     (int)(right-left), (int)(bottom-top));
590                         }
591                     }
592         });
593     }
594 
595     @LayoutlibDelegate
native_drawOval(long nativeCanvas, final float left, final float top, final float right, final float bottom, long paint)596     /*package*/ static void native_drawOval(long nativeCanvas, final float left,
597             final float top, final float right, final float bottom, long paint) {
598         if (right > left && bottom > top) {
599             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
600                     new GcSnapshot.Drawable() {
601                         @Override
602                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
603                             int style = paintDelegate.getStyle();
604 
605                             // draw
606                             if (style == Paint.Style.FILL.nativeInt ||
607                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
608                                 graphics.fillOval((int)left, (int)top,
609                                         (int)(right - left), (int)(bottom - top));
610                             }
611 
612                             if (style == Paint.Style.STROKE.nativeInt ||
613                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
614                                 graphics.drawOval((int)left, (int)top,
615                                         (int)(right - left), (int)(bottom - top));
616                             }
617                         }
618             });
619         }
620     }
621 
622     @LayoutlibDelegate
native_drawCircle(long nativeCanvas, float cx, float cy, float radius, long paint)623     /*package*/ static void native_drawCircle(long nativeCanvas,
624             float cx, float cy, float radius, long paint) {
625         native_drawOval(nativeCanvas,
626                 cx - radius, cy - radius, cx + radius, cy + radius,
627                 paint);
628     }
629 
630     @LayoutlibDelegate
native_drawArc(long nativeCanvas, final float left, final float top, final float right, final float bottom, final float startAngle, final float sweep, final boolean useCenter, long paint)631     /*package*/ static void native_drawArc(long nativeCanvas,
632             final float left, final float top, final float right, final float bottom,
633             final float startAngle, final float sweep,
634             final boolean useCenter, long paint) {
635         if (right > left && bottom > top) {
636             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
637                     new GcSnapshot.Drawable() {
638                         @Override
639                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
640                             int style = paintDelegate.getStyle();
641 
642                             Arc2D.Float arc = new Arc2D.Float(
643                                     left, top, right - left, bottom - top,
644                                     -startAngle, -sweep,
645                                     useCenter ? Arc2D.PIE : Arc2D.OPEN);
646 
647                             // draw
648                             if (style == Paint.Style.FILL.nativeInt ||
649                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
650                                 graphics.fill(arc);
651                             }
652 
653                             if (style == Paint.Style.STROKE.nativeInt ||
654                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
655                                 graphics.draw(arc);
656                             }
657                         }
658             });
659         }
660     }
661 
662     @LayoutlibDelegate
native_drawRoundRect(long nativeCanvas, final float left, final float top, final float right, final float bottom, final float rx, final float ry, long paint)663     /*package*/ static void native_drawRoundRect(long nativeCanvas,
664             final float left, final float top, final float right, final float bottom,
665             final float rx, final float ry, long paint) {
666         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
667                 new GcSnapshot.Drawable() {
668                     @Override
669                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
670                         int style = paintDelegate.getStyle();
671 
672                         // draw
673                         if (style == Paint.Style.FILL.nativeInt ||
674                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
675                             graphics.fillRoundRect(
676                                     (int)left, (int)top,
677                                     (int)(right - left), (int)(bottom - top),
678                                     (int)rx, (int)ry);
679                         }
680 
681                         if (style == Paint.Style.STROKE.nativeInt ||
682                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
683                             graphics.drawRoundRect(
684                                     (int)left, (int)top,
685                                     (int)(right - left), (int)(bottom - top),
686                                     (int)rx, (int)ry);
687                         }
688                     }
689         });
690     }
691 
692     @LayoutlibDelegate
native_drawPath(long nativeCanvas, long path, long paint)693     /*package*/ static void native_drawPath(long nativeCanvas, long path, long paint) {
694         final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
695         if (pathDelegate == null) {
696             return;
697         }
698 
699         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
700                 new GcSnapshot.Drawable() {
701                     @Override
702                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
703                         Shape shape = pathDelegate.getJavaShape();
704                         int style = paintDelegate.getStyle();
705 
706                         if (style == Paint.Style.FILL.nativeInt ||
707                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
708                             graphics.fill(shape);
709                         }
710 
711                         if (style == Paint.Style.STROKE.nativeInt ||
712                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
713                             graphics.draw(shape);
714                         }
715                     }
716         });
717     }
718 
719     @LayoutlibDelegate
native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap, float left, float top, long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity)720     /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap,
721                                                  float left, float top,
722                                                  long nativePaintOrZero,
723                                                  int canvasDensity,
724                                                  int screenDensity,
725                                                  int bitmapDensity) {
726         // get the delegate from the native int.
727         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
728         if (bitmapDelegate == null) {
729             return;
730         }
731 
732         BufferedImage image = bitmapDelegate.getImage();
733         float right = left + image.getWidth();
734         float bottom = top + image.getHeight();
735 
736         drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
737                 0, 0, image.getWidth(), image.getHeight(),
738                 (int)left, (int)top, (int)right, (int)bottom);
739     }
740 
741     @LayoutlibDelegate
native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity)742     /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap,
743                                  float srcLeft, float srcTop, float srcRight, float srcBottom,
744                                  float dstLeft, float dstTop, float dstRight, float dstBottom,
745                                  long nativePaintOrZero, int screenDensity, int bitmapDensity) {
746         // get the delegate from the native int.
747         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
748         if (bitmapDelegate == null) {
749             return;
750         }
751 
752         drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
753                 (int)srcLeft, (int)srcTop, (int)srcRight, (int)srcBottom,
754                 (int)dstLeft, (int)dstTop, (int)dstRight, (int)dstBottom);
755     }
756 
757     @LayoutlibDelegate
native_drawBitmap(long nativeCanvas, int[] colors, int offset, int stride, final float x, final float y, int width, int height, boolean hasAlpha, long nativePaintOrZero)758     /*package*/ static void native_drawBitmap(long nativeCanvas, int[] colors,
759                                                 int offset, int stride, final float x,
760                                                  final float y, int width, int height,
761                                                  boolean hasAlpha,
762                                                  long nativePaintOrZero) {
763         // create a temp BufferedImage containing the content.
764         final BufferedImage image = new BufferedImage(width, height,
765                 hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
766         image.setRGB(0, 0, width, height, colors, offset, stride);
767 
768         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
769                 new GcSnapshot.Drawable() {
770                     @Override
771                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
772                         if (paint != null && paint.isFilterBitmap()) {
773                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
774                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
775                         }
776 
777                         graphics.drawImage(image, (int) x, (int) y, null);
778                     }
779         });
780     }
781 
782     @LayoutlibDelegate
nativeDrawBitmapMatrix(long nCanvas, long nBitmap, long nMatrix, long nPaint)783     /*package*/ static void nativeDrawBitmapMatrix(long nCanvas, long nBitmap,
784                                                       long nMatrix, long nPaint) {
785         // get the delegate from the native int.
786         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
787         if (canvasDelegate == null) {
788             return;
789         }
790 
791         // get the delegate from the native int, which can be null
792         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
793 
794         // get the delegate from the native int.
795         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap);
796         if (bitmapDelegate == null) {
797             return;
798         }
799 
800         final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
801 
802         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
803         if (matrixDelegate == null) {
804             return;
805         }
806 
807         final AffineTransform mtx = matrixDelegate.getAffineTransform();
808 
809         canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
810                 @Override
811                 public void draw(Graphics2D graphics, Paint_Delegate paint) {
812                     if (paint != null && paint.isFilterBitmap()) {
813                         graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
814                                 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
815                     }
816 
817                     //FIXME add support for canvas, screen and bitmap densities.
818                     graphics.drawImage(image, mtx, null);
819                 }
820         }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
821     }
822 
823     @LayoutlibDelegate
nativeDrawBitmapMesh(long nCanvas, long nBitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nPaint)824     /*package*/ static void nativeDrawBitmapMesh(long nCanvas, long nBitmap,
825             int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
826             int colorOffset, long nPaint) {
827         // FIXME
828         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
829                 "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
830     }
831 
832     @LayoutlibDelegate
nativeDrawVertices(long nCanvas, int mode, int n, float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, int indexOffset, int indexCount, long nPaint)833     /*package*/ static void nativeDrawVertices(long nCanvas, int mode, int n,
834             float[] verts, int vertOffset,
835             float[] texs, int texOffset,
836             int[] colors, int colorOffset,
837             short[] indices, int indexOffset,
838             int indexCount, long nPaint) {
839         // FIXME
840         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
841                 "Canvas.drawVertices is not supported.", null, null /*data*/);
842     }
843 
844     @LayoutlibDelegate
native_drawText(long nativeCanvas, char[] text, int index, int count, float startX, float startY, int flags, long paint, long typeface)845     /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count,
846             float startX, float startY, int flags, long paint, long typeface) {
847         drawText(nativeCanvas, text, index, count, startX, startY, flags == Canvas.DIRECTION_RTL,
848                 paint, typeface);
849     }
850 
851     @LayoutlibDelegate
native_drawText(long nativeCanvas, String text, int start, int end, float x, float y, final int flags, long paint, long typeface)852     /*package*/ static void native_drawText(long nativeCanvas, String text,
853             int start, int end, float x, float y, final int flags, long paint,
854             long typeface) {
855         int count = end - start;
856         char[] buffer = TemporaryBuffer.obtain(count);
857         TextUtils.getChars(text, start, end, buffer, 0);
858 
859         native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
860     }
861 
862     @LayoutlibDelegate
native_drawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, long paint, long typeface)863     /*package*/ static void native_drawTextRun(long nativeCanvas, String text,
864             int start, int end, int contextStart, int contextEnd,
865             float x, float y, boolean isRtl, long paint, long typeface) {
866         int count = end - start;
867         char[] buffer = TemporaryBuffer.obtain(count);
868         TextUtils.getChars(text, start, end, buffer, 0);
869 
870         drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
871     }
872 
873     @LayoutlibDelegate
native_drawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long paint, long typeface)874     /*package*/ static void native_drawTextRun(long nativeCanvas, char[] text,
875             int start, int count, int contextStart, int contextCount,
876             float x, float y, boolean isRtl, long paint, long typeface) {
877         drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
878     }
879 
880     @LayoutlibDelegate
native_drawTextOnPath(long nativeCanvas, char[] text, int index, int count, long path, float hOffset, float vOffset, int bidiFlags, long paint, long typeface)881     /*package*/ static void native_drawTextOnPath(long nativeCanvas,
882                                                      char[] text, int index,
883                                                      int count, long path,
884                                                      float hOffset,
885                                                      float vOffset, int bidiFlags,
886                                                      long paint, long typeface) {
887         // FIXME
888         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
889                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
890     }
891 
892     @LayoutlibDelegate
native_drawTextOnPath(long nativeCanvas, String text, long path, float hOffset, float vOffset, int bidiFlags, long paint, long typeface)893     /*package*/ static void native_drawTextOnPath(long nativeCanvas,
894                                                      String text, long path,
895                                                      float hOffset,
896                                                      float vOffset,
897                                                      int bidiFlags, long paint,
898                                                      long typeface) {
899         // FIXME
900         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
901                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
902     }
903 
904     @LayoutlibDelegate
finalizer(long nativeCanvas)905     /*package*/ static void finalizer(long nativeCanvas) {
906         // get the delegate from the native int so that it can be disposed.
907         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
908         if (canvasDelegate == null) {
909             return;
910         }
911 
912         canvasDelegate.dispose();
913 
914         // remove it from the manager.
915         sManager.removeJavaReferenceFor(nativeCanvas);
916     }
917 
918 
919     // ---- Private delegate/helper methods ----
920 
921     /**
922      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
923      * <p>Note that the drawable may actually be executed several times if there are
924      * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
925      */
draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode, GcSnapshot.Drawable drawable)926     private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
927             GcSnapshot.Drawable drawable) {
928         // get the delegate from the native int.
929         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
930         if (canvasDelegate == null) {
931             return;
932         }
933 
934         // get the paint which can be null if nPaint is 0;
935         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
936 
937         canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
938     }
939 
940     /**
941      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
942      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
943      * <p>Note that the drawable may actually be executed several times if there are
944      * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
945      */
draw(long nCanvas, GcSnapshot.Drawable drawable)946     private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
947         // get the delegate from the native int.
948         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
949         if (canvasDelegate == null) {
950             return;
951         }
952 
953         canvasDelegate.mSnapshot.draw(drawable);
954     }
955 
drawText(long nativeCanvas, final char[] text, final int index, final int count, final float startX, final float startY, final boolean isRtl, long paint, final long typeface)956     private static void drawText(long nativeCanvas, final char[] text, final int index,
957             final int count, final float startX, final float startY, final boolean isRtl,
958             long paint, final long typeface) {
959 
960         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
961                 new GcSnapshot.Drawable() {
962             @Override
963             public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
964                 // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
965                 // Any change to this method should be reflected in Paint.measureText
966 
967                 // assert that the typeface passed is actually the one stored in paint.
968                 assert (typeface == paintDelegate.mNativeTypeface);
969 
970                 // Paint.TextAlign indicates how the text is positioned relative to X.
971                 // LEFT is the default and there's nothing to do.
972                 float x = startX;
973                 int limit = index + count;
974                 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
975                     RectF bounds = paintDelegate.measureText(text, index, count, null, 0,
976                             isRtl);
977                     float m = bounds.right - bounds.left;
978                     if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
979                         x -= m / 2;
980                     } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
981                         x -= m;
982                     }
983                 }
984 
985                 new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x, startY)
986                         .renderText(index, limit, isRtl, null, 0, true);
987             }
988         });
989     }
990 
Canvas_Delegate(Bitmap_Delegate bitmap)991     private Canvas_Delegate(Bitmap_Delegate bitmap) {
992         mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
993     }
994 
Canvas_Delegate()995     private Canvas_Delegate() {
996         mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
997     }
998 
999     /**
1000      * Disposes of the {@link Graphics2D} stack.
1001      */
dispose()1002     private void dispose() {
1003         mSnapshot.dispose();
1004     }
1005 
save(int saveFlags)1006     private int save(int saveFlags) {
1007         // get the current save count
1008         int count = mSnapshot.size();
1009 
1010         mSnapshot = mSnapshot.save(saveFlags);
1011 
1012         // return the old save count
1013         return count;
1014     }
1015 
saveLayerAlpha(RectF rect, int alpha, int saveFlags)1016     private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
1017         Paint_Delegate paint = new Paint_Delegate();
1018         paint.setAlpha(alpha);
1019         return saveLayer(rect, paint, saveFlags);
1020     }
1021 
saveLayer(RectF rect, Paint_Delegate paint, int saveFlags)1022     private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
1023         // get the current save count
1024         int count = mSnapshot.size();
1025 
1026         mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
1027 
1028         // return the old save count
1029         return count;
1030     }
1031 
1032     /**
1033      * Restores the {@link GcSnapshot} to <var>saveCount</var>
1034      * @param saveCount the saveCount
1035      */
restoreTo(int saveCount)1036     private void restoreTo(int saveCount) {
1037         mSnapshot = mSnapshot.restoreTo(saveCount);
1038     }
1039 
1040     /**
1041      * Restores the {@link GcSnapshot} to <var>saveCount</var>
1042      * @param saveCount the saveCount
1043      */
restore()1044     private void restore() {
1045         mSnapshot = mSnapshot.restore();
1046     }
1047 
clipRect(float left, float top, float right, float bottom, int regionOp)1048     private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
1049         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
1050     }
1051 
drawBitmap( long nativeCanvas, Bitmap_Delegate bitmap, long nativePaintOrZero, final int sleft, final int stop, final int sright, final int sbottom, final int dleft, final int dtop, final int dright, final int dbottom)1052     private static void drawBitmap(
1053             long nativeCanvas,
1054             Bitmap_Delegate bitmap,
1055             long nativePaintOrZero,
1056             final int sleft, final int stop, final int sright, final int sbottom,
1057             final int dleft, final int dtop, final int dright, final int dbottom) {
1058         // get the delegate from the native int.
1059         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1060         if (canvasDelegate == null) {
1061             return;
1062         }
1063 
1064         // get the paint, which could be null if the int is 0
1065         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1066 
1067         final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
1068 
1069         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
1070                 new GcSnapshot.Drawable() {
1071                     @Override
1072                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
1073                         if (paint != null && paint.isFilterBitmap()) {
1074                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1075                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1076                         }
1077 
1078                         //FIXME add support for canvas, screen and bitmap densities.
1079                         graphics.drawImage(image, dleft, dtop, dright, dbottom,
1080                                 sleft, stop, sright, sbottom, null);
1081                     }
1082         });
1083     }
1084 
1085 
1086     /**
1087      * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
1088      * The image returns, through a 1-size boolean array, whether the drawing code should
1089      * use a SRC composite no matter what the paint says.
1090      *
1091      * @param bitmap the bitmap
1092      * @param paint the paint that will be used to draw
1093      * @param forceSrcMode whether the composite will have to be SRC
1094      * @return the image to draw
1095      */
getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint, boolean[] forceSrcMode)1096     private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
1097             boolean[] forceSrcMode) {
1098         BufferedImage image = bitmap.getImage();
1099         forceSrcMode[0] = false;
1100 
1101         // if the bitmap config is alpha_8, then we erase all color value from it
1102         // before drawing it.
1103         if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
1104             fixAlpha8Bitmap(image);
1105         } else if (bitmap.hasAlpha() == false) {
1106             // hasAlpha is merely a rendering hint. There can in fact be alpha values
1107             // in the bitmap but it should be ignored at drawing time.
1108             // There is two ways to do this:
1109             // - override the composite to be SRC. This can only be used if the composite
1110             //   was going to be SRC or SRC_OVER in the first place
1111             // - Create a different bitmap to draw in which all the alpha channel values is set
1112             //   to 0xFF.
1113             if (paint != null) {
1114                 Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
1115                 if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
1116                     PorterDuff.Mode mode =
1117                         ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
1118 
1119                     forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
1120                             mode == PorterDuff.Mode.SRC;
1121                 }
1122             }
1123 
1124             // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
1125             if (forceSrcMode[0] == false) {
1126                 image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
1127             }
1128         }
1129 
1130         return image;
1131     }
1132 
fixAlpha8Bitmap(final BufferedImage image)1133     private static void fixAlpha8Bitmap(final BufferedImage image) {
1134         int w = image.getWidth();
1135         int h = image.getHeight();
1136         int[] argb = new int[w * h];
1137         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
1138 
1139         final int length = argb.length;
1140         for (int i = 0 ; i < length; i++) {
1141             argb[i] &= 0xFF000000;
1142         }
1143         image.setRGB(0, 0, w, h, argb, 0, w);
1144     }
1145 }
1146 
1147