1/*
2 * Copyright 2012 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#ifdef SK_DEBUG
9
10#include "SkBitmap.h"
11#include "SkBitmapFactory.h"
12#include "SkCanvas.h"
13#include "SkColor.h"
14#include "SkData.h"
15#include "SkImageDecoder.h"
16#include "SkImageEncoder.h"
17#include "SkLazyPixelRef.h"
18#include "SkLruImageCache.h"
19#include "SkPaint.h"
20#include "SkPurgeableImageCache.h"
21#include "SkStream.h"
22#include "SkTemplates.h"
23#include "Test.h"
24
25static SkBitmap* create_bitmap() {
26    SkBitmap* bm = SkNEW(SkBitmap);
27    // Use a large bitmap.
28    const int W = 1000, H = 1000;
29    bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
30    bm->allocPixels();
31    bm->eraseColor(SK_ColorBLACK);
32    SkCanvas canvas(*bm);
33    SkPaint paint;
34    paint.setColor(SK_ColorBLUE);
35    canvas.drawRectCoords(0, 0, SkIntToScalar(W/2), SkIntToScalar(H/2), paint);
36    return bm;
37}
38
39static SkData* create_data_from_bitmap(const SkBitmap& bm) {
40    SkDynamicMemoryWStream stream;
41    if (SkImageEncoder::EncodeStream(&stream, bm, SkImageEncoder::kPNG_Type, 100)) {
42        return stream.copyToData();
43    }
44    return NULL;
45}
46
47static void assert_bounds_equal(skiatest::Reporter* reporter, const SkBitmap& bm1,
48                                const SkBitmap& bm2) {
49    REPORTER_ASSERT(reporter, bm1.width() == bm2.width());
50    REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
51}
52
53static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache) {
54    // Test the cache directly:
55    cache->purgeAllUnpinnedCaches();
56    intptr_t ID = SkImageCache::UNINITIALIZED_ID;
57    const size_t size = 1000;
58    char buffer[size];
59    sk_bzero((void*) buffer, size);
60    void* memory = cache->allocAndPinCache(size, &ID);
61    if (memory != NULL) {
62        memcpy(memory, (void*)buffer, size);
63        REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) == SkImageCache::kPinned_MemoryStatus);
64        cache->releaseCache(ID);
65        REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) != SkImageCache::kPinned_MemoryStatus);
66        SkImageCache::DataStatus dataStatus;
67        memory = cache->pinCache(ID, &dataStatus);
68        if (memory != NULL) {
69            REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
70                                      == SkImageCache::kPinned_MemoryStatus);
71            if (SkImageCache::kRetained_DataStatus == dataStatus) {
72                REPORTER_ASSERT(reporter, !memcmp(memory, (void*) buffer, size));
73            }
74            cache->releaseCache(ID);
75            REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
76                                      != SkImageCache::kPinned_MemoryStatus);
77            cache->purgeAllUnpinnedCaches();
78            REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
79                                      != SkImageCache::kPinned_MemoryStatus);
80            memory = cache->pinCache(ID, &dataStatus);
81            if (memory != NULL) {
82                // The memory block may or may not have survived the purging (at the
83                // memory manager's whim) so we cannot check dataStatus here.
84                cache->releaseCache(ID);
85                REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
86                                          != SkImageCache::kPinned_MemoryStatus);
87                cache->throwAwayCache(ID);
88                REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
89                                          == SkImageCache::kFreed_MemoryStatus);
90            } else {
91                REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
92                                          == SkImageCache::kFreed_MemoryStatus);
93            }
94        }
95    }
96}
97
98static void test_factory(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
99                       const SkBitmap& origBitmap) {
100    SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
101    factory.setImageCache(cache);
102    SkAutoTDelete<SkBitmap> bitmapFromFactory(SkNEW(SkBitmap));
103    bool success = factory.installPixelRef(encodedData, bitmapFromFactory.get());
104    // This assumes that if the encoder worked, the decoder should also work, so the above call
105    // should not fail.
106    REPORTER_ASSERT(reporter, success);
107    assert_bounds_equal(reporter, origBitmap, *bitmapFromFactory.get());
108
109    SkPixelRef* pixelRef = bitmapFromFactory->pixelRef();
110    REPORTER_ASSERT(reporter, pixelRef != NULL);
111    if (NULL == cache) {
112        // This assumes that installPixelRef called lockPixels.
113        REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
114    } else {
115        // Lazy decoding
116        REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
117        SkLazyPixelRef* lazyRef = static_cast<SkLazyPixelRef*>(pixelRef);
118        intptr_t cacheID = lazyRef->getCacheId();
119        REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
120                                  != SkImageCache::kPinned_MemoryStatus);
121        {
122            SkAutoLockPixels alp(*bitmapFromFactory.get());
123            REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
124            cacheID = lazyRef->getCacheId();
125            REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
126                                      == SkImageCache::kPinned_MemoryStatus);
127        }
128        REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
129        REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
130                                  != SkImageCache::kPinned_MemoryStatus);
131        bitmapFromFactory.free();
132        REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
133                                  == SkImageCache::kFreed_MemoryStatus);
134    }
135}
136
137class ImageCacheHolder : public SkNoncopyable {
138
139public:
140    ~ImageCacheHolder() {
141        fCaches.safeUnrefAll();
142    }
143
144    void addImageCache(SkImageCache* cache) {
145        SkSafeRef(cache);
146        *fCaches.append() = cache;
147    }
148
149    int count() const { return fCaches.count(); }
150
151    SkImageCache* getAt(int i) {
152        if (i < 0 || i > fCaches.count()) {
153            return NULL;
154        }
155        return fCaches.getAt(i);
156    }
157
158private:
159    SkTDArray<SkImageCache*> fCaches;
160};
161
162static void TestBitmapFactory(skiatest::Reporter* reporter) {
163    SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
164    SkASSERT(bitmap.get() != NULL);
165
166    SkAutoDataUnref encodedBitmap(create_data_from_bitmap(*bitmap.get()));
167    bool encodeSucceeded = encodedBitmap.get() != NULL;
168    SkASSERT(encodeSucceeded);
169
170    ImageCacheHolder cacheHolder;
171
172    SkAutoTUnref<SkLruImageCache> lruCache(SkNEW_ARGS(SkLruImageCache, (1024 * 1024)));
173    cacheHolder.addImageCache(lruCache);
174
175    cacheHolder.addImageCache(NULL);
176
177    SkImageCache* purgeableCache = SkPurgeableImageCache::Create();
178    if (purgeableCache != NULL) {
179        cacheHolder.addImageCache(purgeableCache);
180        purgeableCache->unref();
181    }
182
183    for (int i = 0; i < cacheHolder.count(); i++) {
184        SkImageCache* cache = cacheHolder.getAt(i);
185        if (cache != NULL) {
186            test_cache(reporter, cache);
187        }
188        if (encodeSucceeded) {
189            test_factory(reporter, cache, encodedBitmap, *bitmap.get());
190        }
191    }
192}
193
194#include "TestClassDef.h"
195DEFINE_TESTCLASS("BitmapFactory", TestBitmapFactoryClass, TestBitmapFactory)
196
197#endif // SK_DEBUG
198