#include "SkBlitRow.h"
#include "SkColorPriv.h"
#include "SkDither.h"

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

static void S32_D4444_Opaque(uint16_t* SK_RESTRICT dst,
                             const SkPMColor* SK_RESTRICT src, int count,
                             U8CPU alpha, int /*x*/, int /*y*/) {
    SkASSERT(255 == alpha);

    if (count > 0) {
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
            SkASSERT(SkGetPackedA32(c) == 255);
            *dst++ = SkPixel32ToPixel4444(c);
        } while (--count != 0);
    }
}

static void S32_D4444_Blend(uint16_t* SK_RESTRICT dst,
                            const SkPMColor* SK_RESTRICT src, int count,
                            U8CPU alpha, int /*x*/, int /*y*/) {
    SkASSERT(255 > alpha);

    if (count > 0) {
        unsigned scale16 = SkAlpha255To256(alpha) >> 4;
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
            SkASSERT(SkGetPackedA32(c) == 255);

            uint32_t src_expand = SkExpand32_4444(c);
            uint32_t dst_expand = SkExpand_4444(*dst);
            dst_expand += (src_expand - dst_expand) * scale16 >> 4;
            *dst++ = SkCompact_4444(dst_expand);
        } while (--count != 0);
    }
}

static void S32A_D4444_Opaque(uint16_t* SK_RESTRICT dst,
                              const SkPMColor* SK_RESTRICT src, int count,
                              U8CPU alpha, int /*x*/, int /*y*/) {
    SkASSERT(255 == alpha);

    if (count > 0) {
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
//            if (__builtin_expect(c!=0, 1))
            if (c)
            {
                unsigned scale16 = SkAlpha255To256(255 - SkGetPackedA32(c)) >> 4;
                uint32_t src_expand = SkExpand_8888(c);
                uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
                *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
            }
            dst += 1;
        } while (--count != 0);
    }
}

static void S32A_D4444_Blend(uint16_t* SK_RESTRICT dst,
                             const SkPMColor* SK_RESTRICT src, int count,
                             U8CPU alpha, int /*x*/, int /*y*/) {
    SkASSERT(255 > alpha);
    
    if (count > 0) {
        int src_scale = SkAlpha255To256(alpha) >> 4;
        do {
            SkPMColor sc = *src++;
            SkPMColorAssert(sc);

            if (sc) {
                unsigned dst_scale = 16 - (SkGetPackedA32(sc) * src_scale >> 8);
                uint32_t src_expand = SkExpand32_4444(sc) * src_scale;
                uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
                *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
            }
            dst += 1;
        } while (--count != 0);
    }
}

/////////////////////////////////////////////////////////////////////////////
                               
static void S32_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
                                    const SkPMColor* SK_RESTRICT src,
                                    int count, U8CPU alpha, int x, int y) {
    SkASSERT(255 == alpha);
    
    if (count > 0) {
        DITHER_4444_SCAN(y);
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
            SkASSERT(SkGetPackedA32(c) == 255);

            unsigned dither = DITHER_VALUE(x);
            *dst++ = SkDitherARGB32To4444(c, dither);
            DITHER_INC_X(x);
        } while (--count != 0);
    }
}

static void S32_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst,
                                   const SkPMColor* SK_RESTRICT src,
                                   int count, U8CPU alpha, int x, int y) {
    SkASSERT(255 > alpha);
    
    if (count > 0) {
        int scale16 = SkAlpha255To256(alpha) >> 4;
        DITHER_4444_SCAN(y);
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
            SkASSERT(SkGetPackedA32(c) == 255);

            uint32_t src_expand = SkExpand32_4444(c) * scale16;
            uint32_t dst_expand = SkExpand_4444(*dst) * (16 - scale16);
            
            c = SkCompact_8888(src_expand + dst_expand); // convert back to SkPMColor
            *dst++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
            DITHER_INC_X(x);
        } while (--count != 0);
    }
}

static void S32A_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
                                     const SkPMColor* SK_RESTRICT src,
                                     int count, U8CPU alpha, int x, int y) {
    SkASSERT(255 == alpha);
    
    if (count > 0) {
        DITHER_4444_SCAN(y);
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
            if (c) {
                unsigned a = SkGetPackedA32(c);                
                int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
                
                unsigned scale16 = SkAlpha255To256(255 - a) >> 4;
                uint32_t src_expand = SkExpand_8888(c);
                uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
                // convert back to SkPMColor
                c = SkCompact_8888(src_expand + dst_expand);
                *dst = SkDitherARGB32To4444(c, d);
            }
            dst += 1;
            DITHER_INC_X(x);
        } while (--count != 0);
    }
}

// need DitherExpand888To4444(expand, dither)

static void S32A_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst,
                                    const SkPMColor* SK_RESTRICT src,
                                    int count, U8CPU alpha, int x, int y) {
    SkASSERT(255 > alpha);
    
    if (count > 0) {
        int src_scale = SkAlpha255To256(alpha) >> 4;
        DITHER_4444_SCAN(y);
        do {
            SkPMColor c = *src++;
            SkPMColorAssert(c);
            if (c) {
                unsigned a = SkAlpha255To256(SkGetPackedA32(c));
                int d = SkAlphaMul(DITHER_VALUE(x), a);
                
                unsigned dst_scale = 16 - SkAlphaMul(src_scale, a);
                uint32_t src_expand = SkExpand32_4444(c) * src_scale;
                uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
                // convert back to SkPMColor
                c = SkCompact_8888(src_expand + dst_expand);
                *dst = SkDitherARGB32To4444(c, d);
            }
            dst += 1;
            DITHER_INC_X(x);
        } while (--count != 0);
    }
}

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

static const SkBlitRow::Proc gProcs4444[] = {
    // no dither
    S32_D4444_Opaque,
    S32_D4444_Blend,
    
    S32A_D4444_Opaque,
    S32A_D4444_Blend,
    
    // dither
    S32_D4444_Opaque_Dither,
    S32_D4444_Blend_Dither,
    
    S32A_D4444_Opaque_Dither,
    S32A_D4444_Blend_Dither
};
    
SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags)
{
    SkASSERT(flags < SK_ARRAY_COUNT(gProcs4444));
    
    return gProcs4444[flags];
}