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    @LayoutlibDelegate
66    /*package*/ static int nativePostCreate1(int native_shader, float cx, float cy,
67            int[] colors, float[] positions) {
68        // nothing to be done here.
69        return 0;
70    }
71
72    @LayoutlibDelegate
73    /*package*/ static int nativePostCreate2(int native_shader, float cx, float cy,
74            int color0, int color1) {
75        // nothing to be done here.
76        return 0;
77    }
78
79    // ---- Private delegate/helper methods ----
80
81    /**
82     * A subclass of Shader that draws a sweep gradient around a center point.
83     *
84     * @param cx       The x-coordinate of the center
85     * @param cy       The y-coordinate of the center
86     * @param colors   The colors to be distributed between around the center.
87     *                 There must be at least 2 colors in the array.
88     * @param positions May be NULL. The relative position of
89     *                 each corresponding color in the colors array, beginning
90     *                 with 0 and ending with 1.0. If the values are not
91     *                 monotonic, the drawing may produce unexpected results.
92     *                 If positions is NULL, then the colors are automatically
93     *                 spaced evenly.
94     */
95    private SweepGradient_Delegate(float cx, float cy,
96                         int colors[], float positions[]) {
97        super(colors, positions);
98        mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
99    }
100
101    private class SweepGradientPaint extends GradientPaint {
102
103        private final float mCx;
104        private final float mCy;
105
106        public SweepGradientPaint(float cx, float cy, int[] colors,
107                float[] positions) {
108            super(colors, positions, null /*tileMode*/);
109            mCx = cx;
110            mCy = cy;
111        }
112
113        @Override
114        public java.awt.PaintContext createContext(
115                java.awt.image.ColorModel     colorModel,
116                java.awt.Rectangle            deviceBounds,
117                java.awt.geom.Rectangle2D     userBounds,
118                java.awt.geom.AffineTransform xform,
119                java.awt.RenderingHints       hints) {
120            precomputeGradientColors();
121
122            java.awt.geom.AffineTransform canvasMatrix;
123            try {
124                canvasMatrix = xform.createInverse();
125            } catch (java.awt.geom.NoninvertibleTransformException e) {
126                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
127                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
128                canvasMatrix = new java.awt.geom.AffineTransform();
129            }
130
131            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
132            try {
133                localMatrix = localMatrix.createInverse();
134            } catch (java.awt.geom.NoninvertibleTransformException e) {
135                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
136                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
137                localMatrix = new java.awt.geom.AffineTransform();
138            }
139
140            return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
141        }
142
143        private class SweepGradientPaintContext implements java.awt.PaintContext {
144
145            private final java.awt.geom.AffineTransform mCanvasMatrix;
146            private final java.awt.geom.AffineTransform mLocalMatrix;
147            private final java.awt.image.ColorModel mColorModel;
148
149            public SweepGradientPaintContext(
150                    java.awt.geom.AffineTransform canvasMatrix,
151                    java.awt.geom.AffineTransform localMatrix,
152                    java.awt.image.ColorModel colorModel) {
153                mCanvasMatrix = canvasMatrix;
154                mLocalMatrix = localMatrix;
155                mColorModel = colorModel;
156            }
157
158            @Override
159            public void dispose() {
160            }
161
162            @Override
163            public java.awt.image.ColorModel getColorModel() {
164                return mColorModel;
165            }
166
167            @Override
168            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
169                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
170                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
171
172                int[] data = new int[w*h];
173
174                // compute angle from each point to the center, and figure out the distance from
175                // it.
176                int index = 0;
177                float[] pt1 = new float[2];
178                float[] pt2 = new float[2];
179                for (int iy = 0 ; iy < h ; iy++) {
180                    for (int ix = 0 ; ix < w ; ix++) {
181                        // handle the canvas transform
182                        pt1[0] = x + ix;
183                        pt1[1] = y + iy;
184                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
185
186                        // handle the local matrix
187                        pt1[0] = pt2[0] - mCx;
188                        pt1[1] = pt2[1] - mCy;
189                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
190
191                        float dx = pt2[0];
192                        float dy = pt2[1];
193
194                        float angle;
195                        if (dx == 0) {
196                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
197                        } else if (dy == 0) {
198                            angle = (float) (dx < 0 ? Math.PI : 0);
199                        } else {
200                            angle = (float) Math.atan(dy / dx);
201                            if (dx > 0) {
202                                if (dy < 0) {
203                                    angle += Math.PI * 2;
204                                }
205                            } else {
206                                angle += Math.PI;
207                            }
208                        }
209
210                        // convert to 0-1. value and get color
211                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
212                    }
213                }
214
215                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
216
217                return image.getRaster();
218            }
219
220        }
221    }
222}
223