blob: 8924dd32fb2ad4003fd5a38466964eadb0cc4498 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#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
17SkImageView::SkImageView()
18{
deanm@chromium.org1599a432009-06-04 15:37:11 +000019 fMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000020 fScaleType = kMatrix_ScaleType;
21
deanm@chromium.org1599a432009-06-04 15:37:11 +000022 fData.fAnim = NULL; // handles initializing the other union values
reed@android.com8a1c16f2008-12-17 15:59:43 +000023 fDataIsAnim = true;
24
25 fUriIsValid = false; // an empty string is not valid
26}
27
28SkImageView::~SkImageView()
29{
30 if (fMatrix)
31 sk_free(fMatrix);
32
33 this->freeData();
34}
35
36void SkImageView::getUri(SkString* uri) const
37{
38 if (uri)
39 *uri = fUri;
40}
41
42void SkImageView::setUri(const char uri[])
43{
44 if (!fUri.equals(uri))
45 {
46 fUri.set(uri);
47 this->onUriChange();
48 }
49}
50
51void SkImageView::setUri(const SkString& uri)
52{
53 if (fUri != uri)
54 {
55 fUri = uri;
56 this->onUriChange();
57 }
58}
59
60void 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)
deanm@chromium.org1599a432009-06-04 15:37:11 +000068 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 }
70}
71
72bool 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
89void SkImageView::setImageMatrix(const SkMatrix* matrix)
90{
91 bool changed = false;
92
93 if (matrix && !matrix->isIdentity())
94 {
deanm@chromium.org1599a432009-06-04 15:37:11 +000095 if (fMatrix == NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 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);
deanm@chromium.org1599a432009-06-04 15:37:11 +0000106 fMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 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)
deanm@chromium.org1599a432009-06-04 15:37:11 +0000113 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114}
115
116///////////////////////////////////////////////////////////////////////////////////////////////
117
118bool SkImageView::onEvent(const SkEvent& evt)
119{
120 if (evt.isType(SK_EventType_Inval))
121 {
122 if (fUriIsValid)
deanm@chromium.org1599a432009-06-04 15:37:11 +0000123 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 return true;
125 }
126 return this->INHERITED::onEvent(evt);
127}
128
129static 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
142void 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
174SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
175
176 if (diff == SkAnimator::kDifferent)
deanm@chromium.org1599a432009-06-04 15:37:11 +0000177 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 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
190void 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
207void SkImageView::onUriChange()
208{
209 if (this->freeData())
deanm@chromium.org1599a432009-06-04 15:37:11 +0000210 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri
212}
213
214bool 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
deanm@chromium.org1599a432009-06-04 15:37:11 +0000223 fData.fAnim = NULL; // valid for all union values
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 return true;
225 }
226 return false;
227}
228
229bool 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
258bool 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