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