
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkChunkAlloc.h"
#include "SkPackBits.h"
#include "SkBitmap.h"
#include "SkPixelRef.h"

class RLEPixelRef : public SkPixelRef {
public:
    RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable);
    virtual ~RLEPixelRef();
    
protected:
    // overrides from SkPixelRef
    virtual void* onLockPixels(SkColorTable**);
    virtual void onUnlockPixels();
    
private:
    SkBitmap::RLEPixels* fRLEPixels;
    SkColorTable*        fCTable;
};

RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable)
        : SkPixelRef(NULL) {
    fRLEPixels = rlep;  // we now own this ptr
    fCTable = ctable;
    SkSafeRef(ctable);
}

RLEPixelRef::~RLEPixelRef() {
    SkDELETE(fRLEPixels);
    SkSafeUnref(fCTable);
}

void* RLEPixelRef::onLockPixels(SkColorTable** ct) {
    *ct = fCTable;
    return fRLEPixels;
}

void RLEPixelRef::onUnlockPixels() {
    // nothing to do
}

/////////////////////////////////////////////////////////////////////////////

class ChunkRLEPixels : public SkBitmap::RLEPixels {
public:
    ChunkRLEPixels(int width, int height, size_t chunkSize)
        : SkBitmap::RLEPixels(width, height), fStorage(chunkSize) {
    }
    
    SkChunkAlloc fStorage;
};

SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src);
SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) {
    
    if (SkBitmap::kIndex8_Config != src.config() &&
            SkBitmap::kA8_Config != src.config()) {
        return NULL;
    }
    
    size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width());

    // estimate the rle size based on the original size
    size_t size = src.getSize() >> 3;
    if (size < maxPacked) {
        size = maxPacked;
    }

    ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels,
                                           (src.width(), src.height(), size));

    uint8_t* dstRow = NULL;
    size_t free = 0;
    size_t totalPacked = 0;

    for (int y = 0; y < src.height(); y++) {
        const uint8_t* srcRow = src.getAddr8(0, y);
        
        if (free < maxPacked) {
            dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size);
            free = size;
        }
        size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow);
        SkASSERT(packedSize <= free);
        rlePixels->setPackedAtY(y, dstRow);
        
        dstRow += packedSize;
        free -= packedSize;
        
        totalPacked += packedSize;
    }
    
//#ifdef SK_DEBUG
#if 0
    // test
    uint8_t* buffer = new uint8_t[src.width()];
    for (int y = 0; y < src.height(); y++) {
        const uint8_t* srcRow = src.getAddr8(0, y);
        SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y));
        int n = memcmp(buffer, srcRow, src.width());
        if (n) {
            SkDebugf("----- memcmp returned %d on line %d\n", n, y);
        }
        SkASSERT(n == 0);
    }
    delete[] buffer;

    size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked;
    
    SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n",
             src.width(), src.height(), src.getSize(),
             src.height() * sizeof(uint8_t*), totalPacked,
             (float)totalAlloc / src.getSize());

#endif

    // transfer ownership of rlePixels to our pixelref
    return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable()));
}
        
