/* Copyright (c) 2009-2010 The Khronos Group Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#ifdef __cplusplus
extern "C"
{
#endif

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "owfobject.h"
#include "owfimage.h"
#include "owfutils.h"
#include "owfmemory.h"
#include "owfdebug.h"
#include "owfdisplaycontextgeneral.h"

#ifdef OWF_IMAGE_INTERNAL_PIXEL_IS_FLOAT
#define roundSubPixel(p) ((OWFuint32)((p) + 0.5f))
#else
#define roundSubPixel(p) (p)
#endif


/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_IMAGE_Ctor(void* self)
{
    self = self;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_IMAGE_Dtor(void* self)
{
    OWF_IMAGE* image;

    image = (OWF_IMAGE*)self;

    if (image && image->data)
    {
        if (!image->foreign)
        {
            OWF_Image_FreeData(0, &image->data);
        }
    }

    if (image)
    {
        memset(image, 0, sizeof(OWF_IMAGE));
    }
}

/*----------------------------------------------------------------------------*/
static OWFfloat
NonLinear(OWFfloat x)
{
    if (x <= 0.00304f)
    {
        return 12.92f * x;
    }
    return 1.0556f * pow(x, 1.f/2.4f) - 0.0556f;
}

/*----------------------------------------------------------------------------*/
static OWFfloat
Linear(OWFfloat x)
{
    if (x <= 0.03928)
    {
        return x / 12.92f;
    }
    return pow((x + 0.0556f) / 1.0556f, 2.4f);
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_NonLinearizeData(OWF_IMAGE* image)\
{
    OWFpixel*            ptr;
    OWFint               count;

    OWF_ASSERT(image != NULL && image->data != NULL);
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    if (!image->format.linear)
    {
        return;
    }

    ptr = (OWFpixel*) image->data;
    count = image->width * image->height;

    while (count > 0)
    {
        ptr->color.red   = (OWFsubpixel) NonLinear(ptr->color.red /
                               OWF_RED_MAX_VALUE) * OWF_RED_MAX_VALUE;
        ptr->color.green = (OWFsubpixel) NonLinear(ptr->color.green /
                               OWF_GREEN_MAX_VALUE) * OWF_GREEN_MAX_VALUE;
        ptr->color.blue  = (OWFsubpixel) NonLinear(ptr->color.blue /
                               OWF_BLUE_MAX_VALUE) * OWF_BLUE_MAX_VALUE;

        --count;
        ptr++;
    }

    image->format.linear = OWF_FALSE;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_LinearizeData(OWF_IMAGE* image)
{
    OWFpixel*               ptr;
    OWFuint                 count;

    OWF_ASSERT(image != NULL && image->data != NULL);
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    if (image->format.linear)
    {
        return;
    }

    ptr = (OWFpixel*) image->data;
    count = image->width * image->height;

    while (count > 0)
    {
        ptr->color.red   = (OWFsubpixel) Linear(ptr->color.red /
                               OWF_RED_MAX_VALUE) * OWF_RED_MAX_VALUE;
        ptr->color.green = (OWFsubpixel) Linear(ptr->color.green /
                               OWF_GREEN_MAX_VALUE) * OWF_GREEN_MAX_VALUE;
        ptr->color.blue  = (OWFsubpixel) Linear(ptr->color.blue /
                               OWF_BLUE_MAX_VALUE) * OWF_BLUE_MAX_VALUE;

        --count;
        ptr += image->pixelSize;
    }

    image->format.linear = OWF_TRUE;

}

/*----------------------------------------------------------------------------*/
#define GAMMA(color, max, gamma) (max * pow(color/max, gamma))

OWF_API_CALL void
OWF_Image_Gamma(OWF_IMAGE* image, OWFfloat gamma)
{
    OWFpixel*            ptr;
    OWFint               count;

    OWF_ASSERT(image != NULL && image->data != NULL);
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    if (gamma == 1.0f)
    {
        return;
    }

    ptr = (OWFpixel*) image->data;
    count = image->width * image->height;

    while (count > 0)
    {
        ptr->color.red   =
            (OWFsubpixel) GAMMA(ptr->color.red, OWF_RED_MAX_VALUE, gamma);
        ptr->color.green =
            (OWFsubpixel) GAMMA(ptr->color.green, OWF_GREEN_MAX_VALUE, gamma);
        ptr->color.blue  =
            (OWFsubpixel) GAMMA(ptr->color.blue, OWF_BLUE_MAX_VALUE, gamma);

        --count;
        ptr++;
    }
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_EdgeReplication(OWF_IMAGE* image)
{
    OWFint          y;
    OWFint          copyStride;
    OWFuint8*       srcPtr      = NULL;
    OWFuint8*       dstPtr      = NULL;
    
    OWF_ASSERT(image);
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    OWF_ASSERT(image->width >= 3 && image->height >= 3);
    
    copyStride = image->width * image->pixelSize;    
    
    /* top side replication */
    srcPtr = (OWFuint8*) image->data;
    srcPtr += 1 * image->stride + 0 * image->pixelSize;
    dstPtr = (OWFuint8*) image->data;

    memcpy(dstPtr, srcPtr, copyStride);
    
    /* bottom side replication */
    srcPtr = (OWFuint8*) image->data;
    srcPtr += (image->height-2) * image->stride + 0 * image->pixelSize;        
    dstPtr = (OWFuint8*) image->data;
    dstPtr += (image->height-1) * image->stride + 0 * image->pixelSize;
    
    memcpy(dstPtr, srcPtr, copyStride);
    
    /* left side replication */
    for (y = 0; y < image->height; y++)
    {
        OWF_Image_SetPixel(image, 0, y, OWF_Image_GetPixelPtr(image, 1, y));
    }
    
    /* right side replication */
    for (y = 0; y < image->height; y++)
    {
        OWF_Image_SetPixel(image, image->width-1, y, OWF_Image_GetPixelPtr(image,
                                                              image->width-2,
                                                              y));
    }   
    
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_SourceFormatConversion(OWF_IMAGE* dst, OWF_IMAGE* src)
{
    OWFint      countY, widthDiff, heightDiff;
    void*       srcLinePtr;
    OWFpixel*   dstLinePtr;
    OWFboolean  replicateEdges = OWF_FALSE;
    

    OWF_ASSERT(dst != 0 && dst->data != NULL);
    OWF_ASSERT(src != 0 && src->data != NULL);
    OWF_ASSERT(dst->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    
    srcLinePtr = src->data;
    dstLinePtr = (OWFpixel*) dst->data;    
    
    /* dst image must either be the same size as the src image or 2 pixels 
       bigger (enough space to perform edge replication) */
    if (dst->width != src->width || dst->height != src->height)
    {
        widthDiff = dst->width - src->width;
        heightDiff = dst->height - src->height;
        
        if (widthDiff == 2 && heightDiff == 2)
        {
            replicateEdges = OWF_TRUE;   
            /* start of the destination buffer should have a 1 pixel offset */
            dstLinePtr = (OWFpixel*) dst->data + 1 * dst->width + 1;
        }
        else
        {
            return OWF_FALSE;
        }
    }

    if (dst->format.pixelFormat != OWF_IMAGE_ARGB_INTERNAL)
    {
        return OWF_FALSE;
    }

    for (countY = src->height; countY; countY--)
    {
        OWFint count = src->width;
        OWFpixel* dstPtr = dstLinePtr;
    
        switch (src->format.pixelFormat)
        {
            case OWF_IMAGE_ARGB8888:
            {
                OWFuint32* srcPtr = (OWFuint32*) srcLinePtr;
    
                while (count > 0)
                {
                    dstPtr->color.alpha = (OWFsubpixel)
                        OWF_ALPHA_MAX_VALUE * ((*srcPtr & ARGB8888_ALPHA_MASK) >> ARGB8888_ALPHA_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr->color.red   = (OWFsubpixel)
                        OWF_RED_MAX_VALUE * ((*srcPtr & ARGB8888_RED_MASK) >> ARGB8888_RED_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr->color.green = (OWFsubpixel)
                        OWF_GREEN_MAX_VALUE * ((*srcPtr & ARGB8888_GREEN_MASK)>> ARGB8888_GREEN_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr->color.blue  = (OWFsubpixel)
                        OWF_BLUE_MAX_VALUE * ((*srcPtr & ARGB8888_BLUE_MASK) >> ARGB8888_BLUE_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr ++;
                    srcPtr ++;
                    count--;
                }
                break;
            }
    
            case OWF_IMAGE_XRGB8888:
            {
                OWFuint32* srcPtr = (OWFuint32*) srcLinePtr;
                
                while (count > 0)
                {
                    dstPtr->color.alpha = OWF_FULLY_OPAQUE;
                    dstPtr->color.red   = (OWFsubpixel)
                        OWF_RED_MAX_VALUE * ((*srcPtr & ARGB8888_RED_MASK) >> ARGB8888_RED_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr->color.green = (OWFsubpixel)
                        OWF_GREEN_MAX_VALUE * ((*srcPtr & ARGB8888_GREEN_MASK) >> ARGB8888_GREEN_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr->color.blue  = (OWFsubpixel)
                        OWF_BLUE_MAX_VALUE * ((*srcPtr & ARGB8888_BLUE_MASK) >> ARGB8888_BLUE_SHIFT) / OWF_BYTE_MAX_VALUE;
                    dstPtr ++;
                    srcPtr ++;
                    count--;
                }
                break;
            }
    
            case OWF_IMAGE_RGB565:
            {
                OWFfloat tmp;
                OWFuint16* srcPtr = (OWFuint16*) srcLinePtr;
                
                while (count > 0)
                {
                    /*
                     * Formula for converting channel value is:
                     * Each channel is multiplied by (2^d - 1)/(2^s -1)
                     * where d is dest channel bits and s is source channel bits.
                     */
                    dstPtr->color.alpha = (OWFsubpixel) OWF_FULLY_OPAQUE;
    
                    tmp = (OWFfloat)((*srcPtr & RGB565_RED_MASK) >> RGB565_RED_SHIFT);
                    dstPtr->color.red   = (OWFsubpixel)OWF_RED_MAX_VALUE * tmp / 31.0f + OWF_SOURCE_CONVERSION_ROUNDING_VALUE;
    
                    tmp = (OWFfloat)((*srcPtr & RGB565_GREEN_MASK) >> RGB565_GREEN_SHIFT);
                    dstPtr->color.green = (OWFsubpixel)OWF_GREEN_MAX_VALUE * tmp / 63.0f + OWF_SOURCE_CONVERSION_ROUNDING_VALUE;
    
                    tmp = (OWFfloat)(*srcPtr & RGB565_BLUE_MASK);
                    dstPtr->color.blue  = (OWFsubpixel)OWF_BLUE_MAX_VALUE * tmp / 31.0f + OWF_SOURCE_CONVERSION_ROUNDING_VALUE;
    
                    dstPtr ++;
                    srcPtr ++;
                    count--;
                }
                break;
            }
    
            default:
            {
                return OWF_FALSE; /* source format not supported */
            }
        }
        
        dstLinePtr+=dst->width;
        srcLinePtr=(OWFuint8*)srcLinePtr+src->stride;
    }
    
    if (replicateEdges)
    {
        OWF_Image_EdgeReplication(dst);
    }

    return OWF_TRUE;
}

/*----------------------------------------------------------------------------*/
OWF_PUBLIC OWFint
OWF_Image_GetStride(OWFint width, const OWF_IMAGE_FORMAT* format, OWFint minimumStride)
{
    OWFint                  size;
    OWFint                  pixelSize;

    OWF_ASSERT(format);

    pixelSize = OWF_Image_GetFormatPixelSize(format->pixelFormat);

    if (pixelSize < 0)
    {
        /* negative pixelSize means that pixel size is a fraction
         * (1/-pixelSize) of a byte, e.g. -8 means pixel has size
         * of one bit. */

        size = (width + 1) / -pixelSize;
    }
    else
    {
        size = width * pixelSize;
    }
    if (size<minimumStride)
    {
        size=minimumStride;
    }
    if (format->rowPadding)
    {
        size += format->rowPadding - 1;
        size -= size % format->rowPadding;
    }
    return size;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_IsValidDestinationFormat(OWF_IMAGE_FORMAT* format)
{
    switch (format->pixelFormat)
    {
        case OWF_IMAGE_ARGB8888:
        {
            return OWF_TRUE;
        }
        case OWF_IMAGE_XRGB8888:
        {
            return OWF_TRUE;
        }
#ifdef SUPPORT_565_OUTPUT
        case OWF_IMAGE_RGB565:
        {
            return OWF_TRUE;
        }
#endif
        default:
        {
            return OWF_FALSE; /* destination format not supported */
        }
    }
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_DestinationFormatConversion(OWF_IMAGE* dst, OWF_IMAGE* src)
{
    OWFint                  countY;
    OWFuint32*              dstPtr;
    OWFpixel*               srcPtr;

    OWF_ASSERT(dst != 0 && dst->data != NULL);
    OWF_ASSERT(src != 0 && src->data != NULL);
    OWF_ASSERT(src->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    if (src->format.pixelFormat != OWF_IMAGE_ARGB_INTERNAL)
    {
        return OWF_FALSE;
    }

    /* src and data must have equal amount of pixels */
    if (dst->width != src->width || dst->height != src->height)
    {
        return OWF_FALSE;
    }

    dstPtr = (OWFuint32*) dst->data;
    srcPtr = (OWFpixel*) src->data;

    if (dst->format.premultiplied && !src->format.premultiplied)
    {
        OWF_Image_PremultiplyAlpha(src);
    }
    else if (!dst->format.premultiplied && src->format.premultiplied)
    {
        OWF_Image_UnpremultiplyAlpha(src);
    }

    for (countY = 0; countY < src->height; countY++)
    {   
        OWFuint8* destination = (OWFuint8*) dst->data;
        destination += countY*dst->stride;
        dstPtr = (OWFuint32*) destination;    
    
        switch (dst->format.pixelFormat)
        {
            case OWF_IMAGE_ARGB8888:
            {
                OWFint countX;
                OWFuint32 dstPixel = 0;               
                
                for (countX = 0; countX < src->width; countX++)
                {
                    dstPixel  = ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.alpha / OWF_ALPHA_MAX_VALUE)) <<
                                ARGB8888_ALPHA_SHIFT);
                    dstPixel |= ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.red / OWF_RED_MAX_VALUE))   <<
                                ARGB8888_RED_SHIFT);
                    dstPixel |= ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.green / OWF_GREEN_MAX_VALUE)) <<
                                ARGB8888_GREEN_SHIFT);
                    dstPixel |= ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.blue / OWF_BLUE_MAX_VALUE))  <<
                                ARGB8888_BLUE_SHIFT);
                    *dstPtr = dstPixel;
                    dstPtr ++;
                    srcPtr ++;
                }
                break;
            }
    
            case OWF_IMAGE_XRGB8888:
            {
                OWFint countX;
                OWFuint32 dstPixel = 0;
                
                for (countX = 0; countX < src->width; countX++)
                {
                    dstPixel  = ARGB8888_ALPHA_MASK;
                    dstPixel |= ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.red / OWF_RED_MAX_VALUE))   <<
                                ARGB8888_RED_SHIFT);
                    dstPixel |= ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.green / OWF_GREEN_MAX_VALUE)) <<
                                ARGB8888_GREEN_SHIFT);
                    dstPixel |= ((OWFuint8)(roundSubPixel(OWF_BYTE_MAX_VALUE * srcPtr->color.blue / OWF_BLUE_MAX_VALUE))  <<
                                ARGB8888_BLUE_SHIFT);
                    *dstPtr = dstPixel;
                    dstPtr ++;
                    srcPtr ++;
                }
                break;
            }
    
            default:
            {
                return OWF_FALSE; /* destination format not supported */
            }
        }
    }

    return OWF_TRUE;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_Init(OWF_IMAGE* image)
{
    OWF_ASSERT(NULL != image);
    memset(image, 0, sizeof(OWF_IMAGE));
}

