/* * 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 = nullptr; fScaleType = kMatrix_ScaleType; fData.fAnim = nullptr; // 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(nullptr); } } 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 == nullptr) 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 = nullptr; 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(nullptr); } /////////////////////////////////////////////////////////////////////////////////////////////// bool SkImageView::onEvent(const SkEvent& evt) { if (evt.isType(SK_EventType_Inval)) { if (fUriIsValid) this->inval(nullptr); 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(nullptr); 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(nullptr); 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 = nullptr; // 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; }