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