code:
/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SkBitmap_DEFINED
#define SkBitmap_DEFINED

#include "Sk64.h"
#include "SkColor.h"
#include "SkPoint.h"
#include "SkRefCnt.h"

struct SkIRect;
class SkColorTable;
class SkPaint;
class SkPixelRef;
class SkRegion;
class SkFlattenableReadBuffer;
class SkFlattenableWriteBuffer;

// This is an opaque class, not interpreted by skia
class SkGpuTexture;

/** \class SkBitmap

    The SkBitmap class specifies a raster bitmap. A bitmap has an integer width
    and height, and a format (config), and a pointer to the actual pixels.
    Bitmaps can be drawn into a SkCanvas, but they are also used to specify the
    target of a SkCanvas' drawing operations.
    A const SkBitmap exposes getAddr(), which lets a caller write its pixels;
    the constness is considered to apply to the bitmap's configuration, not
    its contents.
*/
class SK_API SkBitmap {
public:
    class Allocator;

    enum Config {
        kNo_Config,         //!< bitmap has not been configured
        /**
         *  1-bit per pixel, (0 is transparent, 1 is opaque)
         *  Valid as a destination (target of a canvas), but not valid as a src.
         *  i.e. you can draw into a 1-bit bitmap, but you cannot draw from one.
         */
        kA1_Config,
        kA8_Config,         //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque)
        kIndex8_Config,     //!< 8-bits per pixel, using SkColorTable to specify the colors
        kRGB_565_Config,    //!< 16-bits per pixel, (see SkColorPriv.h for packing)
        kARGB_4444_Config,  //!< 16-bits per pixel, (see SkColorPriv.h for packing)
        kARGB_8888_Config,  //!< 32-bits per pixel, (see SkColorPriv.h for packing)
        /**
         *  Custom compressed format, not supported on all platforms.
         *  Cannot be used as a destination (target of a canvas).
         *  i.e. you may be able to draw from one, but you cannot draw into one.
         */
        kRLE_Index8_Config,

        kConfigCount
    };

    /** Default construct creates a bitmap with zero width and height, and no pixels.
        Its config is set to kNo_Config.
    */
    SkBitmap();
    /** Constructor initializes the new bitmap by copying the src bitmap. All fields are copied,
        but ownership of the pixels remains with the src bitmap.
    */
    SkBitmap(const SkBitmap& src);
    /** Decrements our (shared) pixel ownership if needed.
    */
    ~SkBitmap();

    /** Copies the src bitmap into this bitmap. Ownership of the src bitmap's pixels remains
        with the src bitmap.
    */
    SkBitmap& operator=(const SkBitmap& src);
    /** Swap the fields of the two bitmaps. This routine is guaranteed to never fail or throw.
    */
    //  This method is not exported to java.
    void swap(SkBitmap& other);

    /** Return true iff the bitmap has empty dimensions.
    */
    bool empty() const { return 0 == fWidth || 0 == fHeight; }

    /** Return true iff the bitmap has no pixels nor a pixelref. Note: this can
        return true even if the dimensions of the bitmap are > 0 (see empty()).
    */
    bool isNull() const { return NULL == fPixels && NULL == fPixelRef; }

    /** Return the config for the bitmap.
    */
    Config  config() const { return (Config)fConfig; }
    /** DEPRECATED, use config()
    */
    Config  getConfig() const { return this->config(); }
    /** Return the bitmap's width, in pixels.
    */
    int width() const { return fWidth; }
    /** Return the bitmap's height, in pixels.
    */
    int height() const { return fHeight; }
    /** Return the number of bytes between subsequent rows of the bitmap.
    */
    int rowBytes() const { return fRowBytes; }

    /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for
        2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0
        for configs that are not at least 1-byte per pixel (e.g. kA1_Config
        or kNo_Config)
    */
    int shiftPerPixel() const { return fBytesPerPixel >> 1; }

    /** Return the number of bytes per pixel based on the config. If the config
        does not have at least 1 byte per (e.g. kA1_Config) then 0 is returned.
    */
    int bytesPerPixel() const { return fBytesPerPixel; }

    /** Return the rowbytes expressed as a number of pixels (like width and
        height). Note, for 1-byte per pixel configs like kA8_Config, this will
        return the same as rowBytes(). Is undefined for configs that are less
        than 1-byte per pixel (e.g. kA1_Config)
    */
    int rowBytesAsPixels() const { return fRowBytes >> (fBytesPerPixel >> 1); }

    /** Return the address of the pixels for this SkBitmap.
    */
    void* getPixels() const { return fPixels; }

    /** Return the byte size of the pixels, based on the height and rowBytes.
        Note this truncates the result to 32bits. Call getSize64() to detect
        if the real size exceeds 32bits.
    */
    size_t getSize() const { return fHeight * fRowBytes; }

    /** Return the number of bytes from the pointer returned by getPixels()
        to the end of the allocated space in the buffer. Required in
        cases where extractBitmap has been called.
    */
    size_t getSafeSize() const ;

    /** Return the byte size of the pixels, based on the height and rowBytes.
        This routine is slightly slower than getSize(), but does not truncate
        the answer to 32bits.
    */
    Sk64 getSize64() const {
        Sk64 size;
        size.setMul(fHeight, fRowBytes);
        return size;
    }

    /** Same as getSafeSize(), but does not truncate the answer to 32bits.
    */
    Sk64 getSafeSize64() const ;

    /** Returns true if the bitmap is opaque (has no translucent/transparent pixels).
    */
    bool isOpaque() const;
    /** Specify if this bitmap's pixels are all opaque or not. Is only meaningful for configs
        that support per-pixel alpha (RGB32, A1, A8).
    */
    void setIsOpaque(bool);

    /** Reset the bitmap to its initial state (see default constructor). If we are a (shared)
        owner of the pixels, that ownership is decremented.
    */
    void reset();

    /** Given a config and a width, this computes the optimal rowBytes value. This is called automatically
        if you pass 0 for rowBytes to setConfig().
    */
    static int ComputeRowBytes(Config c, int width);

    /** Return the bytes-per-pixel for the specified config. If the config is
        not at least 1-byte per pixel, return 0, including for kNo_Config.
    */
    static int ComputeBytesPerPixel(Config c);

    /** Return the shift-per-pixel for the specified config. If the config is
     not at least 1-byte per pixel, return 0, including for kNo_Config.
     */
    static int ComputeShiftPerPixel(Config c) {
        return ComputeBytesPerPixel(c) >> 1;
    }

    static Sk64 ComputeSize64(Config, int width, int height);
    static size_t ComputeSize(Config, int width, int height);

    /** Set the bitmap's config and dimensions. If rowBytes is 0, then
        ComputeRowBytes() is called to compute the optimal value. This resets
        any pixel/colortable ownership, just like reset().
    */
    void setConfig(Config, int width, int height, int rowBytes = 0);
    /** Use this to assign a new pixel address for an existing bitmap. This
        will automatically release any pixelref previously installed. Only call
        this if you are handling ownership/lifetime of the pixel memory.

        If the bitmap retains a reference to the colortable (assuming it is
        not null) it will take care of incrementing the reference count.

        @param pixels   Address for the pixels, managed by the caller.
        @param ctable   ColorTable (or null) that matches the specified pixels
    */
    void setPixels(void* p, SkColorTable* ctable = NULL);

