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

#include "SkCodecPriv.h"
#include "SkColorPriv.h"
#include "SkMaskSwizzler.h"
#include "SkScaledCodec.h"

static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_opaque(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint16_t* srcPtr = ((uint16_t*) srcRow) + startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    for (int i = 0; i < width; i++) {
        uint16_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
        srcPtr += sampleX;
    }
    return SkSwizzler::kOpaque_ResultAlpha;
}

static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_unpremul(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint16_t* srcPtr = ((uint16_t*) srcRow) + startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    INIT_RESULT_ALPHA;
    for (int i = 0; i < width; i++) {
        uint16_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        uint8_t alpha = masks->getAlpha(p);
        UPDATE_RESULT_ALPHA(alpha);
        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
        srcPtr += sampleX;
    }
    return COMPUTE_RESULT_ALPHA;
}

static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_premul(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint16_t* srcPtr = ((uint16_t*) srcRow) + startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    INIT_RESULT_ALPHA;
    for (int i = 0; i < width; i++) {
        uint16_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        uint8_t alpha = masks->getAlpha(p);
        UPDATE_RESULT_ALPHA(alpha);
        dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue);
        srcPtr += sampleX;
    }
    return COMPUTE_RESULT_ALPHA;
}

// TODO (msarett): We have promoted a two byte per pixel image to 8888, only to
// convert it back to 565. Instead, we should swizzle to 565 directly.
static SkSwizzler::ResultAlpha swizzle_mask16_to_565(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint16_t* srcPtr = ((uint16_t*) srcRow) + startX;
    uint16_t* dstPtr = (uint16_t*) dstRow;
    for (int i = 0; i < width; i++) {
        uint16_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        dstPtr[i] = SkPack888ToRGB16(red, green, blue);
        srcPtr += sampleX;
    }
    return SkSwizzler::kOpaque_ResultAlpha;
}

static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_opaque(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    srcRow += 3 * startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16;
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
        srcRow += 3 * sampleX;
    }
    return SkSwizzler::kOpaque_ResultAlpha;
}

static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_unpremul(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    srcRow += 3 * startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    INIT_RESULT_ALPHA;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16;
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        uint8_t alpha = masks->getAlpha(p);
        UPDATE_RESULT_ALPHA(alpha);
        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
        srcRow += 3 * sampleX;
    }
    return COMPUTE_RESULT_ALPHA;
}

static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_premul(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    srcRow += 3 * startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    INIT_RESULT_ALPHA;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16;
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        uint8_t alpha = masks->getAlpha(p);
        UPDATE_RESULT_ALPHA(alpha);
        dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue);
        srcRow += 3 * sampleX;
    }
    return COMPUTE_RESULT_ALPHA;
}

static SkSwizzler::ResultAlpha swizzle_mask24_to_565(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    srcRow += 3 * startX;
    uint16_t* dstPtr = (uint16_t*) dstRow;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16;
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        dstPtr[i] = SkPack888ToRGB16(red, green, blue);
        srcRow += 3 * sampleX;
    }
    return SkSwizzler::kOpaque_ResultAlpha;
}

static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_opaque(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint32_t* srcPtr = ((uint32_t*) srcRow) + startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
        srcPtr += sampleX;
    }
    return SkSwizzler::kOpaque_ResultAlpha;
}

static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_unpremul(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint32_t* srcPtr = ((uint32_t*) srcRow) + startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    INIT_RESULT_ALPHA;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        uint8_t alpha = masks->getAlpha(p);
        UPDATE_RESULT_ALPHA(alpha);
        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
        srcPtr += sampleX;
    }
    return COMPUTE_RESULT_ALPHA;
}

static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_premul(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {

    // Use the masks to decode to the destination
    uint32_t* srcPtr = ((uint32_t*) srcRow) + startX;
    SkPMColor* dstPtr = (SkPMColor*) dstRow;
    INIT_RESULT_ALPHA;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        uint8_t alpha = masks->getAlpha(p);
        UPDATE_RESULT_ALPHA(alpha);
        dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue);
        srcPtr += sampleX;
    }
    return COMPUTE_RESULT_ALPHA;
}

