1 /* 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" 10 #include "SkBitmapProvider.h" 11 #include "SkMatrix.h" 12 #include "SkPixelRef.h" 13 #include "SkTemplates.h" 14 15 // RESIZE_LANCZOS3 is another good option, but chrome prefers mitchell at the moment 16 #define kHQ_RESIZE_METHOD SkBitmapScaler::RESIZE_MITCHELL 17 18 /////////////////////////////////////////////////////////////////////////////////////////////////// 19 20 SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider, 21 const SkMatrix& inv, 22 SkFilterQuality quality, 23 void* storage, size_t storageSize) { 24 State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize); 25 if (state) { 26 if (nullptr == state->fPixmap.addr()) { 27 SkInPlaceDeleteCheck(state, storage); 28 state = nullptr; 29 } 30 } 31 return state; 32 } 33 34 /////////////////////////////////////////////////////////////////////////////////////////////////// 35 36 #include "SkBitmapCache.h" 37 #include "SkBitmapScaler.h" 38 #include "SkMipMap.h" 39 #include "SkResourceCache.h" 40 41 class SkDefaultBitmapControllerState : public SkBitmapController::State { 42 public: 43 SkDefaultBitmapControllerState(const SkBitmapProvider&, 44 const SkMatrix& inv, 45 SkFilterQuality, 46 bool canShadeHQ); 47 48 private: 49 SkBitmap fResultBitmap; 50 sk_sp<const SkMipMap> fCurrMip; 51 bool fCanShadeHQ; 52 53 bool processHQRequest(const SkBitmapProvider&); 54 bool processMediumRequest(const SkBitmapProvider&); 55 }; 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. 59 static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) { 60 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: 67 const size_t size = provider.info().getSafeSize(provider.info().minRowBytes()); 68 SkScalar invScaleSqr = invMat.getScaleX() * invMat.getScaleY(); 69 return size < (maximumAllocation * SkScalarAbs(invScaleSqr)); 70 } 71 72 /* 73 * High quality is implemented by performing up-right scale-only filtering and then 74 * using bilerp for any remaining transformations. 75 */ 76 bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) { 77 if (fQuality != kHigh_SkFilterQuality) { 78 return false; 79 } 80 81 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap 82 // to a valid bitmap. If we succeed, we will set this to Low instead. 83 fQuality = kMedium_SkFilterQuality; 84 #ifdef SK_USE_MIP_FOR_DOWNSCALE_HQ 85 return false; 86 #endif 87 88 bool supported = false; 89 switch (provider.info().colorType()) { 90 case kRGBA_8888_SkColorType: 91 case kBGRA_8888_SkColorType: 92 supported = true; 93 break; 94 default: 95 break; 96 } 97 if (!supported || !cache_size_okay(provider, fInvMatrix) || fInvMatrix.hasPerspective()) { 98 return false; // can't handle the reqeust 99 } 100 101 SkScalar invScaleX = fInvMatrix.getScaleX(); 102 SkScalar invScaleY = fInvMatrix.getScaleY(); 103 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) { 104 SkSize scale; 105 if (!fInvMatrix.decomposeScale(&scale)) { 106 return false; 107 } 108 invScaleX = scale.width(); 109 invScaleY = scale.height(); 110 } 111 invScaleX = SkScalarAbs(invScaleX); 112 invScaleY = SkScalarAbs(invScaleY); 113 114 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) { 115 return false; // no need for HQ 116 } 117 118 if (invScaleX > 1 || invScaleY > 1) { 119 return false; // only use HQ when upsampling 120 } 121 122 // If the shader can natively handle HQ filtering, let it do it. 123 if (fCanShadeHQ) { 124 fQuality = kHigh_SkFilterQuality; 125 SkAssertResult(provider.asBitmap(&fResultBitmap)); 126 return true; 127 } 128 129 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX); 130 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY); 131 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH); 132 133 if (!SkBitmapCache::Find(desc, &fResultBitmap)) { 134 SkBitmap orig; 135 if (!provider.asBitmap(&orig)) { 136 return false; 137 } 138 SkPixmap src; 139 if (!orig.peekPixels(&src)) { 140 return false; 141 } 142 143 SkPixmap dst; 144 SkBitmapCache::RecPtr rec; 145 const SkImageInfo info = SkImageInfo::Make(desc.fScaledWidth, desc.fScaledHeight, 146 src.colorType(), src.alphaType()); 147 if (provider.isVolatile()) { 148 if (!fResultBitmap.tryAllocPixels(info)) { 149 return false; 150 } 151 SkASSERT(fResultBitmap.getPixels()); 152 fResultBitmap.peekPixels(&dst); 153 fResultBitmap.setImmutable(); // a little cheat, as we haven't resized yet, but ok 154 } else { 155 rec = SkBitmapCache::Alloc(desc, info, &dst); 156 if (!rec) { 157 return false; 158 } 159 } 160 if (!SkBitmapScaler::Resize(dst, src, kHQ_RESIZE_METHOD)) { 161 return false; // we failed to create fScaledBitmap 162 } 163 if (rec) { 164 SkBitmapCache::Add(std::move(rec), &fResultBitmap); 165 SkASSERT(fResultBitmap.getPixels()); 166 provider.notifyAddedToCache(); 167 } 168 } 169 170 SkASSERT(fResultBitmap.getPixels()); 171 SkASSERT(fResultBitmap.isImmutable()); 172 173 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(), 174 SkIntToScalar(dstH) / provider.height()); 175 fQuality = kLow_SkFilterQuality; 176 return true; 177 } 178 179 /* 180 * Modulo internal errors, this should always succeed *if* the matrix is downscaling 181 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling) 182 */ 183 bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) { 184 SkASSERT(fQuality <= kMedium_SkFilterQuality); 185 if (fQuality != kMedium_SkFilterQuality) { 186 return false; 187 } 188 189 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap 190 // to a valid bitmap. 191 fQuality = kLow_SkFilterQuality; 192 193 SkSize invScaleSize; 194 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) { 195 return false; 196 } 197 198 SkDestinationSurfaceColorMode colorMode = provider.dstColorSpace() 199 ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware 200 : SkDestinationSurfaceColorMode::kLegacy; 201 if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) { 202 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), colorMode)); 203 if (nullptr == fCurrMip.get()) { 204 SkBitmap orig; 205 if (!provider.asBitmap(&orig)) { 206 return false; 207 } 208 fCurrMip.reset(SkMipMapCache::AddAndRef(orig, colorMode)); 209 if (nullptr == fCurrMip.get()) { 210 return false; 211 } 212 } 213 // diagnostic for a crasher... 214 if (nullptr == fCurrMip->data()) { 215 sk_throw(); 216 } 217 218 const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()), 219 SkScalarInvert(invScaleSize.height())); 220 SkMipMap::Level level; 221 if (fCurrMip->extractLevel(scale, &level)) { 222 const SkSize& invScaleFixup = level.fScale; 223 fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height()); 224 225 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install 226 // that here, and not need to explicitly track it ourselves. 227 return fResultBitmap.installPixels(level.fPixmap); 228 } else { 229 // failed to extract, so release the mipmap 230 fCurrMip.reset(nullptr); 231 } 232 } 233 return false; 234 } 235 236 SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider, 237 const SkMatrix& inv, 238 SkFilterQuality qual, 239 bool canShadeHQ) { 240 fInvMatrix = inv; 241 fQuality = qual; 242 fCanShadeHQ = canShadeHQ; 243 244 bool processed = this->processHQRequest(provider) || this->processMediumRequest(provider); 245 246 if (processed) { 247 SkASSERT(fResultBitmap.getPixels()); 248 } else { 249 (void)provider.asBitmap(&fResultBitmap); 250 } 251 SkASSERT(fCanShadeHQ || fQuality <= kLow_SkFilterQuality); 252 253 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr() 254 // and will destroy us if it is nullptr. 255 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes()); 256 } 257 258 SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm, 259 const SkMatrix& inverse, 260 SkFilterQuality quality, 261 void* storage, size_t size) { 262 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, 263 bm, inverse, quality, fCanShadeHQ); 264 } 265