/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkImagePriv.h"
#include "SkImage_Base.h"

static SkImage_Base* as_IB(SkImage* image) {
    return static_cast<SkImage_Base*>(image);
}

static const SkImage_Base* as_IB(const SkImage* image) {
    return static_cast<const SkImage_Base*>(image);
}

uint32_t SkImage::NextUniqueID() {
    static int32_t gUniqueID;

    // never return 0;
    uint32_t id;
    do {
        id = sk_atomic_inc(&gUniqueID) + 1;
    } while (0 == id);
    return id;
}

void SkImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
    as_IB(this)->onDraw(canvas, x, y, paint);
}

void SkImage::draw(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
                   const SkPaint* paint) const {
    as_IB(this)->onDrawRectToRect(canvas, src, dst, paint);
}

const void* SkImage::peekPixels(SkImageInfo* info, size_t* rowBytes) const {
    SkImageInfo infoStorage;
    size_t rowBytesStorage;
    if (NULL == info) {
        info = &infoStorage;
    }
    if (NULL == rowBytes) {
        rowBytes = &rowBytesStorage;
    }
    return as_IB(this)->onPeekPixels(info, rowBytes);
}

bool SkImage::readPixels(SkBitmap* bitmap, const SkIRect* subset) const {
    if (NULL == bitmap) {
        return false;
    }

    SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());

    // trim against the bitmap, if its already been allocated
    if (bitmap->pixelRef()) {
        bounds.fRight = SkMin32(bounds.fRight, bitmap->width());
        bounds.fBottom = SkMin32(bounds.fBottom, bitmap->height());
        if (bounds.isEmpty()) {
            return false;
        }
    }

    if (subset && !bounds.intersect(*subset)) {
        // perhaps we could return true + empty-bitmap?
        return false;
    }
    return as_IB(this)->onReadPixels(bitmap, bounds);
}

GrTexture* SkImage::getTexture() {
    return as_IB(this)->onGetTexture();
}

SkShader* SkImage::newShader(SkShader::TileMode tileX,
                             SkShader::TileMode tileY,
                             const SkMatrix* localMatrix) const {
    return as_IB(this)->onNewShader(tileX, tileY, localMatrix);
}

SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const {
    SkBitmap bm;
    if (as_IB(this)->getROPixels(&bm)) {
        return SkImageEncoder::EncodeData(bm, type, quality);
    }
    return NULL;
}

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

static bool raster_canvas_supports(const SkImageInfo& info) {
    switch (info.colorType()) {
        case kN32_SkColorType:
            return kUnpremul_SkAlphaType != info.alphaType();
        case kRGB_565_SkColorType:
            return true;
        case kAlpha_8_SkColorType:
            return true;
        default:
            break;
    }
    return false;
}

bool SkImage_Base::onReadPixels(SkBitmap* bitmap, const SkIRect& subset) const {
    if (bitmap->pixelRef()) {
        const SkImageInfo info = bitmap->info();
        if (kUnknown_SkColorType == info.colorType()) {
            return false;
        }
        if (!raster_canvas_supports(info)) {
            return false;
        }
    } else {
        SkBitmap tmp;
        if (!tmp.tryAllocN32Pixels(subset.width(), subset.height())) {
            return false;
        }
        *bitmap = tmp;
    }

    SkRect srcR, dstR;
    srcR.set(subset);
    dstR = srcR;
    dstR.offset(-dstR.left(), -dstR.top());

    SkCanvas canvas(*bitmap);

    SkPaint paint;
    paint.setXfermodeMode(SkXfermode::kClear_Mode);
    canvas.drawRect(dstR, paint);

    const_cast<SkImage_Base*>(this)->onDrawRectToRect(&canvas, &srcR, dstR, NULL);
    return true;
}