static SkSwizzler::ResultAlpha swizzle_mask32_to_565(
        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks,
        uint32_t startX, uint32_t sampleX) {
    // Use the masks to decode to the destination
    uint32_t* srcPtr = ((uint32_t*) srcRow) + startX;
    uint16_t* dstPtr = (uint16_t*) dstRow;
    for (int i = 0; i < width; i++) {
        uint32_t p = srcPtr[0];
        uint8_t red = masks->getRed(p);
        uint8_t green = masks->getGreen(p);
        uint8_t blue = masks->getBlue(p);
        dstPtr[i] = SkPack888ToRGB16(red, green, blue);
        srcPtr += sampleX;
    }
    return SkSwizzler::kOpaque_ResultAlpha;
}

/*
 *
 * Create a new mask swizzler
 *
 */
SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(
        const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, SkMasks* masks,
        uint32_t bitsPerPixel) {

    // Choose the appropriate row procedure
    RowProc proc = nullptr;
    switch (bitsPerPixel) {
        case 16:
            switch (dstInfo.colorType()) {
                case kN32_SkColorType:
                    switch (dstInfo.alphaType()) {
                        case kUnpremul_SkAlphaType:
                            proc = &swizzle_mask16_to_n32_unpremul;
                            break;
                        case kPremul_SkAlphaType:
                            proc = &swizzle_mask16_to_n32_premul;
                            break;
                        case kOpaque_SkAlphaType:
                            proc = &swizzle_mask16_to_n32_opaque;
                            break;
                        default:
                            break;
                    }
                    break;
                case kRGB_565_SkColorType:
                    switch (dstInfo.alphaType()) {
                        case kOpaque_SkAlphaType:
                            proc = &swizzle_mask16_to_565;
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            break;
        case 24:
            switch (dstInfo.colorType()) {
                case kN32_SkColorType:
                    switch (dstInfo.alphaType()) {
                        case kUnpremul_SkAlphaType:
                            proc = &swizzle_mask24_to_n32_unpremul;
                            break;
                        case kPremul_SkAlphaType:
                            proc = &swizzle_mask24_to_n32_premul;
                            break;
                        case kOpaque_SkAlphaType:
                            proc = &swizzle_mask24_to_n32_opaque;
                            break;
                        default:
                            break;
                    }
                    break;
                case kRGB_565_SkColorType:
                    switch (dstInfo.alphaType()) {
                        case kOpaque_SkAlphaType:
                            proc = &swizzle_mask24_to_565;
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            break;
        case 32:
            switch (dstInfo.colorType()) {
                case kN32_SkColorType:
                    switch (dstInfo.alphaType()) {
                        case kUnpremul_SkAlphaType:
                            proc = &swizzle_mask32_to_n32_unpremul;
                            break;
                        case kPremul_SkAlphaType:
                            proc = &swizzle_mask32_to_n32_premul;
                            break;
                        case kOpaque_SkAlphaType:
                            proc = &swizzle_mask32_to_n32_opaque;
                            break;
                        default:
                            break;
                    }
                    break;
                case kRGB_565_SkColorType:
                    switch (dstInfo.alphaType()) {
                        case kOpaque_SkAlphaType:
                            proc = &swizzle_mask32_to_565;
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            break;
        default:
            SkASSERT(false);
            return nullptr;
    }

    return new SkMaskSwizzler(dstInfo.width(), masks, proc);
}

/*
 *
 * Constructor for mask swizzler
 *
 */
SkMaskSwizzler::SkMaskSwizzler(int width, SkMasks* masks, RowProc proc)
    : fMasks(masks)
    , fRowProc(proc)
    , fSrcWidth(width)
    , fDstWidth(width)
    , fSampleX(1)
    , fX0(0)
{}

int SkMaskSwizzler::onSetSampleX(int sampleX) {
    // FIXME: Share this function with SkSwizzler?
    SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be
                           // way to report failure?
    fSampleX = sampleX;
    fX0 = get_start_coord(sampleX);
    fDstWidth = get_scaled_dimension(fSrcWidth, sampleX);

    // check that fX0 is less than original width
    SkASSERT(fX0 >= 0 && fX0 < fSrcWidth);
    return fDstWidth;
}

/*
 *
 * Swizzle the specified row
 *
 */
SkSwizzler::ResultAlpha SkMaskSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) {
    SkASSERT(nullptr != dst && nullptr != src);
    return fRowProc(dst, src, fDstWidth, fMasks, fX0, fSampleX);
}