    /** Copies the bitmap's pixels to the location pointed at by dst and returns
        true if possible, returns false otherwise.

        In the event that the bitmap's stride is equal to dstRowBytes, and if
        it is greater than strictly required by the bitmap's current config
        (this may happen if the bitmap is an extracted subset of another), then
        this function will copy bytes past the eand of each row, excluding the
        last row. No copies are made outside of the declared size of dst,
        however.

        Always returns false for RLE formats.

        @param dst      Location of destination buffer.
        @param dstSize  Size of destination buffer. Must be large enough to hold
                        pixels using indicated stride.
        @param dstRowBytes  Width of each line in the buffer. If -1, uses
                            bitmap's internal stride.
    */
    bool copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes = -1)
         const;

    /** Copies the pixels at location src to the bitmap's pixel buffer.
        Returns true if copy if possible (bitmap buffer is large enough),
        false otherwise.

        Like copyPixelsTo, this function may write values beyond the end of
        each row, although never outside the defined buffer.

        Always returns false for RLE formats.

        @param src      Location of the source buffer.
        @param srcSize  Height of source buffer in pixels.
        @param srcRowBytes  Width of each line in the buffer. If -1, uses i
                            bitmap's internal stride.
    */
    bool copyPixelsFrom(const void* const src, size_t srcSize,
                        int srcRowBytes = -1);

    /** Use the standard HeapAllocator to create the pixelref that manages the
        pixel memory. It will be sized based on the current width/height/config.
        If this is called multiple times, a new pixelref object will be created
        each time.

        If the bitmap retains a reference to the colortable (assuming it is
        not null) it will take care of incrementing the reference count.

        @param ctable   ColorTable (or null) to use with the pixels that will
                        be allocated. Only used if config == Index8_Config
        @return true if the allocation succeeds. If not the pixelref field of
                     the bitmap will be unchanged.
    */
    bool allocPixels(SkColorTable* ctable = NULL) {
        return this->allocPixels(NULL, ctable);
    }

    /** Use the specified Allocator to create the pixelref that manages the
        pixel memory. It will be sized based on the current width/height/config.
        If this is called multiple times, a new pixelref object will be created
        each time.

        If the bitmap retains a reference to the colortable (assuming it is
        not null) it will take care of incrementing the reference count.

        @param allocator The Allocator to use to create a pixelref that can
                         manage the pixel memory for the current
                         width/height/config. If allocator is NULL, the standard
                         HeapAllocator will be used.
        @param ctable   ColorTable (or null) to use with the pixels that will
                        be allocated. Only used if config == Index8_Config.
                        If it is non-null and the config is not Index8, it will
                        be ignored.
        @return true if the allocation succeeds. If not the pixelref field of
                     the bitmap will be unchanged.
    */
    bool allocPixels(Allocator* allocator, SkColorTable* ctable);

    /** Return the current pixelref object, if any
    */
    SkPixelRef* pixelRef() const { return fPixelRef; }
    /** Return the offset into the pixelref, if any. Will return 0 if there is
        no pixelref installed.
    */
    size_t pixelRefOffset() const { return fPixelRefOffset; }
    /** Assign a pixelref and optional offset. Pixelrefs are reference counted,
        so the existing one (if any) will be unref'd and the new one will be
        ref'd.
    */
    SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset = 0);

    /** Call this to ensure that the bitmap points to the current pixel address
        in the pixelref. Balance it with a call to unlockPixels(). These calls
        are harmless if there is no pixelref.
    */
    void lockPixels() const;
    /** When you are finished access the pixel memory, call this to balance a
        previous call to lockPixels(). This allows pixelrefs that implement
        cached/deferred image decoding to know when there are active clients of
        a given image.
    */
    void unlockPixels() const;

    /** Call this to be sure that the bitmap is valid enough to be drawn (i.e.
        it has non-null pixels, and if required by its config, it has a
        non-null colortable. Returns true if all of the above are met.
    */
    bool readyToDraw() const {
        return this->getPixels() != NULL &&
               ((this->config() != kIndex8_Config &&
                 this->config() != kRLE_Index8_Config) ||
                       fColorTable != NULL);
    }

    /** Returns the pixelRef's texture, or NULL
     */
    SkGpuTexture* getTexture() const;

    /** Return the bitmap's colortable (if any). Does not affect the colortable's
        reference count.
    */
    SkColorTable* getColorTable() const { return fColorTable; }

    /** Returns a non-zero, unique value corresponding to the pixels in our
        pixelref (or raw pixels set via setPixels). Each time the pixels are
        changed (and notifyPixelsChanged is called), a different generation ID
        will be returned.
    */
    uint32_t getGenerationID() const;

    /** Call this if you have changed the contents of the pixels. This will in-
        turn cause a different generation ID value to be returned from
        getGenerationID().
    */
    void notifyPixelsChanged() const;

    /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format
        for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is ignored.
        If the config is kA8_Config, then the r,g,b parameters are ignored.
    */
    void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const;
    /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format
        for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is presumed
        to be 0xFF. If the config is kA8_Config, then the r,g,b parameters are ignored and the
        pixels are all set to 0xFF.
    */
    void eraseRGB(U8CPU r, U8CPU g, U8CPU b) const {
        this->eraseARGB(0xFF, r, g, b);
    }
    /** Initialize the bitmap's pixels with the specified color, automatically converting into the correct format
        for the bitmap's config. If the config is kRGB_565_Config, then the color's alpha value is presumed
        to be 0xFF. If the config is kA8_Config, then only the color's alpha value is used.
    */
    void eraseColor(SkColor c) const {
        this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c),
                        SkColorGetB(c));
    }

    /** Scroll (a subset of) the contents of this bitmap by dx/dy. If there are
        no pixels allocated (i.e. getPixels() returns null) the method will
        still update the inval region (if present).

        @param subset The subset of the bitmap to scroll/move. To scroll the
                      entire contents, specify [0, 0, width, height] or just
                      pass null.
        @param dx The amount to scroll in X
        @param dy The amount to scroll in Y
        @param inval Optional (may be null). Returns the area of the bitmap that
                     was scrolled away. E.g. if dx = dy = 0, then inval would
                     be set to empty. If dx >= width or dy >= height, then
                     inval would be set to the entire bounds of the bitmap.
        @return true if the scroll was doable. Will return false if the bitmap
                     uses an unsupported config for scrolling (only kA8,
                     kIndex8, kRGB_565, kARGB_4444, kARGB_8888 are supported).
                     If no pixels are present (i.e. getPixels() returns false)
                     inval will still be updated, and true will be returned.
    */
    bool scrollRect(const SkIRect* subset, int dx, int dy,
                    SkRegion* inval = NULL) const;

    /**
     *  Return the SkColor of the specified pixel.  In most cases this will
     *  require un-premultiplying the color.  Alpha only configs (A1 and A8)
     *  return black with the appropriate alpha set.  The value is undefined
     *  for kNone_Config or if x or y are out of bounds, or if the bitmap
     *  does not have any pixels (or has not be locked with lockPixels()).
     */
    SkColor getColor(int x, int y) const;

    /** Returns the address of the specified pixel. This performs a runtime
        check to know the size of the pixels, and will return the same answer
        as the corresponding size-specific method (e.g. getAddr16). Since the
        check happens at runtime, it is much slower than using a size-specific
        version. Unlike the size-specific methods, this routine also checks if
        getPixels() returns null, and returns that. The size-specific routines
        perform a debugging assert that getPixels() is not null, but they do
        not do any runtime checks.
    */
    void* getAddr(int x, int y) const;

    /** Returns the address of the pixel specified by x,y for 32bit pixels.
     *  In debug build, this asserts that the pixels are allocated and locked,
     *  and that the config is 32-bit, however none of these checks are performed
     *  in the release build.
     */
    inline uint32_t* getAddr32(int x, int y) const;

    /** Returns the address of the pixel specified by x,y for 16bit pixels.
     *  In debug build, this asserts that the pixels are allocated and locked,
     *  and that the config is 16-bit, however none of these checks are performed
     *  in the release build.
     */
    inline uint16_t* getAddr16(int x, int y) const;

    /** Returns the address of the pixel specified by x,y for 8bit pixels.
     *  In debug build, this asserts that the pixels are allocated and locked,
     *  and that the config is 8-bit, however none of these checks are performed
     *  in the release build.
     */
    inline uint8_t* getAddr8(int x, int y) const;

    /** Returns the address of the byte containing the pixel specified by x,y
     *  for 1bit pixels.
     *  In debug build, this asserts that the pixels are allocated and locked,
     *  and that the config is 1-bit, however none of these checks are performed
     *  in the release build.
     */
    inline uint8_t* getAddr1(int x, int y) const;

    /** Returns the color corresponding to the pixel specified by x,y for
     *  colortable based bitmaps.
     *  In debug build, this asserts that the pixels are allocated and locked,
     *  that the config is kIndex8, and that the colortable is allocated,
     *  however none of these checks are performed in the release build.
     */
    inline SkPMColor getIndex8Color(int x, int y) const;

    /** Set dst to be a setset of this bitmap. If possible, it will share the
        pixel memory, and just point into a subset of it. However, if the config
        does not support this, a local copy will be made and associated with
        the dst bitmap. If the subset rectangle, intersected with the bitmap's
        dimensions is empty, or if there is an unsupported config, false will be
        returned and dst will be untouched.
        @param dst  The bitmap that will be set to a subset of this bitmap
        @param subset The rectangle of pixels in this bitmap that dst will
                      reference.
        @return true if the subset copy was successfully made.
    */
    bool extractSubset(SkBitmap* dst, const SkIRect& subset) const;

    /** Makes a deep copy of this bitmap, respecting the requested config.
        Returns false if either there is an error (i.e. the src does not have
        pixels) or the request cannot be satisfied (e.g. the src has per-pixel
        alpha, and the requested config does not support alpha).
        @param dst The bitmap to be sized and allocated
        @param c The desired config for dst
        @param allocator Allocator used to allocate the pixelref for the dst
                         bitmap. If this is null, the standard HeapAllocator
                         will be used.
        @return true if the copy could be made.
    */
    bool copyTo(SkBitmap* dst, Config c, Allocator* allocator = NULL) const;

    /** Returns true if this bitmap can be deep copied into the requested config
        by calling copyTo().
     */
    bool canCopyTo(Config newConfig) const;

    bool hasMipMap() const;
    void buildMipMap(bool forceRebuild = false);
    void freeMipMap();

    /** Given scale factors sx, sy, determine the miplevel available in the
        bitmap, and return it (this is the amount to shift matrix iterators
        by). If dst is not null, it is set to the correct level.
    */
    int extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy);

    bool extractAlpha(SkBitmap* dst) const {
        return this->extractAlpha(dst, NULL, NULL, NULL);
    }

    bool extractAlpha(SkBitmap* dst, const SkPaint* paint,
                      SkIPoint* offset) const {
        return this->extractAlpha(dst, paint, NULL, offset);
    }

    /** Set dst to contain alpha layer of this bitmap. If destination bitmap
        fails to be initialized, e.g. because allocator can't allocate pixels
        for it, dst will not be modified and false will be returned.

        @param dst The bitmap to be filled with alpha layer
        @param paint The paint to draw with
        @param allocator Allocator used to allocate the pixelref for the dst
                         bitmap. If this is null, the standard HeapAllocator
                         will be used.
        @param offset If not null, it is set to top-left coordinate to position
                      the returned bitmap so that it visually lines up with the
                      original
    */
    bool extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator,
                      SkIPoint* offset) const;

    void flatten(SkFlattenableWriteBuffer&) const;
    void unflatten(SkFlattenableReadBuffer&);

    SkDEBUGCODE(void validate() const;)

    class Allocator : public SkRefCnt {
    public:
        /** Allocate the pixel memory for the bitmap, given its dimensions and
            config. Return true on success, where success means either setPixels
            or setPixelRef was called. The pixels need not be locked when this
            returns. If the config requires a colortable, it also must be
            installed via setColorTable. If false is returned, the bitmap and
            colortable should be left unchanged.
        */
        virtual bool allocPixelRef(SkBitmap*, SkColorTable*) = 0;
    };

    /** Subclass of Allocator that returns a pixelref that allocates its pixel
        memory from the heap. This is the default Allocator invoked by
        allocPixels().
    */
    class HeapAllocator : public Allocator {
    public:
        virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
    };

    class RLEPixels {
    public:
        RLEPixels(int width, int height);
        virtual ~RLEPixels();

        uint8_t* packedAtY(int y) const {
            SkASSERT((unsigned)y < (unsigned)fHeight);
            return fYPtrs[y];
        }

        // called by subclasses during creation
        void setPackedAtY(int y, uint8_t* addr) {
            SkASSERT((unsigned)y < (unsigned)fHeight);
            fYPtrs[y] = addr;
        }

    private:
        uint8_t** fYPtrs;
        int       fHeight;
    };

