/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkArenaAlloc.h"
#include "SkBitmap.h"
#include "SkBitmapCache.h"
#include "SkBitmapController.h"
#include "SkBitmapProvider.h"
#include "SkMatrix.h"
#include "SkMipMap.h"
#include "SkTemplates.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
SkBitmapController::State* SkBitmapController::RequestBitmap(const SkBitmapProvider& provider,
const SkMatrix& inv,
SkFilterQuality quality,
SkArenaAlloc* alloc) {
auto* state = alloc->make<SkBitmapController::State>(provider, inv, quality);
return state->pixmap().addr() ? state : nullptr;
}
bool SkBitmapController::State::processHighRequest(const SkBitmapProvider& provider) {
if (fQuality != kHigh_SkFilterQuality) {
return false;
}
fQuality = kMedium_SkFilterQuality;
SkScalar invScaleX = fInvMatrix.getScaleX();
SkScalar invScaleY = fInvMatrix.getScaleY();
if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
SkSize scale;
if (!fInvMatrix.decomposeScale(&scale)) {
return false;
}
invScaleX = scale.width();
invScaleY = scale.height();
}
invScaleX = SkScalarAbs(invScaleX);
invScaleY = SkScalarAbs(invScaleY);
if (invScaleX >= 1 - SK_ScalarNearlyZero || invScaleY >= 1 - SK_ScalarNearlyZero) {
// we're down-scaling so abort HQ
return false;
}
// Confirmed that we can use HQ (w/ rasterpipeline)
fQuality = kHigh_SkFilterQuality;
(void)provider.asBitmap(&fResultBitmap);
return true;
}
/*
* Modulo internal errors, this should always succeed *if* the matrix is downscaling
* (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
*/
bool SkBitmapController::State::processMediumRequest(const SkBitmapProvider& provider) {
SkASSERT(fQuality <= kMedium_SkFilterQuality);
if (fQuality != kMedium_SkFilterQuality) {
return false;
}
// Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
// to a valid bitmap.
fQuality = kLow_SkFilterQuality;
SkSize invScaleSize;
if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
return false;
}
if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
if (nullptr == fCurrMip.get()) {
fCurrMip.reset(SkMipMapCache::AddAndRef(provider));
if (nullptr == fCurrMip.get()) {
return false;
}
}
// diagnostic for a crasher...
SkASSERT_RELEASE(fCurrMip->data());
const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
SkScalarInvert(invScaleSize.height()));
SkMipMap::Level level;
if (fCurrMip->extractLevel(scale, &level)) {
const SkSize& invScaleFixup = level.fScale;
fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
// todo: if we could wrap the fCurrMip in a pixelref, then we could just install
// that here, and not need to explicitly track it ourselves.
return fResultBitmap.installPixels(level.fPixmap);
} else {
// failed to extract, so release the mipmap
fCurrMip.reset(nullptr);
}
}
return false;
}
SkBitmapController::State::State(const SkBitmapProvider& provider,
const SkMatrix& inv,
SkFilterQuality qual) {
fInvMatrix = inv;
fQuality = qual;
if (this->processHighRequest(provider) || this->processMediumRequest(provider)) {
SkASSERT(fResultBitmap.getPixels());
} else {
(void)provider.asBitmap(&fResultBitmap);
}
// fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
// and will destroy us if it is nullptr.
fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
}