xref: /aosp_15_r20/external/skia/src/core/SkMipmap.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 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 #include "src/core/SkMipmap.h"
8 
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/base/SkTo.h"
14 #include "src/base/SkMathPriv.h"
15 #include "src/core/SkImageInfoPriv.h"
16 #include "src/core/SkMipmapBuilder.h"
17 
18 #include <new>
19 
20 //
21 // ColorTypeFilter is the "Type" we pass to some downsample template functions.
22 // It controls how we expand a pixel into a large type, with space between each component,
23 // so we can then perform our simple filter (either box or triangle) and store the intermediates
24 // in the expanded type.
25 //
26 
27 ///////////////////////////////////////////////////////////////////////////////////////////////////
28 
SkMipmap(void * malloc,size_t size)29 SkMipmap::SkMipmap(void* malloc, size_t size) : SkCachedData(malloc, size) {}
SkMipmap(size_t size,SkDiscardableMemory * dm)30 SkMipmap::SkMipmap(size_t size, SkDiscardableMemory* dm) : SkCachedData(size, dm) {}
31 
32 SkMipmap::~SkMipmap() = default;
33 
AllocLevelsSize(int levelCount,size_t pixelSize)34 size_t SkMipmap::AllocLevelsSize(int levelCount, size_t pixelSize) {
35     if (levelCount < 0) {
36         return 0;
37     }
38     int64_t size = sk_64_mul(levelCount + 1, sizeof(Level)) + pixelSize;
39     if (!SkTFitsIn<int32_t>(size)) {
40         return 0;
41     }
42     return SkTo<int32_t>(size);
43 }
44 
Build(const SkPixmap & src,SkDiscardableFactoryProc fact,bool computeContents)45 SkMipmap* SkMipmap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact,
46                           bool computeContents) {
47     if (src.width() <= 1 && src.height() <= 1) {
48         return nullptr;
49     }
50 
51     const SkColorType ct = src.colorType();
52     const SkAlphaType at = src.alphaType();
53 
54     // whip through our loop to compute the exact size needed
55     size_t size = 0;
56     int countLevels = ComputeLevelCount(src.width(), src.height());
57     for (int currentMipLevel = countLevels; currentMipLevel >= 0; currentMipLevel--) {
58         SkISize mipSize = ComputeLevelSize(src.width(), src.height(), currentMipLevel);
59         size += SkColorTypeMinRowBytes(ct, mipSize.fWidth) * mipSize.fHeight;
60     }
61 
62     size_t storageSize = SkMipmap::AllocLevelsSize(countLevels, size);
63     if (0 == storageSize) {
64         return nullptr;
65     }
66 
67     SkMipmap* mipmap;
68     if (fact) {
69         SkDiscardableMemory* dm = fact(storageSize);
70         if (nullptr == dm) {
71             return nullptr;
72         }
73         mipmap = new SkMipmap(storageSize, dm);
74     } else {
75         void* tmp = sk_malloc_canfail(storageSize);
76         if (!tmp) {
77             return nullptr;
78         }
79 
80         mipmap = new SkMipmap(tmp, storageSize);
81     }
82 
83     // init
84     mipmap->fCS = sk_ref_sp(src.info().colorSpace());
85     mipmap->fCount = countLevels;
86     mipmap->fLevels = (Level*)mipmap->writable_data();
87     SkASSERT(mipmap->fLevels);
88 
89     Level* levels = mipmap->fLevels;
90     uint8_t*    baseAddr = (uint8_t*)&levels[countLevels];
91     uint8_t*    addr = baseAddr;
92     int         width = src.width();
93     int         height = src.height();
94     uint32_t    rowBytes;
95     SkPixmap    srcPM(src);
96 
97     // Depending on architecture and other factors, the pixel data alignment may need to be as
98     // large as 8 (for F16 pixels). See the comment on SkMipmap::Level.
99     SkASSERT(SkIsAlign8((uintptr_t)addr));
100 
101     std::unique_ptr<SkMipmapDownSampler> downsampler;
102     if (computeContents) {
103         downsampler = MakeDownSampler(src);
104         if (!downsampler) {
105             return nullptr;
106         }
107     }
108 
109     for (int i = 0; i < countLevels; ++i) {
110         width = std::max(1, width >> 1);
111         height = std::max(1, height >> 1);
112         rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width));
113 
114         // We make the Info w/o any colorspace, since that storage is not under our control, and
115         // will not be deleted in a controlled fashion. When the caller is given the pixmap for
116         // a given level, we augment this pixmap with fCS (which we do manage).
117         new (&levels[i].fPixmap) SkPixmap(SkImageInfo::Make(width, height, ct, at), addr, rowBytes);
118         levels[i].fScale  = SkSize::Make(SkIntToScalar(width)  / src.width(),
119                                          SkIntToScalar(height) / src.height());
120 
121         const SkPixmap& dstPM = levels[i].fPixmap;
122         if (downsampler) {
123             downsampler->buildLevel(dstPM, srcPM);
124         }
125         srcPM = dstPM;
126         addr += height * rowBytes;
127     }
128     SkASSERT(addr == baseAddr + size);
129 
130     SkASSERT(mipmap->fLevels);
131     return mipmap;
132 }
133 
ComputeLevelCount(int baseWidth,int baseHeight)134 int SkMipmap::ComputeLevelCount(int baseWidth, int baseHeight) {
135     if (baseWidth < 1 || baseHeight < 1) {
136         return 0;
137     }
138 
139     // OpenGL's spec requires that each mipmap level have height/width equal to
140     // max(1, floor(original_height / 2^i)
141     // (or original_width) where i is the mipmap level.
142     // Continue scaling down until both axes are size 1.
143 
144     const int largestAxis = std::max(baseWidth, baseHeight);
145     if (largestAxis < 2) {
146         // SkMipmap::Build requires a minimum size of 2.
147         return 0;
148     }
149     const int leadingZeros = SkCLZ(static_cast<uint32_t>(largestAxis));
150     // If the value 00011010 has 3 leading 0s then it has 5 significant bits
151     // (the bits which are not leading zeros)
152     const int significantBits = (sizeof(uint32_t) * 8) - leadingZeros;
153     // This is making the assumption that the size of a byte is 8 bits
154     // and that sizeof(uint32_t)'s implementation-defined behavior is 4.
155     int mipLevelCount = significantBits;
156 
157     // SkMipmap does not include the base mip level.
158     // For example, it contains levels 1-x instead of 0-x.
159     // This is because the image used to create SkMipmap is the base level.
160     // So subtract 1 from the mip level count.
161     if (mipLevelCount > 0) {
162         --mipLevelCount;
163     }
164 
165     return mipLevelCount;
166 }
167 
ComputeLevelSize(int baseWidth,int baseHeight,int level)168 SkISize SkMipmap::ComputeLevelSize(int baseWidth, int baseHeight, int level) {
169     if (baseWidth < 1 || baseHeight < 1) {
170         return SkISize::Make(0, 0);
171     }
172 
173     int maxLevelCount = ComputeLevelCount(baseWidth, baseHeight);
174     if (level >= maxLevelCount || level < 0) {
175         return SkISize::Make(0, 0);
176     }
177     // OpenGL's spec requires that each mipmap level have height/width equal to
178     // max(1, floor(original_height / 2^i)
179     // (or original_width) where i is the mipmap level.
180 
181     // SkMipmap does not include the base mip level.
182     // For example, it contains levels 1-x instead of 0-x.
183     // This is because the image used to create SkMipmap is the base level.
184     // So subtract 1 from the mip level to get the index stored by SkMipmap.
185     int width = std::max(1, baseWidth >> (level + 1));
186     int height = std::max(1, baseHeight >> (level + 1));
187 
188     return SkISize::Make(width, height);
189 }
190 
191 ///////////////////////////////////////////////////////////////////////////////
192 
193 // Returns fractional level value. floor(level) is the index of the larger level.
194 // < 0 means failure.
ComputeLevel(SkSize scaleSize)195 float SkMipmap::ComputeLevel(SkSize scaleSize) {
196     SkASSERT(scaleSize.width() >= 0 && scaleSize.height() >= 0);
197 
198 #ifndef SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
199     // Use the smallest scale to match the GPU impl.
200     const float scale = std::min(scaleSize.width(), scaleSize.height());
201 #else
202     // Ideally we'd pick the smaller scale, to match Ganesh.  But ignoring one of the
203     // scales can produce some atrocious results, so for now we use the geometric mean.
204     // (https://bugs.chromium.org/p/skia/issues/detail?id=4863)
205     const float scale = std::sqrt(scaleSize.width() * scaleSize.height());
206 #endif
207 
208     if (scale >= SK_Scalar1 || scale <= 0 || !SkIsFinite(scale)) {
209         return -1;
210     }
211 
212     // The -0.5 bias here is to emulate GPU's sharpen mipmap option.
213     float L = std::max(-SkScalarLog2(scale) - 0.5f, 0.f);
214     if (!SkIsFinite(L)) {
215         return -1;
216     }
217     return L;
218 }
219 
extractLevel(SkSize scaleSize,Level * levelPtr) const220 bool SkMipmap::extractLevel(SkSize scaleSize, Level* levelPtr) const {
221     if (nullptr == fLevels) {
222         return false;
223     }
224 
225     float L = ComputeLevel(scaleSize);
226     int level = sk_float_round2int(L);
227     if (level <= 0) {
228         return false;
229     }
230 
231     if (level > fCount) {
232         level = fCount;
233     }
234     if (levelPtr) {
235         *levelPtr = fLevels[level - 1];
236         // need to augment with our colorspace
237         levelPtr->fPixmap.setColorSpace(fCS);
238     }
239     return true;
240 }
241 
validForRootLevel(const SkImageInfo & root) const242 bool SkMipmap::validForRootLevel(const SkImageInfo& root) const {
243     if (nullptr == fLevels) {
244         return false;
245     }
246 
247     const SkISize dimension = root.dimensions();
248     if (dimension.width() <= 1 && dimension.height() <= 1) {
249         return false;
250     }
251 
252     if (fLevels[0].fPixmap. width() != std::max(1, dimension. width() >> 1) ||
253         fLevels[0].fPixmap.height() != std::max(1, dimension.height() >> 1)) {
254         return false;
255     }
256 
257     for (int i = 0; i < this->countLevels(); ++i) {
258         if (fLevels[i].fPixmap.colorType() != root.colorType() ||
259             fLevels[i].fPixmap.alphaType() != root.alphaType()) {
260             return false;
261         }
262     }
263     return true;
264 }
265 
266 // Helper which extracts a pixmap from the src bitmap
267 //
Build(const SkBitmap & src,SkDiscardableFactoryProc fact)268 SkMipmap* SkMipmap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) {
269     SkPixmap srcPixmap;
270     if (!src.peekPixels(&srcPixmap)) {
271         return nullptr;
272     }
273     return Build(srcPixmap, fact);
274 }
275 
countLevels() const276 int SkMipmap::countLevels() const {
277     return fCount;
278 }
279 
getLevel(int index,Level * levelPtr) const280 bool SkMipmap::getLevel(int index, Level* levelPtr) const {
281     if (nullptr == fLevels) {
282         return false;
283     }
284     if (index < 0) {
285         return false;
286     }
287     if (index > fCount - 1) {
288         return false;
289     }
290     if (levelPtr) {
291         *levelPtr = fLevels[index];
292         // need to augment with our colorspace
293         levelPtr->fPixmap.setColorSpace(fCS);
294     }
295     return true;
296 }
297