blob: c6fde7be289bc0d06d7dbdb10031dada9bbbad9f [file] [log] [blame]
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -08001/*
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
Xavier Ducrohet918aaa52011-01-13 10:59:34 -080019import com.android.ide.common.rendering.api.LayoutLog;
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -080020import com.android.layoutlib.bridge.Bridge;
21import com.android.layoutlib.bridge.impl.DelegateManager;
Xavier Ducrohet9a4fe292011-02-09 17:17:49 -080022import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -080023
24import android.graphics.Shader.TileMode;
25
26/**
27 * Delegate implementing the native methods of android.graphics.BitmapShader
28 *
29 * Through the layoutlib_create tool, the original native methods of BitmapShader have been
30 * replaced by calls to methods of the same name in this delegate class.
31 *
32 * This class behaves like the original native implementation, but in Java, keeping previously
33 * native data into its own objects and mapping them to int that are sent back and forth between
34 * it and the original BitmapShader class.
35 *
36 * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
37 * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
38 *
39 * @see Shader_Delegate
40 *
41 */
42public class BitmapShader_Delegate extends Shader_Delegate {
43
44 // ---- delegate data ----
45 private java.awt.Paint mJavaPaint;
46
47 // ---- Public Helper methods ----
48
49 @Override
50 public java.awt.Paint getJavaPaint() {
51 return mJavaPaint;
52 }
53
54 @Override
55 public boolean isSupported() {
56 return true;
57 }
58
59 @Override
60 public String getSupportMessage() {
61 // no message since isSupported returns true;
62 return null;
63 }
64
65 // ---- native methods ----
66
Xavier Ducrohet9a4fe292011-02-09 17:17:49 -080067 @LayoutlibDelegate
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -080068 /*package*/ static int nativeCreate(int native_bitmap, int shaderTileModeX,
69 int shaderTileModeY) {
70 Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap);
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -080071 if (bitmap == null) {
72 return 0;
73 }
74
75 BitmapShader_Delegate newDelegate = new BitmapShader_Delegate(
76 bitmap.getImage(),
77 Shader_Delegate.getTileMode(shaderTileModeX),
78 Shader_Delegate.getTileMode(shaderTileModeY));
79 return sManager.addDelegate(newDelegate);
80 }
81
Xavier Ducrohet9a4fe292011-02-09 17:17:49 -080082 @LayoutlibDelegate
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -080083 /*package*/ static int nativePostCreate(int native_shader, int native_bitmap,
84 int shaderTileModeX, int shaderTileModeY) {
85 // pass, not needed.
86 return 0;
87 }
88
89 // ---- Private delegate/helper methods ----
90
91 private BitmapShader_Delegate(java.awt.image.BufferedImage image,
92 TileMode tileModeX, TileMode tileModeY) {
93 mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY);
94 }
95
96 private class BitmapShaderPaint implements java.awt.Paint {
97 private final java.awt.image.BufferedImage mImage;
98 private final TileMode mTileModeX;
99 private final TileMode mTileModeY;
100
101 BitmapShaderPaint(java.awt.image.BufferedImage image,
102 TileMode tileModeX, TileMode tileModeY) {
103 mImage = image;
104 mTileModeX = tileModeX;
105 mTileModeY = tileModeY;
106 }
107
108 public java.awt.PaintContext createContext(
109 java.awt.image.ColorModel colorModel,
110 java.awt.Rectangle deviceBounds,
111 java.awt.geom.Rectangle2D userBounds,
112 java.awt.geom.AffineTransform xform,
113 java.awt.RenderingHints hints) {
114
115 java.awt.geom.AffineTransform canvasMatrix;
116 try {
117 canvasMatrix = xform.createInverse();
118 } catch (java.awt.geom.NoninvertibleTransformException e) {
Xavier Ducrohet918aaa52011-01-13 10:59:34 -0800119 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
Xavier Ducrohet51a7e542011-01-14 16:40:43 -0800120 "Unable to inverse matrix in BitmapShader", e, null /*data*/);
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -0800121 canvasMatrix = new java.awt.geom.AffineTransform();
122 }
123
124 java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
125 try {
126 localMatrix = localMatrix.createInverse();
127 } catch (java.awt.geom.NoninvertibleTransformException e) {
Xavier Ducrohet918aaa52011-01-13 10:59:34 -0800128 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
Xavier Ducrohet51a7e542011-01-14 16:40:43 -0800129 "Unable to inverse matrix in BitmapShader", e, null /*data*/);
Xavier Ducrohetd348b6e2010-12-20 08:22:47 -0800130 localMatrix = new java.awt.geom.AffineTransform();
131 }
132
133 return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel);
134 }
135
136 private class BitmapShaderContext implements java.awt.PaintContext {
137
138 private final java.awt.geom.AffineTransform mCanvasMatrix;
139 private final java.awt.geom.AffineTransform mLocalMatrix;
140 private final java.awt.image.ColorModel mColorModel;
141
142 public BitmapShaderContext(
143 java.awt.geom.AffineTransform canvasMatrix,
144 java.awt.geom.AffineTransform localMatrix,
145 java.awt.image.ColorModel colorModel) {
146 mCanvasMatrix = canvasMatrix;
147 mLocalMatrix = localMatrix;
148 mColorModel = colorModel;
149 }
150
151 public void dispose() {
152 }
153
154 public java.awt.image.ColorModel getColorModel() {
155 return mColorModel;
156 }
157
158 public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
159 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
160 java.awt.image.BufferedImage.TYPE_INT_ARGB);
161
162 int[] data = new int[w*h];
163
164 int index = 0;
165 float[] pt1 = new float[2];
166 float[] pt2 = new float[2];
167 for (int iy = 0 ; iy < h ; iy++) {
168 for (int ix = 0 ; ix < w ; ix++) {
169 // handle the canvas transform
170 pt1[0] = x + ix;
171 pt1[1] = y + iy;
172 mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
173
174 // handle the local matrix.
175 pt1[0] = pt2[0];
176 pt1[1] = pt2[1];
177 mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
178
179 data[index++] = getColor(pt2[0], pt2[1]);
180 }
181 }
182
183 image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
184
185 return image.getRaster();
186 }
187 }
188
189 /**
190 * Returns a color for an arbitrary point.
191 */
192 private int getColor(float fx, float fy) {
193 int x = getCoordinate(Math.round(fx), mImage.getWidth(), mTileModeX);
194 int y = getCoordinate(Math.round(fy), mImage.getHeight(), mTileModeY);
195
196 return mImage.getRGB(x, y);
197 }
198
199 private int getCoordinate(int i, int size, TileMode mode) {
200 if (i < 0) {
201 switch (mode) {
202 case CLAMP:
203 i = 0;
204 break;
205 case REPEAT:
206 i = size - 1 - (-i % size);
207 break;
208 case MIRROR:
209 // this is the same as the positive side, just make the value positive
210 // first.
211 i = -i;
212 int count = i / size;
213 i = i % size;
214
215 if ((count % 2) == 1) {
216 i = size - 1 - i;
217 }
218 break;
219 }
220 } else if (i >= size) {
221 switch (mode) {
222 case CLAMP:
223 i = size - 1;
224 break;
225 case REPEAT:
226 i = i % size;
227 break;
228 case MIRROR:
229 int count = i / size;
230 i = i % size;
231
232 if ((count % 2) == 1) {
233 i = size - 1 - i;
234 }
235 break;
236 }
237 }
238
239 return i;
240 }
241
242
243 public int getTransparency() {
244 return java.awt.Paint.TRANSLUCENT;
245 }
246 }
247}