| |
| /* |
| * 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; |
| } |
| |