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 "src/core/SkMipmapAccessor.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkSamplingOptions.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkSize.h"
15 #include "include/private/base/SkFloatingPoint.h"
16 #include "src/base/SkArenaAlloc.h"
17 #include "src/core/SkBitmapCache.h"
18 #include "src/core/SkMipmap.h"
19 #include "src/image/SkImage_Base.h"
20
21 class SkImage;
22
23 // Try to load from the base image, or from the cache
try_load_mips(const SkImage_Base * image)24 static sk_sp<const SkMipmap> try_load_mips(const SkImage_Base* image) {
25 sk_sp<const SkMipmap> mips = image->refMips();
26 if (!mips) {
27 mips.reset(SkMipmapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
28 }
29 if (!mips) {
30 mips.reset(SkMipmapCache::AddAndRef(image));
31 }
32 return mips;
33 }
34
SkMipmapAccessor(const SkImage_Base * image,const SkMatrix & inv,SkMipmapMode requestedMode)35 SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& inv,
36 SkMipmapMode requestedMode) {
37 SkMipmapMode resolvedMode = requestedMode;
38 fLowerWeight = 0;
39
40 auto load_upper_from_base = [&]() {
41 // only do this once
42 if (fBaseStorage.getPixels() == nullptr) {
43 auto dContext = as_IB(image)->directContext();
44 (void)image->getROPixels(dContext, &fBaseStorage);
45 fUpper.reset(fBaseStorage.info(), fBaseStorage.getPixels(), fBaseStorage.rowBytes());
46 }
47 };
48
49 float level = 0;
50 if (requestedMode != SkMipmapMode::kNone) {
51 SkSize scale;
52 if (!inv.decomposeScale(&scale, nullptr)) {
53 resolvedMode = SkMipmapMode::kNone;
54 } else {
55 level = SkMipmap::ComputeLevel({1/scale.width(), 1/scale.height()});
56 if (level <= 0) {
57 resolvedMode = SkMipmapMode::kNone;
58 level = 0;
59 }
60 }
61 }
62
63 auto scale = [image](const SkPixmap& pm) {
64 return SkMatrix::Scale(SkIntToScalar(pm.width()) / image->width(),
65 SkIntToScalar(pm.height()) / image->height());
66 };
67
68 // Nearest mode uses this level, so we round to pick the nearest. In linear mode we use this
69 // level as the lower of the two to interpolate between, so we take the floor.
70 int levelNum = resolvedMode == SkMipmapMode::kNearest ? sk_float_round2int(level)
71 : sk_float_floor2int(level);
72 float lowerWeight = level - levelNum; // fract(level)
73 SkASSERT(levelNum >= 0);
74
75 if (levelNum == 0) {
76 load_upper_from_base();
77 }
78 // load fCurrMip if needed
79 if (levelNum > 0 || (resolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
80 fCurrMip = try_load_mips(image);
81 if (!fCurrMip) {
82 load_upper_from_base();
83 resolvedMode = SkMipmapMode::kNone;
84 } else {
85 SkMipmap::Level levelRec;
86
87 SkASSERT(resolvedMode != SkMipmapMode::kNone);
88 if (levelNum > 0) {
89 if (fCurrMip->getLevel(levelNum - 1, &levelRec)) {
90 fUpper = levelRec.fPixmap;
91 } else {
92 load_upper_from_base();
93 resolvedMode = SkMipmapMode::kNone;
94 }
95 }
96
97 if (resolvedMode == SkMipmapMode::kLinear) {
98 if (fCurrMip->getLevel(levelNum, &levelRec)) {
99 fLower = levelRec.fPixmap;
100 fLowerWeight = lowerWeight;
101 fLowerInv = scale(fLower);
102 } else {
103 resolvedMode = SkMipmapMode::kNearest;
104 }
105 }
106 }
107 }
108 fUpperInv = scale(fUpper);
109 }
110
Make(SkArenaAlloc * alloc,const SkImage * image,const SkMatrix & inv,SkMipmapMode mipmap)111 SkMipmapAccessor* SkMipmapAccessor::Make(SkArenaAlloc* alloc, const SkImage* image,
112 const SkMatrix& inv, SkMipmapMode mipmap) {
113 auto* access = alloc->make<SkMipmapAccessor>(as_IB(image), inv, mipmap);
114 // return null if we failed to get the level (so the caller won't try to use it)
115 return access->fUpper.addr() ? access : nullptr;
116 }
117