private:
    struct MipMap;
    mutable MipMap* fMipMap;

    mutable SkPixelRef* fPixelRef;
    mutable size_t      fPixelRefOffset;
    mutable int         fPixelLockCount;
    // either user-specified (in which case it is not treated as mutable)
    // or a cache of the returned value from fPixelRef->lockPixels()
    mutable void*       fPixels;
    mutable SkColorTable* fColorTable;    // only meaningful for kIndex8
    // When there is no pixel ref (setPixels was called) we still need a
    // gen id for SkDevice implementations that may cache a copy of the
    // pixels (e.g. as a gpu texture)
    mutable int         fRawPixelGenerationID;

    enum Flags {
        kImageIsOpaque_Flag  = 0x01
    };

    uint32_t    fRowBytes;
    uint32_t    fWidth;
    uint32_t    fHeight;
    uint8_t     fConfig;
    uint8_t     fFlags;
    uint8_t     fBytesPerPixel; // based on config

    /* Internal computations for safe size.
    */
    static Sk64 ComputeSafeSize64(Config config,
                                  uint32_t width,
                                  uint32_t height,
                                  uint32_t rowBytes);
    static size_t ComputeSafeSize(Config   config,
                                  uint32_t width,
                                  uint32_t height,
                                  uint32_t rowBytes);

    /*  Unreference any pixelrefs or colortables
    */
    void freePixels();
    void updatePixelsFromRef() const;

    static SkFixed ComputeMipLevel(SkFixed sx, SkFixed dy);
};

/** \class SkColorTable

    SkColorTable holds an array SkPMColors (premultiplied 32-bit colors) used by
    8-bit bitmaps, where the bitmap bytes are interpreted as indices into the colortable.
*/
class SkColorTable : public SkRefCnt {
public:
    /** Makes a deep copy of colors.
     */
    SkColorTable(const SkColorTable& src);
    /** Preallocates the colortable to have 'count' colors, which
     *  are initially set to 0.
    */
    explicit SkColorTable(int count);
    explicit SkColorTable(SkFlattenableReadBuffer&);
    SkColorTable(const SkPMColor colors[], int count);
    virtual ~SkColorTable();

    enum Flags {
        kColorsAreOpaque_Flag   = 0x01  //!< if set, all of the colors in the table are opaque (alpha==0xFF)
    };
    /** Returns the flag bits for the color table. These can be changed with setFlags().
    */
    unsigned getFlags() const { return fFlags; }
    /** Set the flags for the color table. See the Flags enum for possible values.
    */
    void    setFlags(unsigned flags);

    bool isOpaque() const { return (fFlags & kColorsAreOpaque_Flag) != 0; }
    void setIsOpaque(bool isOpaque);

    /** Returns the number of colors in the table.
    */
    int count() const { return fCount; }

    /** Returns the specified color from the table. In the debug build, this asserts that
        the index is in range (0 <= index < count).
    */
    SkPMColor operator[](int index) const {
        SkASSERT(fColors != NULL && (unsigned)index < fCount);
        return fColors[index];
    }

    /** Specify the number of colors in the color table. This does not initialize the colors
        to any value, just allocates memory for them. To initialize the values, either call
        setColors(array, count), or follow setCount(count) with a call to
        lockColors()/{set the values}/unlockColors(true).
    */
//    void    setColors(int count) { this->setColors(NULL, count); }
//    void    setColors(const SkPMColor[], int count);

    /** Return the array of colors for reading and/or writing. This must be
        balanced by a call to unlockColors(changed?), telling the colortable if
        the colors were changed during the lock.
    */
    SkPMColor* lockColors() {
        SkDEBUGCODE(fColorLockCount += 1;)
        return fColors;
    }
    /** Balancing call to lockColors(). If the colors have been changed, pass true.
    */
    void unlockColors(bool changed);

    /** Similar to lockColors(), lock16BitCache() returns the array of
        RGB16 colors that mirror the 32bit colors. However, this function
        will return null if kColorsAreOpaque_Flag is not set.
        Also, unlike lockColors(), the returned array here cannot be modified.
    */
    const uint16_t* lock16BitCache();
    /** Balancing call to lock16BitCache().
    */
    void unlock16BitCache() {
        SkASSERT(f16BitCacheLockCount > 0);
        SkDEBUGCODE(f16BitCacheLockCount -= 1);
    }

    void flatten(SkFlattenableWriteBuffer&) const;

private:
    SkPMColor*  fColors;
    uint16_t*   f16BitCache;
    uint16_t    fCount;
    uint8_t     fFlags;
    SkDEBUGCODE(int fColorLockCount;)
    SkDEBUGCODE(int f16BitCacheLockCount;)

    void inval16BitCache();
};

class SkAutoLockPixels {
public:
    SkAutoLockPixels(const SkBitmap& bitmap) : fBitmap(bitmap) {
        bitmap.lockPixels();
    }
    ~SkAutoLockPixels() {
        fBitmap.unlockPixels();
    }

private:
    const SkBitmap& fBitmap;
};

/** Helper class that performs the lock/unlockColors calls on a colortable.
    The destructor will call unlockColors(false) if it has a bitmap's colortable
*/
class SkAutoLockColors : public SkNoncopyable {
public:
    /** Initialize with no bitmap. Call lockColors(bitmap) to lock bitmap's
        colortable
     */
    SkAutoLockColors() : fCTable(NULL), fColors(NULL) {}
    /** Initialize with bitmap, locking its colortable if present
     */
    explicit SkAutoLockColors(const SkBitmap& bm) {
        fCTable = bm.getColorTable();
        fColors = fCTable ? fCTable->lockColors() : NULL;
    }
    /** Initialize with a colortable (may be null)
     */
    explicit SkAutoLockColors(SkColorTable* ctable) {
        fCTable = ctable;
        fColors = ctable ? ctable->lockColors() : NULL;
    }
    ~SkAutoLockColors() {
        if (fCTable) {
            fCTable->unlockColors(false);
        }
    }

    /** Return the currently locked colors, or NULL if no bitmap's colortable
        is currently locked.
    */
    const SkPMColor* colors() const { return fColors; }

    /** Locks the table and returns is colors (assuming ctable is not null) and
        unlocks the previous table if one was present
     */
    const SkPMColor* lockColors(SkColorTable* ctable) {
        if (fCTable) {
            fCTable->unlockColors(false);
        }
        fCTable = ctable;
        fColors = ctable ? ctable->lockColors() : NULL;
        return fColors;
    }

    const SkPMColor* lockColors(const SkBitmap& bm) {
        return this->lockColors(bm.getColorTable());
    }

private:
    SkColorTable*    fCTable;
    const SkPMColor* fColors;
};

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

inline uint32_t* SkBitmap::getAddr32(int x, int y) const {
    SkASSERT(fPixels);
    SkASSERT(fConfig == kARGB_8888_Config);
    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
    return (uint32_t*)((char*)fPixels + y * fRowBytes + (x << 2));
}

inline uint16_t* SkBitmap::getAddr16(int x, int y) const {
    SkASSERT(fPixels);
    SkASSERT(fConfig == kRGB_565_Config || fConfig == kARGB_4444_Config);
    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
    return (uint16_t*)((char*)fPixels + y * fRowBytes + (x << 1));
}

inline uint8_t* SkBitmap::getAddr8(int x, int y) const {
    SkASSERT(fPixels);
    SkASSERT(fConfig == kA8_Config || fConfig == kIndex8_Config);
    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
    return (uint8_t*)fPixels + y * fRowBytes + x;
}

inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const {
    SkASSERT(fPixels);
    SkASSERT(fConfig == kIndex8_Config);
    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
    SkASSERT(fColorTable);
    return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)];
}

// returns the address of the byte that contains the x coordinate
inline uint8_t* SkBitmap::getAddr1(int x, int y) const {
    SkASSERT(fPixels);
    SkASSERT(fConfig == kA1_Config);
    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
    return (uint8_t*)fPixels + y * fRowBytes + (x >> 3);
}

#endif


