
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#ifndef SkBitmapSampler_DEFINED
#define SkBitmapSampler_DEFINED

#include "SkBitmap.h"
#include "SkPaint.h"
#include "SkShader.h"

typedef int (*SkTileModeProc)(int value, unsigned max);

class SkBitmapSampler {
public:
    SkBitmapSampler(const SkBitmap&, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy);
    virtual ~SkBitmapSampler() {}

    const SkBitmap&     getBitmap() const { return fBitmap; }
    bool                getFilterBitmap() const { return fFilterBitmap; }
    SkShader::TileMode  getTileModeX() const { return fTileModeX; }
    SkShader::TileMode  getTileModeY() const { return fTileModeY; }

    /** Given a pixel center at [x,y], return the color sample
    */
    virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0;

    virtual void setPaint(const SkPaint& paint);

    // This is the factory for finding an optimal subclass
    static SkBitmapSampler* Create(const SkBitmap&, bool filter,
                                   SkShader::TileMode tmx, SkShader::TileMode tmy);

protected:
    const SkBitmap&     fBitmap;
    uint16_t            fMaxX, fMaxY;
    bool                fFilterBitmap;
    SkShader::TileMode  fTileModeX;
    SkShader::TileMode  fTileModeY;
    SkTileModeProc      fTileProcX;
    SkTileModeProc      fTileProcY;

    // illegal
    SkBitmapSampler& operator=(const SkBitmapSampler&);
};

static inline int fixed_clamp(SkFixed x)
{
#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
    if (x >> 16)
        x = 0xFFFF;
    if (x < 0)
        x = 0;
#else
    if (x >> 16)
    {
        if (x < 0)
            x = 0;
        else
            x = 0xFFFF;
    }
#endif
    return x;
}

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

static inline int fixed_repeat(SkFixed x)
{
    return x & 0xFFFF;
}

static inline int fixed_mirror(SkFixed x)
{
    SkFixed s = x << 15 >> 31;
    // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
    return (x ^ s) & 0xFFFF;
}

static inline bool is_pow2(int count)
{
    SkASSERT(count > 0);
    return (count & (count - 1)) == 0;
}

static inline int do_clamp(int index, unsigned max)
{
    SkASSERT((int)max >= 0);

#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
    if (index > (int)max)
        index = max;
    if (index < 0)
        index = 0;
#else
    if ((unsigned)index > max)
    {
        if (index < 0)
            index = 0;
        else
            index = max;
    }
#endif
    return index;
}

static inline int do_repeat_mod(int index, unsigned max)
{
    SkASSERT((int)max >= 0);

    if ((unsigned)index > max)
    {
        if (index < 0)
            index = max - (~index % (max + 1));
        else
            index = index % (max + 1);
    }
    return index;
}

static inline int do_repeat_pow2(int index, unsigned max)
{
    SkASSERT((int)max >= 0 && is_pow2(max + 1));

    return index & max;
}

static inline int do_mirror_mod(int index, unsigned max)
{
    SkASSERT((int)max >= 0);

    // have to handle negatives so that
    // -1 -> 0, -2 -> 1, -3 -> 2, etc.
    // so we can't just cal abs
    index ^= index >> 31;

    if ((unsigned)index > max)
    {
        int mod = (max + 1) << 1;
        index = index % mod;
        if ((unsigned)index > max)
            index = mod - index - 1;
    }
    return index;
}

static inline int do_mirror_pow2(int index, unsigned max)
{
    SkASSERT((int)max >= 0 && is_pow2(max + 1));

    int s = (index & (max + 1)) - 1;
    s = ~(s >> 31);
    // at this stage, s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
    return (index ^ s) & max;
}

#endif
