1/*
2 * Copyright 2014 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 "SkBitmapCache.h"
9#include "SkResourceCache.h"
10#include "SkMipMap.h"
11#include "SkRect.h"
12
13SkBitmap::Allocator* SkBitmapCache::GetAllocator() {
14    return SkResourceCache::GetAllocator();
15}
16
17/**
18 This function finds the bounds of the bitmap *within its pixelRef*.
19 If the bitmap lacks a pixelRef, it will return an empty rect, since
20 that doesn't make sense.  This may be a useful enough function that
21 it should be somewhere else (in SkBitmap?).
22 */
23static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
24    if (!(bm.pixelRef())) {
25        return SkIRect::MakeEmpty();
26    }
27    SkIPoint origin = bm.pixelRefOrigin();
28    return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
29}
30
31struct BitmapKey : public SkResourceCache::Key {
32public:
33    BitmapKey(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds)
34    : fGenID(genID)
35    , fScaleX(scaleX)
36    , fScaleY(scaleY)
37    , fBounds(bounds)
38    {
39        this->init(sizeof(fGenID) + sizeof(fScaleX) + sizeof(fScaleY) + sizeof(fBounds));
40    }
41
42    uint32_t    fGenID;
43    SkScalar    fScaleX;
44    SkScalar    fScaleY;
45    SkIRect     fBounds;
46};
47
48//////////////////////////////////////////////////////////////////////////////////////////
49
50struct BitmapRec : public SkResourceCache::Rec {
51    BitmapRec(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds,
52              const SkBitmap& result)
53        : fKey(genID, scaleX, scaleY, bounds)
54        , fBitmap(result)
55    {}
56
57    BitmapKey   fKey;
58    SkBitmap    fBitmap;
59
60    virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
61    virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + fBitmap.getSize(); }
62
63    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
64        const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec);
65        SkBitmap* result = (SkBitmap*)contextBitmap;
66
67        *result = rec.fBitmap;
68        result->lockPixels();
69        return SkToBool(result->getPixels());
70    }
71};
72
73#define CHECK_LOCAL(localCache, localName, globalName, ...) \
74    (localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__)
75
76bool SkBitmapCache::Find(const SkBitmap& src, SkScalar invScaleX, SkScalar invScaleY, SkBitmap* result,
77                         SkResourceCache* localCache) {
78    if (0 == invScaleX || 0 == invScaleY) {
79        // degenerate, and the key we use for mipmaps
80        return false;
81    }
82    BitmapKey key(src.getGenerationID(), invScaleX, invScaleY, get_bounds_from_bitmap(src));
83
84    return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Visitor, result);
85}
86
87void SkBitmapCache::Add(const SkBitmap& src, SkScalar invScaleX, SkScalar invScaleY,
88                        const SkBitmap& result, SkResourceCache* localCache) {
89    if (0 == invScaleX || 0 == invScaleY) {
90        // degenerate, and the key we use for mipmaps
91        return;
92    }
93    SkASSERT(result.isImmutable());
94    BitmapRec* rec = SkNEW_ARGS(BitmapRec, (src.getGenerationID(), invScaleX, invScaleY,
95                                            get_bounds_from_bitmap(src), result));
96    CHECK_LOCAL(localCache, add, Add, rec);
97}
98
99bool SkBitmapCache::Find(uint32_t genID, const SkIRect& subset, SkBitmap* result,
100                         SkResourceCache* localCache) {
101    BitmapKey key(genID, SK_Scalar1, SK_Scalar1, subset);
102
103    return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Visitor, result);
104}
105
106bool SkBitmapCache::Add(uint32_t genID, const SkIRect& subset, const SkBitmap& result,
107                        SkResourceCache* localCache) {
108    SkASSERT(result.isImmutable());
109
110    if (subset.isEmpty()
111        || subset.top() < 0
112        || subset.left() < 0
113        || result.width() != subset.width()
114        || result.height() != subset.height()) {
115        return false;
116    } else {
117        BitmapRec* rec = SkNEW_ARGS(BitmapRec, (genID, SK_Scalar1, SK_Scalar1, subset, result));
118
119        CHECK_LOCAL(localCache, add, Add, rec);
120        return true;
121    }
122}
123//////////////////////////////////////////////////////////////////////////////////////////
124
125struct MipMapRec : public SkResourceCache::Rec {
126    MipMapRec(const SkBitmap& src, const SkMipMap* result)
127        : fKey(src.getGenerationID(), 0, 0, get_bounds_from_bitmap(src))
128        , fMipMap(SkRef(result))
129    {}
130
131    virtual ~MipMapRec() {
132        fMipMap->unref();
133    }
134
135    BitmapKey       fKey;
136    const SkMipMap* fMipMap;
137
138    virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
139    virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + fMipMap->getSize(); }
140
141    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextMip) {
142        const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec);
143        const SkMipMap** result = (const SkMipMap**)contextMip;
144
145        *result = SkRef(rec.fMipMap);
146        // mipmaps don't use the custom allocator yet, so we don't need to check pixels
147        return true;
148    }
149};
150
151const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmap& src) {
152    BitmapKey key(src.getGenerationID(), 0, 0, get_bounds_from_bitmap(src));
153    const SkMipMap* result;
154    if (!SkResourceCache::Find(key, MipMapRec::Visitor, &result)) {
155        result = NULL;
156    }
157    return result;
158}
159
160void SkMipMapCache::Add(const SkBitmap& src, const SkMipMap* result) {
161    if (result) {
162        SkResourceCache::Add(SkNEW_ARGS(MipMapRec, (src, result)));
163    }
164}
165
166