code:
/*
    Copyright 2010 Google Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

#ifndef SkBitmapCache_DEFINED
#define SkBitmapCache_DEFINED

#include "SkBitmap.h"

class SkBitmapCache : SkNoncopyable {
public:
    SkBitmapCache(int maxEntries);
    ~SkBitmapCache();

    bool find(const void* buffer, size_t len, SkBitmap*) const;
    void add(const void* buffer, size_t len, const SkBitmap&);

private:
    int fEntryCount;
    const int fMaxEntries;

    struct Entry;
    mutable Entry*  fHead;
    mutable Entry*  fTail;

    inline Entry* detach(Entry*) const;
    inline void attachToHead(Entry*) const;

#ifdef SK_DEBUG
    void validate() const;
#else
    void validate() const {}
#endif

    class AutoValidate : SkNoncopyable {
    public:
        AutoValidate(const SkBitmapCache* bc) : fBC(bc) { bc->validate(); }
        ~AutoValidate() { fBC->validate(); }
    private:
        const SkBitmapCache* fBC;
    };
};

#endif

license: mit


code:
/* libs/graphics/sgl/SkBitmapShader.h
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef SkBitmapProcShader_DEFINED
#define SkBitmapProcShader_DEFINED

#include "SkShader.h"
#include "SkBitmapProcState.h"

class SkBitmapProcShader : public SkShader {
public:
    SkBitmapProcShader(const SkBitmap& src, TileMode tx, TileMode ty);

    // overrides from SkShader
    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
    virtual uint32_t getFlags() { return fFlags; }
    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
    virtual void beginSession();
    virtual void endSession();
    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
                                 SkScalar* twoPointRadialParams) const;

    static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);

    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
        return SkNEW_ARGS(SkBitmapProcShader, (buffer));
    }

    // override from flattenable
    virtual bool toDumpString(SkString* str) const;

protected:
    SkBitmapProcShader(SkFlattenableReadBuffer& );
    virtual void flatten(SkFlattenableWriteBuffer& );
    virtual Factory getFactory() { return CreateProc; }

    SkBitmap          fRawBitmap;   // experimental for RLE encoding
    SkBitmapProcState fState;
    uint32_t          fFlags;

private:
    typedef SkShader INHERITED;
};

#endif


code:
/*
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef SkBitmapProcState_DEFINED
#define SkBitmapProcState_DEFINED

#include "SkBitmap.h"
#include "SkMatrix.h"

class SkPaint;

struct SkBitmapProcState {

    typedef void (*ShaderProc32)(const SkBitmapProcState&, int x, int y,
                                 SkPMColor[], int count);

    typedef void (*ShaderProc16)(const SkBitmapProcState&, int x, int y,
                                 uint16_t[], int count);

    typedef void (*MatrixProc)(const SkBitmapProcState&,
                               uint32_t bitmapXY[],
                               int count,
                               int x, int y);

    typedef void (*SampleProc32)(const SkBitmapProcState&,
                                 const uint32_t[],
                                 int count,
                                 SkPMColor colors[]);

    typedef void (*SampleProc16)(const SkBitmapProcState&,
                                 const uint32_t[],
                                 int count,
                                 uint16_t colors[]);

    typedef U16CPU (*FixedTileProc)(SkFixed);   // returns 0..0xFFFF
    typedef U16CPU (*IntTileProc)(int value, int count);   // returns 0..count-1

    // If a shader proc is present, then the corresponding matrix/sample procs
    // are ignored
    ShaderProc32        fShaderProc32;      // chooseProcs
    ShaderProc16        fShaderProc16;      // chooseProcs
    // These are used if the shaderproc is NULL
    MatrixProc          fMatrixProc;        // chooseProcs
    SampleProc32        fSampleProc32;      // chooseProcs
    SampleProc16        fSampleProc16;      // chooseProcs

    const SkBitmap*     fBitmap;            // chooseProcs - orig or mip
    const SkMatrix*     fInvMatrix;         // chooseProcs
    SkMatrix::MapXYProc fInvProc;           // chooseProcs

    FixedTileProc       fTileProcX;         // chooseProcs
    FixedTileProc       fTileProcY;         // chooseProcs
    IntTileProc         fIntTileProcY;      // chooseProcs
    SkFixed             fFilterOneX;
    SkFixed             fFilterOneY;

    SkPMColor           fPaintPMColor;      // chooseProcs - A8 config
    SkFixed             fInvSx;             // chooseProcs
    SkFixed             fInvKy;             // chooseProcs
    uint16_t            fAlphaScale;        // chooseProcs
    uint8_t             fInvType;           // chooseProcs
    uint8_t             fTileModeX;         // CONSTRUCTOR
    uint8_t             fTileModeY;         // CONSTRUCTOR
    SkBool8             fDoFilter;          // chooseProcs

    /** Platforms implement this, and can optionally overwrite only the
        following fields:

        fShaderProc32
        fShaderProc16
        fMatrixProc
        fSampleProc32
        fSampleProc32

        They will already have valid function pointers, so a platform that does
        not have an accelerated version can just leave that field as is. A valid
        implementation can do nothing (see SkBitmapProcState_opts_none.cpp)
     */
    void platformProcs();

    /** Given the byte size of the index buffer to be passed to the matrix proc,
        return the maximum number of resulting pixels that can be computed
        (i.e. the number of SkPMColor values to be written by the sample proc).
        This routine takes into account that filtering and scale-vs-affine
        affect the amount of buffer space needed.

        Only valid to call after chooseProcs (setContext) has been called. It is
        safe to call this inside the shader's shadeSpan() method.
     */
    int maxCountForBufferSize(size_t bufferSize) const;

private:
    friend class SkBitmapProcShader;

    SkMatrix            fUnitInvMatrix;     // chooseProcs
    SkBitmap            fOrigBitmap;        // CONSTRUCTOR
    SkBitmap            fMipBitmap;

    MatrixProc chooseMatrixProc(bool trivial_matrix);
    bool chooseProcs(const SkMatrix& inv, const SkPaint&);
};

/*  Macros for packing and unpacking pairs of 16bit values in a 32bit uint.
    Used to allow access to a stream of uint16_t either one at a time, or
    2 at a time by unpacking a uint32_t
 */
#ifdef SK_CPU_BENDIAN
    #define PACK_TWO_SHORTS(pri, sec) ((pri) << 16 | (sec))
    #define UNPACK_PRIMARY_SHORT(packed)    ((uint32_t)(packed) >> 16)
    #define UNPACK_SECONDARY_SHORT(packed)  ((packed) & 0xFFFF)
#else
    #define PACK_TWO_SHORTS(pri, sec) ((pri) | ((sec) << 16))
    #define UNPACK_PRIMARY_SHORT(packed)    ((packed) & 0xFFFF)
    #define UNPACK_SECONDARY_SHORT(packed)  ((uint32_t)(packed) >> 16)
#endif

#ifdef SK_DEBUG
    static inline uint32_t pack_two_shorts(U16CPU pri, U16CPU sec) {
        SkASSERT((uint16_t)pri == pri);
        SkASSERT((uint16_t)sec == sec);
        return PACK_TWO_SHORTS(pri, sec);
    }
#else
    #define pack_two_shorts(pri, sec)   PACK_TWO_SHORTS(pri, sec)
#endif

// These functions are generated via macros, but are exposed here so that
// platformProcs may test for them by name.
void S32_opaque_D32_filter_DX(const SkBitmapProcState& s, const uint32_t xy[],
                              int count, SkPMColor colors[]);
void S32_alpha_D32_filter_DX(const SkBitmapProcState& s, const uint32_t xy[],
                             int count, SkPMColor colors[]);

#endif


code:
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef __arm__
#ifdef ANDROID
    #include <machine/cpu-features.h>
#endif
#endif

#include "SkColorPriv.h"

/*
    Filter_32_opaque

    There is no hard-n-fast rule that the filtering must produce
    exact results for the color components, but if the 4 incoming colors are
    all opaque, then the output color must also be opaque. Subsequent parts of
    the drawing pipeline may rely on this (e.g. which blitrow proc to use).
 */

#if defined(__ARM_HAVE_NEON) && !defined(SK_CPU_BENDIAN)
static inline void Filter_32_opaque_neon(unsigned x, unsigned y,
                                    SkPMColor a00, SkPMColor a01,
                                    SkPMColor a10, SkPMColor a11,
                                    SkPMColor *dst) {
    asm volatile(
                 "vdup.8         d0, %[y]                \n\t"   // duplicate y into d0
                 "vmov.u8        d16, #16                \n\t"   // set up constant in d16
                 "vsub.u8        d1, d16, d0             \n\t"   // d1 = 16-y

                 "vdup.32        d4, %[a00]              \n\t"   // duplicate a00 into d4
                 "vdup.32        d5, %[a10]              \n\t"   // duplicate a10 into d5
                 "vmov.32        d4[1], %[a01]           \n\t"   // set top of d4 to a01
                 "vmov.32        d5[1], %[a11]           \n\t"   // set top of d5 to a11

                 "vmull.u8       q3, d4, d1              \n\t"   // q3 = [a01|a00] * (16-y)
                 "vmull.u8       q0, d5, d0              \n\t"   // q0 = [a11|a10] * y

                 "vdup.16        d5, %[x]                \n\t"   // duplicate x into d5
                 "vmov.u16       d16, #16                \n\t"   // set up constant in d16
                 "vsub.u16       d3, d16, d5             \n\t"   // d3 = 16-x

                 "vmul.i16       d4, d7, d5              \n\t"   // d4  = a01 * x
                 "vmla.i16       d4, d1, d5              \n\t"   // d4 += a11 * x
                 "vmla.i16       d4, d6, d3              \n\t"   // d4 += a00 * (16-x)
                 "vmla.i16       d4, d0, d3              \n\t"   // d4 += a10 * (16-x)
                 "vshrn.i16      d0, q2, #8              \n\t"   // shift down result by 8
                 "vst1.32        {d0[0]}, [%[dst]]       \n\t"   // store result
                 :
                 : [x] "r" (x), [y] "r" (y), [a00] "r" (a00), [a01] "r" (a01), [a10] "r" (a10), [a11] "r" (a11), [dst] "r" (dst)
                 : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16"
                 );
}

static inline void Filter_32_alpha_neon(unsigned x, unsigned y,
                                          SkPMColor a00, SkPMColor a01,
                                          SkPMColor a10, SkPMColor a11,
                                          SkPMColor *dst, uint16_t scale) {
    asm volatile(
                 "vdup.8         d0, %[y]                \n\t"   // duplicate y into d0
                 "vmov.u8        d16, #16                \n\t"   // set up constant in d16
                 "vsub.u8        d1, d16, d0             \n\t"   // d1 = 16-y

                 "vdup.32        d4, %[a00]              \n\t"   // duplicate a00 into d4
                 "vdup.32        d5, %[a10]              \n\t"   // duplicate a10 into d5
                 "vmov.32        d4[1], %[a01]           \n\t"   // set top of d4 to a01
                 "vmov.32        d5[1], %[a11]           \n\t"   // set top of d5 to a11

                 "vmull.u8       q3, d4, d1              \n\t"   // q3 = [a01|a00] * (16-y)
                 "vmull.u8       q0, d5, d0              \n\t"   // q0 = [a11|a10] * y

                 "vdup.16        d5, %[x]                \n\t"   // duplicate x into d5
                 "vmov.u16       d16, #16                \n\t"   // set up constant in d16
                 "vsub.u16       d3, d16, d5             \n\t"   // d3 = 16-x

                 "vmul.i16       d4, d7, d5              \n\t"   // d4  = a01 * x
                 "vmla.i16       d4, d1, d5              \n\t"   // d4 += a11 * x
                 "vmla.i16       d4, d6, d3              \n\t"   // d4 += a00 * (16-x)
                 "vmla.i16       d4, d0, d3              \n\t"   // d4 += a10 * (16-x)
                 "vdup.16        d3, %[scale]            \n\t"   // duplicate scale into d3
                 "vshr.u16       d4, d4, #8              \n\t"   // shift down result by 8
                 "vmul.i16       d4, d4, d3              \n\t"   // multiply result by scale
                 "vshrn.i16      d0, q2, #8              \n\t"   // shift down result by 8
                 "vst1.32        {d0[0]}, [%[dst]]       \n\t"   // store result
                 :
                 : [x] "r" (x), [y] "r" (y), [a00] "r" (a00), [a01] "r" (a01), [a10] "r" (a10), [a11] "r" (a11), [dst] "r" (dst), [scale] "r" (scale)
                 : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16"
                 );
}
#define Filter_32_opaque    Filter_32_opaque_neon
#define Filter_32_alpha     Filter_32_alpha_neon
#else
static inline void Filter_32_opaque_portable(unsigned x, unsigned y,
                                             SkPMColor a00, SkPMColor a01,
                                             SkPMColor a10, SkPMColor a11,
                                             SkPMColor* dstColor) {
    SkASSERT((unsigned)x <= 0xF);
    SkASSERT((unsigned)y <= 0xF);

    int xy = x * y;
    static const uint32_t mask = gMask_00FF00FF; //0xFF00FF;

    int scale = 256 - 16*y - 16*x + xy;
    uint32_t lo = (a00 & mask) * scale;
    uint32_t hi = ((a00 >> 8) & mask) * scale;

    scale = 16*x - xy;
    lo += (a01 & mask) * scale;
    hi += ((a01 >> 8) & mask) * scale;

    scale = 16*y - xy;
    lo += (a10 & mask) * scale;
    hi += ((a10 >> 8) & mask) * scale;

    lo += (a11 & mask) * xy;
    hi += ((a11 >> 8) & mask) * xy;

    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
}