/*----------------------------------------------------------------------------*/
OWF_PUBLIC OWF_IMAGE*
OWF_Image_Create(OWFint width,
                 OWFint height,
                 const OWF_IMAGE_FORMAT* format,
                 void* buffer,
                 OWFint minimumStride
                 )
{
    OWF_IMAGE*      image;

    image = CREATE(OWF_IMAGE);

    if (image)
    {

        image->format.pixelFormat   = format->pixelFormat;
        image->format.linear        = format->linear;
        image->format.premultiplied = format->premultiplied;
        image->format.rowPadding    = format->rowPadding;

        image->pixelSize    = OWF_Image_GetFormatPixelSize(format->pixelFormat);
        image->width        = width;
        image->height       = height;

        /* stride is row length in bytes (aligned according to image format) */
        image->stride = OWF_Image_GetStride(width, &image->format, minimumStride);
        image->foreign  = (buffer) ? OWF_TRUE : OWF_FALSE;
        image->dataMax = image->stride * height;

        DPRINT(("OWF_Image_Create:"));
        DPRINT(("  Pixel format     = %x", format->pixelFormat));
        DPRINT(("  Linear           = %d", format->linear));
        DPRINT(("  Premultiplied    = %d", format->premultiplied));
        DPRINT(("  Row alignment    = %d bytes", format->rowPadding));
        DPRINT(("  Pixel size       = %d", image->pixelSize));
        DPRINT(("  Width            = %d", image->width));
        DPRINT(("  Height           = %d", image->height));
        DPRINT(("  Stride           = %d", image->stride));
        DPRINT(("  Foreign data     = %d", image->foreign));
        DPRINT(("  Data size        = %d", image->dataMax));
        if (image->dataMax > 0)
        {
            image->data = (image->foreign) ? (buffer) :
                OWF_Image_AllocData(0, width, height, format->pixelFormat);
        }
        else
        {
            /* either overflow occured or width/height is zero */
            if (width && height)
            {
                DPRINT(("OWF_Image_Create: Overflow in image size calculation."));
            }
            else
            {
                DPRINT(("OWF_Image_Create: width or height is zero."));
            }
        }
    }

    if (!(image && image->data && (image->dataMax > 0)))
    {
        DPRINT(("OWF_Image_Create: Image creation failed (image = %p, "
                "data = %p, dataSize = %d)",
                image, image ? (void*)(image->data) : (void*)NULL, image?image->dataMax:-1));
        DESTROY(image);
        return NULL;
    }
    return image;
}

