|
1 /* |
|
2 * Copyright (C) 2013 Square, Inc. |
|
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 package com.squareup.picasso; |
|
17 |
|
18 import android.net.Uri; |
|
19 import java.util.ArrayList; |
|
20 import java.util.List; |
|
21 |
|
22 import static java.util.Collections.unmodifiableList; |
|
23 |
|
24 /** Immutable data about an image and the transformations that will be applied to it. */ |
|
25 public final class Request { |
|
26 /** |
|
27 * The image URI. |
|
28 * <p> |
|
29 * This is mutually exclusive with {@link #resourceId}. |
|
30 */ |
|
31 public final Uri uri; |
|
32 /** |
|
33 * The image resource ID. |
|
34 * <p> |
|
35 * This is mutually exclusive with {@link #uri}. |
|
36 */ |
|
37 public final int resourceId; |
|
38 /** List of custom transformations to be applied after the built-in transformations. */ |
|
39 public final List<Transformation> transformations; |
|
40 /** Target image width for resizing. */ |
|
41 public final int targetWidth; |
|
42 /** Target image height for resizing. */ |
|
43 public final int targetHeight; |
|
44 /** |
|
45 * True if the final image should use the 'centerCrop' scale technique. |
|
46 * <p> |
|
47 * This is mutually exclusive with {@link #centerInside}. |
|
48 */ |
|
49 public final boolean centerCrop; |
|
50 /** |
|
51 * True if the final image should use the 'centerInside' scale technique. |
|
52 * <p> |
|
53 * This is mutually exclusive with {@link #centerCrop}. |
|
54 */ |
|
55 public final boolean centerInside; |
|
56 /** Amount to rotate the image in degrees. */ |
|
57 public final float rotationDegrees; |
|
58 /** Rotation pivot on the X axis. */ |
|
59 public final float rotationPivotX; |
|
60 /** Rotation pivot on the Y axis. */ |
|
61 public final float rotationPivotY; |
|
62 /** Whether or not {@link #rotationPivotX} and {@link #rotationPivotY} are set. */ |
|
63 public final boolean hasRotationPivot; |
|
64 |
|
65 private Request(Uri uri, int resourceId, List<Transformation> transformations, int targetWidth, |
|
66 int targetHeight, boolean centerCrop, boolean centerInside, float rotationDegrees, |
|
67 float rotationPivotX, float rotationPivotY, boolean hasRotationPivot) { |
|
68 this.uri = uri; |
|
69 this.resourceId = resourceId; |
|
70 if (transformations == null) { |
|
71 this.transformations = null; |
|
72 } else { |
|
73 this.transformations = unmodifiableList(transformations); |
|
74 } |
|
75 this.targetWidth = targetWidth; |
|
76 this.targetHeight = targetHeight; |
|
77 this.centerCrop = centerCrop; |
|
78 this.centerInside = centerInside; |
|
79 this.rotationDegrees = rotationDegrees; |
|
80 this.rotationPivotX = rotationPivotX; |
|
81 this.rotationPivotY = rotationPivotY; |
|
82 this.hasRotationPivot = hasRotationPivot; |
|
83 } |
|
84 |
|
85 String getName() { |
|
86 if (uri != null) { |
|
87 return uri.getPath(); |
|
88 } |
|
89 return Integer.toHexString(resourceId); |
|
90 } |
|
91 |
|
92 public boolean hasSize() { |
|
93 return targetWidth != 0; |
|
94 } |
|
95 |
|
96 boolean needsTransformation() { |
|
97 return needsMatrixTransform() || hasCustomTransformations(); |
|
98 } |
|
99 |
|
100 boolean needsMatrixTransform() { |
|
101 return targetWidth != 0 || rotationDegrees != 0; |
|
102 } |
|
103 |
|
104 boolean hasCustomTransformations() { |
|
105 return transformations != null; |
|
106 } |
|
107 |
|
108 public Builder buildUpon() { |
|
109 return new Builder(this); |
|
110 } |
|
111 |
|
112 /** Builder for creating {@link Request} instances. */ |
|
113 public static final class Builder { |
|
114 private Uri uri; |
|
115 private int resourceId; |
|
116 private int targetWidth; |
|
117 private int targetHeight; |
|
118 private boolean centerCrop; |
|
119 private boolean centerInside; |
|
120 private float rotationDegrees; |
|
121 private float rotationPivotX; |
|
122 private float rotationPivotY; |
|
123 private boolean hasRotationPivot; |
|
124 private List<Transformation> transformations; |
|
125 |
|
126 /** Start building a request using the specified {@link Uri}. */ |
|
127 public Builder(Uri uri) { |
|
128 setUri(uri); |
|
129 } |
|
130 |
|
131 /** Start building a request using the specified resource ID. */ |
|
132 public Builder(int resourceId) { |
|
133 setResourceId(resourceId); |
|
134 } |
|
135 |
|
136 Builder(Uri uri, int resourceId) { |
|
137 this.uri = uri; |
|
138 this.resourceId = resourceId; |
|
139 } |
|
140 |
|
141 private Builder(Request request) { |
|
142 uri = request.uri; |
|
143 resourceId = request.resourceId; |
|
144 targetWidth = request.targetWidth; |
|
145 targetHeight = request.targetHeight; |
|
146 centerCrop = request.centerCrop; |
|
147 centerInside = request.centerInside; |
|
148 rotationDegrees = request.rotationDegrees; |
|
149 rotationPivotX = request.rotationPivotX; |
|
150 rotationPivotY = request.rotationPivotY; |
|
151 hasRotationPivot = request.hasRotationPivot; |
|
152 if (request.transformations != null) { |
|
153 transformations = new ArrayList<Transformation>(request.transformations); |
|
154 } |
|
155 } |
|
156 |
|
157 boolean hasImage() { |
|
158 return uri != null || resourceId != 0; |
|
159 } |
|
160 |
|
161 boolean hasSize() { |
|
162 return targetWidth != 0; |
|
163 } |
|
164 |
|
165 /** |
|
166 * Set the target image Uri. |
|
167 * <p> |
|
168 * This will clear an image resource ID if one is set. |
|
169 */ |
|
170 public Builder setUri(Uri uri) { |
|
171 if (uri == null) { |
|
172 throw new IllegalArgumentException("Image URI may not be null."); |
|
173 } |
|
174 this.uri = uri; |
|
175 this.resourceId = 0; |
|
176 return this; |
|
177 } |
|
178 |
|
179 /** |
|
180 * Set the target image resource ID. |
|
181 * <p> |
|
182 * This will clear an image Uri if one is set. |
|
183 */ |
|
184 public Builder setResourceId(int resourceId) { |
|
185 if (resourceId == 0) { |
|
186 throw new IllegalArgumentException("Image resource ID may not be 0."); |
|
187 } |
|
188 this.resourceId = resourceId; |
|
189 this.uri = null; |
|
190 return this; |
|
191 } |
|
192 |
|
193 /** Resize the image to the specified size in pixels. */ |
|
194 public Builder resize(int targetWidth, int targetHeight) { |
|
195 if (targetWidth <= 0) { |
|
196 throw new IllegalArgumentException("Width must be positive number."); |
|
197 } |
|
198 if (targetHeight <= 0) { |
|
199 throw new IllegalArgumentException("Height must be positive number."); |
|
200 } |
|
201 this.targetWidth = targetWidth; |
|
202 this.targetHeight = targetHeight; |
|
203 return this; |
|
204 } |
|
205 |
|
206 /** Clear the resize transformation, if any. This will also clear center crop/inside if set. */ |
|
207 public Builder clearResize() { |
|
208 targetWidth = 0; |
|
209 targetHeight = 0; |
|
210 centerCrop = false; |
|
211 centerInside = false; |
|
212 return this; |
|
213 } |
|
214 |
|
215 /** |
|
216 * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than |
|
217 * distorting the aspect ratio. This cropping technique scales the image so that it fills the |
|
218 * requested bounds and then crops the extra. |
|
219 */ |
|
220 public Builder centerCrop() { |
|
221 if (centerInside) { |
|
222 throw new IllegalStateException("Center crop can not be used after calling centerInside"); |
|
223 } |
|
224 centerCrop = true; |
|
225 return this; |
|
226 } |
|
227 |
|
228 /** Clear the center crop transformation flag, if set. */ |
|
229 public Builder clearCenterCrop() { |
|
230 centerCrop = false; |
|
231 return this; |
|
232 } |
|
233 |
|
234 /** |
|
235 * Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales |
|
236 * the image so that both dimensions are equal to or less than the requested bounds. |
|
237 */ |
|
238 public Builder centerInside() { |
|
239 if (centerCrop) { |
|
240 throw new IllegalStateException("Center inside can not be used after calling centerCrop"); |
|
241 } |
|
242 centerInside = true; |
|
243 return this; |
|
244 } |
|
245 |
|
246 /** Clear the center inside transformation flag, if set. */ |
|
247 public Builder clearCenterInside() { |
|
248 centerInside = false; |
|
249 return this; |
|
250 } |
|
251 |
|
252 /** Rotate the image by the specified degrees. */ |
|
253 public Builder rotate(float degrees) { |
|
254 rotationDegrees = degrees; |
|
255 return this; |
|
256 } |
|
257 |
|
258 /** Rotate the image by the specified degrees around a pivot point. */ |
|
259 public Builder rotate(float degrees, float pivotX, float pivotY) { |
|
260 rotationDegrees = degrees; |
|
261 rotationPivotX = pivotX; |
|
262 rotationPivotY = pivotY; |
|
263 hasRotationPivot = true; |
|
264 return this; |
|
265 } |
|
266 |
|
267 /** Clear the rotation transformation, if any. */ |
|
268 public Builder clearRotation() { |
|
269 rotationDegrees = 0; |
|
270 rotationPivotX = 0; |
|
271 rotationPivotY = 0; |
|
272 hasRotationPivot = false; |
|
273 return this; |
|
274 } |
|
275 |
|
276 /** |
|
277 * Add a custom transformation to be applied to the image. |
|
278 * <p/> |
|
279 * Custom transformations will always be run after the built-in transformations. |
|
280 */ |
|
281 public Builder transform(Transformation transformation) { |
|
282 if (transformation == null) { |
|
283 throw new IllegalArgumentException("Transformation must not be null."); |
|
284 } |
|
285 if (transformations == null) { |
|
286 transformations = new ArrayList<Transformation>(2); |
|
287 } |
|
288 transformations.add(transformation); |
|
289 return this; |
|
290 } |
|
291 |
|
292 /** Create the immutable {@link Request} object. */ |
|
293 public Request build() { |
|
294 if (centerInside && centerCrop) { |
|
295 throw new IllegalStateException("Center crop and center inside can not be used together."); |
|
296 } |
|
297 if (centerCrop && targetWidth == 0) { |
|
298 throw new IllegalStateException("Center crop requires calling resize."); |
|
299 } |
|
300 if (centerInside && targetWidth == 0) { |
|
301 throw new IllegalStateException("Center inside requires calling resize."); |
|
302 } |
|
303 return new Request(uri, resourceId, transformations, targetWidth, targetHeight, centerCrop, |
|
304 centerInside, rotationDegrees, rotationPivotX, rotationPivotY, hasRotationPivot); |
|
305 } |
|
306 } |
|
307 } |