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 "SkBitmapCache.h"
10 #include "SkBitmapController.h"
11 #include "SkBitmapProvider.h"
12 #include "SkMatrix.h"
13 #include "SkMipMap.h"
14 #include "SkTemplates.h"
15 
16 ///////////////////////////////////////////////////////////////////////////////////////////////////
17 
requestBitmap(const SkBitmapProvider & provider,const SkMatrix & inv,SkFilterQuality quality,void * storage,size_t storageSize)18 SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
19                                                              const SkMatrix& inv,
20                                                              SkFilterQuality quality,
21                                                              void* storage, size_t storageSize) {
22     State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
23     if (state) {
24         if (nullptr == state->fPixmap.addr()) {
25             SkInPlaceDeleteCheck(state, storage);
26             state = nullptr;
27         }
28     }
29     return state;
30 }
31 
32 ///////////////////////////////////////////////////////////////////////////////////////////////////
33 
34 class SkDefaultBitmapControllerState : public SkBitmapController::State {
35 public:
36     SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
37 
38 private:
39     SkBitmap                fResultBitmap;
40     sk_sp<const SkMipMap>   fCurrMip;
41 
42     bool processHighRequest(const SkBitmapProvider&);
43     bool processMediumRequest(const SkBitmapProvider&);
44 };
45 
processHighRequest(const SkBitmapProvider & provider)46 bool SkDefaultBitmapControllerState::processHighRequest(const SkBitmapProvider& provider) {
47     if (fQuality != kHigh_SkFilterQuality) {
48         return false;
49     }
50 
51     fQuality = kMedium_SkFilterQuality;
52 
53     SkScalar invScaleX = fInvMatrix.getScaleX();
54     SkScalar invScaleY = fInvMatrix.getScaleY();
55     if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
56         SkSize scale;
57         if (!fInvMatrix.decomposeScale(&scale)) {
58             return false;
59         }
60         invScaleX = scale.width();
61         invScaleY = scale.height();
62     }
63     invScaleX = SkScalarAbs(invScaleX);
64     invScaleY = SkScalarAbs(invScaleY);
65 
66     if (invScaleX >= 1 - SK_ScalarNearlyZero || invScaleY >= 1 - SK_ScalarNearlyZero) {
67         // we're down-scaling so abort HQ
68         return false;
69     }
70 
71     // Confirmed that we can use HQ (w/ rasterpipeline)
72     fQuality = kHigh_SkFilterQuality;
73     (void)provider.asBitmap(&fResultBitmap);
74     return true;
75 }
76 
77 /*
78  *  Modulo internal errors, this should always succeed *if* the matrix is downscaling
79  *  (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
80  */
processMediumRequest(const SkBitmapProvider & provider)81 bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
82     SkASSERT(fQuality <= kMedium_SkFilterQuality);
83     if (fQuality != kMedium_SkFilterQuality) {
84         return false;
85     }
86 
87     // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
88     // to a valid bitmap.
89     fQuality = kLow_SkFilterQuality;
90 
91     SkSize invScaleSize;
92     if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
93         return false;
94     }
95 
96     SkDestinationSurfaceColorMode colorMode = provider.dstColorSpace()
97         ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
98         : SkDestinationSurfaceColorMode::kLegacy;
99     if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
100         fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), colorMode));
101         if (nullptr == fCurrMip.get()) {
102             SkBitmap orig;
103             if (!provider.asBitmap(&orig)) {
104                 return false;
105             }
106             fCurrMip.reset(SkMipMapCache::AddAndRef(orig, colorMode));
107             if (nullptr == fCurrMip.get()) {
108                 return false;
109             }
110         }
111         // diagnostic for a crasher...
112         SkASSERT_RELEASE(fCurrMip->data());
113 
114         const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
115                                           SkScalarInvert(invScaleSize.height()));
116         SkMipMap::Level level;
117         if (fCurrMip->extractLevel(scale, &level)) {
118             const SkSize& invScaleFixup = level.fScale;
119             fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
120 
121             // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
122             //       that here, and not need to explicitly track it ourselves.
123             return fResultBitmap.installPixels(level.fPixmap);
124         } else {
125             // failed to extract, so release the mipmap
126             fCurrMip.reset(nullptr);
127         }
128     }
129     return false;
130 }
131 
SkDefaultBitmapControllerState(const SkBitmapProvider & provider,const SkMatrix & inv,SkFilterQuality qual)132 SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
133                                                                const SkMatrix& inv,
134                                                                SkFilterQuality qual) {
135     fInvMatrix = inv;
136     fQuality = qual;
137 
138     if (this->processHighRequest(provider) || this->processMediumRequest(provider)) {
139         SkASSERT(fResultBitmap.getPixels());
140     } else {
141         (void)provider.asBitmap(&fResultBitmap);
142     }
143 
144     // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
145     // and will destroy us if it is nullptr.
146     fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
147 }
148 
onRequestBitmap(const SkBitmapProvider & bm,const SkMatrix & inverse,SkFilterQuality quality,void * storage,size_t size)149 SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
150                                                                       const SkMatrix& inverse,
151                                                                       SkFilterQuality quality,
152                                                                       void* storage, size_t size) {
153     return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
154 }
155