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.tools.layoutlib.annotations.LayoutlibDelegate;
23
24/**
25 * Delegate implementing the native methods of android.graphics.SweepGradient
26 *
27 * Through the layoutlib_create tool, the original native methods of SweepGradient have been
28 * replaced by calls to methods of the same name in this delegate class.
29 *
30 * This class behaves like the original native implementation, but in Java, keeping previously
31 * native data into its own objects and mapping them to int that are sent back and forth between
32 * it and the original SweepGradient class.
33 *
34 * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
35 * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
36 *
37 * @see Shader_Delegate
38 *
39 */
40public class SweepGradient_Delegate extends Gradient_Delegate {
41
42    // ---- delegate data ----
43    private java.awt.Paint mJavaPaint;
44
45    // ---- Public Helper methods ----
46
47    @Override
48    public java.awt.Paint getJavaPaint() {
49        return mJavaPaint;
50    }
51
52    // ---- native methods ----
53
54    @LayoutlibDelegate
55    /*package*/ static int nativeCreate1(float x, float y, int colors[], float positions[]) {
56        SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(x, y, colors, positions);
57        return sManager.addNewDelegate(newDelegate);
58    }
59
60    @LayoutlibDelegate
61    /*package*/ static int nativeCreate2(float x, float y, int color0, int color1) {
62        return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
63    }
64
65    // ---- Private delegate/helper methods ----
66
67    /**
68     * A subclass of Shader that draws a sweep gradient around a center point.
69     *
70     * @param cx       The x-coordinate of the center
71     * @param cy       The y-coordinate of the center
72     * @param colors   The colors to be distributed between around the center.
73     *                 There must be at least 2 colors in the array.
74     * @param positions May be NULL. The relative position of
75     *                 each corresponding color in the colors array, beginning
76     *                 with 0 and ending with 1.0. If the values are not
77     *                 monotonic, the drawing may produce unexpected results.
78     *                 If positions is NULL, then the colors are automatically
79     *                 spaced evenly.
80     */
81    private SweepGradient_Delegate(float cx, float cy,
82                         int colors[], float positions[]) {
83        super(colors, positions);
84        mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
85    }
86
87    private class SweepGradientPaint extends GradientPaint {
88
89        private final float mCx;
90        private final float mCy;
91
92        public SweepGradientPaint(float cx, float cy, int[] colors,
93                float[] positions) {
94            super(colors, positions, null /*tileMode*/);
95            mCx = cx;
96            mCy = cy;
97        }
98
99        public java.awt.PaintContext createContext(
100                java.awt.image.ColorModel     colorModel,
101                java.awt.Rectangle            deviceBounds,
102                java.awt.geom.Rectangle2D     userBounds,
103                java.awt.geom.AffineTransform xform,
104                java.awt.RenderingHints       hints) {
105            precomputeGradientColors();
106
107            java.awt.geom.AffineTransform canvasMatrix;
108            try {
109                canvasMatrix = xform.createInverse();
110            } catch (java.awt.geom.NoninvertibleTransformException e) {
111                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
112                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
113                canvasMatrix = new java.awt.geom.AffineTransform();
114            }
115
116            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
117            try {
118                localMatrix = localMatrix.createInverse();
119            } catch (java.awt.geom.NoninvertibleTransformException e) {
120                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
121                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
122                localMatrix = new java.awt.geom.AffineTransform();
123            }
124
125            return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
126        }
127
128        private class SweepGradientPaintContext implements java.awt.PaintContext {
129
130            private final java.awt.geom.AffineTransform mCanvasMatrix;
131            private final java.awt.geom.AffineTransform mLocalMatrix;
132            private final java.awt.image.ColorModel mColorModel;
133
134            public SweepGradientPaintContext(
135                    java.awt.geom.AffineTransform canvasMatrix,
136                    java.awt.geom.AffineTransform localMatrix,
137                    java.awt.image.ColorModel colorModel) {
138                mCanvasMatrix = canvasMatrix;
139                mLocalMatrix = localMatrix;
140                mColorModel = colorModel;
141            }
142
143            public void dispose() {
144            }
145
146            public java.awt.image.ColorModel getColorModel() {
147                return mColorModel;
148            }
149
150            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
151                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
152                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
153
154                int[] data = new int[w*h];
155
156                // compute angle from each point to the center, and figure out the distance from
157                // it.
158                int index = 0;
159                float[] pt1 = new float[2];
160                float[] pt2 = new float[2];
161                for (int iy = 0 ; iy < h ; iy++) {
162                    for (int ix = 0 ; ix < w ; ix++) {
163                        // handle the canvas transform
164                        pt1[0] = x + ix;
165                        pt1[1] = y + iy;
166                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
167
168                        // handle the local matrix
169                        pt1[0] = pt2[0] - mCx;
170                        pt1[1] = pt2[1] - mCy;
171                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
172
173                        float dx = pt2[0];
174                        float dy = pt2[1];
175
176                        float angle;
177                        if (dx == 0) {
178                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
179                        } else if (dy == 0) {
180                            angle = (float) (dx < 0 ? Math.PI : 0);
181                        } else {
182                            angle = (float) Math.atan(dy / dx);
183                            if (dx > 0) {
184                                if (dy < 0) {
185                                    angle += Math.PI * 2;
186                                }
187                            } else {
188                                angle += Math.PI;
189                            }
190                        }
191
192                        // convert to 0-1. value and get color
193                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
194                    }
195                }
196
197                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
198
199                return image.getRaster();
200            }
201
202        }
203    }
204}
205