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 20SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider, 21 const SkMatrix& inv, 22 SkFilterQuality quality, 23 void* storage, size_t storageSize) { 24 if (!provider.validForDrawing()) { 25 return nullptr; 26 } 27 28 State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize); 29 if (state) { 30 if (nullptr == state->fPixmap.addr()) { 31 SkInPlaceDeleteCheck(state, storage); 32 state = nullptr; 33 } 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: 47 SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality); 48 49private: 50 SkBitmap fResultBitmap; 51 SkAutoTUnref<const SkMipMap> fCurrMip; 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. 59static 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 */ 76bool 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 85 if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) || 86 fInvMatrix.hasPerspective()) 87 { 88 return false; // can't handle the reqeust 89 } 90 91 SkScalar invScaleX = fInvMatrix.getScaleX(); 92 SkScalar invScaleY = fInvMatrix.getScaleY(); 93 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) { 94 SkSize scale; 95 if (!fInvMatrix.decomposeScale(&scale)) { 96 return false; 97 } 98 invScaleX = scale.width(); 99 invScaleY = scale.height(); 100 } 101 invScaleX = SkScalarAbs(invScaleX); 102 invScaleY = SkScalarAbs(invScaleY); 103 104 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) { 105 return false; // no need for HQ 106 } 107 108 if (invScaleX > 1 || invScaleY > 1) { 109 return false; // only use HQ when upsampling 110 } 111 112 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX); 113 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY); 114 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH); 115 116 if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) { 117 SkBitmap orig; 118 if (!provider.asBitmap(&orig)) { 119 return false; 120 } 121 SkAutoPixmapUnlock src; 122 if (!orig.requestLock(&src)) { 123 return false; 124 } 125 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD, 126 dstW, dstH, SkResourceCache::GetAllocator())) { 127 return false; // we failed to create fScaledBitmap 128 } 129 130 SkASSERT(fResultBitmap.getPixels()); 131 fResultBitmap.setImmutable(); 132 if (!provider.isVolatile()) { 133 if (SkBitmapCache::AddWH(desc, fResultBitmap)) { 134 provider.notifyAddedToCache(); 135 } 136 } 137 } 138 139 SkASSERT(fResultBitmap.getPixels()); 140 141 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(), 142 SkIntToScalar(dstH) / provider.height()); 143 fQuality = kLow_SkFilterQuality; 144 return true; 145} 146 147/* 148 * Modulo internal errors, this should always succeed *if* the matrix is downscaling 149 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling) 150 */ 151bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) { 152 SkASSERT(fQuality <= kMedium_SkFilterQuality); 153 if (fQuality != kMedium_SkFilterQuality) { 154 return false; 155 } 156 157 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap 158 // to a valid bitmap. 159 fQuality = kLow_SkFilterQuality; 160 161 SkSize invScaleSize; 162 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) { 163 return false; 164 } 165 166 if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) { 167 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc())); 168 if (nullptr == fCurrMip.get()) { 169 SkBitmap orig; 170 if (!provider.asBitmap(&orig)) { 171 return false; 172 } 173 fCurrMip.reset(SkMipMapCache::AddAndRef(orig)); 174 if (nullptr == fCurrMip.get()) { 175 return false; 176 } 177 } 178 // diagnostic for a crasher... 179 if (nullptr == fCurrMip->data()) { 180 sk_throw(); 181 } 182 183 const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()), 184 SkScalarInvert(invScaleSize.height())); 185 SkMipMap::Level level; 186 if (fCurrMip->extractLevel(scale, &level)) { 187 const SkSize& invScaleFixup = level.fScale; 188 fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height()); 189 190 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install 191 // that here, and not need to explicitly track it ourselves. 192 return fResultBitmap.installPixels(level.fPixmap); 193 } else { 194 // failed to extract, so release the mipmap 195 fCurrMip.reset(nullptr); 196 } 197 } 198 return false; 199} 200 201SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider, 202 const SkMatrix& inv, 203 SkFilterQuality qual) { 204 fInvMatrix = inv; 205 fQuality = qual; 206 207 if (this->processHQRequest(provider) || this->processMediumRequest(provider)) { 208 SkASSERT(fResultBitmap.getPixels()); 209 } else { 210 (void)provider.asBitmap(&fResultBitmap); 211 fResultBitmap.lockPixels(); 212 // lock may fail to give us pixels 213 } 214 SkASSERT(fQuality <= kLow_SkFilterQuality); 215 216 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr() 217 // and will destroy us if it is nullptr. 218 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(), 219 fResultBitmap.getColorTable()); 220} 221 222SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm, 223 const SkMatrix& inverse, 224 SkFilterQuality quality, 225 void* storage, size_t size) { 226 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality); 227} 228 229