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 "include/core/SkBitmap.h"
9#include "include/core/SkMatrix.h"
10#include "include/private/SkTemplates.h"
11#include "src/core/SkArenaAlloc.h"
12#include "src/core/SkBitmapCache.h"
13#include "src/core/SkBitmapController.h"
14#include "src/core/SkMatrixPriv.h"
15#include "src/core/SkMipmap.h"
16#include "src/image/SkImage_Base.h"
17
18// Try to load from the base image, or from the cache
19static sk_sp<const SkMipmap> try_load_mips(const SkImage_Base* image) {
20 sk_sp<const SkMipmap> mips = image->refMips();
21 if (!mips) {
22 mips.reset(SkMipmapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
23 }
24 if (!mips) {
25 mips.reset(SkMipmapCache::AddAndRef(image));
26 }
27 return mips;
28}
29
30///////////////////////////////////////////////////////////////////////////////////////////////////
31
32SkBitmapController::State* SkBitmapController::RequestBitmap(const SkImage_Base* image,
33 const SkMatrix& inv,
34 SkFilterQuality quality,
35 SkArenaAlloc* alloc) {
36 auto* state = alloc->make<SkBitmapController::State>(image, inv, quality);
37
38 return state->pixmap().addr() ? state : nullptr;
39}
40
41bool SkBitmapController::State::processHighRequest(const SkImage_Base* image) {
42 if (fQuality != kHigh_SkFilterQuality) {
43 return false;
44 }
45
46 if (SkMatrixPriv::AdjustHighQualityFilterLevel(fInvMatrix, true) != kHigh_SkFilterQuality) {
47 fQuality = kMedium_SkFilterQuality;
48 return false;
49 }
50
51 (void)image->getROPixels(&fResultBitmap);
52 return true;
53}
54
55/*
56 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
57 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
58 */
59bool SkBitmapController::State::processMediumRequest(const SkImage_Base* image) {
60 SkASSERT(fQuality <= kMedium_SkFilterQuality);
61 if (fQuality != kMedium_SkFilterQuality) {
62 return false;
63 }
64
65 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
66 // to a valid bitmap.
67 fQuality = kLow_SkFilterQuality;
68
69 SkSize invScaleSize;
70 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
71 return false;
72 }
73
74 if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
75 fCurrMip = try_load_mips(image);
76 if (!fCurrMip) {
77 return false;
78 }
79 // diagnostic for a crasher...
80 SkASSERT_RELEASE(fCurrMip->data());
81
82 const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
83 SkScalarInvert(invScaleSize.height()));
84 SkMipmap::Level level;
85 if (fCurrMip->extractLevel(scale, &level)) {
86 const SkSize& invScaleFixup = level.fScale;
87 fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
88
89 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
90 // that here, and not need to explicitly track it ourselves.
91 return fResultBitmap.installPixels(level.fPixmap);
92 } else {
93 // failed to extract, so release the mipmap
94 fCurrMip.reset(nullptr);
95 }
96 }
97 return false;
98}
99
100SkBitmapController::State::State(const SkImage_Base* image,
101 const SkMatrix& inv,
102 SkFilterQuality qual) {
103 fInvMatrix = inv;
104 fQuality = qual;
105
106 if (this->processHighRequest(image) || this->processMediumRequest(image)) {
107 SkASSERT(fResultBitmap.getPixels());
108 } else {
109 (void)image->getROPixels(&fResultBitmap);
110 }
111
112 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
113 // and will destroy us if it is nullptr.
114 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
115}
116
117///////////////////////////////////////////////////////////////////////////////////////////////////
118
119SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& inv,
120 SkMipmapMode requestedMode) {
121 fResolvedMode = requestedMode;
122 fLowerWeight = 0;
123
124 auto load_upper_from_base = [&]() {
125 // only do this once
126 if (fBaseStorage.getPixels() == nullptr) {
127 (void)image->getROPixels(&fBaseStorage);
128 fUpper.reset(fBaseStorage.info(), fBaseStorage.getPixels(), fBaseStorage.rowBytes());
129 }
130 };
131
132 float level = 0;
133 if (requestedMode != SkMipmapMode::kNone) {
134 SkSize scale;
135 if (!inv.decomposeScale(&scale, nullptr)) {
136 fResolvedMode = SkMipmapMode::kNone;
137 } else {
138 level = SkMipmap::ComputeLevel({1/scale.width(), 1/scale.height()});
139 if (level <= 0) {
140 fResolvedMode = SkMipmapMode::kNone;
141 level = 0;
142 }
143 }
144 }
145
146 int levelNum = sk_float_floor2int(level);
147 float lowerWeight = level - levelNum; // fract(level)
148 SkASSERT(levelNum >= 0);
149
150 if (levelNum == 0) {
151 load_upper_from_base();
152 }
153 // load fCurrMip if needed
154 if (levelNum > 0 || (fResolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
155 fCurrMip = try_load_mips(image);
156 if (!fCurrMip) {
157 load_upper_from_base();
158 fResolvedMode = SkMipmapMode::kNone;
159 } else {
160 SkMipmap::Level levelRec;
161
162 SkASSERT(fResolvedMode != SkMipmapMode::kNone);
163 if (levelNum > 0) {
164 if (fCurrMip->getLevel(levelNum - 1, &levelRec)) {
165 fUpper = levelRec.fPixmap;
166 } else {
167 load_upper_from_base();
168 fResolvedMode = SkMipmapMode::kNone;
169 }
170 }
171
172 if (fResolvedMode == SkMipmapMode::kLinear) {
173 if (fCurrMip->getLevel(levelNum, &levelRec)) {
174 fLower = levelRec.fPixmap;
175 fLowerWeight = lowerWeight;
176 } else {
177 fResolvedMode = SkMipmapMode::kNearest;
178 }
179 }
180 }
181 }
182}
183