static inline void Filter_32_alpha_portable(unsigned x, unsigned y,
                                            SkPMColor a00, SkPMColor a01,
                                            SkPMColor a10, SkPMColor a11,
                                            SkPMColor* dstColor,
                                            unsigned alphaScale) {
    SkASSERT((unsigned)x <= 0xF);
    SkASSERT((unsigned)y <= 0xF);
    SkASSERT(alphaScale <= 256);

    int xy = x * y;
    static const uint32_t mask = gMask_00FF00FF; //0xFF00FF;

    int scale = 256 - 16*y - 16*x + xy;
    uint32_t lo = (a00 & mask) * scale;
    uint32_t hi = ((a00 >> 8) & mask) * scale;

    scale = 16*x - xy;
    lo += (a01 & mask) * scale;
    hi += ((a01 >> 8) & mask) * scale;

    scale = 16*y - xy;
    lo += (a10 & mask) * scale;
    hi += ((a10 >> 8) & mask) * scale;

    lo += (a11 & mask) * xy;
    hi += ((a11 >> 8) & mask) * xy;

    lo = ((lo >> 8) & mask) * alphaScale;
    hi = ((hi >> 8) & mask) * alphaScale;

    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
}
#define Filter_32_opaque    Filter_32_opaque_portable
#define Filter_32_alpha     Filter_32_alpha_portable
#endif


code:
/*
    Copyright 2011 Google Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale)
#define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine)
#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine)
#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp)
#define PERSP_FILTER_NAME       MAKENAME(_filter_persp)

#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)

#ifndef PREAMBLE
    #define PREAMBLE(state)
    #define PREAMBLE_PARAM_X
    #define PREAMBLE_PARAM_Y
    #define PREAMBLE_ARG_X
    #define PREAMBLE_ARG_Y
#endif

void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
                                uint32_t xy[], int count, int x, int y) {
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask)) == 0);

    PREAMBLE(s);
    // we store y, x, x, x, x, x

    const unsigned maxX = s.fBitmap->width() - 1;
    SkFixed fx;
    {
        SkPoint pt;
        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
        fx = SkScalarToFixed(pt.fY);
        const unsigned maxY = s.fBitmap->height() - 1;
        *xy++ = TILEY_PROCF(fx, maxY);
        fx = SkScalarToFixed(pt.fX);
    }

    if (0 == maxX) {
        // all of the following X values must be 0
        memset(xy, 0, count * sizeof(uint16_t));
        return;
    }

    const SkFixed dx = s.fInvSx;

#ifdef CHECK_FOR_DECAL
    // test if we don't need to apply the tile proc
    if ((unsigned)(fx >> 16) <= maxX &&
        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
        decal_nofilter_scale(xy, fx, dx, count);
    } else
#endif
    {
        int i;
        for (i = (count >> 2); i > 0; --i) {
            unsigned a, b;
            a = TILEX_PROCF(fx, maxX); fx += dx;
            b = TILEX_PROCF(fx, maxX); fx += dx;
#ifdef SK_CPU_BENDIAN
            *xy++ = (a << 16) | b;
#else
            *xy++ = (b << 16) | a;
#endif
            a = TILEX_PROCF(fx, maxX); fx += dx;
            b = TILEX_PROCF(fx, maxX); fx += dx;
#ifdef SK_CPU_BENDIAN
            *xy++ = (a << 16) | b;
#else
            *xy++ = (b << 16) | a;
#endif
        }
        uint16_t* xx = (uint16_t*)xy;
        for (i = (count & 3); i > 0; --i) {
            *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
        }
    }
}

// note: we could special-case on a matrix which is skewed in X but not Y.
// this would require a more general setup thatn SCALE does, but could use
// SCALE's inner loop that only looks at dx

void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
                                 uint32_t xy[], int count, int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask |
                             SkMatrix::kAffine_Mask)) == 0);

    PREAMBLE(s);
    SkPoint srcPt;
    s.fInvProc(*s.fInvMatrix,
               SkIntToScalar(x) + SK_ScalarHalf,
               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);

    SkFixed fx = SkScalarToFixed(srcPt.fX);
    SkFixed fy = SkScalarToFixed(srcPt.fY);
    SkFixed dx = s.fInvSx;
    SkFixed dy = s.fInvKy;
    int maxX = s.fBitmap->width() - 1;
    int maxY = s.fBitmap->height() - 1;

    for (int i = count; i > 0; --i) {
        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
        fx += dx; fy += dy;
    }
}

void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
                                uint32_t* SK_RESTRICT xy,
                                int count, int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);

    PREAMBLE(s);
    int maxX = s.fBitmap->width() - 1;
    int maxY = s.fBitmap->height() - 1;

    SkPerspIter   iter(*s.fInvMatrix,
                       SkIntToScalar(x) + SK_ScalarHalf,
                       SkIntToScalar(y) + SK_ScalarHalf, count);

    while ((count = iter.next()) != 0) {
        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
        while (--count >= 0) {
            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
                     TILEX_PROCF(srcXY[0], maxX);
            srcXY += 2;
        }
    }
}

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

static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
                                          SkFixed one PREAMBLE_PARAM_Y) {
    unsigned i = TILEY_PROCF(f, max);
    i = (i << 4) | TILEY_LOW_BITS(f, max);
    return (i << 14) | (TILEY_PROCF((f + one), max));
}

static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
                                          SkFixed one PREAMBLE_PARAM_X) {
    unsigned i = TILEX_PROCF(f, max);
    i = (i << 4) | TILEX_LOW_BITS(f, max);
    return (i << 14) | (TILEX_PROCF((f + one), max));
}

void SCALE_FILTER_NAME(const SkBitmapProcState& s,
                              uint32_t xy[], int count, int x, int y) {
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask)) == 0);
    SkASSERT(s.fInvKy == 0);

    PREAMBLE(s);

    const unsigned maxX = s.fBitmap->width() - 1;
    const SkFixed one = s.fFilterOneX;
    const SkFixed dx = s.fInvSx;
    SkFixed fx;

    {
        SkPoint pt;
        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
        const unsigned maxY = s.fBitmap->height() - 1;
        // compute our two Y values up front
        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
        // now initialize fx
        fx = SkScalarToFixed(pt.fX) - (one >> 1);
    }

#ifdef CHECK_FOR_DECAL
    // test if we don't need to apply the tile proc
    if (dx > 0 &&
            (unsigned)(fx >> 16) <= maxX &&
            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
        decal_filter_scale(xy, fx, dx, count);
    } else
#endif
    {
        do {
            *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
            fx += dx;
        } while (--count != 0);
    }
}

void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
                               uint32_t xy[], int count, int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask |
                             SkMatrix::kAffine_Mask)) == 0);

    PREAMBLE(s);
    SkPoint srcPt;
    s.fInvProc(*s.fInvMatrix,
               SkIntToScalar(x) + SK_ScalarHalf,
               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);

    SkFixed oneX = s.fFilterOneX;
    SkFixed oneY = s.fFilterOneY;
    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
    SkFixed dx = s.fInvSx;
    SkFixed dy = s.fInvKy;
    unsigned maxX = s.fBitmap->width() - 1;
    unsigned maxY = s.fBitmap->height() - 1;

    do {
        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
        fy += dy;
        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
        fx += dx;
    } while (--count != 0);
}

void PERSP_FILTER_NAME(const SkBitmapProcState& s,
                              uint32_t* SK_RESTRICT xy, int count,
                              int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);

    PREAMBLE(s);
    unsigned maxX = s.fBitmap->width() - 1;
    unsigned maxY = s.fBitmap->height() - 1;
    SkFixed oneX = s.fFilterOneX;
    SkFixed oneY = s.fFilterOneY;

    SkPerspIter   iter(*s.fInvMatrix,
                       SkIntToScalar(x) + SK_ScalarHalf,
                       SkIntToScalar(y) + SK_ScalarHalf, count);

    while ((count = iter.next()) != 0) {
        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
        do {
            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
                                       oneY PREAMBLE_ARG_Y);
            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
                                       oneX PREAMBLE_ARG_X);
            srcXY += 2;
        } while (--count != 0);
    }
}

static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
    SCALE_NOFILTER_NAME,
    SCALE_FILTER_NAME,
    AFFINE_NOFILTER_NAME,
    AFFINE_FILTER_NAME,
    PERSP_NOFILTER_NAME,
    PERSP_FILTER_NAME
};

#undef MAKENAME
#undef TILEX_PROCF
#undef TILEY_PROCF
#ifdef CHECK_FOR_DECAL
    #undef CHECK_FOR_DECAL
#endif

#undef SCALE_NOFILTER_NAME
#undef SCALE_FILTER_NAME
#undef AFFINE_NOFILTER_NAME
#undef AFFINE_FILTER_NAME
#undef PERSP_NOFILTER_NAME
#undef PERSP_FILTER_NAME

#undef PREAMBLE
#undef PREAMBLE_PARAM_X
#undef PREAMBLE_PARAM_Y
#undef PREAMBLE_ARG_X
#undef PREAMBLE_ARG_Y

#undef TILEX_LOW_BITS
#undef TILEY_LOW_BITS


code:
/* NEON optimized code (C) COPYRIGHT 2009 Motorola */
/*
 * Modifications done in-house at Motorola
 *
 * this is a clone of SkBitmapProcState_matrix.h
 * and has been tuned to work with the NEON unit.
 *
 * Still going back and forth between whether this approach
 * (clone the entire SkBitmapProcState_matrix.h file or
 * if I should put just the modified routines in here and
 * then use a construct like #define DONT_DO_THIS_FUNCTION or
 * something like that...
 *
 * This is for the ClampX_ClampY instance
 *
 */

