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/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 post_scale = [image, inv](const SkPixmap& pm) {
58 return SkMatrix::Scale(SkIntToScalar(pm.width()) / image->width(),
59 SkIntToScalar(pm.height()) / image->height()) * inv;
60 };
61
62 int levelNum = sk_float_floor2int(level);
63 float lowerWeight = level - levelNum; // fract(level)
64 SkASSERT(levelNum >= 0);
65
66 if (levelNum == 0) {
67 load_upper_from_base();
68 }
69 // load fCurrMip if needed
70 if (levelNum > 0 || (resolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
71 fCurrMip = try_load_mips(image);
72 if (!fCurrMip) {
73 load_upper_from_base();
74 resolvedMode = SkMipmapMode::kNone;
75 } else {
76 SkMipmap::Level levelRec;
77
78 SkASSERT(resolvedMode != SkMipmapMode::kNone);
79 if (levelNum > 0) {
80 if (fCurrMip->getLevel(levelNum - 1, &levelRec)) {
81 fUpper = levelRec.fPixmap;
82 } else {
83 load_upper_from_base();
84 resolvedMode = SkMipmapMode::kNone;
85 }
86 }
87
88 if (resolvedMode == SkMipmapMode::kLinear) {
89 if (fCurrMip->getLevel(levelNum, &levelRec)) {
90 fLower = levelRec.fPixmap;
91 fLowerWeight = lowerWeight;
92 fLowerInv = post_scale(fLower);
93 } else {
94 resolvedMode = SkMipmapMode::kNearest;
95 }
96 }
97 }
98 }
99 fUpperInv = post_scale(fUpper);
100 }
101
Make(SkArenaAlloc * alloc,const SkImage * image,const SkMatrix & inv,SkMipmapMode mipmap)102 SkMipmapAccessor* SkMipmapAccessor::Make(SkArenaAlloc* alloc, const SkImage* image,
103 const SkMatrix& inv, SkMipmapMode mipmap) {
104 auto* access = alloc->make<SkMipmapAccessor>(as_IB(image), inv, mipmap);
105 // return null if we failed to get the level (so the caller won't try to use it)
106 return access->fUpper.addr() ? access : nullptr;
107 }
108