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        = NULL;
20     fScaleType    = kMatrix_ScaleType;
21 
22     fData.fAnim    = NULL;        // 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(NULL);
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 == NULL)
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 = NULL;
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(NULL);
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(NULL);
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(NULL);
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(NULL);
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 = NULL;    // 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