/*----------------------------------------------------------------------------*/
OWF_PUBLIC void OWF_Image_Destroy(OWF_IMAGE* image)
{
    if (image)
        {
        DESTROY(image);
        }
}


/*----------------------------------------------------------------------------*/
OWF_API_CALL OWF_IMAGE*
OWF_Image_Copy(const OWF_IMAGE* image)
{
    OWF_IMAGE* newImage = NULL;

    OWF_ASSERT(image);

    newImage = CREATE(OWF_IMAGE);
    if (newImage)
    {
        memcpy(newImage, image, sizeof(*newImage));
        if (!image->foreign)
        {
            newImage->data = xalloc(1, image->dataMax);
            if (newImage->data)
            {
                memcpy(newImage->data,
                       image->data,
                       image->height*image->stride);
            }
        }
    }

    return newImage;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_SetSize(OWF_IMAGE* image,
                  OWFint width,
                  OWFint height)
{
    OWFint                  size;
    OWFint                  stride;

    OWF_ASSERT(image);
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL ||
            image->format.pixelFormat==OWF_IMAGE_L32);
    
    /** note that this setsize ignores any specialised stride **/
    stride = OWF_Image_GetStride(width, &image->format, 0);
    size = height * stride;

    /* change source size if buffer have enough space */
    if (size > 0 && size <= image->dataMax)
    {
        image->height = height;
        image->width  = width;
        image->stride = stride;
        return OWF_TRUE;
    }
    return OWF_FALSE;
}
/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_SetFlags(OWF_IMAGE* image,
                  OWFboolean premultiply,
                  OWFboolean linear)
    {
    OWF_ASSERT(image);
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL ||
            image->format.pixelFormat==OWF_IMAGE_L32);
    image->format.linear=linear;
    image->format.premultiplied=premultiply;
    }

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_SetPixelBuffer(OWF_IMAGE* image,      void* buffer)
    {
    OWF_ASSERT(image);
    OWF_ASSERT(buffer);
    OWF_ASSERT(image->foreign);
    if (image->foreign)
        {
        image->data=buffer;
        }
    }
