1
2 /*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8 #include "SkImageView.h"
9 #include "SkAnimator.h"
10 #include "SkBitmap.h"
11 #include "SkCanvas.h"
12 #include "SkImageDecoder.h"
13 #include "SkMatrix.h"
14 #include "SkSystemEventTypes.h"
15 #include "SkTime.h"
16
SkImageView()17 SkImageView::SkImageView()
18 {
19 fMatrix = nullptr;
20 fScaleType = kMatrix_ScaleType;
21
22 fData.fAnim = nullptr; // handles initializing the other union values
23 fDataIsAnim = true;
24
25 fUriIsValid = false; // an empty string is not valid
26 }
27
~SkImageView()28 SkImageView::~SkImageView()
29 {
30 if (fMatrix)
31 sk_free(fMatrix);
32
33 this->freeData();
34 }
35
getUri(SkString * uri) const36 void SkImageView::getUri(SkString* uri) const
37 {
38 if (uri)
39 *uri = fUri;
40 }
41
setUri(const char uri[])42 void SkImageView::setUri(const char uri[])
43 {
44 if (!fUri.equals(uri))
45 {
46 fUri.set(uri);
47 this->onUriChange();
48 }
49 }
50
setUri(const SkString & uri)51 void SkImageView::setUri(const SkString& uri)
52 {
53 if (fUri != uri)
54 {
55 fUri = uri;
56 this->onUriChange();
57 }
58 }
59
setScaleType(ScaleType st)60 void SkImageView::setScaleType(ScaleType st)
61 {
62 SkASSERT((unsigned)st <= kFitEnd_ScaleType);
63
64 if ((ScaleType)fScaleType != st)
65 {
66 fScaleType = SkToU8(st);
67 if (fUriIsValid)
68 this->inval(nullptr);
69 }
70 }
71
getImageMatrix(SkMatrix * matrix) const72 bool SkImageView::getImageMatrix(SkMatrix* matrix) const
73 {
74 if (fMatrix)
75 {
76 SkASSERT(!fMatrix->isIdentity());
77 if (matrix)
78 *matrix = *fMatrix;
79 return true;
80 }
81 else
82 {
83 if (matrix)
84 matrix->reset();
85 return false;
86 }
87 }
88
setImageMatrix(const SkMatrix * matrix)89 void SkImageView::setImageMatrix(const SkMatrix* matrix)
90 {
91 bool changed = false;
92
93 if (matrix && !matrix->isIdentity())
94 {
95 if (fMatrix == nullptr)
96 fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
97 *fMatrix = *matrix;
98 changed = true;
99 }
100 else // set us to identity
101 {
102 if (fMatrix)
103 {
104 SkASSERT(!fMatrix->isIdentity());
105 sk_free(fMatrix);
106 fMatrix = nullptr;
107 changed = true;
108 }
109 }
110
111 // only redraw if we changed our matrix and we're not in scaleToFit mode
112 if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
113 this->inval(nullptr);
114 }
115
116 ///////////////////////////////////////////////////////////////////////////////////////////////
117
onEvent(const SkEvent & evt)118 bool SkImageView::onEvent(const SkEvent& evt)
119 {
120 if (evt.isType(SK_EventType_Inval))
121 {
122 if (fUriIsValid)
123 this->inval(nullptr);
124 return true;
125 }
126 return this->INHERITED::onEvent(evt);
127 }
128
scaleTypeToScaleToFit(SkImageView::ScaleType st)129 static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
130 {
131 SkASSERT(st != SkImageView::kMatrix_ScaleType);
132 SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
133
134 SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
135 SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
136 SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
137 SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
138
139 return (SkMatrix::ScaleToFit)(st - 1);
140 }
141
onDraw(SkCanvas * canvas)142 void SkImageView::onDraw(SkCanvas* canvas)
143 {
144 SkRect src;
145 if (!this->getDataBounds(&src))
146 {
147 SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
148 return; // nothing to draw
149 }
150
151 SkAutoCanvasRestore restore(canvas, true);
152 SkMatrix matrix;
153
154 if (this->getScaleType() == kMatrix_ScaleType)
155 (void)this->getImageMatrix(&matrix);
156 else
157 {
158 SkRect dst;
159 dst.set(0, 0, this->width(), this->height());
160 matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
161 }
162 canvas->concat(matrix);
163
164 SkPaint paint;
165
166 paint.setAntiAlias(true);
167
168 if (fDataIsAnim)
169 {
170 SkMSec now = SkTime::GetMSecs();
171
172 SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
173
174 SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
175
176 if (diff == SkAnimator::kDifferent)
177 this->inval(nullptr);
178 else if (diff == SkAnimator::kPartiallyDifferent)
179 {
180 SkRect bounds;
181 fData.fAnim->getInvalBounds(&bounds);
182 matrix.mapRect(&bounds); // get the bounds into view coordinates
183 this->inval(&bounds);
184 }
185 }
186 else
187 canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
188 }
189
onInflate(const SkDOM & dom,const SkDOMNode * node)190 void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
191 {
192 this->INHERITED::onInflate(dom, node);
193
194 const char* src = dom.findAttr(node, "src");
195 if (src)
196 this->setUri(src);
197
198 int index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
199 if (index >= 0)
200 this->setScaleType((ScaleType)index);
201
202 // need inflate syntax/reader for matrix
203 }
204
205 /////////////////////////////////////////////////////////////////////////////////////
206
onUriChange()207 void SkImageView::onUriChange()
208 {
209 if (this->freeData())
210 this->inval(nullptr);
211 fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri
212 }
213
freeData()214 bool SkImageView::freeData()
215 {
216 if (fData.fAnim) // test is valid for all union values
217 {
218 if (fDataIsAnim)
219 delete fData.fAnim;
220 else
221 delete fData.fBitmap;
222
223 fData.fAnim = nullptr; // valid for all union values
224 return true;
225 }
226 return false;
227 }
228
getDataBounds(SkRect * bounds)229 bool SkImageView::getDataBounds(SkRect* bounds)
230 {
231 SkASSERT(bounds);
232
233 if (this->ensureUriIsLoaded())
234 {
235 SkScalar width, height;
236
237 if (fDataIsAnim)
238 {
239 if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
240 SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
241 {
242 // cons up fake bounds
243 width = this->width();
244 height = this->height();
245 }
246 }
247 else
248 {
249 width = SkIntToScalar(fData.fBitmap->width());
250 height = SkIntToScalar(fData.fBitmap->height());
251 }
252 bounds->set(0, 0, width, height);
253 return true;
254 }
255 return false;
256 }
257
ensureUriIsLoaded()258 bool SkImageView::ensureUriIsLoaded()
259 {
260 if (fData.fAnim) // test is valid for all union values
261 {
262 SkASSERT(fUriIsValid);
263 return true;
264 }
265 if (!fUriIsValid)
266 return false;
267
268 // try to load the url
269 if (fUri.endsWith(".xml")) // assume it is screenplay
270 {
271 SkAnimator* anim = new SkAnimator;
272
273 if (!anim->decodeURI(fUri.c_str()))
274 {
275 delete anim;
276 fUriIsValid = false;
277 return false;
278 }
279 anim->setHostEventSink(this);
280
281 fData.fAnim = anim;
282 fDataIsAnim = true;
283 }
284 else // assume it is an image format
285 {
286 #if 0
287 SkBitmap* bitmap = new SkBitmap;
288
289 if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
290 {
291 delete bitmap;
292 fUriIsValid = false;
293 return false;
294 }
295 fData.fBitmap = bitmap;
296 fDataIsAnim = false;
297 #else
298 return false;
299 #endif
300 }
301 return true;
302 }
303