#if	!defined(__ARM_HAVE_NEON)
#error	this file can be used only when the NEON unit is enabled
#endif

#include <arm_neon.h>

/*
 * This has been modified on the knowledge that (at the time)
 * we had the following macro definitions in the parent file
 *
 * #define MAKENAME(suffix)        ClampX_ClampY ## suffix
 * #define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
 * #define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
 * #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
 * #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
 * #define CHECK_FOR_DECAL
 */

/* SkClampMax(val,max) -- bound to 0..max */

#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale_neon)
#define SCALE_FILTER_NAME       MAKENAME(_filter_scale_neon)
#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine_neon)
#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine_neon)
#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp_neon)
#define PERSP_FILTER_NAME       MAKENAME(_filter_persp_neon)

#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)

#ifndef PREAMBLE
    #define PREAMBLE(state)
    #define PREAMBLE_PARAM_X
    #define PREAMBLE_PARAM_Y
    #define PREAMBLE_ARG_X
    #define PREAMBLE_ARG_Y
#endif

static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
                                uint32_t xy[], int count, int x, int y) {
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask)) == 0);

    PREAMBLE(s);
    // we store y, x, x, x, x, x

    const unsigned maxX = s.fBitmap->width() - 1;
    SkFixed fx;
    {
        SkPoint pt;
        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
        fx = SkScalarToFixed(pt.fY);
        const unsigned maxY = s.fBitmap->height() - 1;
        *xy++ = TILEY_PROCF(fx, maxY);
        fx = SkScalarToFixed(pt.fX);
    }

    if (0 == maxX) {
        // all of the following X values must be 0
        memset(xy, 0, count * sizeof(uint16_t));
        return;
    }

    const SkFixed dx = s.fInvSx;

#ifdef CHECK_FOR_DECAL
    // test if we don't need to apply the tile proc
    if ((unsigned)(fx >> 16) <= maxX &&
        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
        decal_nofilter_scale(xy, fx, dx, count);
        return;
    }
#endif

    int i;

	/* very much like done in decal_nofilter, but with
	 * an extra clamping function applied.
	 * TILEX_PROCF(fx,max) SkClampMax((fx)>>16, max)
	 */
	if (count >= 8) {
	    /* SkFixed is 16.16 fixed point */
	    SkFixed dx2 = dx+dx;
	    SkFixed dx4 = dx2+dx2;
	    SkFixed dx8 = dx4+dx4;

	    /* now build fx/fx+dx/fx+2dx/fx+3dx */
	    SkFixed fx1, fx2, fx3;
	    int32x2_t lower, upper;
	    int32x4_t lbase, hbase;
	    int16_t *dst16 = (int16_t *)xy;

	    fx1 = fx+dx;
	    fx2 = fx1+dx;
	    fx3 = fx2+dx;

	    /* build my template(s) */
	    /* avoid the 'lbase unitialized' warning */
	    lbase = vdupq_n_s32(fx);
	    lbase = vsetq_lane_s32(fx1, lbase, 1);
	    lbase = vsetq_lane_s32(fx2, lbase, 2);
	    lbase = vsetq_lane_s32(fx3, lbase, 3);

	    hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));

	    /* store & bump */
	    do {
	        int32x4_t lout;
            int32x4_t hout;
            int16x8_t hi16;

            /* get the hi 16s of all those 32s */
            lout = lbase;
            hout = hbase;
            /* this sets up all lout's then all hout's in hout */
            asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
            hi16 = vreinterpretq_s16_s32(hout);

            /* clamp & output */
            hi16 = vmaxq_s16(hi16, vdupq_n_s16(0));
            hi16 = vminq_s16(hi16, vdupq_n_s16(maxX));
            vst1q_s16(dst16, hi16);

            /* but preserving base & on to the next */
            lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
            hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
            dst16 += 8;
            count -= 8;
            fx += dx8;
	    } while (count >= 8);
	    xy = (uint32_t *) dst16;
	}

    uint16_t* xx = (uint16_t*)xy;
    for (i = count; i > 0; --i) {
        *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
    }
}

// note: we could special-case on a matrix which is skewed in X but not Y.
// this would require a more general setup thatn SCALE does, but could use
// SCALE's inner loop that only looks at dx

static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
                                 uint32_t xy[], int count, int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask |
                             SkMatrix::kAffine_Mask)) == 0);

    PREAMBLE(s);
    SkPoint srcPt;
    s.fInvProc(*s.fInvMatrix,
               SkIntToScalar(x) + SK_ScalarHalf,
               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);

    SkFixed fx = SkScalarToFixed(srcPt.fX);
    SkFixed fy = SkScalarToFixed(srcPt.fY);
    SkFixed dx = s.fInvSx;
    SkFixed dy = s.fInvKy;
    int maxX = s.fBitmap->width() - 1;
    int maxY = s.fBitmap->height() - 1;

    /* NEON lets us do an 8x unrolling */
    if (count >= 8) {
        /* SkFixed is 16.16 fixed point */
        SkFixed dx4 = dx * 4;
        SkFixed dy4 = dy * 4;
        SkFixed dx8 = dx * 8;
        SkFixed dy8 = dy * 8;

        int32x4_t xbase, ybase;
        int32x4_t x2base, y2base;
        int16_t *dst16 = (int16_t *) xy;

        /* my sets of maxx/maxy for clamping */
        int32_t maxpair = (maxX&0xffff) | ((maxY&0xffff)<<16);
        int16x8_t maxXY = vreinterpretq_s16_s32(vdupq_n_s32(maxpair));

        /* now build fx/fx+dx/fx+2dx/fx+3dx */
        /* avoid the 'xbase unitialized' warning...*/
        xbase = vdupq_n_s32(fx);
        xbase = vsetq_lane_s32(fx+dx, xbase, 1);
        xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2);
        xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3);

        /* same for fy */
        /* avoid the 'ybase unitialized' warning...*/
        ybase = vdupq_n_s32(fy);
        ybase = vsetq_lane_s32(fy+dy, ybase, 1);
        ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2);
        ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3);

        x2base = vaddq_s32(xbase, vdupq_n_s32(dx4));
        y2base = vaddq_s32(ybase, vdupq_n_s32(dy4));

        /* store & bump */
        do {
            int32x4_t xout, yout;
            int32x4_t x2out, y2out;
            int16x8_t hi16, hi16_2;

            xout = xbase;
            yout = ybase;

            /* overlay y's low16 with hi16 from x */
            /* so we properly shifted xyxyxyxy */
            yout = vsriq_n_s32(yout, xout, 16);
            hi16 = vreinterpretq_s16_s32 (yout);

            /* do the clamping; both guys get 0's */
            hi16 = vmaxq_s16 (hi16, vdupq_n_s16(0));
            hi16 = vminq_s16 (hi16, maxXY);

            vst1q_s16 (dst16, hi16);

            /* and for the other 4 pieces of this iteration */
            x2out = x2base;
            y2out = y2base;

            /* overlay y's low16 with hi16 from x */
            /* so we properly shifted xyxyxyxy */
            y2out = vsriq_n_s32(y2out, x2out, 16);
            hi16_2 = vreinterpretq_s16_s32 (y2out);

            /* do the clamping; both guys get 0's */
            hi16_2 = vmaxq_s16 (hi16_2, vdupq_n_s16(0));
            hi16_2 = vminq_s16 (hi16_2, maxXY);

            /* RBE: gcc regenerates dst16+8 all the time instead
             * of folding it into an addressing mode. *sigh* */
            vst1q_s16 (dst16+8, hi16_2);

            /* moving base and on to the next */
            xbase = vaddq_s32 (xbase, vdupq_n_s32 (dx8));
            ybase = vaddq_s32 (ybase, vdupq_n_s32 (dy8));
            x2base = vaddq_s32 (x2base, vdupq_n_s32 (dx8));
            y2base = vaddq_s32 (y2base, vdupq_n_s32 (dy8));

            dst16 += 16;		/* 8x32 aka 16x16 */
            count -= 8;
            fx += dx8;
            fy += dy8;
        } while (count >= 8);
        xy = (uint32_t *) dst16;
    }

    for (int i = count; i > 0; --i) {
        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
        fx += dx; fy += dy;
    }
}

#undef	DEBUG_PERSP_NOFILTER

