1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SkChunkAlloc.h"
9#include "SkPackBits.h"
10#include "SkBitmap.h"
11#include "SkPixelRef.h"
12
13class RLEPixelRef : public SkPixelRef {
14public:
15    RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable);
16    virtual ~RLEPixelRef();
17
18protected:
19    // overrides from SkPixelRef
20    virtual void* onLockPixels(SkColorTable**);
21    virtual void onUnlockPixels();
22
23private:
24    SkBitmap::RLEPixels* fRLEPixels;
25    SkColorTable*        fCTable;
26};
27
28RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable)
29        : SkPixelRef(NULL) {
30    fRLEPixels = rlep;  // we now own this ptr
31    fCTable = ctable;
32    SkSafeRef(ctable);
33}
34
35RLEPixelRef::~RLEPixelRef() {
36    SkDELETE(fRLEPixels);
37    SkSafeUnref(fCTable);
38}
39
40void* RLEPixelRef::onLockPixels(SkColorTable** ct) {
41    *ct = fCTable;
42    return fRLEPixels;
43}
44
45void RLEPixelRef::onUnlockPixels() {
46    // nothing to do
47}
48
49/////////////////////////////////////////////////////////////////////////////
50
51class ChunkRLEPixels : public SkBitmap::RLEPixels {
52public:
53    ChunkRLEPixels(int width, int height, size_t chunkSize)
54        : SkBitmap::RLEPixels(width, height), fStorage(chunkSize) {
55    }
56
57    SkChunkAlloc fStorage;
58};
59
60SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src);
61SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) {
62
63    if (SkBitmap::kIndex8_Config != src.config() &&
64            SkBitmap::kA8_Config != src.config()) {
65        return NULL;
66    }
67
68    size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width());
69
70    // estimate the rle size based on the original size
71    size_t size = src.getSize() >> 3;
72    if (size < maxPacked) {
73        size = maxPacked;
74    }
75
76    ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels,
77                                           (src.width(), src.height(), size));
78
79    uint8_t* dstRow = NULL;
80    size_t free = 0;
81    size_t totalPacked = 0;
82
83    for (int y = 0; y < src.height(); y++) {
84        const uint8_t* srcRow = src.getAddr8(0, y);
85
86        if (free < maxPacked) {
87            dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size);
88            free = size;
89        }
90        size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow);
91        SkASSERT(packedSize <= free);
92        rlePixels->setPackedAtY(y, dstRow);
93
94        dstRow += packedSize;
95        free -= packedSize;
96
97        totalPacked += packedSize;
98    }
99
100//#ifdef SK_DEBUG
101#if 0
102    // test
103    uint8_t* buffer = new uint8_t[src.width()];
104    for (int y = 0; y < src.height(); y++) {
105        const uint8_t* srcRow = src.getAddr8(0, y);
106        SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y));
107        int n = memcmp(buffer, srcRow, src.width());
108        if (n) {
109            SkDebugf("----- memcmp returned %d on line %d\n", n, y);
110        }
111        SkASSERT(n == 0);
112    }
113    delete[] buffer;
114
115    size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked;
116
117    SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n",
118             src.width(), src.height(), src.getSize(),
119             src.height() * sizeof(uint8_t*), totalPacked,
120             (float)totalAlloc / src.getSize());
121
122#endif
123
124    // transfer ownership of rlePixels to our pixelref
125    return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable()));
126}
127
128