/*----------------------------------------------------------------------------*/
/* NEVER USED
OWF_API_CALL OWFboolean
OWF_Image_SetPixelData(OWF_IMAGE* image,
                       OWFint width,
                       OWFint height,
                       const OWF_IMAGE_FORMAT* format,
                       void* buffer)
{
    OWFint                  size = 0,
                            stride = 0;

    OWF_ASSERT(image && format);

    stride  = OWF_Image_GetStride(width, format);
    size    = height * stride;

    if (size <= 0)
    {
        return OWF_FALSE; 
    }

    if (!image->foreign)
    {
        OWF_Image_FreeData(0, &image->data);
    }

    image->format.pixelFormat   = format->pixelFormat;
    image->format.linear        = format->linear;
    image->format.premultiplied = format->premultiplied;
    image->format.rowPadding    = format->rowPadding;

    image->pixelSize    = OWF_Image_GetFormatPixelSize(format->pixelFormat);
    image->width        = width;
    image->height       = height;
    image->stride       = stride;
    image->foreign      = OWF_TRUE;
    image->dataMax      = size;
    image->data         = buffer;

    return OWF_TRUE;
}
*/
/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_Blit(OWF_IMAGE* dst,
               OWF_RECTANGLE const* dstRect,
               OWF_IMAGE const* src,
               OWF_RECTANGLE const* srcRect)
{
    OWF_RECTANGLE           bounds, rect, drect, srect;
    OWFint                  lineCount   = 0;
    OWFint                  copyStride  = 0;
    OWFuint8*               srcPtr      = NULL;
    OWFuint8*               dstPtr      = NULL;

    OWF_ASSERT(dst != 0 && dst->data != NULL);
    OWF_ASSERT(src != 0 && src->data != NULL);
    OWF_ASSERT(dst->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    OWF_ASSERT(src->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    OWF_Rect_Set(&bounds, 0, 0, dst->width, dst->height);
    OWF_Rect_Set(&rect,
                 dstRect->x, dstRect->y,
                 srcRect->width, srcRect->height);
    OWF_Rect_Set(&srect,
                 srcRect->x, srcRect->y,
                 srcRect->width, srcRect->height);

    /* clip destination rectangle against bounds */
    if (!OWF_Rect_Clip(&drect, &rect, &bounds))
    {
        return OWF_FALSE;
    }

    if (src->pixelSize != dst->pixelSize)
    {
        DPRINT(("OWF_Image_Blit(): pixels sizes differ\n"));
        return OWF_FALSE;
    }

    lineCount = srect.height;
    copyStride = srect.width * src->pixelSize;

    /* use bytepointers in copy  - generic */
    srcPtr = (OWFuint8*) src->data;
    srcPtr += srect.y * src->stride + srect.x * src->pixelSize;
    dstPtr = (OWFuint8*)dst->data;
    dstPtr += drect.y * dst->stride + drect.x * src->pixelSize;

    while (lineCount > 0)
    {
        --lineCount;

        memcpy(dstPtr, srcPtr, copyStride);

        srcPtr += src->stride;
        dstPtr += dst->stride;
    }

    return OWF_TRUE;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFpixel*
OWF_Image_GetPixelPtr(OWF_IMAGE* image,
                      OWFint x,
                      OWFint y)
{
    OWF_ASSERT(image && image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    if (!(image && image->data))
    {
        return 0;
    }

    x = CLAMP(x, 0, image->width-1);
    y = CLAMP(y, 0, image->height-1);

    return (OWFpixel*)image->data + y * image->width  + x ;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_GetPixel(OWF_IMAGE* image,
                   OWFint x,
                   OWFint y,
                   OWFpixel* pixel)
{
    OWFpixel*               temp = NULL;

    OWF_ASSERT(pixel);

    if (!(image && image->data))
    {
        return;
    }
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    pixel->color.alpha = 0;
    pixel->color.red = 0;
    pixel->color.green = 0;
    pixel->color.blue = 0;

    if (x < 0 || y < 0 || x >= image->width || y >= image->height)
    {
        return;
    }

    temp = (OWFpixel*)image->data + y * image->width  + x;

    pixel->color.alpha = temp->color.alpha;
    pixel->color.red = temp->color.red;
    pixel->color.green = temp->color.green;
    pixel->color.blue = temp->color.blue;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_SetPixel(OWF_IMAGE* image,
                   OWFint x,
                   OWFint y,
                   OWFpixel const* pixel)
{
    OWFpixel*               data = NULL;

    if (!(image && image->data))
    {
        return;
    }
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    if(x < 0 || y < 0 || x >= image->width || y >= image->height)
    {
        return;
    }


    data = (OWFpixel*)image->data + y * image->width + x;

    data->color.red = pixel->color.red;
    data->color.green = pixel->color.green;
    data->color.blue = pixel->color.blue;
    data->color.alpha = pixel->color.alpha;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_PointSamplingStretchBlit(OWF_IMAGE* dst,
                                   OWF_RECTANGLE* dstRect,
                                   OWF_IMAGE* src,
                                   OWFfloat* srcRect)
{
    OWFint                  ox = 0, oy = 0;
    OWFfloat                dx = 0.f, dy = 0.f; 
    OWFint                  x, y;

    /* images must be valid */
    if (!((src != NULL) && (src->data != NULL) &&
          (dst != NULL) && (dst->data != NULL)))
    {
        return OWF_FALSE;
    }
    OWF_ASSERT(src->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    OWF_ASSERT(dst->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    /* ditto with rectangles, too */
    if (!((dstRect != NULL) && (dstRect->width && dstRect->height) &&
          (srcRect != NULL) && (srcRect[2] && srcRect[3])))
    {
        return OWF_FALSE;
    }

    /* Note: bounds check missing */

    if (src->pixelSize != dst->pixelSize)
    {
        return OWF_FALSE;
    }

    /* solve scaling ratios for image */
    dx = (OWFfloat) srcRect[2] / (OWFfloat) dstRect->width;
    dy = (OWFfloat) srcRect[3] / (OWFfloat) dstRect->height;

    for (y = 0; y < dstRect->height; y++)
    {
        for (x = 0; x < dstRect->width; x++) 
        {
            OWFpixel*       pixel;         
            
            /* NOTE This code uses pixel center points to calculate distances
                    and factors. Results can differ slightly when pixel corner
                    coordinates are used */            
            
            /* coordinates of nearest pixel in original image */
            ox = (int) floor((((OWFfloat) x + 0.5) * dx) + srcRect[0]);
            oy = (int) floor((((OWFfloat) y + 0.5) * dy) + srcRect[1]);     
            
            pixel = OWF_Image_GetPixelPtr(src,
                                          ox,
                                          oy);
            
            OWF_Image_SetPixel(dst,
                               dstRect->x + x,
                               dstRect->y + y,
                               pixel);
            
        }
    }
    return OWF_TRUE;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_BilinearStretchBlit(OWF_IMAGE* dst,
                              OWF_RECTANGLE* dstRect,
                              OWF_IMAGE* src,
                              OWFfloat* srcRect)
{
    OWFint                  x = 0, y = 0;
    OWFint                  ox = 0, oy = 0;
    OWFfloat                dx = 0.f, dy = 0.f, wx = 0.f, wy = 0.f;
    OWFfloat                w[2 * 2];
    OWFpixel*               sample[4];
    OWFpixel*               pixel = NULL;

    /* images must be valid */
    if (!((src != NULL) && (src->data != NULL) &&
          (dst != NULL) && (dst->data != NULL)))
    {
        return OWF_FALSE;
    }
    OWF_ASSERT(src->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    OWF_ASSERT(dst->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    /* ditto with rectangles, too */
    if (!((dstRect != NULL) && (dstRect->width && dstRect->height) &&
          (srcRect != NULL) && (srcRect[2] && srcRect[3])))
    {
        return OWF_FALSE;
    }

    if (src->pixelSize != dst->pixelSize)
    {
        return OWF_FALSE;
    }

    /* solve scaling ratios for image */
    dx = (OWFfloat) srcRect[2] / (OWFfloat) dstRect->width;
    dy = (OWFfloat) srcRect[3] / (OWFfloat) dstRect->height;

    for (y = 0; y < dstRect->height; y++)
    {
        for (x = 0; x < dstRect->width; x++)
        {
            OWFfloat tempOx, tempOy;            
            
            /* NOTE This code uses pixel center points to calculate distances
                    and factors. Results can differ slightly when pixel corner
                    coordinates are used */

            /* coordinates of nearest pixel in original image */
            tempOx = (((OWFfloat) x + 0.5) * dx) + srcRect[0];
            tempOy = (((OWFfloat) y + 0.5) * dy) + srcRect[1];            
            
            ox = (int) floor((((OWFfloat) x + 0.5) * dx) + srcRect[0]);
            oy = (int) floor((((OWFfloat) y + 0.5) * dy) + srcRect[1]);    

            /* Distances to nearest pixel, eg. fractional part of coordinate */    
            wx = (OWFfloat) ox + 0.5 - tempOx;
            wy = (OWFfloat) oy + 0.5 - tempOy;

            /* If distance is positive, we should use left or upper pixel for
             * second nearest pixel. */
            if (wx > 0.0)
            {
                ox--;
                wx = 1.0 - wx;
            }
            else
            {
                wx = -wx; /* abs */
            }

            if (wy > 0.0)
            {
                oy--;
                wy = 1.0 - wy;
            }
            else
            {
                wy = -wy;
            }
            
            /* Calculate weights for samples */
            w[0] = (1.0 - wx) * (1.0 - wy);
            w[1] = wx * (1.0 - wy);
            w[2] = (1.0 - wx) * wy;
            w[3] = wx * wy;

            /* get sample */
            sample[0] = OWF_Image_GetPixelPtr(src, ox + 0, oy + 0);
            sample[1] = OWF_Image_GetPixelPtr(src, ox + 1, oy + 0);
            sample[2] = OWF_Image_GetPixelPtr(src, ox + 0, oy + 1);
            sample[3] = OWF_Image_GetPixelPtr(src, ox + 1, oy + 1);

            
            /* get result pixel */
            pixel = OWF_Image_GetPixelPtr(dst, x, y);

            /* calculate final color */
            pixel->color.red   =
                sample[0]->color.red * w[0] + sample[1]->color.red * w[1] +
                sample[2]->color.red * w[2] + sample[3]->color.red * w[3] + OWF_BILINEAR_ROUNDING_VALUE;

            pixel->color.green =
                sample[0]->color.green * w[0] + sample[1]->color.green * w[1] +
                sample[2]->color.green * w[2] + sample[3]->color.green * w[3] + OWF_BILINEAR_ROUNDING_VALUE;

            pixel->color.blue  =
                sample[0]->color.blue * w[0] + sample[1]->color.blue * w[1] +
                sample[2]->color.blue * w[2] + sample[3]->color.blue * w[3] + OWF_BILINEAR_ROUNDING_VALUE;

            pixel->color.alpha =
                sample[0]->color.alpha * w[0] + sample[1]->color.alpha * w[1] +
                sample[2]->color.alpha * w[2] + sample[3]->color.alpha * w[3] + OWF_BILINEAR_ROUNDING_VALUE;
        }
    }
    return OWF_TRUE;

}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_Stretch(OWF_IMAGE* dst,
                  OWF_RECTANGLE* dstRect,
                  OWF_IMAGE* src,
                  OWFfloat* srcRect,
                  OWF_FILTERING filter)
{
    OWFboolean          result = OWF_FALSE;

    switch (filter)
    {
        case OWF_FILTER_POINT_SAMPLING:
        {
            result = OWF_Image_PointSamplingStretchBlit(dst, dstRect, src,
                                                        srcRect);
            break;
        }
        case OWF_FILTER_BILINEAR:
        {
            result = OWF_Image_BilinearStretchBlit(dst, dstRect, src, srcRect);
            break;
        }
    }

    return result;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_Clear(OWF_IMAGE* image,
                OWFsubpixel red,
                OWFsubpixel green,
                OWFsubpixel blue,
                OWFsubpixel alpha)
{
    OWFint                  i, numPixels;
    OWFpixel*                pixels;

    OWF_ASSERT(image != 0);
    OWF_ASSERT(image->data != 0);
    OWF_ASSERT(image->format.pixelFormat == OWF_IMAGE_ARGB_INTERNAL);

    numPixels = image->width * image->height;
    pixels = (OWFpixel*) image->data;

    for (i = 0; i < numPixels; i++)
    {
        pixels[i].color.red   = (OWFsubpixel) red;
        pixels[i].color.green = (OWFsubpixel) green;
        pixels[i].color.blue  = (OWFsubpixel) blue;
        pixels[i].color.alpha = (OWFsubpixel) alpha;
    }
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_PremultiplyAlpha(OWF_IMAGE* image)
{
    OWFint       x, y;

    OWF_ASSERT(image != 0);

    if (image->format.premultiplied)
    {
        return;
    }

    /* only for internal format */
    if (image->format.pixelFormat != OWF_IMAGE_ARGB_INTERNAL)
    {
        return;
    }

    for (y = 0; y < image->height; y++)
    {
        for (x = 0; x < image->width; x++)
        {
            OWFpixel* pixel;
            OWFsubpixel alpha;

            pixel = OWF_Image_GetPixelPtr(image, x, y);

            alpha = pixel->color.alpha;

            if (0 == alpha)
            {
                pixel->color.red =
                pixel->color.green =
                pixel->color.blue = 0;
            }
            else
            {
                pixel->color.red    = (pixel->color.red * alpha + OWF_PREMUL_ROUNDING_FACTOR ) /
                                      OWF_ALPHA_MAX_VALUE;
                pixel->color.green  = (pixel->color.green * alpha + OWF_PREMUL_ROUNDING_FACTOR ) /
                                      OWF_ALPHA_MAX_VALUE;
                pixel->color.blue   = (pixel->color.blue * alpha + OWF_PREMUL_ROUNDING_FACTOR ) /
                                      OWF_ALPHA_MAX_VALUE;
            }
        }
    }

    image->format.premultiplied = OWF_TRUE;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_UnpremultiplyAlpha(OWF_IMAGE* image)
{
    OWFint       count;
    OWFpixel*    pixelPtr;

    OWF_ASSERT(image != 0);

    if (!image->format.premultiplied)
    {
        return;
    }

    /* only for internal format */
    if (image->format.pixelFormat != OWF_IMAGE_ARGB_INTERNAL)
    {
        return;
    }

    count = image->width * image->height;
    pixelPtr = (OWFpixel*)image->data;

    while (count > 0)
    {

        OWFsubpixel a = pixelPtr->color.alpha;

        #ifdef OWF_IMAGE_INTERNAL_PIXEL_IS_FLOAT
			OWF_ASSERT(a <= OWF_ALPHA_MAX_VALUE && a >= OWF_ALPHA_MIN_VALUE);
		#endif

        if (a > OWF_ALPHA_MIN_VALUE)
        {
            OWFsubpixel r = pixelPtr->color.red   * OWF_RED_MAX_VALUE / a;
            OWFsubpixel g = pixelPtr->color.green * OWF_GREEN_MAX_VALUE / a;
            OWFsubpixel b = pixelPtr->color.blue  * OWF_BLUE_MAX_VALUE / a;

            pixelPtr->color.red   = r;
            pixelPtr->color.green = g;
            pixelPtr->color.blue  = b;

            --count;
            pixelPtr++;
        }
    }

    image->format.premultiplied = OWF_TRUE;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_Rotate(OWF_IMAGE* dst,
                 OWF_IMAGE* src,
                 OWF_ROTATION rotation)
{
    OWFint                  ox = 0, oy = 0,
                            w = 0, h = 0,
                            x = 0, y = 0;
    OWFint                  xx = 0, xy = 0,
                            yx = 0, yy = 0;

    OWF_ASSERT(src && src->data);
    OWF_ASSERT(dst && dst->data);
    OWF_ASSERT(src->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);
    OWF_ASSERT(dst->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    w = src->width;
    h = src->height;

    switch (rotation)
    {
        case OWF_ROTATION_0:
        {
            /*
             * origin: (0, 0)
             * x-axis: (1, 0)
             * y-axis: (0, 1)
             */
            ox = 0;
            oy = 0;
            xx = 1;
            xy = 0;
            yx = 0;
            yy = 1;
            break;
        }
        case OWF_ROTATION_90:
        {
            /*
             * origin: (height-1, 0)
             * x-axis: (0, 1)
             * y-axis: (-1, 0)
             */
            ox = h - 1;
            oy = 0;
            xx = 0;
            xy = 1;
            yx = -1;
            yy = 0;
            break;
        }
        case OWF_ROTATION_180:
        {
            /*
             * origin: (width-1, height-1)
             * x-axis: (-1, 0)
             * y-axis: (0, -1)
             */
            ox = w - 1;
            oy = h - 1;
            xx = -1;
            xy = 0;
            yx = 0;
            yy = -1;
            break;
        }
        case OWF_ROTATION_270:
        {
            /*
             * origin: (0, height-1)
             * x-axis: (0, -1)
             * y-axis: (1, 0)
             */
            ox = 0;
            oy = w - 1;
            xx = 0;
            xy = -1;
            yx = 1;
            yy = 0;
            break;

        }
    }

    for (y = 0; y < h; y++)
    {
        for (x = 0; x < w; x++)
        {
        /*
         * O = [ox oy]' X = [xx xy]' Y = [yx yy]'
         *
         * p_dst(x_src,y_src) = O + x_src*X + y_src*Y
         */
        OWF_Image_SetPixel(dst,
                           ox + x * xx + y * yx,
                           oy + x * xy + y * yy,
                           OWF_Image_GetPixelPtr(src, x, y));
        }
    }
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_Flip(OWF_IMAGE* image,
               OWF_FLIP_DIRECTION dir)
{
    OWFint                  x, y;

    if (!image)
    {
        return;
    }
    OWF_ASSERT(image->format.pixelFormat==OWF_IMAGE_ARGB_INTERNAL);

    if (dir & OWF_FLIP_VERTICALLY)
    {
        OWFint h = image->height/2;

        for (y = 0; y < h; y++)
        {
            for (x = 0; x < image->width; x++)
            {
                OWFpixel        pix;

                OWF_Image_GetPixel(image, x, y, &pix);
                OWF_Image_SetPixel(image, x, y,
                                   OWF_Image_GetPixelPtr(image,
                                                         x,
                                                         image->height - 1 - y
                                                         ));
                OWF_Image_SetPixel(image, x, image->height - 1 - y, &pix);
            }
        }
    }

    if (dir & OWF_FLIP_HORIZONTALLY)
    {
        OWFint w = image->width/2;

        for (y = 0; y < image->height; y++)
        {
            for (x = 0; x < w; x++)
            {
                OWFpixel        pix;

                OWF_Image_GetPixel(image, x, y, &pix);
                OWF_Image_SetPixel(image, x, y,
                                   OWF_Image_GetPixelPtr(image,
                                                         image->width - 1 - x,
                                                         y
                                                         ));
                OWF_Image_SetPixel(image, image->width - 1 - x, y, &pix);
            }
        }
    }
}

/*----------------------------------------------------------------------------*/
#define BLENDER_INNER_LOOP_BEGIN \
    OWFint rowCount = drect.height; \
    while (rowCount > 0) { \
        OWFint colCount = drect.width; \
        while (colCount > 0) { \
            if (!(blend->tsColor && COLOR_MATCH(SC, TSC))) \
            { \

#define BLENDER_INNER_LOOP_END \
                DA = blend->destinationFullyOpaque ? OWF_FULLY_OPAQUE : DA; \
            } /* end tsColor check */ \
            srcPtr ++; \
            dstPtr ++; \
            maskPtr++; \
            --colCount; \
        } \
        srcPtr += srcLineDelta; \
        dstPtr += dstLineDelta; \
        maskPtr += maskLineDelta; \
        --rowCount; \
    }
	
#define BLENDER_INNER_LOOP_END_NO_MASK \
                DA = blend->destinationFullyOpaque ? OWF_FULLY_OPAQUE : DA; \
            } /* end tsColor check */ \
            srcPtr ++; \
            dstPtr ++; \
            --colCount; \
        } \
        srcPtr += srcLineDelta; \
        dstPtr += dstLineDelta; \
        --rowCount; \
    }

#define TSC blend->tsColor->color
#define SC srcPtr->color

/* Note: actually would be better to compare integer values
 * for TSC match -> eliminate float arithmetic pitfalls
 */
#define COLOR_MATCH(x, y) (x.red==y.red && x.green==y.green && x.blue==y.blue)

#define SA srcPtr->color.alpha
#define SR srcPtr->color.red
#define SG srcPtr->color.green
#define SB srcPtr->color.blue

#define DA dstPtr->color.alpha
#define DR dstPtr->color.red
#define DG dstPtr->color.green
#define DB dstPtr->color.blue

#define MA *maskPtr
#define GA blend->globalAlpha

OWF_API_CALL void
OWF_Image_Blend(OWF_BLEND_INFO* blend,
                OWF_TRANSPARENCY transparency)
{
    OWF_IMAGE*              dst;
    OWF_IMAGE*              src;
    OWF_IMAGE*              mask;
    OWF_RECTANGLE*          srcRect;
    OWF_RECTANGLE*          dstRect;
    OWF_RECTANGLE           bounds, srect, drect, rect;
    OWFint                  srcLineDelta, dstLineDelta, maskLineDelta;
    OWFpixel*               srcPtr;
    OWFpixel*               dstPtr;
    OWFsubpixel*            maskPtr;
    
    
    /* preparation */
    OWF_ASSERT(blend);
    DPRINT(("OWF_Image_Blend: transparency = %d", transparency));
    /* Mask must be set if mask-transparency is used */
    OWF_ASSERT(((transparency & OWF_TRANSPARENCY_MASK) && blend->mask) ||
           !(transparency & OWF_TRANSPARENCY_MASK));

    OWF_ASSERT(blend->source.image->format.pixelFormat == OWF_IMAGE_ARGB_INTERNAL);
    OWF_ASSERT(blend->destination.image->format.pixelFormat == OWF_IMAGE_ARGB_INTERNAL);
    if (blend->mask)
    {
        OWF_ASSERT(blend->mask->format.pixelFormat == OWF_IMAGE_L32);
    }
    
    dst     = blend->destination.image;
    src     = blend->source.image;
    mask    = blend->mask;
    dstRect = blend->destination.rectangle;
    srcRect = blend->source.rectangle;    

    /* this is actually asserted above */
    if (OWF_TRANSPARENCY_MASK == (transparency & OWF_TRANSPARENCY_MASK) &&
        NULL == mask)
    {
        return;
    }

    OWF_Rect_Set(&bounds, 0, 0, dst->width, dst->height);
    /* NOTE: src and dst rects should be of same size!!! */
    OWF_Rect_Set(&rect,
                 dstRect->x, dstRect->y,
                 dstRect->width, dstRect->height);
    OWF_Rect_Set(&srect,
                 srcRect->x, srcRect->y,
                 srcRect->width, srcRect->height);

    /* clip destination rectangle against bounds */
    if (!OWF_Rect_Clip(&drect, &rect, &bounds))
    {
        return;
    }

    /* adjust source rectangle if needed */
    if (drect.x > rect.x)
    {
        OWFint dx = drect.x - rect.x;
        srect.x += dx;
        srect.width -= dx;
    }

    if (drect.y > rect.y)
    {
        OWFint dy = drect.y - rect.y;
        srect.y += dy;
        srect.height -= dy;
    }

    if (drect.width < srect.width)
    {
        srect.width = drect.width;
    }

    if (drect.height < srect.height)
    {
        srect.height = drect.height;
    }


    srcPtr  = (OWFpixel*) src->data;
    srcPtr  += srect.y * src->width + srect.x;
    dstPtr  = (OWFpixel*) dst->data;
    dstPtr  += drect.y * dst->width + drect.x;

    if (mask)
    {
        maskPtr = (OWFsubpixel*) mask->data + srect.y * mask->width + srect.x;
        maskLineDelta = mask->width - drect.width;
    }
    else
    {
        maskPtr = 0;
        maskLineDelta = 0;
    }
    srcLineDelta = src->width - srect.width;
    dstLineDelta = dst->width - drect.width;

    /* inner loops */
    switch (transparency)
    {
        case OWF_TRANSPARENCY_NONE:
        {
            /*
            rgb     = src.rgb
            alpha    = 1
            */
            BLENDER_INNER_LOOP_BEGIN;
                DR = SR;
                DG = SG;
                DB = SB;
                DA = OWF_FULLY_OPAQUE;
            BLENDER_INNER_LOOP_END_NO_MASK;
            break;
        }

        case OWF_TRANSPARENCY_GLOBAL_ALPHA:
        {
            /*
            rgb        = src.rgb * elem.alpha + dst.rgb * (1 - elem.alpha)
            alpha   = elem.alpha + dst.alpha * (1 - elem.alpha)
            */
            BLENDER_INNER_LOOP_BEGIN;
                DR = (SR * GA + DR * (OWF_FULLY_OPAQUE - GA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DG = (SG * GA + DG * (OWF_FULLY_OPAQUE - GA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DB = (SB * GA + DB * (OWF_FULLY_OPAQUE - GA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DA = GA + (DA * (OWF_FULLY_OPAQUE - GA) + OWF_BLEND_ROUNDING_VALUE) / 
                     OWF_ALPHA_MAX_VALUE;
            BLENDER_INNER_LOOP_END_NO_MASK;
            break;
        }

        case OWF_TRANSPARENCY_SOURCE_ALPHA:
        {
            /*
            rgb     = src.rgb + dst.rgb * (1 - src.alpha)
            alpha    = src.alpha + dst.alpha * (1 - src.alpha)
            */
            BLENDER_INNER_LOOP_BEGIN;
                DR = SR + (DR * (OWF_FULLY_OPAQUE - SA) + OWF_BLEND_ROUNDING_VALUE) / OWF_ALPHA_MAX_VALUE;
                DG = SG + (DG * (OWF_FULLY_OPAQUE - SA) + OWF_BLEND_ROUNDING_VALUE) / OWF_ALPHA_MAX_VALUE;
                DB = SB + (DB * (OWF_FULLY_OPAQUE - SA) + OWF_BLEND_ROUNDING_VALUE) / OWF_ALPHA_MAX_VALUE;
                DA = SA + (DA * (OWF_FULLY_OPAQUE - SA) + OWF_BLEND_ROUNDING_VALUE) / OWF_ALPHA_MAX_VALUE;
            BLENDER_INNER_LOOP_END_NO_MASK;
            break;
        }

        case OWF_TRANSPARENCY_MASK:
        {
            /*
            rgb     = src.rgb * mask.alpha + dst.rgb * (1 - mask.alpha)
            alpha    = mask.alpha + dst.alpha * (1 - mask.alpha)
            */
            BLENDER_INNER_LOOP_BEGIN;
                DR = (SR * MA + DR * (OWF_FULLY_OPAQUE - MA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DG = (SG * MA + DG * (OWF_FULLY_OPAQUE - MA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DB = (SB * MA + DB * (OWF_FULLY_OPAQUE - MA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DA = MA + (DA * (OWF_FULLY_OPAQUE - MA) + OWF_BLEND_ROUNDING_VALUE) / 
                     OWF_ALPHA_MAX_VALUE;
            BLENDER_INNER_LOOP_END;
            break;
        }

        case OWF_TRANSPARENCY_GLOBAL_ALPHA | OWF_TRANSPARENCY_SOURCE_ALPHA:
        {
            /*
            rgb = src.rgb * elem.a + dst.rgb * (1 - src.a * elem.a)
            a = src.a * elem.a + dst.a * (1 - src.a * elem.a)
            */
            OWFsubpixel         SAEA;

            BLENDER_INNER_LOOP_BEGIN;
                SAEA = SA * GA / OWF_ALPHA_MAX_VALUE;
                DR = (SR * GA + DR * (OWF_FULLY_OPAQUE - SAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DG = (SG * GA + DG * (OWF_FULLY_OPAQUE - SAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DB = (SB * GA + DB * (OWF_FULLY_OPAQUE - SAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DA = SAEA + (DA * (OWF_FULLY_OPAQUE - SAEA) + OWF_BLEND_ROUNDING_VALUE) /
                         OWF_ALPHA_MAX_VALUE;
            BLENDER_INNER_LOOP_END_NO_MASK;
            break;
        }

        case OWF_TRANSPARENCY_GLOBAL_ALPHA | OWF_TRANSPARENCY_MASK:
        {
            /*
            rgb    = src.rgb * mask.a * elem.a + dst.rgb * (1 - mask.a * elem.a)
            a     = mask.a * elem.a + dest.a * (1 - mask.a * elem.a)
            */
            OWFsubpixel             MAEA;

            BLENDER_INNER_LOOP_BEGIN;
                MAEA = MA * GA / OWF_ALPHA_MAX_VALUE;
                DR = (SR * MAEA + DR * (OWF_FULLY_OPAQUE - MAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DG = (SG * MAEA + DG * (OWF_FULLY_OPAQUE - MAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DB = (SB * MAEA + DB * (OWF_FULLY_OPAQUE - MAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                DA = MAEA + (DA * (OWF_FULLY_OPAQUE - MAEA) + OWF_BLEND_ROUNDING_VALUE) /
                     OWF_ALPHA_MAX_VALUE;
                //No need to check with OWF_ALPHA_MIN_VALUE as it is zero
                OWF_ASSERT(GA <= OWF_ALPHA_MAX_VALUE);
            BLENDER_INNER_LOOP_END;
            break;
        }

        default:
        {
            DPRINT(("OWF_Image_Blend: whooops. invalid blending mode\n"));
            abort();
            break;
        }
    }
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void*
OWF_Image_AllocData(OWF_DISPCTX dc, OWFint width, OWFint height, OWF_PIXEL_FORMAT pixelFormat)
{
    OWF_IMAGE_FORMAT        imgf;
    OWFint                  stride;

    /* kludge. GetStride need pixelFormat. */
    imgf.pixelFormat = pixelFormat;
    imgf.rowPadding = OWF_Image_GetFormatPadding(pixelFormat);
    stride = OWF_Image_GetStride(width, &imgf, 0);

    OWF_ASSERT (width > 0 && height > 0);

    if (stride == 0)
    {
        return NULL;
    }

    if (dc)
        {
        return OWF_DisplayContext_ScratchBuffer_Allocate(dc, height * stride);
        }
    
    return xalloc(1, height * stride);
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_FreeData(OWF_DISPCTX dc, void** data)
{
    if (*data)
    {
    if (dc)
        {
        OWF_DisplayContext_ScratchBuffer_Destroy(dc, *data);
        }
    else
        {
        xfree(*data);
        }
    }
    *data = NULL;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFint
OWF_Image_GetFormatPixelSize(OWF_PIXEL_FORMAT format)
{
    switch (format)
    {
        case OWF_IMAGE_ARGB_INTERNAL:
        {
            return sizeof(OWFpixel);
        }

        case OWF_IMAGE_ARGB8888:
        case OWF_IMAGE_XRGB8888:
        case OWF_IMAGE_L32:
        {
            return 4;
        }

        case OWF_IMAGE_RGB888:
        {
            return 3;
        }

        case OWF_IMAGE_RGB565:
        case OWF_IMAGE_L16:
        {
            return 2;
        }

        case OWF_IMAGE_L8:
        {
            return 1;
        }

        case OWF_IMAGE_L1:
        {
            /* Use negative numbers for divisor, e.g., -8 = 1/8. */
            /* L1 is 1 bit alpha, LSB->MSB, each row padded to 32-bit
             * boundary. */
            return -8;
        }
        default:
        {
            return 0;
        }
    }
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFint
OWF_Image_GetFormatPadding(OWF_PIXEL_FORMAT format)
{
    OWFint                  padding = 1;

    switch (format)
    {
        case OWF_IMAGE_ARGB_INTERNAL:
        {
            padding = sizeof(OWFpixel);
            break;
        }

        case OWF_IMAGE_ARGB8888:
        case OWF_IMAGE_XRGB8888:
        case OWF_IMAGE_L32:
        {
            padding = 4;
            break;
        }

        /*
        case OWF_IMAGE_RGB888:
        {
            return 3;
        }
        */

        case OWF_IMAGE_RGB565:
        case OWF_IMAGE_L16:
        {
            padding = 2;
            break;
        }

        case OWF_IMAGE_L8:
        {
            padding = 1;
            break;
        }

        case OWF_IMAGE_L1:
        {
            /* Use negative numbers for divisor, e.g., -8 = 1/8. */
            /* L1 is 1 bit alpha, LSB->MSB, each row padded to 32-bit
             * boundary. */
            padding = 4;
            break;
        }
        default:
        {
			OWF_ASSERT(0);
            break;
        }
    }


    return padding;
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL void
OWF_Image_SwapWidthAndHeight(OWF_IMAGE* image)
{
    /* swap w & h. Note that original stride is not restored if swap back. */
    image->width ^= image->height;
    image->height ^= image->width;
    image->width ^= image->height;

    image->stride = OWF_Image_GetStride(image->width, &image->format, 0);
}

/*----------------------------------------------------------------------------*/
OWF_API_CALL OWFboolean
OWF_Image_ConvertMask(OWF_IMAGE* output, OWF_IMAGE* input)
{
    OWFboolean              result = OWF_TRUE;
    void*                   srcLinePtr;
    OWFsubpixel*            dstLinePtr;
    OWFint                  countY;

    DPRINT(("OWF_Image_ConvertMask:"));
    DPRINT(("  Converting mask from stream format to internal 8-bit"));

    OWF_ASSERT(input);
    OWF_ASSERT(output);
    
    srcLinePtr = input->data;
    dstLinePtr = (OWFsubpixel*) output->data;    

    for (countY = input->height; countY; countY--)
    {
        OWFsubpixel* dstData = dstLinePtr;    
    
        switch (input->format.pixelFormat)
        {
            case OWF_IMAGE_L1:
            {
                OWFint          countX;
                OWFuint8*       srcData = (OWFuint8*) srcLinePtr;
    
                DPRINT(("1-bit alpha, width = %d, height = %d",
                       input->width, input->height));
                
                for (countX = 0; countX < input->width; countX++)
                {
                    /*
                     * alpha pixel ordering is LSB -> MSB
                     *
                     * byte# |----- byte 0 ----|----- byte 1-----|--
                     * bit#  | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
                     * pix#  | 7 6 5 4 3 2 1 0 | f e d c b a 9 8 | ...
                     */
                    if (srcData[countX >> 3] & (1 << (countX & 7)))
                    {
                        dstData[countX] = OWF_FULLY_OPAQUE;
                    }
                    else
                    {
                        dstData[countX] = OWF_FULLY_TRANSPARENT;
                    }
                }           
                break;
            }
            case OWF_IMAGE_L8:
            {
                OWFint          countX;
                OWFuint8*       srcData = (OWFuint8*) srcLinePtr;
    
                DPRINT(("8-bit alpha, width = %d, height = %d",
                       input->width, input->height));
                
                for (countX = 0; countX < input->width; countX++)
                {
                    dstData[countX] = srcData[countX] * OWF_FULLY_OPAQUE /
                                 (OWFfloat) OWF_BYTE_MAX_VALUE;                
                }
                break;
            }
            case OWF_IMAGE_ARGB8888:
            {
                /* ARGB image - take the alpha channel and discard
                 * everything else */
                OWFint          countX;
                OWFuint32*      srcData = (OWFuint32*) srcLinePtr;
    
                DPRINT(("32-bit ARGB, width = %d, height = %d",
                       input->width, input->height));
                
                for (countX = 0; countX < input->width; countX++)
                {
                    dstData[countX] = (srcData[countX] >> 24) * OWF_FULLY_OPAQUE /
                                 (OWFfloat) OWF_BYTE_MAX_VALUE;          
                }
                break;
            }
            default:
            {
                DPRINT(("Unsupported alpha format, ignoring mask"));
    
                result = OWF_FALSE;
                break;
            }
        }
        
        dstLinePtr+=output->width;
        /* Presumes that the stride is always whole bytes - eg. a 2x2-pixel mono 
         * image takes at least 2 bytes */
        srcLinePtr=(OWFuint8*)srcLinePtr+input->stride;        
    }
    return result;
}

#ifdef __cplusplus
}
#endif