static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
                                uint32_t* SK_RESTRICT xy,
                                int count, int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);

    PREAMBLE(s);
    /* max{X,Y} are int here, but later shown/assumed to fit in 16 bits */
    int maxX = s.fBitmap->width() - 1;
    int maxY = s.fBitmap->height() - 1;

    SkPerspIter   iter(*s.fInvMatrix,
                       SkIntToScalar(x) + SK_ScalarHalf,
                       SkIntToScalar(y) + SK_ScalarHalf, count);

    while ((count = iter.next()) != 0) {
        const SkFixed* SK_RESTRICT srcXY = iter.getXY();

#if defined(DEBUG_PERSP_NOFILTER)
	/* debugging stuff */
	const SkFixed *end_srcXY = srcXY + (count*2);
	uint32_t *end_xy = xy + (count);
	const SkFixed *base_srcXY = srcXY;
	uint32_t *base_xy = xy;
	int base_count = count;
#endif

#if 1
        // 2009/9/30: crashes in ApiDemos - Views - Animation - 3D Transition
	// 2009/10/9: reworked to avoid illegal (but allowed by gas) insn

        /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1...
         * but we immediately discard the low 16 bits...
         * so what we're going to do is vld4, which will give us
         * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo'
         * parts....
         */
        if (count >= 8) {
            int16_t *mysrc = (int16_t *) srcXY;
            int16_t *mydst = (int16_t *) xy;
            int16x4_t maxX4 = vdup_n_s16((int16_t)maxX);
            int16x4_t maxY4 = vdup_n_s16((int16_t)maxY);
            int16x4_t zero4 = vdup_n_s16(0);

	    /* The constructs with local blocks for register assignments
	     * and asm() instructions is to make keep any hard register
	     * assignments to as small a scope as possible. and to avoid
	     * burning call-preserved hard registers on the vld/vst
	     * instructions.
	     */

            do {
                int16x4_t xlo, xhi, ylo, yhi;
                int16x4_t x2lo, x2hi, y2lo, y2hi;

                /* vld4 does the de-interleaving for us */
		{
                    register int16x4_t t_xlo asm("d0");
                    register int16x4_t t_xhi asm("d1");
                    register int16x4_t t_ylo asm("d2");
                    register int16x4_t t_yhi asm("d3");

                    asm ("vld4.16	{d0-d3},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
                        : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
                        : "r" (mysrc)
                    );
		    xlo = t_xlo;
		    xhi = t_xhi;
		    ylo = t_ylo;
		    yhi = t_yhi;
		}

                /* clamp X>>16 (aka xhi) to 0..maxX */
                xhi = vmax_s16(xhi, zero4);	/* now 0.. */
                xhi = vmin_s16(xhi, maxX4);	/* now 0..maxX */

                /* clamp Y>>16 (aka yhi) to 0..maxY */
                yhi = vmax_s16(yhi, zero4);	/* now 0.. */
                yhi = vmin_s16(yhi, maxY4);	/* now 0..maxY */

		/* deal with the second set of numbers */
		{
                    register int16x4_t t_xlo asm("d4");
                    register int16x4_t t_xhi asm("d5");
                    register int16x4_t t_ylo asm("d6");
                    register int16x4_t t_yhi asm("d7");

                    /* offset == 256 bits == 32 bytes == 8 longs == 16 shorts */
                    asm ("vld4.16	{d4-d7},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
                        : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
                        : "r" (mysrc+16)
                    );
		    x2lo = t_xlo;
		    x2hi = t_xhi;
		    y2lo = t_ylo;
		    y2hi = t_yhi;
		}

                /* clamp the second 4 here */

		if (0) { extern void rbe(void); rbe(); }

                /* clamp X>>16 (aka xhi) to 0..maxX */
                x2hi = vmax_s16(x2hi, zero4);	/* now 0.. */
                x2hi = vmin_s16(x2hi, maxX4);	/* now 0..maxX */

                /* clamp Y>>16 (aka yhi) to 0..maxY */
                y2hi = vmax_s16(y2hi, zero4);	/* now 0.. */
                y2hi = vmin_s16(y2hi, maxY4);	/* now 0..maxY */

                /* we're storing as {x,y}s: x is [0], y is [1] */
                /* we'll use vst2 to make this happen */

		{
                    register int16x4_t out_x asm("d16") = xhi;
                    register int16x4_t out_y asm("d17") = yhi;

                    asm ("vst2.16	{d16-d17},[%2]  /* xlo=%P0 xhi=%P1 */"
			:
			: "w" (out_x), "w" (out_y), "r" (mydst)
			);
		}
		{
                    register int16x4_t out_x asm("d18") = x2hi;
                    register int16x4_t out_y asm("d19") = y2hi;

                    asm ("vst2.16	{d18-d19},[%2]  /* xlo=%P0 xhi=%P1 */"
			:
			: "w" (out_x), "w" (out_y), "r" (mydst+8)
			);
		}

                /* XXX: gcc isn't interleaving these with the NEON ops
                 * but i think that all the scoreboarding works out */
                count -= 8;	/* 8 iterations */
                mysrc += 32;	/* 16 longs, aka 32 shorts */
                mydst += 16;	/* 16 shorts, aka 8 longs */
            } while (count >= 8);
            /* get xy and srcXY fixed up */
            srcXY = (const SkFixed *) mysrc;
            xy = (uint32_t *) mydst;
        }
#endif

        while (--count >= 0) {
            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
                     TILEX_PROCF(srcXY[0], maxX);
            srcXY += 2;
        }

#if defined(DEBUG_PERSP_NOFILTER)
	/* for checking our NEON-produced results against vanilla code */
	{
	    int bad = (-1);
	    for (int i = 0; i < base_count; i++) {
            uint32_t val;
            val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
                    TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);

            if (val != base_xy[i]) {
                bad = i;
                break;
            }
	    }
	    if (bad >= 0) {
            SkDebugf("clamp-nofilter-persp failed piece %d\n", bad);
            SkDebugf("    maxX %08x maxY %08x\n", maxX, maxY);
            bad -= (bad & 0x7);	       /* align */
            for (int i = bad; i < bad + 8; i++) {
                uint32_t val;
                val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
                TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);

                SkDebugf("%d: got %08x want %08x srcXY[0] %08x srcXY[1] %08x\n",
                          i, base_xy[i], val, base_srcXY[i * 2 + 0],
                 base_srcXY[i * 2 + 1]);
            }
            SkDebugf ("---\n");
	    }

	    if (end_xy != xy) {
            SkDebugf("xy ended at %08x, should be %08x\n", xy, end_xy);
	    }
	    if (end_srcXY != srcXY) {
            SkDebugf("srcXY ended at %08x, should be %08x\n", srcXY,
                      end_srcXY);
	    }
	}
#endif
    }
}

#undef	DEBUG_PERSP_NOFILTER

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

static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
                                          SkFixed one PREAMBLE_PARAM_Y) {
    unsigned i = TILEY_PROCF(f, max);
    i = (i << 4) | TILEY_LOW_BITS(f, max);
    return (i << 14) | (TILEY_PROCF((f + one), max));
}

static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
                                          SkFixed one PREAMBLE_PARAM_X) {
    unsigned i = TILEX_PROCF(f, max);
    i = (i << 4) | TILEX_LOW_BITS(f, max);
    return (i << 14) | (TILEX_PROCF((f + one), max));
}

static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
                              uint32_t xy[], int count, int x, int y) {
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask)) == 0);
    SkASSERT(s.fInvKy == 0);

    PREAMBLE(s);

    const unsigned maxX = s.fBitmap->width() - 1;
    const SkFixed one = s.fFilterOneX;
    const SkFixed dx = s.fInvSx;
    SkFixed fx;

    {
        SkPoint pt;
        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
        const unsigned maxY = s.fBitmap->height() - 1;
        // compute our two Y values up front
        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
        // now initialize fx
        fx = SkScalarToFixed(pt.fX) - (one >> 1);
    }

#ifdef CHECK_FOR_DECAL
    // test if we don't need to apply the tile proc
    if (dx > 0 &&
            (unsigned)(fx >> 16) <= maxX &&
            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
        decal_filter_scale(xy, fx, dx, count);
    } else
#endif

    if (count >= 4) {
        int32x4_t wide_dx, wide_one;
        int32x4_t wide_fx, wide_fx1, wide_i, wide_lo;
    #if 0
        /* verification hooks -- see below */
        SkFixed debug_fx = fx;
        int count_done = 0;
    #endif

        wide_fx = vdupq_n_s32(fx);
        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);

        wide_dx = vdupq_n_s32(dx);
        wide_one = vdupq_n_s32(one);

        while (count >= 4) {
            /* original expands to:
             * unsigned i = SkClampMax((f) >> 16, max);
             * i = (i << 4) | (((f) >> 12) & 0xF);
             * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
             */

            /* i = SkClampMax(f>>16, maxX) */
            wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));

            /* i<<4 | TILEX_LOW_BITS(fx) */
            wide_lo = vshrq_n_s32(wide_fx, 12);
            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);

            /* i<<14 */
            wide_i = vshlq_n_s32(wide_i, 14);

            /* SkClampMax(((f + one)) >> 16, max) */
            wide_fx1 = vaddq_s32(wide_fx, wide_one);
            wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
            wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));

            /* final combination */
            wide_i = vorrq_s32(wide_i, wide_fx1);

            vst1q_u32(xy, vreinterpretq_u32_s32(wide_i));

    #if 0
            /* having a verification hook is a good idea */
            /* use debug_fx, debug_fx+dx, etc. */

            for (int i=0;i<4;i++) {
            uint32_t want = PACK_FILTER_X_NAME(debug_fx, maxX, one PREAMBLE_ARG_X);
                    if (xy[i] != want)
                {
                /* print a nastygram */
                SkDebugf("clamp-filter-scale fails\n");
                SkDebugf("got %08x want %08x\n", xy[i], want);
                SkDebugf("fx %08x debug_fx %08x dx %08x done %d\n",
                fx, debug_fx, dx, count_done);
                SkDebugf(" maxX %08x one %08x\n", maxX, one);

                }
            debug_fx += dx;
            count_done++;
            }
    #endif
            wide_fx += vdupq_n_s32(dx+dx+dx+dx);
            fx += dx+dx+dx+dx;
            xy += 4;
            count -= 4;
        }
    }

    while (--count >= 0) {
        *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
        fx += dx;
    }
}

