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