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