Canvas_Delegate.java revision 83a14824ce79a109de4a3452a8a43e13e861e1be
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.mNativeCanvas);
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(int 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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas);
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.mNativeCanvas, 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 int initRaster(int 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(int srcCanvas, int 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(int nativeCanvas, RectF bounds,
348                                               int 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(int nativeCanvas, float l,
365                                               float t, float r, float b,
366                                               int 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(int 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(int 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(int nCanvas, int 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(int nCanvas, int 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(int 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(int nativeCanvas,
484                                                  int 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(int nativeCanvas,
501                                                    int 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(int nativeCanvas, int 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(int 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(int canvas, int 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(int nativeCanvas,
572                                                     RectF rect) {
573        // FIXME properly implement quickReject
574        return false;
575    }
576
577    @LayoutlibDelegate
578    /*package*/ static boolean native_quickReject(int nativeCanvas,
579                                                     int path) {
580        // FIXME properly implement quickReject
581        return false;
582    }
583
584    @LayoutlibDelegate
585    /*package*/ static boolean native_quickReject(int 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(int 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(int 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(int nativeCanvas, int color) {
607        native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
608    }
609
610    @LayoutlibDelegate
611    /*package*/ static void native_drawColor(int 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(
631                        PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
632                if (composite != null) {
633                    graphics.setComposite(composite);
634                }
635
636                graphics.fillRect(0, 0, w, h);
637            }
638        });
639    }
640
641    @LayoutlibDelegate
642    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
643        // FIXME
644        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
645                "Canvas.drawPaint is not supported.", null, null /*data*/);
646    }
647
648    @LayoutlibDelegate
649    /*package*/ static void native_drawLine(int nativeCanvas,
650            final float startX, final float startY, final float stopX, final float stopY,
651            int paint) {
652
653        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
654                new GcSnapshot.Drawable() {
655                    @Override
656                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
657                        graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
658                    }
659        });
660    }
661
662    @LayoutlibDelegate
663    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
664                                               int paint) {
665        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
666    }
667
668    @LayoutlibDelegate
669    /*package*/ static void native_drawRect(int nativeCanvas,
670            final float left, final float top, final float right, final float bottom, int paint) {
671
672        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
673                new GcSnapshot.Drawable() {
674                    @Override
675                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
676                        int style = paintDelegate.getStyle();
677
678                        // draw
679                        if (style == Paint.Style.FILL.nativeInt ||
680                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
681                            graphics.fillRect((int)left, (int)top,
682                                    (int)(right-left), (int)(bottom-top));
683                        }
684
685                        if (style == Paint.Style.STROKE.nativeInt ||
686                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
687                            graphics.drawRect((int)left, (int)top,
688                                    (int)(right-left), (int)(bottom-top));
689                        }
690                    }
691        });
692    }
693
694    @LayoutlibDelegate
695    /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) {
696        if (oval.right > oval.left && oval.bottom > oval.top) {
697            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
698                    new GcSnapshot.Drawable() {
699                        @Override
700                        public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
701                            int style = paintDelegate.getStyle();
702
703                            // draw
704                            if (style == Paint.Style.FILL.nativeInt ||
705                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
706                                graphics.fillOval((int)oval.left, (int)oval.top,
707                                        (int)oval.width(), (int)oval.height());
708                            }
709
710                            if (style == Paint.Style.STROKE.nativeInt ||
711                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
712                                graphics.drawOval((int)oval.left, (int)oval.top,
713                                        (int)oval.width(), (int)oval.height());
714                            }
715                        }
716            });
717        }
718    }
719
720    @LayoutlibDelegate
721    /*package*/ static void native_drawCircle(int nativeCanvas,
722            float cx, float cy, float radius, int paint) {
723        native_drawOval(nativeCanvas,
724                new RectF(cx - radius, cy - radius, cx + radius, cy + radius),
725                paint);
726    }
727
728    @LayoutlibDelegate
729    /*package*/ static void native_drawArc(int nativeCanvas,
730            final RectF oval, final float startAngle, final float sweep,
731            final boolean useCenter, int paint) {
732        if (oval.right > oval.left && oval.bottom > oval.top) {
733            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
734                    new GcSnapshot.Drawable() {
735                        @Override
736                        public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
737                            int style = paintDelegate.getStyle();
738
739                            Arc2D.Float arc = new Arc2D.Float(
740                                    oval.left, oval.top, oval.width(), oval.height(),
741                                    -startAngle, -sweep,
742                                    useCenter ? Arc2D.PIE : Arc2D.OPEN);
743
744                            // draw
745                            if (style == Paint.Style.FILL.nativeInt ||
746                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
747                                graphics.fill(arc);
748                            }
749
750                            if (style == Paint.Style.STROKE.nativeInt ||
751                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
752                                graphics.draw(arc);
753                            }
754                        }
755            });
756        }
757    }
758
759    @LayoutlibDelegate
760    /*package*/ static void native_drawRoundRect(int nativeCanvas,
761            final RectF rect, final float rx, final float ry, int paint) {
762
763        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
764                new GcSnapshot.Drawable() {
765                    @Override
766                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
767                        int style = paintDelegate.getStyle();
768
769                        // draw
770                        if (style == Paint.Style.FILL.nativeInt ||
771                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
772                            graphics.fillRoundRect(
773                                    (int)rect.left, (int)rect.top,
774                                    (int)rect.width(), (int)rect.height(),
775                                    (int)rx, (int)ry);
776                        }
777
778                        if (style == Paint.Style.STROKE.nativeInt ||
779                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
780                            graphics.drawRoundRect(
781                                    (int)rect.left, (int)rect.top,
782                                    (int)rect.width(), (int)rect.height(),
783                                    (int)rx, (int)ry);
784                        }
785                    }
786        });
787    }
788
789    @LayoutlibDelegate
790    /*package*/ static void native_drawPath(int nativeCanvas, int path, int paint) {
791        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
792        if (pathDelegate == null) {
793            return;
794        }
795
796        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
797                new GcSnapshot.Drawable() {
798                    @Override
799                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
800                        Shape shape = pathDelegate.getJavaShape();
801                        int style = paintDelegate.getStyle();
802
803                        if (style == Paint.Style.FILL.nativeInt ||
804                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
805                            graphics.fill(shape);
806                        }
807
808                        if (style == Paint.Style.STROKE.nativeInt ||
809                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
810                            graphics.draw(shape);
811                        }
812                    }
813        });
814    }
815
816    @LayoutlibDelegate
817    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
818                                                 float left, float top,
819                                                 int nativePaintOrZero,
820                                                 int canvasDensity,
821                                                 int screenDensity,
822                                                 int bitmapDensity) {
823        // get the delegate from the native int.
824        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
825        if (bitmapDelegate == null) {
826            return;
827        }
828
829        BufferedImage image = bitmapDelegate.getImage();
830        float right = left + image.getWidth();
831        float bottom = top + image.getHeight();
832
833        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
834                0, 0, image.getWidth(), image.getHeight(),
835                (int)left, (int)top, (int)right, (int)bottom);
836    }
837
838    @LayoutlibDelegate
839    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
840                                                 Rect src, RectF dst,
841                                                 int nativePaintOrZero,
842                                                 int screenDensity,
843                                                 int bitmapDensity) {
844        // get the delegate from the native int.
845        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
846        if (bitmapDelegate == null) {
847            return;
848        }
849
850        BufferedImage image = bitmapDelegate.getImage();
851
852        if (src == null) {
853            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
854                    0, 0, image.getWidth(), image.getHeight(),
855                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
856        } else {
857            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
858                    src.left, src.top, src.width(), src.height(),
859                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
860        }
861    }
862
863    @LayoutlibDelegate
864    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
865                                                 Rect src, Rect dst,
866                                                 int nativePaintOrZero,
867                                                 int screenDensity,
868                                                 int bitmapDensity) {
869        // get the delegate from the native int.
870        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
871        if (bitmapDelegate == null) {
872            return;
873        }
874
875        BufferedImage image = bitmapDelegate.getImage();
876
877        if (src == null) {
878            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
879                    0, 0, image.getWidth(), image.getHeight(),
880                    dst.left, dst.top, dst.right, dst.bottom);
881        } else {
882            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
883                    src.left, src.top, src.width(), src.height(),
884                    dst.left, dst.top, dst.right, dst.bottom);
885        }
886    }
887
888    @LayoutlibDelegate
889    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
890                                                int offset, int stride, final float x,
891                                                 final float y, int width, int height,
892                                                 boolean hasAlpha,
893                                                 int nativePaintOrZero) {
894
895        // create a temp BufferedImage containing the content.
896        final BufferedImage image = new BufferedImage(width, height,
897                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
898        image.setRGB(0, 0, width, height, colors, offset, stride);
899
900        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
901                new GcSnapshot.Drawable() {
902                    @Override
903                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
904                        if (paint != null && paint.isFilterBitmap()) {
905                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
906                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
907                        }
908
909                        graphics.drawImage(image, (int) x, (int) y, null);
910                    }
911        });
912    }
913
914    @LayoutlibDelegate
915    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
916                                                      int nMatrix, int nPaint) {
917        // get the delegate from the native int.
918        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
919        if (canvasDelegate == null) {
920            return;
921        }
922
923        // get the delegate from the native int, which can be null
924        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
925
926        // get the delegate from the native int.
927        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap);
928        if (bitmapDelegate == null) {
929            return;
930        }
931
932        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
933
934        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
935        if (matrixDelegate == null) {
936            return;
937        }
938
939        final AffineTransform mtx = matrixDelegate.getAffineTransform();
940
941        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
942                @Override
943                public void draw(Graphics2D graphics, Paint_Delegate paint) {
944                    if (paint != null && paint.isFilterBitmap()) {
945                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
946                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
947                    }
948
949                    //FIXME add support for canvas, screen and bitmap densities.
950                    graphics.drawImage(image, mtx, null);
951                }
952        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
953    }
954
955    @LayoutlibDelegate
956    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
957            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
958            int colorOffset, int nPaint) {
959        // FIXME
960        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
961                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
962    }
963
964    @LayoutlibDelegate
965    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
966            float[] verts, int vertOffset,
967            float[] texs, int texOffset,
968            int[] colors, int colorOffset,
969            short[] indices, int indexOffset,
970            int indexCount, int nPaint) {
971        // FIXME
972        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
973                "Canvas.drawVertices is not supported.", null, null /*data*/);
974    }
975
976    @LayoutlibDelegate
977    /*package*/ static void native_drawText(int nativeCanvas,
978            final char[] text, final int index, final int count,
979            final float startX, final float startY, final int flags, int paint) {
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                // Paint.TextAlign indicates how the text is positioned relative to X.
988                // LEFT is the default and there's nothing to do.
989                float x = startX;
990                int limit = index + count;
991                boolean isRtl = flags == Canvas.DIRECTION_RTL;
992                if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
993                    float m = paintDelegate.measureText(text, index, count, isRtl);
994                    if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
995                        x -= m / 2;
996                    } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
997                        x -= m;
998                    }
999                }
1000
1001                new BidiRenderer(graphics, paintDelegate, text).renderText(
1002                        index, limit, isRtl, null, 0, true, x, startY);
1003            }
1004        });
1005    }
1006
1007    @LayoutlibDelegate
1008    /*package*/ static void native_drawText(int nativeCanvas, String text,
1009            int start, int end, float x, float y, final int flags, int paint) {
1010        int count = end - start;
1011        char[] buffer = TemporaryBuffer.obtain(count);
1012        TextUtils.getChars(text, start, end, buffer, 0);
1013
1014        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
1015    }
1016
1017    @LayoutlibDelegate
1018    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
1019            int start, int end, int contextStart, int contextEnd,
1020            float x, float y, int flags, int paint) {
1021        int count = end - start;
1022        char[] buffer = TemporaryBuffer.obtain(count);
1023        TextUtils.getChars(text, start, end, buffer, 0);
1024
1025        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
1026    }
1027
1028    @LayoutlibDelegate
1029    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
1030            int start, int count, int contextStart, int contextCount,
1031            float x, float y, int flags, int paint) {
1032        native_drawText(nativeCanvas, text, start, count, x, y, flags, paint);
1033    }
1034
1035    @LayoutlibDelegate
1036    /*package*/ static void native_drawPosText(int nativeCanvas,
1037                                                  char[] text, int index,
1038                                                  int count, float[] pos,
1039                                                  int paint) {
1040        // FIXME
1041        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1042                "Canvas.drawPosText is not supported.", null, null /*data*/);
1043    }
1044
1045    @LayoutlibDelegate
1046    /*package*/ static void native_drawPosText(int nativeCanvas,
1047                                                  String text, float[] pos,
1048                                                  int paint) {
1049        // FIXME
1050        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1051                "Canvas.drawPosText is not supported.", null, null /*data*/);
1052    }
1053
1054    @LayoutlibDelegate
1055    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
1056                                                     char[] text, int index,
1057                                                     int count, int path,
1058                                                     float hOffset,
1059                                                     float vOffset, int bidiFlags,
1060                                                     int paint) {
1061        // FIXME
1062        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1063                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
1064    }
1065
1066    @LayoutlibDelegate
1067    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
1068                                                     String text, int path,
1069                                                     float hOffset,
1070                                                     float vOffset,
1071                                                     int flags, int paint) {
1072        // FIXME
1073        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1074                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
1075    }
1076
1077    @LayoutlibDelegate
1078    /*package*/ static void finalizer(int nativeCanvas) {
1079        // get the delegate from the native int so that it can be disposed.
1080        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1081        if (canvasDelegate == null) {
1082            return;
1083        }
1084
1085        canvasDelegate.dispose();
1086
1087        // remove it from the manager.
1088        sManager.removeJavaReferenceFor(nativeCanvas);
1089    }
1090
1091    // ---- Private delegate/helper methods ----
1092
1093    /**
1094     * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
1095     * <p>Note that the drawable may actually be executed several times if there are
1096     * layers involved (see {@link #saveLayer(RectF, int, int)}.
1097     */
1098    private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode,
1099            GcSnapshot.Drawable drawable) {
1100        // get the delegate from the native int.
1101        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
1102        if (canvasDelegate == null) {
1103            return;
1104        }
1105
1106        // get the paint which can be null if nPaint is 0;
1107        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
1108
1109        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
1110    }
1111
1112    /**
1113     * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
1114     * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
1115     * <p>Note that the drawable may actually be executed several times if there are
1116     * layers involved (see {@link #saveLayer(RectF, int, int)}.
1117     */
1118    private static void draw(int nCanvas, GcSnapshot.Drawable drawable) {
1119        // get the delegate from the native int.
1120        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
1121        if (canvasDelegate == null) {
1122            return;
1123        }
1124
1125        canvasDelegate.mSnapshot.draw(drawable);
1126    }
1127
1128    private Canvas_Delegate(Bitmap_Delegate bitmap) {
1129        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
1130    }
1131
1132    private Canvas_Delegate() {
1133        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
1134    }
1135
1136    /**
1137     * Disposes of the {@link Graphics2D} stack.
1138     */
1139    private void dispose() {
1140        mSnapshot.dispose();
1141    }
1142
1143    private int save(int saveFlags) {
1144        // get the current save count
1145        int count = mSnapshot.size();
1146
1147        mSnapshot = mSnapshot.save(saveFlags);
1148
1149        // return the old save count
1150        return count;
1151    }
1152
1153    private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
1154        Paint_Delegate paint = new Paint_Delegate();
1155        paint.setAlpha(alpha);
1156        return saveLayer(rect, paint, saveFlags);
1157    }
1158
1159    private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
1160        // get the current save count
1161        int count = mSnapshot.size();
1162
1163        mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
1164
1165        // return the old save count
1166        return count;
1167    }
1168
1169    /**
1170     * Restores the {@link GcSnapshot} to <var>saveCount</var>
1171     * @param saveCount the saveCount
1172     */
1173    private void restoreTo(int saveCount) {
1174        mSnapshot = mSnapshot.restoreTo(saveCount);
1175    }
1176
1177    /**
1178     * Restores the {@link GcSnapshot} to <var>saveCount</var>
1179     * @param saveCount the saveCount
1180     */
1181    private void restore() {
1182        mSnapshot = mSnapshot.restore();
1183    }
1184
1185    private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
1186        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
1187    }
1188
1189    private void setBitmap(Bitmap_Delegate bitmap) {
1190        mBitmap = bitmap;
1191        assert mSnapshot.size() == 1;
1192        mSnapshot.setBitmap(mBitmap);
1193    }
1194
1195    private static void drawBitmap(
1196            int nativeCanvas,
1197            Bitmap_Delegate bitmap,
1198            int nativePaintOrZero,
1199            final int sleft, final int stop, final int sright, final int sbottom,
1200            final int dleft, final int dtop, final int dright, final int dbottom) {
1201        // get the delegate from the native int.
1202        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1203        if (canvasDelegate == null) {
1204            return;
1205        }
1206
1207        // get the paint, which could be null if the int is 0
1208        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1209
1210        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
1211
1212        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
1213                new GcSnapshot.Drawable() {
1214                    @Override
1215                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
1216                        if (paint != null && paint.isFilterBitmap()) {
1217                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1218                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1219                        }
1220
1221                        //FIXME add support for canvas, screen and bitmap densities.
1222                        graphics.drawImage(image, dleft, dtop, dright, dbottom,
1223                                sleft, stop, sright, sbottom, null);
1224                    }
1225        });
1226    }
1227
1228
1229    /**
1230     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
1231     * The image returns, through a 1-size boolean array, whether the drawing code should
1232     * use a SRC composite no matter what the paint says.
1233     *
1234     * @param bitmap the bitmap
1235     * @param paint the paint that will be used to draw
1236     * @param forceSrcMode whether the composite will have to be SRC
1237     * @return the image to draw
1238     */
1239    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
1240            boolean[] forceSrcMode) {
1241        BufferedImage image = bitmap.getImage();
1242        forceSrcMode[0] = false;
1243
1244        // if the bitmap config is alpha_8, then we erase all color value from it
1245        // before drawing it.
1246        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
1247            fixAlpha8Bitmap(image);
1248        } else if (bitmap.hasAlpha() == false) {
1249            // hasAlpha is merely a rendering hint. There can in fact be alpha values
1250            // in the bitmap but it should be ignored at drawing time.
1251            // There is two ways to do this:
1252            // - override the composite to be SRC. This can only be used if the composite
1253            //   was going to be SRC or SRC_OVER in the first place
1254            // - Create a different bitmap to draw in which all the alpha channel values is set
1255            //   to 0xFF.
1256            if (paint != null) {
1257                Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
1258                if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
1259                    PorterDuff.Mode mode =
1260                        ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
1261
1262                    forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
1263                            mode == PorterDuff.Mode.SRC;
1264                }
1265            }
1266
1267            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
1268            if (forceSrcMode[0] == false) {
1269                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
1270            }
1271        }
1272
1273        return image;
1274    }
1275
1276    private static void fixAlpha8Bitmap(final BufferedImage image) {
1277        int w = image.getWidth();
1278        int h = image.getHeight();
1279        int[] argb = new int[w * h];
1280        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
1281
1282        final int length = argb.length;
1283        for (int i = 0 ; i < length; i++) {
1284            argb[i] &= 0xFF000000;
1285        }
1286        image.setRGB(0, 0, w, h, argb, 0, w);
1287    }
1288}
1289
1290