blob: f4ee0fb6c1b8894264bd3f5cf8bab578bfeabec5 [file] [log] [blame]
reed64045422015-06-04 06:31:31 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkBitmap.h"
9#include "SkBitmapController.h"
reed013e9e32015-09-15 14:46:27 -070010#include "SkBitmapProvider.h"
reed64045422015-06-04 06:31:31 -070011#include "SkMatrix.h"
reed98ed7b62015-09-15 12:38:12 -070012#include "SkPixelRef.h"
bungemanf3c15b72015-08-19 11:56:48 -070013#include "SkTemplates.h"
reed64045422015-06-04 06:31:31 -070014
reed99138872015-08-31 15:16:17 -070015// RESIZE_LANCZOS3 is another good option, but chrome prefers mitchell at the moment
16#define kHQ_RESIZE_METHOD SkBitmapScaler::RESIZE_MITCHELL
17
reed64045422015-06-04 06:31:31 -070018///////////////////////////////////////////////////////////////////////////////////////////////////
19
reed98ed7b62015-09-15 12:38:12 -070020SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
reed64045422015-06-04 06:31:31 -070021 const SkMatrix& inv,
22 SkFilterQuality quality,
23 void* storage, size_t storageSize) {
reed98ed7b62015-09-15 12:38:12 -070024 if (!provider.validForDrawing()) {
halcanary96fcdcc2015-08-27 07:41:13 -070025 return nullptr;
reed64045422015-06-04 06:31:31 -070026 }
27
reed98ed7b62015-09-15 12:38:12 -070028 State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
reed64045422015-06-04 06:31:31 -070029 if (state) {
halcanary96fcdcc2015-08-27 07:41:13 -070030 if (nullptr == state->fPixmap.addr()) {
reedad7ae6c2015-06-04 14:12:25 -070031 SkInPlaceDeleteCheck(state, storage);
halcanary96fcdcc2015-08-27 07:41:13 -070032 state = nullptr;
reed64045422015-06-04 06:31:31 -070033 }
34 }
35 return state;
36}
37
38///////////////////////////////////////////////////////////////////////////////////////////////////
39
40#include "SkBitmapCache.h"
41#include "SkBitmapScaler.h"
42#include "SkMipMap.h"
43#include "SkResourceCache.h"
44
45class SkDefaultBitmapControllerState : public SkBitmapController::State {
46public:
reed6644d932016-06-10 11:41:47 -070047 SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality,
48 SkSourceGammaTreatment);
reed64045422015-06-04 06:31:31 -070049
50private:
reedad7ae6c2015-06-04 14:12:25 -070051 SkBitmap fResultBitmap;
reed6644d932016-06-10 11:41:47 -070052 SkSourceGammaTreatment fSrcGammaTreatment;
reed64045422015-06-04 06:31:31 -070053 SkAutoTUnref<const SkMipMap> fCurrMip;
halcanary9d524f22016-03-29 09:03:52 -070054
reed98ed7b62015-09-15 12:38:12 -070055 bool processHQRequest(const SkBitmapProvider&);
56 bool processMediumRequest(const SkBitmapProvider&);
reed64045422015-06-04 06:31:31 -070057};
58
59// Check to see that the size of the bitmap that would be produced by
60// scaling by the given inverted matrix is less than the maximum allowed.
reed98ed7b62015-09-15 12:38:12 -070061static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
reed64045422015-06-04 06:31:31 -070062 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
63 if (0 == maximumAllocation) {
64 return true;
65 }
66 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
67 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
68 // Skip the division step:
reed98ed7b62015-09-15 12:38:12 -070069 const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
reeda3d99a52016-02-03 19:07:54 -080070 SkScalar invScaleSqr = invMat.getScaleX() * invMat.getScaleY();
reed04882022016-02-08 11:57:52 -080071 return size < (maximumAllocation * SkScalarAbs(invScaleSqr));
reed64045422015-06-04 06:31:31 -070072}
73
74/*
75 * High quality is implemented by performing up-right scale-only filtering and then
76 * using bilerp for any remaining transformations.
77 */
reed98ed7b62015-09-15 12:38:12 -070078bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
reed64045422015-06-04 06:31:31 -070079 if (fQuality != kHigh_SkFilterQuality) {
80 return false;
81 }
halcanary9d524f22016-03-29 09:03:52 -070082
reed64045422015-06-04 06:31:31 -070083 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
84 // to a valid bitmap. If we succeed, we will set this to Low instead.
85 fQuality = kMedium_SkFilterQuality;
halcanary9d524f22016-03-29 09:03:52 -070086
reed98ed7b62015-09-15 12:38:12 -070087 if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
reed64045422015-06-04 06:31:31 -070088 fInvMatrix.hasPerspective())
89 {
90 return false; // can't handle the reqeust
91 }
halcanary9d524f22016-03-29 09:03:52 -070092
reed64045422015-06-04 06:31:31 -070093 SkScalar invScaleX = fInvMatrix.getScaleX();
94 SkScalar invScaleY = fInvMatrix.getScaleY();
95 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
96 SkSize scale;
97 if (!fInvMatrix.decomposeScale(&scale)) {
98 return false;
99 }
100 invScaleX = scale.width();
101 invScaleY = scale.height();
102 }
reeda3d99a52016-02-03 19:07:54 -0800103 invScaleX = SkScalarAbs(invScaleX);
104 invScaleY = SkScalarAbs(invScaleY);
reeda3d99a52016-02-03 19:07:54 -0800105
reed64045422015-06-04 06:31:31 -0700106 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
107 return false; // no need for HQ
108 }
fmalita3e2f5622015-12-09 07:18:16 -0800109
fmalita3e2f5622015-12-09 07:18:16 -0800110 if (invScaleX > 1 || invScaleY > 1) {
111 return false; // only use HQ when upsampling
112 }
fmalita49b9f8c2016-02-11 08:06:39 -0800113
reed98ed7b62015-09-15 12:38:12 -0700114 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
115 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
116 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
117
118 if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
119 SkBitmap orig;
120 if (!provider.asBitmap(&orig)) {
121 return false;
122 }
reed3c834322015-06-12 07:09:59 -0700123 SkAutoPixmapUnlock src;
reed98ed7b62015-09-15 12:38:12 -0700124 if (!orig.requestLock(&src)) {
reed3c834322015-06-12 07:09:59 -0700125 return false;
126 }
reed99138872015-08-31 15:16:17 -0700127 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
128 dstW, dstH, SkResourceCache::GetAllocator())) {
reed64045422015-06-04 06:31:31 -0700129 return false; // we failed to create fScaledBitmap
130 }
halcanary9d524f22016-03-29 09:03:52 -0700131
reedad7ae6c2015-06-04 14:12:25 -0700132 SkASSERT(fResultBitmap.getPixels());
133 fResultBitmap.setImmutable();
reed09553032015-11-23 12:32:16 -0800134 if (!provider.isVolatile()) {
135 if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
136 provider.notifyAddedToCache();
137 }
reed98ed7b62015-09-15 12:38:12 -0700138 }
reed64045422015-06-04 06:31:31 -0700139 }
halcanary9d524f22016-03-29 09:03:52 -0700140
reedad7ae6c2015-06-04 14:12:25 -0700141 SkASSERT(fResultBitmap.getPixels());
halcanary9d524f22016-03-29 09:03:52 -0700142
reed98ed7b62015-09-15 12:38:12 -0700143 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
144 SkIntToScalar(dstH) / provider.height());
reed64045422015-06-04 06:31:31 -0700145 fQuality = kLow_SkFilterQuality;
146 return true;
147}
148
149/*
150 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
151 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
152 */
reed98ed7b62015-09-15 12:38:12 -0700153bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
reed64045422015-06-04 06:31:31 -0700154 SkASSERT(fQuality <= kMedium_SkFilterQuality);
155 if (fQuality != kMedium_SkFilterQuality) {
156 return false;
157 }
halcanary9d524f22016-03-29 09:03:52 -0700158
reed64045422015-06-04 06:31:31 -0700159 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
160 // to a valid bitmap.
161 fQuality = kLow_SkFilterQuality;
halcanary9d524f22016-03-29 09:03:52 -0700162
reed64045422015-06-04 06:31:31 -0700163 SkSize invScaleSize;
halcanary96fcdcc2015-08-27 07:41:13 -0700164 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
reed64045422015-06-04 06:31:31 -0700165 return false;
166 }
fmalita921d7ac2016-01-22 11:45:39 -0800167
fmalita33ed3ad2016-02-09 08:20:18 -0800168 if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
reed6644d932016-06-10 11:41:47 -0700169 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), fSrcGammaTreatment));
halcanary96fcdcc2015-08-27 07:41:13 -0700170 if (nullptr == fCurrMip.get()) {
reed98ed7b62015-09-15 12:38:12 -0700171 SkBitmap orig;
172 if (!provider.asBitmap(&orig)) {
173 return false;
174 }
reed6644d932016-06-10 11:41:47 -0700175 fCurrMip.reset(SkMipMapCache::AddAndRef(orig, fSrcGammaTreatment));
halcanary96fcdcc2015-08-27 07:41:13 -0700176 if (nullptr == fCurrMip.get()) {
reed64045422015-06-04 06:31:31 -0700177 return false;
178 }
179 }
180 // diagnostic for a crasher...
halcanary96fcdcc2015-08-27 07:41:13 -0700181 if (nullptr == fCurrMip->data()) {
reed64045422015-06-04 06:31:31 -0700182 sk_throw();
183 }
halcanary9d524f22016-03-29 09:03:52 -0700184
fmalita33ed3ad2016-02-09 08:20:18 -0800185 const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
186 SkScalarInvert(invScaleSize.height()));
reed64045422015-06-04 06:31:31 -0700187 SkMipMap::Level level;
fmalita33ed3ad2016-02-09 08:20:18 -0800188 if (fCurrMip->extractLevel(scale, &level)) {
fmalita921d7ac2016-01-22 11:45:39 -0800189 const SkSize& invScaleFixup = level.fScale;
190 fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
191
reed64045422015-06-04 06:31:31 -0700192 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
193 // that here, and not need to explicitly track it ourselves.
reed67b09bf2016-01-16 18:50:35 -0800194 return fResultBitmap.installPixels(level.fPixmap);
reed64045422015-06-04 06:31:31 -0700195 } else {
196 // failed to extract, so release the mipmap
halcanary96fcdcc2015-08-27 07:41:13 -0700197 fCurrMip.reset(nullptr);
reed64045422015-06-04 06:31:31 -0700198 }
199 }
200 return false;
201}
202
reed98ed7b62015-09-15 12:38:12 -0700203SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
reed64045422015-06-04 06:31:31 -0700204 const SkMatrix& inv,
reed6644d932016-06-10 11:41:47 -0700205 SkFilterQuality qual,
206 SkSourceGammaTreatment treatment) {
reed64045422015-06-04 06:31:31 -0700207 fInvMatrix = inv;
208 fQuality = qual;
reed6644d932016-06-10 11:41:47 -0700209 fSrcGammaTreatment = treatment;
reed64045422015-06-04 06:31:31 -0700210
reed98ed7b62015-09-15 12:38:12 -0700211 if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
reedad7ae6c2015-06-04 14:12:25 -0700212 SkASSERT(fResultBitmap.getPixels());
213 } else {
reed98ed7b62015-09-15 12:38:12 -0700214 (void)provider.asBitmap(&fResultBitmap);
reedad7ae6c2015-06-04 14:12:25 -0700215 fResultBitmap.lockPixels();
216 // lock may fail to give us pixels
reed64045422015-06-04 06:31:31 -0700217 }
218 SkASSERT(fQuality <= kLow_SkFilterQuality);
reedad7ae6c2015-06-04 14:12:25 -0700219
220 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
halcanary96fcdcc2015-08-27 07:41:13 -0700221 // and will destroy us if it is nullptr.
reedad7ae6c2015-06-04 14:12:25 -0700222 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
223 fResultBitmap.getColorTable());
reed64045422015-06-04 06:31:31 -0700224}
225
reed98ed7b62015-09-15 12:38:12 -0700226SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
reed64045422015-06-04 06:31:31 -0700227 const SkMatrix& inverse,
228 SkFilterQuality quality,
229 void* storage, size_t size) {
reed6644d932016-06-10 11:41:47 -0700230 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality,
231 fSrcGammaTreatment);
reed64045422015-06-04 06:31:31 -0700232}