|  |  | 
|  | /* | 
|  | * Copyright 2011 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  | #include "SkImageView.h" | 
|  | #include "SkAnimator.h" | 
|  | #include "SkBitmap.h" | 
|  | #include "SkCanvas.h" | 
|  | #include "SkImageDecoder.h" | 
|  | #include "SkMatrix.h" | 
|  | #include "SkSystemEventTypes.h" | 
|  | #include "SkTime.h" | 
|  |  | 
|  | SkImageView::SkImageView() | 
|  | { | 
|  | fMatrix        = NULL; | 
|  | fScaleType    = kMatrix_ScaleType; | 
|  |  | 
|  | fData.fAnim    = NULL;        // handles initializing the other union values | 
|  | fDataIsAnim    = true; | 
|  |  | 
|  | fUriIsValid    = false;    // an empty string is not valid | 
|  | } | 
|  |  | 
|  | SkImageView::~SkImageView() | 
|  | { | 
|  | if (fMatrix) | 
|  | sk_free(fMatrix); | 
|  |  | 
|  | this->freeData(); | 
|  | } | 
|  |  | 
|  | void SkImageView::getUri(SkString* uri) const | 
|  | { | 
|  | if (uri) | 
|  | *uri = fUri; | 
|  | } | 
|  |  | 
|  | void SkImageView::setUri(const char uri[]) | 
|  | { | 
|  | if (!fUri.equals(uri)) | 
|  | { | 
|  | fUri.set(uri); | 
|  | this->onUriChange(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkImageView::setUri(const SkString& uri) | 
|  | { | 
|  | if (fUri != uri) | 
|  | { | 
|  | fUri = uri; | 
|  | this->onUriChange(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkImageView::setScaleType(ScaleType st) | 
|  | { | 
|  | SkASSERT((unsigned)st <= kFitEnd_ScaleType); | 
|  |  | 
|  | if ((ScaleType)fScaleType != st) | 
|  | { | 
|  | fScaleType = SkToU8(st); | 
|  | if (fUriIsValid) | 
|  | this->inval(NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool SkImageView::getImageMatrix(SkMatrix* matrix) const | 
|  | { | 
|  | if (fMatrix) | 
|  | { | 
|  | SkASSERT(!fMatrix->isIdentity()); | 
|  | if (matrix) | 
|  | *matrix = *fMatrix; | 
|  | return true; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (matrix) | 
|  | matrix->reset(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkImageView::setImageMatrix(const SkMatrix* matrix) | 
|  | { | 
|  | bool changed = false; | 
|  |  | 
|  | if (matrix && !matrix->isIdentity()) | 
|  | { | 
|  | if (fMatrix == NULL) | 
|  | fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix)); | 
|  | *fMatrix = *matrix; | 
|  | changed = true; | 
|  | } | 
|  | else    // set us to identity | 
|  | { | 
|  | if (fMatrix) | 
|  | { | 
|  | SkASSERT(!fMatrix->isIdentity()); | 
|  | sk_free(fMatrix); | 
|  | fMatrix = NULL; | 
|  | changed = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // only redraw if we changed our matrix and we're not in scaleToFit mode | 
|  | if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid) | 
|  | this->inval(NULL); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | bool SkImageView::onEvent(const SkEvent& evt) | 
|  | { | 
|  | if (evt.isType(SK_EventType_Inval)) | 
|  | { | 
|  | if (fUriIsValid) | 
|  | this->inval(NULL); | 
|  | return true; | 
|  | } | 
|  | return this->INHERITED::onEvent(evt); | 
|  | } | 
|  |  | 
|  | static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st) | 
|  | { | 
|  | SkASSERT(st != SkImageView::kMatrix_ScaleType); | 
|  | SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType); | 
|  |  | 
|  | SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit); | 
|  | SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit); | 
|  | SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit); | 
|  | SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit); | 
|  |  | 
|  | return (SkMatrix::ScaleToFit)(st - 1); | 
|  | } | 
|  |  | 
|  | void SkImageView::onDraw(SkCanvas* canvas) | 
|  | { | 
|  | SkRect    src; | 
|  | if (!this->getDataBounds(&src)) | 
|  | { | 
|  | SkDEBUGCODE(canvas->drawColor(SK_ColorRED);) | 
|  | return;        // nothing to draw | 
|  | } | 
|  |  | 
|  | SkAutoCanvasRestore    restore(canvas, true); | 
|  | SkMatrix            matrix; | 
|  |  | 
|  | if (this->getScaleType() == kMatrix_ScaleType) | 
|  | (void)this->getImageMatrix(&matrix); | 
|  | else | 
|  | { | 
|  | SkRect    dst; | 
|  | dst.set(0, 0, this->width(), this->height()); | 
|  | matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType())); | 
|  | } | 
|  | canvas->concat(matrix); | 
|  |  | 
|  | SkPaint    paint; | 
|  |  | 
|  | paint.setAntiAlias(true); | 
|  |  | 
|  | if (fDataIsAnim) | 
|  | { | 
|  | SkMSec    now = SkTime::GetMSecs(); | 
|  |  | 
|  | SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now); | 
|  |  | 
|  | SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff)); | 
|  |  | 
|  | if (diff == SkAnimator::kDifferent) | 
|  | this->inval(NULL); | 
|  | else if (diff == SkAnimator::kPartiallyDifferent) | 
|  | { | 
|  | SkRect    bounds; | 
|  | fData.fAnim->getInvalBounds(&bounds); | 
|  | matrix.mapRect(&bounds);    // get the bounds into view coordinates | 
|  | this->inval(&bounds); | 
|  | } | 
|  | } | 
|  | else | 
|  | canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint); | 
|  | } | 
|  |  | 
|  | void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node) | 
|  | { | 
|  | this->INHERITED::onInflate(dom, node); | 
|  |  | 
|  | const char* src = dom.findAttr(node, "src"); | 
|  | if (src) | 
|  | this->setUri(src); | 
|  |  | 
|  | int    index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd"); | 
|  | if (index >= 0) | 
|  | this->setScaleType((ScaleType)index); | 
|  |  | 
|  | // need inflate syntax/reader for matrix | 
|  | } | 
|  |  | 
|  | ///////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | void SkImageView::onUriChange() | 
|  | { | 
|  | if (this->freeData()) | 
|  | this->inval(NULL); | 
|  | fUriIsValid = true;        // give ensureUriIsLoaded() a shot at the new uri | 
|  | } | 
|  |  | 
|  | bool SkImageView::freeData() | 
|  | { | 
|  | if (fData.fAnim)    // test is valid for all union values | 
|  | { | 
|  | if (fDataIsAnim) | 
|  | delete fData.fAnim; | 
|  | else | 
|  | delete fData.fBitmap; | 
|  |  | 
|  | fData.fAnim = NULL;    // valid for all union values | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool SkImageView::getDataBounds(SkRect* bounds) | 
|  | { | 
|  | SkASSERT(bounds); | 
|  |  | 
|  | if (this->ensureUriIsLoaded()) | 
|  | { | 
|  | SkScalar width, height; | 
|  |  | 
|  | if (fDataIsAnim) | 
|  | { | 
|  | if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) || | 
|  | SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y"))) | 
|  | { | 
|  | // cons up fake bounds | 
|  | width = this->width(); | 
|  | height = this->height(); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | width = SkIntToScalar(fData.fBitmap->width()); | 
|  | height = SkIntToScalar(fData.fBitmap->height()); | 
|  | } | 
|  | bounds->set(0, 0, width, height); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool SkImageView::ensureUriIsLoaded() | 
|  | { | 
|  | if (fData.fAnim)    // test is valid for all union values | 
|  | { | 
|  | SkASSERT(fUriIsValid); | 
|  | return true; | 
|  | } | 
|  | if (!fUriIsValid) | 
|  | return false; | 
|  |  | 
|  | // try to load the url | 
|  | if (fUri.endsWith(".xml"))    // assume it is screenplay | 
|  | { | 
|  | SkAnimator* anim = new SkAnimator; | 
|  |  | 
|  | if (!anim->decodeURI(fUri.c_str())) | 
|  | { | 
|  | delete anim; | 
|  | fUriIsValid = false; | 
|  | return false; | 
|  | } | 
|  | anim->setHostEventSink(this); | 
|  |  | 
|  | fData.fAnim = anim; | 
|  | fDataIsAnim = true; | 
|  | } | 
|  | else    // assume it is an image format | 
|  | { | 
|  | #if 0 | 
|  | SkBitmap* bitmap = new SkBitmap; | 
|  |  | 
|  | if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap)) | 
|  | { | 
|  | delete bitmap; | 
|  | fUriIsValid = false; | 
|  | return false; | 
|  | } | 
|  | fData.fBitmap = bitmap; | 
|  | fDataIsAnim = false; | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  | return true; | 
|  | } |