blob: 233e81146f0e308260b6584567ffb0148c3a9d9b [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:
reed98ed7b62015-09-15 12:38:12 -070047 SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
reed64045422015-06-04 06:31:31 -070048
49private:
reedad7ae6c2015-06-04 14:12:25 -070050 SkBitmap fResultBitmap;
reed64045422015-06-04 06:31:31 -070051 SkAutoTUnref<const SkMipMap> fCurrMip;
52
reed98ed7b62015-09-15 12:38:12 -070053 bool processHQRequest(const SkBitmapProvider&);
54 bool processMediumRequest(const SkBitmapProvider&);
reed64045422015-06-04 06:31:31 -070055};
56
57// Check to see that the size of the bitmap that would be produced by
58// scaling by the given inverted matrix is less than the maximum allowed.
reed98ed7b62015-09-15 12:38:12 -070059static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
reed64045422015-06-04 06:31:31 -070060 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
61 if (0 == maximumAllocation) {
62 return true;
63 }
64 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
65 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
66 // Skip the division step:
reed98ed7b62015-09-15 12:38:12 -070067 const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
reed64045422015-06-04 06:31:31 -070068 return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
69}
70
71/*
72 * High quality is implemented by performing up-right scale-only filtering and then
73 * using bilerp for any remaining transformations.
74 */
reed98ed7b62015-09-15 12:38:12 -070075bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
reed64045422015-06-04 06:31:31 -070076 if (fQuality != kHigh_SkFilterQuality) {
77 return false;
78 }
79
80 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
81 // to a valid bitmap. If we succeed, we will set this to Low instead.
82 fQuality = kMedium_SkFilterQuality;
83
reed98ed7b62015-09-15 12:38:12 -070084 if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
reed64045422015-06-04 06:31:31 -070085 fInvMatrix.hasPerspective())
86 {
87 return false; // can't handle the reqeust
88 }
89
90 SkScalar invScaleX = fInvMatrix.getScaleX();
91 SkScalar invScaleY = fInvMatrix.getScaleY();
92 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
93 SkSize scale;
94 if (!fInvMatrix.decomposeScale(&scale)) {
95 return false;
96 }
97 invScaleX = scale.width();
98 invScaleY = scale.height();
99 }
100 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
101 return false; // no need for HQ
102 }
103
reed98ed7b62015-09-15 12:38:12 -0700104 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
105 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
106 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
107
108 if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
109 SkBitmap orig;
110 if (!provider.asBitmap(&orig)) {
111 return false;
112 }
reed3c834322015-06-12 07:09:59 -0700113 SkAutoPixmapUnlock src;
reed98ed7b62015-09-15 12:38:12 -0700114 if (!orig.requestLock(&src)) {
reed3c834322015-06-12 07:09:59 -0700115 return false;
116 }
reed99138872015-08-31 15:16:17 -0700117 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
118 dstW, dstH, SkResourceCache::GetAllocator())) {
reed64045422015-06-04 06:31:31 -0700119 return false; // we failed to create fScaledBitmap
120 }
121
reedad7ae6c2015-06-04 14:12:25 -0700122 SkASSERT(fResultBitmap.getPixels());
123 fResultBitmap.setImmutable();
reed09553032015-11-23 12:32:16 -0800124 if (!provider.isVolatile()) {
125 if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
126 provider.notifyAddedToCache();
127 }
reed98ed7b62015-09-15 12:38:12 -0700128 }
reed64045422015-06-04 06:31:31 -0700129 }
130
reedad7ae6c2015-06-04 14:12:25 -0700131 SkASSERT(fResultBitmap.getPixels());
reed64045422015-06-04 06:31:31 -0700132
reed98ed7b62015-09-15 12:38:12 -0700133 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
134 SkIntToScalar(dstH) / provider.height());
reed64045422015-06-04 06:31:31 -0700135 fQuality = kLow_SkFilterQuality;
136 return true;
137}
138
139/*
140 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
141 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
142 */
reed98ed7b62015-09-15 12:38:12 -0700143bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
reed64045422015-06-04 06:31:31 -0700144 SkASSERT(fQuality <= kMedium_SkFilterQuality);
145 if (fQuality != kMedium_SkFilterQuality) {
146 return false;
147 }
148
149 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
150 // to a valid bitmap.
151 fQuality = kLow_SkFilterQuality;
152
153 SkSize invScaleSize;
halcanary96fcdcc2015-08-27 07:41:13 -0700154 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
reed64045422015-06-04 06:31:31 -0700155 return false;
156 }
157 SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height());
158
159 if (invScale > SK_Scalar1) {
reed98ed7b62015-09-15 12:38:12 -0700160 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
halcanary96fcdcc2015-08-27 07:41:13 -0700161 if (nullptr == fCurrMip.get()) {
reed98ed7b62015-09-15 12:38:12 -0700162 SkBitmap orig;
163 if (!provider.asBitmap(&orig)) {
164 return false;
165 }
166 fCurrMip.reset(SkMipMapCache::AddAndRef(orig));
halcanary96fcdcc2015-08-27 07:41:13 -0700167 if (nullptr == fCurrMip.get()) {
reed64045422015-06-04 06:31:31 -0700168 return false;
169 }
170 }
171 // diagnostic for a crasher...
halcanary96fcdcc2015-08-27 07:41:13 -0700172 if (nullptr == fCurrMip->data()) {
reed64045422015-06-04 06:31:31 -0700173 sk_throw();
174 }
175
176 SkScalar levelScale = SkScalarInvert(invScale);
177 SkMipMap::Level level;
178 if (fCurrMip->extractLevel(levelScale, &level)) {
179 SkScalar invScaleFixup = level.fScale;
180 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
181
reed98ed7b62015-09-15 12:38:12 -0700182 const SkImageInfo info = provider.info().makeWH(level.fWidth, level.fHeight);
reed64045422015-06-04 06:31:31 -0700183 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
184 // that here, and not need to explicitly track it ourselves.
reedad7ae6c2015-06-04 14:12:25 -0700185 return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes);
reed64045422015-06-04 06:31:31 -0700186 } else {
187 // failed to extract, so release the mipmap
halcanary96fcdcc2015-08-27 07:41:13 -0700188 fCurrMip.reset(nullptr);
reed64045422015-06-04 06:31:31 -0700189 }
190 }
191 return false;
192}
193
reed98ed7b62015-09-15 12:38:12 -0700194SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
reed64045422015-06-04 06:31:31 -0700195 const SkMatrix& inv,
196 SkFilterQuality qual) {
197 fInvMatrix = inv;
198 fQuality = qual;
199
reed98ed7b62015-09-15 12:38:12 -0700200 if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
reedad7ae6c2015-06-04 14:12:25 -0700201 SkASSERT(fResultBitmap.getPixels());
202 } else {
reed98ed7b62015-09-15 12:38:12 -0700203 (void)provider.asBitmap(&fResultBitmap);
reedad7ae6c2015-06-04 14:12:25 -0700204 fResultBitmap.lockPixels();
205 // lock may fail to give us pixels
reed64045422015-06-04 06:31:31 -0700206 }
207 SkASSERT(fQuality <= kLow_SkFilterQuality);
reedad7ae6c2015-06-04 14:12:25 -0700208
209 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
halcanary96fcdcc2015-08-27 07:41:13 -0700210 // and will destroy us if it is nullptr.
reedad7ae6c2015-06-04 14:12:25 -0700211 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
212 fResultBitmap.getColorTable());
reed64045422015-06-04 06:31:31 -0700213}
214
reed98ed7b62015-09-15 12:38:12 -0700215SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
reed64045422015-06-04 06:31:31 -0700216 const SkMatrix& inverse,
217 SkFilterQuality quality,
218 void* storage, size_t size) {
219 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
220}
221