SkImageView.cpp revision 96fcdcc219d2a0d3579719b84b28bede76efba64
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 17SkImageView::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 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) 68 this->inval(nullptr); 69 } 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 { 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 118bool 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 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) 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 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()) 210 this->inval(nullptr); 211 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 223 fData.fAnim = nullptr; // valid for all union values 224 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