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