static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
                               uint32_t xy[], int count, int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                             SkMatrix::kScale_Mask |
                             SkMatrix::kAffine_Mask)) == 0);

    PREAMBLE(s);
    SkPoint srcPt;
    s.fInvProc(*s.fInvMatrix,
               SkIntToScalar(x) + SK_ScalarHalf,
               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);

    SkFixed oneX = s.fFilterOneX;
    SkFixed oneY = s.fFilterOneY;
    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
    SkFixed dx = s.fInvSx;
    SkFixed dy = s.fInvKy;
    unsigned maxX = s.fBitmap->width() - 1;
    unsigned maxY = s.fBitmap->height() - 1;

    if (count >= 4) {
        int32x4_t wide_one, wide_i, wide_lo;
        int32x4_t wide_dx, wide_fx, wide_onex, wide_fx1;
        int32x4_t wide_dy, wide_fy, wide_oney, wide_fy1;

    #undef	AFFINE_DEBUG
    #if	defined(AFFINE_DEBUG)
        SkFixed fyp = fy;
        SkFixed fxp = fx;
        uint32_t *xyp = xy;
        int count_done = 0;
    #endif

        wide_fx = vdupq_n_s32(fx);
        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
        wide_dx = vdupq_n_s32(dx);

        wide_fy = vdupq_n_s32(fy);
        wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1);
        wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2);
        wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3);
        wide_dy = vdupq_n_s32(dy);

        wide_onex = vdupq_n_s32(oneX);
        wide_oney = vdupq_n_s32(oneY);

        while (count >= 4) {
            int32x4_t wide_x;
            int32x4_t wide_y;

            /* do the X side, then the Y side, then interleave them */

            /* original expands to:
             * unsigned i = SkClampMax((f) >> 16, max);
             * i = (i << 4) | (((f) >> 12) & 0xF);
             * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
             */

            /* i = SkClampMax(f>>16, maxX) */
            wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));

            /* i<<4 | TILEX_LOW_BITS(fx) */
            wide_lo = vshrq_n_s32(wide_fx, 12);
            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);

            /* i<<14 */
            wide_i = vshlq_n_s32(wide_i, 14);

            /* SkClampMax(((f + one)) >> 16, max) */
            wide_fx1 = vaddq_s32(wide_fx, wide_onex);
            wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
            wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));

            /* final combination */
            wide_x = vorrq_s32(wide_i, wide_fx1);

            /* And now the Y side */

            /* i = SkClampMax(f>>16, maxX) */
            wide_i = vmaxq_s32(vshrq_n_s32(wide_fy,16), vdupq_n_s32(0));
            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxY));

            /* i<<4 | TILEX_LOW_BITS(fx) */
            wide_lo = vshrq_n_s32(wide_fy, 12);
            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);

            /* i<<14 */
            wide_i = vshlq_n_s32(wide_i, 14);

            /* SkClampMax(((f + one)) >> 16, max) */
            wide_fy1 = vaddq_s32(wide_fy, wide_oney);
            wide_fy1 = vmaxq_s32(vshrq_n_s32(wide_fy1,16), vdupq_n_s32(0));
            wide_fy1 = vminq_s32(wide_fy1, vdupq_n_s32(maxY));

            /* final combination */
            wide_y = vorrq_s32(wide_i, wide_fy1);

            /* interleave as YXYXYXYX as part of the storing */
	    {
                /* vst2.32 needs side-by-side registers */
                register int32x4_t t_x asm("q1");
                register int32x4_t t_y asm("q0");

		t_x = wide_x; t_y = wide_y;
                asm ("vst2.32	{q0-q1},[%2]  /* y=%q0 x=%q1 */"
                    :
                    : "w" (t_y), "w" (t_x), "r" (xy)
                    );
	    }

    #if	defined(AFFINE_DEBUG)
            /* make sure we're good here -- check the 4 we just output */
            for (int i = 0; i<4;i++) {
            uint32_t val;
            val = PACK_FILTER_Y_NAME(fyp, maxY, oneY PREAMBLE_ARG_Y);
            if (val != xy[i*2+0]) {
                /* print a nastygram */
                SkDebugf("clamp-filter-affine fails\n");
                SkDebugf("[bad-y] got %08x want %08x\n", xy[i*2+0], val);
                SkDebugf("fy %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
                fy, fxp, fyp, dx, dy, count_done);
                SkDebugf(" maxY %08x oneY %08x\n", maxY, oneY);
                }
            val = PACK_FILTER_X_NAME(fxp, maxX, oneX PREAMBLE_ARG_X);
            if (val != xy[i*2+1]) {
                /* print a nastygram */
                SkDebugf("clamp-filter-affine fails\n");
                SkDebugf("[bad-x] got %08x want %08x\n", xy[i*2+1], val);
                SkDebugf("fx %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
                fx, fxp, fyp, dx, dy, count_done);
                SkDebugf(" maxX %08x one %08x\n", maxX, oneX);
            }
            fyp += dy;
            fxp += dx;
            count_done++;
            }
    #endif

            wide_fx += vdupq_n_s32(dx+dx+dx+dx);
            fx += dx+dx+dx+dx;
            wide_fy += vdupq_n_s32(dy+dy+dy+dy);
            fy += dy+dy+dy+dy;
            xy += 8;		/* 4 x's, 4 y's */
            count -= 4;
        }
    }

    while (--count >= 0) {
        /* NB: writing Y/X */
        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
        fy += dy;
        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
        fx += dx;
    }
}

static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
                              uint32_t* SK_RESTRICT xy, int count,
                              int x, int y) {
    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);

    PREAMBLE(s);
    unsigned maxX = s.fBitmap->width() - 1;
    unsigned maxY = s.fBitmap->height() - 1;
    SkFixed oneX = s.fFilterOneX;
    SkFixed oneY = s.fFilterOneY;

    SkPerspIter   iter(*s.fInvMatrix,
                       SkIntToScalar(x) + SK_ScalarHalf,
                       SkIntToScalar(y) + SK_ScalarHalf, count);

    while ((count = iter.next()) != 0) {
        const SkFixed* SK_RESTRICT srcXY = iter.getXY();

        if (count >= 4) {
            int32x4_t wide_one, wide_i, wide_lo;
            int32x4_t wide_fx1;
            int32x4_t wide_fy1;
            int32x4_t wide_x, wide_y;

            while (count >= 4) {
                /* RBE: it's good, but:
                 * -- we spill a constant that could be easily regnerated
                 *    [perhaps tweak gcc's NEON constant costs?]
                 */

                /* load src:  x-y-x-y-x-y-x-y */
		{
		    register int32x4_t q0 asm ("q0");
		    register int32x4_t q1 asm ("q1");
                    asm ("vld2.32	{q0-q1},[%2]  /* x=%q0 y=%q1 */"
                         : "=w" (q0), "=w" (q1)
                         : "r" (srcXY));
		    wide_x = q0; wide_y = q1;
		}

                /* do the X side, then the Y side, then interleave them */

                wide_x = vsubq_s32(wide_x, vdupq_n_s32 (oneX>>1));

                /* original expands to:
                 * unsigned i = SkClampMax((f) >> 16, max);
                 * i = (i << 4) | (((f) >> 12) & 0xF);
                 * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
                 */

                /* i = SkClampMax(f>>16, maxX) */
                wide_i = vmaxq_s32 (vshrq_n_s32 (wide_x, 16), vdupq_n_s32 (0));
                wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxX));

                /* i<<4 | TILEX_LOW_BITS(fx) */
                wide_lo = vshrq_n_s32 (wide_x, 12);
                wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);

                /* i<<14 */
                wide_i = vshlq_n_s32 (wide_i, 14);

                /* SkClampMax(((f + one)) >> 16, max) */
                wide_fx1 = vaddq_s32 (wide_x, vdupq_n_s32(oneX));
                wide_fx1 = vmaxq_s32 (vshrq_n_s32 (wide_fx1, 16), vdupq_n_s32 (0));
                wide_fx1 = vminq_s32 (wide_fx1, vdupq_n_s32 (maxX));

                /* final combination */
                wide_x = vorrq_s32 (wide_i, wide_fx1);

                /* And now the Y side */

                wide_y = vsubq_s32(wide_y, vdupq_n_s32 (oneY>>1));

                /* i = SkClampMax(f>>16, maxX) */
                wide_i = vmaxq_s32 (vshrq_n_s32 (wide_y, 16), vdupq_n_s32 (0));
                wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxY));

                /* i<<4 | TILEX_LOW_BITS(fx) */
                wide_lo = vshrq_n_s32 (wide_y, 12);
                wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);

                /* i<<14 */
                wide_i = vshlq_n_s32 (wide_i, 14);

                /* SkClampMax(((f + one)) >> 16, max) */

                /* wide_fy1_1 and wide_fy1_2 are just temporary variables to
                 * work-around an ICE in debug */
                int32x4_t wide_fy1_1 = vaddq_s32 (wide_y, vdupq_n_s32(oneY));
                int32x4_t wide_fy1_2 = vmaxq_s32 (vshrq_n_s32 (wide_fy1_1, 16),
                                                  vdupq_n_s32 (0));
                wide_fy1 = vminq_s32 (wide_fy1_2, vdupq_n_s32 (maxY));

                /* final combination */
                wide_y = vorrq_s32 (wide_i, wide_fy1);

                /* switch them around; have to do it this way to get them
                 * in the proper registers to match our instruction */

                /* iteration bookkeeping, ahead of the asm() for scheduling */
                srcXY += 2*4;
                count -= 4;

                /* store interleaved as y-x-y-x-y-x-y-x (NB != read order) */
		{
		    register int32x4_t q0 asm ("q0") = wide_y;
		    register int32x4_t q1 asm ("q1") = wide_x;

                    asm ("vst2.32	{q0-q1},[%2]  /* y=%q0 x=%q1 */"
                        :
                        : "w" (q0), "w" (q1), "r" (xy));
		}

                /* on to the next iteration */
                /* count, srcXY are handled above */
                xy += 2*4;
            }
        }

        /* was do-while; NEON code invalidates original count>0 assumption */
        while (--count >= 0) {
	    /* NB: we read x/y, we write y/x */
            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
                                       oneY PREAMBLE_ARG_Y);
            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
                                       oneX PREAMBLE_ARG_X);
            srcXY += 2;
        }
    }
}

static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
    SCALE_NOFILTER_NAME,
    SCALE_FILTER_NAME,
    AFFINE_NOFILTER_NAME,
    AFFINE_FILTER_NAME,
    PERSP_NOFILTER_NAME,
    PERSP_FILTER_NAME
};

#undef MAKENAME
#undef TILEX_PROCF
#undef TILEY_PROCF
#ifdef CHECK_FOR_DECAL
    #undef CHECK_FOR_DECAL
#endif

#undef SCALE_NOFILTER_NAME
#undef SCALE_FILTER_NAME
#undef AFFINE_NOFILTER_NAME
#undef AFFINE_FILTER_NAME
#undef PERSP_NOFILTER_NAME
#undef PERSP_FILTER_NAME

#undef PREAMBLE
#undef PREAMBLE_PARAM_X
#undef PREAMBLE_PARAM_Y
#undef PREAMBLE_ARG_X
#undef PREAMBLE_ARG_Y

#undef TILEX_LOW_BITS
#undef TILEY_LOW_BITS