blob: c75d2ea6bc825c497862c7193b7d74815b105669 [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"
reed98ed7b62015-09-15 12:38:12 -070010#include "SkImage_Base.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 -070020int SkBitmapProvider::width() const {
21 return fImage ? fImage->width() : fBitmap.width();
22}
23
24int SkBitmapProvider::height() const {
25 return fImage ? fImage->height() : fBitmap.height();
26}
27
28uint32_t SkBitmapProvider::getID() const {
29 return fImage ? fImage->uniqueID() : fBitmap.getGenerationID();
30}
31
32bool SkBitmapProvider::validForDrawing() const {
33 if (!fImage) {
34 if (0 == fBitmap.width() || 0 == fBitmap.height()) {
reed64045422015-06-04 06:31:31 -070035 return false;
36 }
reed98ed7b62015-09-15 12:38:12 -070037 if (nullptr == fBitmap.pixelRef()) {
38 return false; // no pixels to read
39 }
40 if (fBitmap.getTexture()) {
41 // we can handle texture (ugh) since lockPixels will perform a read-back
42 return true;
43 }
44 if (kIndex_8_SkColorType == fBitmap.colorType()) {
45 SkAutoLockPixels alp(fBitmap); // but we need to call it before getColorTable() is safe.
46 if (!fBitmap.getColorTable()) {
47 return false;
48 }
49 }
reed64045422015-06-04 06:31:31 -070050 }
51 return true;
52}
53
reed98ed7b62015-09-15 12:38:12 -070054SkImageInfo SkBitmapProvider::info() const {
55 if (fImage) {
56 SkAlphaType at = fImage->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
57 return SkImageInfo::MakeN32(fImage->width(), fImage->height(), at);
58 } else {
59 return fBitmap.info();
60 }
61}
62
63SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc(int w, int h) const {
64 return fImage ? SkBitmapCacheDesc::Make(fImage, w, h) : SkBitmapCacheDesc::Make(fBitmap, w, h);
65}
66
67SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc() const {
68 return fImage ? SkBitmapCacheDesc::Make(fImage) : SkBitmapCacheDesc::Make(fBitmap);
69}
70
71void SkBitmapProvider::notifyAddedToCache() const {
72 if (fImage) {
73 // TODO
74 } else {
75 fBitmap.pixelRef()->notifyAddedToCache();
76 }
77}
78
79bool SkBitmapProvider::asBitmap(SkBitmap* bm) const {
80 if (fImage) {
81 return as_IB(fImage)->getROPixels(bm);
82 } else {
83 *bm = fBitmap;
84 return true;
85 }
86}
87
88///////////////////////////////////////////////////////////////////////////////////////////////////
89
90SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
reed64045422015-06-04 06:31:31 -070091 const SkMatrix& inv,
92 SkFilterQuality quality,
93 void* storage, size_t storageSize) {
reed98ed7b62015-09-15 12:38:12 -070094 if (!provider.validForDrawing()) {
halcanary96fcdcc2015-08-27 07:41:13 -070095 return nullptr;
reed64045422015-06-04 06:31:31 -070096 }
97
reed98ed7b62015-09-15 12:38:12 -070098 State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
reed64045422015-06-04 06:31:31 -070099 if (state) {
halcanary96fcdcc2015-08-27 07:41:13 -0700100 if (nullptr == state->fPixmap.addr()) {
reedad7ae6c2015-06-04 14:12:25 -0700101 SkInPlaceDeleteCheck(state, storage);
halcanary96fcdcc2015-08-27 07:41:13 -0700102 state = nullptr;
reed64045422015-06-04 06:31:31 -0700103 }
104 }
105 return state;
106}
107
108///////////////////////////////////////////////////////////////////////////////////////////////////
109
110#include "SkBitmapCache.h"
111#include "SkBitmapScaler.h"
112#include "SkMipMap.h"
113#include "SkResourceCache.h"
114
115class SkDefaultBitmapControllerState : public SkBitmapController::State {
116public:
reed98ed7b62015-09-15 12:38:12 -0700117 SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
reed64045422015-06-04 06:31:31 -0700118
119private:
reedad7ae6c2015-06-04 14:12:25 -0700120 SkBitmap fResultBitmap;
reed64045422015-06-04 06:31:31 -0700121 SkAutoTUnref<const SkMipMap> fCurrMip;
122
reed98ed7b62015-09-15 12:38:12 -0700123 bool processHQRequest(const SkBitmapProvider&);
124 bool processMediumRequest(const SkBitmapProvider&);
reed64045422015-06-04 06:31:31 -0700125};
126
127// Check to see that the size of the bitmap that would be produced by
128// scaling by the given inverted matrix is less than the maximum allowed.
reed98ed7b62015-09-15 12:38:12 -0700129static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
reed64045422015-06-04 06:31:31 -0700130 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
131 if (0 == maximumAllocation) {
132 return true;
133 }
134 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
135 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
136 // Skip the division step:
reed98ed7b62015-09-15 12:38:12 -0700137 const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
reed64045422015-06-04 06:31:31 -0700138 return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
139}
140
141/*
142 * High quality is implemented by performing up-right scale-only filtering and then
143 * using bilerp for any remaining transformations.
144 */
reed98ed7b62015-09-15 12:38:12 -0700145bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
reed64045422015-06-04 06:31:31 -0700146 if (fQuality != kHigh_SkFilterQuality) {
147 return false;
148 }
149
150 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
151 // to a valid bitmap. If we succeed, we will set this to Low instead.
152 fQuality = kMedium_SkFilterQuality;
153
reed98ed7b62015-09-15 12:38:12 -0700154 if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
reed64045422015-06-04 06:31:31 -0700155 fInvMatrix.hasPerspective())
156 {
157 return false; // can't handle the reqeust
158 }
159
160 SkScalar invScaleX = fInvMatrix.getScaleX();
161 SkScalar invScaleY = fInvMatrix.getScaleY();
162 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
163 SkSize scale;
164 if (!fInvMatrix.decomposeScale(&scale)) {
165 return false;
166 }
167 invScaleX = scale.width();
168 invScaleY = scale.height();
169 }
170 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
171 return false; // no need for HQ
172 }
173
reed98ed7b62015-09-15 12:38:12 -0700174 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
175 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
176 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
177
178 if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
179 SkBitmap orig;
180 if (!provider.asBitmap(&orig)) {
181 return false;
182 }
reed3c834322015-06-12 07:09:59 -0700183 SkAutoPixmapUnlock src;
reed98ed7b62015-09-15 12:38:12 -0700184 if (!orig.requestLock(&src)) {
reed3c834322015-06-12 07:09:59 -0700185 return false;
186 }
reed99138872015-08-31 15:16:17 -0700187 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
188 dstW, dstH, SkResourceCache::GetAllocator())) {
reed64045422015-06-04 06:31:31 -0700189 return false; // we failed to create fScaledBitmap
190 }
191
reedad7ae6c2015-06-04 14:12:25 -0700192 SkASSERT(fResultBitmap.getPixels());
193 fResultBitmap.setImmutable();
reed98ed7b62015-09-15 12:38:12 -0700194 if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
195 provider.notifyAddedToCache();
196 }
reed64045422015-06-04 06:31:31 -0700197 }
198
reedad7ae6c2015-06-04 14:12:25 -0700199 SkASSERT(fResultBitmap.getPixels());
reed64045422015-06-04 06:31:31 -0700200
reed98ed7b62015-09-15 12:38:12 -0700201 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
202 SkIntToScalar(dstH) / provider.height());
reed64045422015-06-04 06:31:31 -0700203 fQuality = kLow_SkFilterQuality;
204 return true;
205}
206
207/*
208 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
209 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
210 */
reed98ed7b62015-09-15 12:38:12 -0700211bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
reed64045422015-06-04 06:31:31 -0700212 SkASSERT(fQuality <= kMedium_SkFilterQuality);
213 if (fQuality != kMedium_SkFilterQuality) {
214 return false;
215 }
216
217 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
218 // to a valid bitmap.
219 fQuality = kLow_SkFilterQuality;
220
221 SkSize invScaleSize;
halcanary96fcdcc2015-08-27 07:41:13 -0700222 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
reed64045422015-06-04 06:31:31 -0700223 return false;
224 }
225 SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height());
226
227 if (invScale > SK_Scalar1) {
reed98ed7b62015-09-15 12:38:12 -0700228 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
halcanary96fcdcc2015-08-27 07:41:13 -0700229 if (nullptr == fCurrMip.get()) {
reed98ed7b62015-09-15 12:38:12 -0700230 SkBitmap orig;
231 if (!provider.asBitmap(&orig)) {
232 return false;
233 }
234 fCurrMip.reset(SkMipMapCache::AddAndRef(orig));
halcanary96fcdcc2015-08-27 07:41:13 -0700235 if (nullptr == fCurrMip.get()) {
reed64045422015-06-04 06:31:31 -0700236 return false;
237 }
238 }
239 // diagnostic for a crasher...
halcanary96fcdcc2015-08-27 07:41:13 -0700240 if (nullptr == fCurrMip->data()) {
reed64045422015-06-04 06:31:31 -0700241 sk_throw();
242 }
243
244 SkScalar levelScale = SkScalarInvert(invScale);
245 SkMipMap::Level level;
246 if (fCurrMip->extractLevel(levelScale, &level)) {
247 SkScalar invScaleFixup = level.fScale;
248 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
249
reed98ed7b62015-09-15 12:38:12 -0700250 const SkImageInfo info = provider.info().makeWH(level.fWidth, level.fHeight);
reed64045422015-06-04 06:31:31 -0700251 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
252 // that here, and not need to explicitly track it ourselves.
reedad7ae6c2015-06-04 14:12:25 -0700253 return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes);
reed64045422015-06-04 06:31:31 -0700254 } else {
255 // failed to extract, so release the mipmap
halcanary96fcdcc2015-08-27 07:41:13 -0700256 fCurrMip.reset(nullptr);
reed64045422015-06-04 06:31:31 -0700257 }
258 }
259 return false;
260}
261
reed98ed7b62015-09-15 12:38:12 -0700262SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
reed64045422015-06-04 06:31:31 -0700263 const SkMatrix& inv,
264 SkFilterQuality qual) {
265 fInvMatrix = inv;
266 fQuality = qual;
267
reed98ed7b62015-09-15 12:38:12 -0700268 if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
reedad7ae6c2015-06-04 14:12:25 -0700269 SkASSERT(fResultBitmap.getPixels());
270 } else {
reed98ed7b62015-09-15 12:38:12 -0700271 (void)provider.asBitmap(&fResultBitmap);
reedad7ae6c2015-06-04 14:12:25 -0700272 fResultBitmap.lockPixels();
273 // lock may fail to give us pixels
reed64045422015-06-04 06:31:31 -0700274 }
275 SkASSERT(fQuality <= kLow_SkFilterQuality);
reedad7ae6c2015-06-04 14:12:25 -0700276
277 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
halcanary96fcdcc2015-08-27 07:41:13 -0700278 // and will destroy us if it is nullptr.
reedad7ae6c2015-06-04 14:12:25 -0700279 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
280 fResultBitmap.getColorTable());
reed64045422015-06-04 06:31:31 -0700281}
282
reed98ed7b62015-09-15 12:38:12 -0700283SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
reed64045422015-06-04 06:31:31 -0700284 const SkMatrix& inverse,
285 SkFilterQuality quality,
286 void* storage, size_t size) {
287 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
288}
289