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