Home | History | Annotate | Download | only in views
      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 
     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 
     28 SkImageView::~SkImageView()
     29 {
     30 	if (fMatrix)
     31 		sk_free(fMatrix);
     32 
     33 	this->freeData();
     34 }
     35 
     36 void SkImageView::getUri(SkString* uri) const
     37 {
     38 	if (uri)
     39 		*uri = fUri;
     40 }
     41 
     42 void SkImageView::setUri(const char uri[])
     43 {
     44 	if (!fUri.equals(uri))
     45 	{
     46 		fUri.set(uri);
     47 		this->onUriChange();
     48 	}
     49 }
     50 
     51 void SkImageView::setUri(const SkString& uri)
     52 {
     53 	if (fUri != uri)
     54 	{
     55 		fUri = uri;
     56 		this->onUriChange();
     57 	}
     58 }
     59 
     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 
     72 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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    304