xref: /aosp_15_r20/external/skia/src/core/SkMipmapAccessor.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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