///////////////////////////////////////////////////////////////////////////////
//
//                          IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/*
 *   This file is part of MiniGUI, a mature cross-platform windowing
 *   and Graphics User Interface (GUI) support system for embedded systems
 *   and smart IoT devices.
 *
 *   Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd.
 *   Copyright (C) 1998~2002, WEI Yongming
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *   Or,
 *
 *   As this program is a library, any link to this program must follow
 *   GNU General Public License version 3 (GPLv3). If you cannot accept
 *   GPLv3, you need to be licensed from FMSoft.
 *
 *   If you have got a commercial license of this program, please use it
 *   under the terms and conditions of the commercial license.
 *
 *   For more information about the commercial license, please refer to
 *   <http://www.minigui.com/en/about/licensing-policy/>.
 */

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

#include "common.h"
#include "newgal.h"
#include "sysvideo.h"
#include "blit.h"
#include "RLEaccel_c.h"
#include "pixels_c.h"
#include "memops.h"
#include "leaks.h"

/* Public routines */
/*
 * Create an empty RGB surface of the appropriate depth
 */
GAL_Surface * GAL_CreateRGBSurface (Uint32 flags,
            int width, int height, int depth,
            Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
{
    GAL_VideoDevice *video = current_video;
    GAL_VideoDevice *this  = current_video;
    GAL_Surface *screen;
    GAL_Surface *surface;

    /* Check to see if we desire the surface in video memory */
    if ( video ) {
        screen = GAL_PublicSurface;
    } else {
        screen = NULL;
    }
    if ( screen && ((screen->flags&GAL_HWSURFACE) == GAL_HWSURFACE) ) {
        if ( (flags&(GAL_SRCCOLORKEY|GAL_SRCALPHA)) != 0 ) {
            flags |= GAL_HWSURFACE;
        }
        if ( (flags & GAL_SRCCOLORKEY) == GAL_SRCCOLORKEY ) {
            if ( ! current_video->info.blit_hw_CC ) {
                flags &= ~GAL_HWSURFACE;
            }
        }
        if ( (flags & GAL_SRCALPHA) == GAL_SRCALPHA ) {
            if ( ! current_video->info.blit_hw_A ) {
                flags &= ~GAL_HWSURFACE;
            }
        }
    } else {
        flags &= ~GAL_HWSURFACE;
    }

    /* Allocate the surface */
    surface = (GAL_Surface *)malloc(sizeof(*surface));
    if ( surface == NULL ) {
        GAL_OutOfMemory();
        return(NULL);
    }
    if ((flags & GAL_HWSURFACE) == GAL_HWSURFACE)
        surface->video = current_video;
    else
        surface->video = NULL;

    surface->flags = GAL_SWSURFACE;

    /* 2011-03-04 WanZheng,
     * make it able to create a hardware surface
     * with different color format against the screen surface
     */
#if 0
    if ( (flags & GAL_HWSURFACE) == GAL_HWSURFACE ) {
        depth = screen->format->BitsPerPixel;
        Rmask = screen->format->Rmask;
        Gmask = screen->format->Gmask;
        Bmask = screen->format->Bmask;
        Amask = screen->format->Amask;
    }
#endif
    if (Amask){
        surface->flags |= GAL_SRCPIXELALPHA;
    }
    surface->format = GAL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask);
    if ( surface->format == NULL ) {
        free(surface);
        return(NULL);
    }
    surface->w = width;
    surface->h = height;
    surface->pitch = GAL_CalculatePitch(surface);
    surface->pixels = NULL;
    surface->offset = 0;
    // for off-screen surface, DPI always be the default value
    surface->dpi = GDCAP_DPI_DEFAULT;
    surface->hwdata = NULL;
    surface->map = NULL;
    surface->format_version = 0;
    GAL_SetClipRect(surface, NULL);

#ifdef _MGUSE_SYNC_UPDATE
    /* Initialize update region */
    InitClipRgn (&surface->update_region, &__mg_free_update_region_list);
#endif

    /* Get the pixels */
    if ( surface->w && surface->h ) {
        if ( ((flags&GAL_HWSURFACE) == GAL_SWSURFACE) ||
                (video->AllocHWSurface(this, surface) < 0) ) {
            surface->pixels = malloc(surface->h*surface->pitch);
            if ( surface->pixels == NULL ) {
                GAL_FreeSurface(surface);
                GAL_OutOfMemory();
                return(NULL);
            }
            /* This is important for bitmaps */
            memset(surface->pixels, 0, surface->h*surface->pitch);
            surface->flags &= ~GAL_HWSURFACE;
        }
    }

    /* Allocate an empty mapping */
    surface->map = GAL_AllocBlitMap();
    if ( surface->map == NULL ) {
        GAL_FreeSurface(surface);
        return(NULL);
    }

    /* The surface is ready to go */
    surface->refcount = 1;
#ifdef CHECK_LEAKS
    ++surfaces_allocated;
#endif
    return(surface);
}
/*
 * Create an RGB surface from an existing memory buffer
 */
GAL_Surface * GAL_CreateRGBSurfaceFrom (void *pixels,
            int width, int height, int depth, int pitch,
            Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
{
    GAL_Surface *surface;

    surface = GAL_CreateRGBSurface(GAL_SWSURFACE, 0, 0, depth,
                                   Rmask, Gmask, Bmask, Amask);
    if ( surface != NULL ) {
        surface->flags |= GAL_PREALLOC;
        surface->pixels = pixels;
        surface->w = width;
        surface->h = height;
        surface->pitch = pitch;
        GAL_SetClipRect(surface, NULL);
    }
    return(surface);
}
/*
 * Set the color key in a blittable surface
 */
int GAL_SetColorKey (GAL_Surface *surface, Uint32 flag, Uint32 key)
{
    /* Sanity check the flag as it gets passed in */
    if ( flag & GAL_SRCCOLORKEY ) {
        if ( flag & (GAL_RLEACCEL|GAL_RLEACCELOK) ) {
            flag = (GAL_SRCCOLORKEY | GAL_RLEACCELOK);
        } else {
            flag = GAL_SRCCOLORKEY;
        }
    } else {
        flag = 0;
    }

    /* Optimize away operations that don't change anything */
    if ( (flag == (surface->flags & (GAL_SRCCOLORKEY|GAL_RLEACCELOK))) &&
         (key == surface->format->colorkey) ) {
        return(0);
    }

    /* UnRLE surfaces before we change the colorkey */
    if ( surface->flags & GAL_RLEACCEL ) {
            GAL_UnRLESurface(surface, 1);
    }

    if ( flag ) {
        GAL_VideoDevice *video = current_video;
        GAL_VideoDevice *this  = current_video;

        surface->flags |= GAL_SRCCOLORKEY;
        surface->format->colorkey = key;
        if ( (surface->flags & GAL_HWACCEL) == GAL_HWACCEL ) {
            if ( (video->SetHWColorKey == NULL) ||
                 (video->SetHWColorKey(this, surface, key) < 0) ) {
                surface->flags &= ~GAL_HWACCEL;
            }
        }
        if ( flag & GAL_RLEACCELOK ) {
            surface->flags |= GAL_RLEACCELOK;
        } else {
            surface->flags &= ~GAL_RLEACCELOK;
        }
    } else {
        surface->flags &= ~(GAL_SRCCOLORKEY|GAL_RLEACCELOK);
        surface->format->colorkey = 0;
    }
    GAL_InvalidateMap(surface->map);
    return(0);
}
int GAL_SetAlpha (GAL_Surface *surface, Uint32 flag, Uint8 value)
{
    Uint32  oldflags = surface->flags;
    Uint32  oldalpha = surface->format->alpha;

    /* Sanity check the flag as it gets passed in */
    if ( flag & GAL_SRCALPHA ) {
        if ( flag & (GAL_RLEACCEL|GAL_RLEACCELOK) ) {
            flag = GAL_SRCALPHA|GAL_RLEACCELOK;
        } else {
            flag = GAL_SRCALPHA;
        }

        /* Optimize away operations that don't change anything */
        if ( (flag == (surface->flags & (GAL_SRCALPHA|GAL_RLEACCELOK)))
                && (!flag || value == oldalpha) ) {
            return(0);
        }
    } else if ( flag & GAL_SRCPIXELALPHA ){
        if ( flag & (GAL_RLEACCEL|GAL_RLEACCELOK) ) {
            flag = GAL_SRCPIXELALPHA|GAL_RLEACCELOK;
        } else {
            flag = GAL_SRCPIXELALPHA;
        }
    } else {
        flag = 0;
    }

    if(!(flag & GAL_RLEACCELOK) && (surface->flags & GAL_RLEACCEL))
        GAL_UnRLESurface(surface, 1);

    if ( flag ) {
        GAL_VideoDevice *video = current_video;
        GAL_VideoDevice *this  = current_video;

        if ( flag & GAL_SRCALPHA ) {
            surface->flags |= GAL_SRCALPHA;
            surface->format->alpha = value;
        } else if ( flag & GAL_SRCPIXELALPHA ){
            surface->flags |= GAL_SRCPIXELALPHA;
        }


        if ( (surface->flags & GAL_HWACCEL) == GAL_HWACCEL ) {
            if ( (video->SetHWAlpha == NULL) ||
                 (video->SetHWAlpha(this, surface, value) < 0) ) {
                surface->flags &= ~GAL_HWACCEL;
            }
        }
        if ( flag & GAL_RLEACCELOK ) {
                surface->flags |= GAL_RLEACCELOK;
        } else {
                surface->flags &= ~GAL_RLEACCELOK;
        }
    } else {
        surface->flags &= ~GAL_SRCALPHA;
        surface->flags &= ~GAL_SRCPIXELALPHA;
        surface->format->alpha = GAL_ALPHA_OPAQUE;
    }
    /*
     * The representation for software surfaces is independent of
     * per-surface alpha, so no need to invalidate the blit mapping
     * if just the alpha value was changed. (If either is 255, we still
     * need to invalidate.)
     */
    if((surface->flags & GAL_HWACCEL) == GAL_HWACCEL
       || oldflags != surface->flags
       || (((oldalpha + 1) ^ (value + 1)) & 0x100))
        GAL_InvalidateMap(surface->map);
    return(0);
}

/*
 * A function to calculate the intersection of two rectangles:
 * return true if the rectangles intersect, false otherwise
 */
static inline
GAL_bool GAL_IntersectRect(const GAL_Rect *A, const GAL_Rect *B, GAL_Rect *intersection)
{
    int Amin, Amax, Bmin, Bmax;

    /* Horizontal intersection */
    Amin = A->x;
    Amax = Amin + A->w;
    Bmin = B->x;
    Bmax = Bmin + B->w;
    if(Bmin > Amin)
            Amin = Bmin;
    intersection->x = Amin;
    if(Bmax < Amax)
            Amax = Bmax;
    intersection->w = Amax - Amin > 0 ? Amax - Amin : 0;

    /* Vertical intersection */
    Amin = A->y;
    Amax = Amin + A->h;
    Bmin = B->y;
    Bmax = Bmin + B->h;
    if(Bmin > Amin)
            Amin = Bmin;
    intersection->y = Amin;
    if(Bmax < Amax)
            Amax = Bmax;
    intersection->h = Amax - Amin > 0 ? Amax - Amin : 0;

    return (intersection->w && intersection->h);
}
/*
 * Set the clipping rectangle for a blittable surface
 */
GAL_bool GAL_SetClipRect(GAL_Surface *surface, GAL_Rect *rect)
{
    GAL_Rect full_rect;

    /* Don't do anything if there's no surface to act on */
    if ( ! surface ) {
        return GAL_FALSE;
    }

    /* Set up the full surface rectangle */
    full_rect.x = 0;
    full_rect.y = 0;
    full_rect.w = surface->w;
    full_rect.h = surface->h;

    /* Set the clipping rectangle */
    if ( ! rect ) {
        surface->clip_rect = full_rect;
        return 1;
    }
    return GAL_IntersectRect(rect, &full_rect, &surface->clip_rect);
}
void GAL_GetClipRect(GAL_Surface *surface, GAL_Rect *rect)
{
    if ( surface && rect ) {
        *rect = surface->clip_rect;
    }
}

#if defined ( _MGGAL_SIGMA8654) || defined (_MGGAL_MSTAR) || defined (_MGGAL_STGFB)
#   define MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT
#endif

#ifdef MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT
#include <assert.h>

static inline void galrect_2_rect(GAL_Rect *gal_rect, RECT *rc) {
    rc->left = gal_rect->x;
    rc->top = gal_rect->y;
    rc->right = gal_rect->x + gal_rect->w;
    rc->bottom = gal_rect->y + gal_rect->h;
}

int own_overlapped_bitblit(GAL_blit real_blit, struct GAL_Surface *src, GAL_Rect *srcrect,
        struct GAL_Surface *dst, GAL_Rect *dstrect) {
    int w, W, x;
    int h, H, y;
    int delta;
    int ret = 0;
    RECT intersect, src_rc, dst_rc;

    assert(srcrect->w == dstrect->w || srcrect->h == dstrect->h);

    galrect_2_rect(srcrect, &src_rc);
    galrect_2_rect(dstrect, &dst_rc);

    /* don't intersect or horizontal left or up or left-up overlapped blit directly */
    if (! IntersectRect(&intersect, &src_rc, &dst_rc)
            || (dst_rc.top <= src_rc.top && dst_rc.left <= src_rc.left)) {
        return real_blit(src, srcrect, dst, dstrect);
    }

    GAL_Rect src1, dst1;

    /* vertical overlapped.
     * if left-up right-up left-down right-down, horizontal is faster than vertical than use horizonatl mode. */
    if ((dst_rc.top != src_rc.top) && (RECTH(intersect) <= RECTW(intersect))) {
        h = RECTH(intersect);
        H = RECTH(src_rc);
        delta = H - h;

        /* down or left-down or right-down overlapped, separate into per not-Intersect rect to blit,
         * from bottom to up */
        if (dst_rc.top > src_rc.top) {
            for (y = H; y > 0; y -= delta) {
                if (y < delta) {
                    delta = y;
                }
                src1.x = src_rc.left;
                src1.y = src_rc.top + y - delta;

                dst1.x = dst_rc.left;
                dst1.y = dst_rc.top + y - delta;

                src1.w = dst1.w = srcrect->w;
                src1.h = dst1.h = delta;

                ret |= real_blit(src, &src1, dst, &dst1);
            }
        }
        /* up or left-up or right-up overlapped, just the same down except from up to bottom */
        else {
            for (y = 0; y < H; y += delta) {
                if (y + delta > H) {
                    delta = H - y;
                }
                src1.x = src_rc.left;
                src1.y = src_rc.top + y;

                dst1.x = dst_rc.left;
                dst1.y = dst_rc.top + y;

                src1.w = dst1.w = srcrect->w;
                src1.h = dst1.h = delta;

                ret |= real_blit(src, &src1, dst, &dst1);
            }
        }
    }
    /* horizontal overlapped */
    else {
        w = RECTW(intersect);
        W = RECTW(src_rc);
        delta = W - w;

        /* horizontal right overlapped, separate into per not-Intersect rect to blit,
         * from right to left */
        if (dst_rc.left > src_rc.left) {
            for (x = W; x > 0; x -= delta) {
                if (x < delta) {
                    delta = x;
                }
                src1.x = src_rc.left + x - delta;
                src1.y = src_rc.top;

                dst1.x = dst_rc.left + x - delta;
                dst1.y = dst_rc.top;

                src1.w = dst1.w = delta;
                src1.h = dst1.h = srcrect->h;

                ret |= real_blit(src, &src1, dst, &dst1);
            }
        }
        /* horizontal left overlapped. just the same right except from left to right.
         * this just for optimization overlapped blit speed. */
        else {
            for (x = 0; x < W; x += delta) {
                if (x + delta > W) {
                    delta = W - x;
                }
                src1.x = src_rc.left + x;
                src1.y = src_rc.top;

                dst1.x = dst_rc.left + x;
                dst1.y = dst_rc.top;

                src1.w = dst1.w = delta;
                src1.h = dst1.h = srcrect->h;

                ret |= real_blit(src, &src1, dst, &dst1);
            }
        }
    }

    return ret;
}
#endif

/*
 * Set up a blit between two surfaces -- split into three parts:
 * The upper part, GAL_UpperBlit(), performs clipping and rectangle
 * verification.  The lower part is a pointer to a low level
 * accelerated blitting function.
 *
 * These parts are separated out and each used internally by this
 * library in the optimimum places.  They are exported so that if
 * you know exactly what you are doing, you can optimize your code
 * by calling the one(s) you need.
 */
int GAL_LowerBlit (GAL_Surface *src, GAL_Rect *srcrect,
                GAL_Surface *dst, GAL_Rect *dstrect)
{
    GAL_blit do_blit;
    int ret;

    /* Check to make sure the blit mapping is valid */
    if ( (src->map->dst != dst) ||
             (src->map->dst->format_version != src->map->format_version) ) {
        if ( GAL_MapSurface(src, dst) < 0 ) {
            return(-1);
        }
    }

    /* Figure out which blitter to use */
    if ( (src->flags & GAL_HWACCEL) == GAL_HWACCEL ) {
        do_blit = src->map->hw_blit;
    } else {
        do_blit = src->map->sw_blit;
    }

#ifdef _MGGAL_S3C6410
    {
        extern int s3c6410_size_table[48];
        int H = src->h / 10;
        if (H >= sizeof(s3c6410_size_table)/sizeof(s3c6410_size_table[0])) {
            H = sizeof(s3c6410_size_table)/sizeof(s3c6410_size_table[0]) - 1;
        }
        if (src->w/10 < s3c6410_size_table[H]) {
            do_blit = src->map->sw_blit;
        }
    }
#endif

#ifdef MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT
    ret = own_overlapped_bitblit(do_blit, src, srcrect, dst, dstrect);
#else
    ret = do_blit(src, srcrect, dst, dstrect);
#endif

#if 0
    {
        static int n_sw=0, n_hw=0;
        if ((src->flags & GAL_HWACCEL) == GAL_HWACCEL)
        {
            n_hw ++;
        }
        else
        {
            n_sw ++;
        }
        printf("[%06u] hw/blit=%d/%d %c size=%dX%d src=(%d %d),%p dst=(%d %d),%p\n",
                times(NULL) % 1000000,
                n_hw, n_hw+n_sw,
                do_blit == src->map->sw_blit ? 'S':'H',
                srcrect->w, srcrect->h,
                srcrect->x, srcrect->y, src,
                dstrect->x, dstrect->y, dst
              );
    }
#endif

    return ret;
}


int GAL_UpperBlit (GAL_Surface *src, GAL_Rect *srcrect,
           GAL_Surface *dst, GAL_Rect *dstrect)
{
    GAL_Rect fulldst;
    int srcx, srcy, w, h;

    if ( ! src || ! dst ) {
        GAL_SetError("NEWGAL: GAL_UpperBlit: passed a NULL surface.\n");
        return(-1);
    }

    /* If the destination rectangle is NULL, use the entire dest surface */
    if ( dstrect == NULL ) {
        fulldst.x = fulldst.y = 0;
        dstrect = &fulldst;
    }

    /* clip the source rectangle to the source surface */
    if(srcrect) {
        int maxw, maxh;

        srcx = srcrect->x;
        w = srcrect->w;
        if(srcx < 0) {
            w += srcx;
            dstrect->x -= srcx;
            srcx = 0;
        }
        maxw = src->w - srcx;
        if(maxw < w)
            w = maxw;

        srcy = srcrect->y;
        h = srcrect->h;
        if(srcy < 0) {
                h += srcy;
            dstrect->y -= srcy;
            srcy = 0;
        }
        maxh = src->h - srcy;
        if(maxh < h)
            h = maxh;

    } else {
        srcx = srcy = 0;
        w = src->w;
        h = src->h;
    }

    /* clip the destination rectangle against the clip rectangle */
    {
        GAL_Rect *clip = &dst->clip_rect;
        int dx, dy;

        dx = clip->x - dstrect->x;
        if(dx > 0) {
            w -= dx;
            dstrect->x += dx;
            srcx += dx;
        }
        dx = dstrect->x + w - clip->x - clip->w;
        if(dx > 0)
            w -= dx;

        dy = clip->y - dstrect->y;
        if(dy > 0) {
            h -= dy;
            dstrect->y += dy;
            srcy += dy;
        }
        dy = dstrect->y + h - clip->y - clip->h;
        if(dy > 0)
            h -= dy;
    }

    if(w > 0 && h > 0) {
        GAL_Rect sr;
        sr.x = srcx;
        sr.y = srcy;
        sr.w = dstrect->w = w;
        sr.h = dstrect->h = h;
        return GAL_LowerBlit(src, &sr, dst, dstrect);
    }
    dstrect->w = dstrect->h = 0;
    return 0;
}

/*
 * This function performs a fast fill of the given rectangle with 'color'
 */
int GAL_FillRect(GAL_Surface *dst, const GAL_Rect *dstrect, Uint32 color)
{
    GAL_VideoDevice *video = dst->video;
    GAL_VideoDevice *this  = dst->video;
    int x, y;
    Uint8 *row;
    GAL_Rect my_dstrect;

    if (!video)
    {
        video = current_video;
        this = current_video;
    }

    /* If 'dstrect' == NULL, then fill the whole surface */
    if ( dstrect ) {
        /* Perform clipping */
        if ( !GAL_IntersectRect(dstrect, &dst->clip_rect, &my_dstrect) ) {
            return(0);
        }
    } else {
        my_dstrect = dst->clip_rect;
    }

    /* Check for hardware acceleration */
    if ( ((dst->flags & GAL_HWSURFACE) == GAL_HWSURFACE) &&
                    video->info.blit_fill ) {
        return(video->FillHWRect(this, dst, &my_dstrect, color));
    }

    row = (Uint8 *)dst->pixels+my_dstrect.y*dst->pitch+
            my_dstrect.x*dst->format->BytesPerPixel;
    if ( dst->format->palette || (color == 0) ) {
        x = my_dstrect.w*dst->format->BytesPerPixel;
        if ( !color && !((long)row&3) && !(x&3) && !(dst->pitch&3) ) {
            int n = x >> 2;
            for ( y=my_dstrect.h; y; --y ) {
                GAL_memset4(row, 0, n);
                row += dst->pitch;
            }
        } else {
#if 0
            /*
             * memset() on PPC (both glibc and codewarrior) uses
             * the dcbz (Data Cache Block Zero) instruction, which
             * causes an alignment exception if the destination is
             * uncachable, so only use it on software surfaces
             */
            if((dst->flags & GAL_HWSURFACE) == GAL_HWSURFACE) {
                if(my_dstrect.w >= 8) {
                    /*
                     * 64-bit stores are probably most
                     * efficient to uncached video memory
                     */
                    double fill;
                    memset(&fill, color, (sizeof fill));
                    for(y = my_dstrect.h; y; y--) {
                        Uint8 *d = row;
                        unsigned n = x;
                        unsigned nn;
                        Uint8 c = color;
                        double f = fill;
                        while((unsigned long)d
                              & (sizeof(double) - 1)) {
                            *d++ = c;
                            n--;
                        }
                        nn = n / (sizeof(double) * 4);
                        while(nn) {
                            ((double *)d)[0] = f;
                            ((double *)d)[1] = f;
                            ((double *)d)[2] = f;
                            ((double *)d)[3] = f;
                            d += 4*sizeof(double);
                            nn--;
                        }
                        n &= ~(sizeof(double) * 4 - 1);
                        nn = n / sizeof(double);
                        while(nn) {
                            *(double *)d = f;
                            d += sizeof(double);
                            nn--;
                        }
                        n &= ~(sizeof(double) - 1);
                        while(n) {
                            *d++ = c;
                            n--;
                        }
                        row += dst->pitch;
                    }
                } else {
                    /* narrow boxes */
                    for(y = my_dstrect.h; y; y--) {
                        Uint8 *d = row;
                        Uint8 c = color;
                        int n = x;
                        while(n) {
                            *d++ = c;
                            n--;
                        }
                        row += dst->pitch;
                    }
                }
            } else
#endif /* __powerpc__ */
            {
                for(y = my_dstrect.h; y; y--) {
                    memset(row, color, x);
                    row += dst->pitch;
                }
            }
        }
    } else {
        switch (dst->format->BytesPerPixel) {
            case 2:
            for ( y=my_dstrect.h; y; --y ) {
                Uint16 *pixels = (Uint16 *)row;
                Uint16 c = color;
                Uint32 cc = (Uint32)c << 16 | c;
                int n = my_dstrect.w;
                if((unsigned long)pixels & 3) {
                    *pixels++ = c;
                    n--;
                }
                if(n >> 1)
                    GAL_memset4(pixels, cc, n >> 1);
                if(n & 1)
                    pixels[n - 1] = c;
                row += dst->pitch;
            }
            break;

            case 3:
            if(GAL_BYTEORDER == GAL_BIG_ENDIAN)
                color <<= 8;
            for ( y=my_dstrect.h; y; --y ) {
                Uint8 *pixels = row;
                for ( x=my_dstrect.w; x; --x ) {
                    memcpy(pixels, &color, 3);
                    pixels += 3;
                }
                row += dst->pitch;
            }
            break;

            case 4:
            for(y = my_dstrect.h; y; --y) {
                GAL_memset4(row, color, my_dstrect.w);
                row += dst->pitch;
            }
            break;
        }
    }

    /* We're done! */
    return(0);
}

/*
 * Calculate the pad-aligned box sizew in a surface
 */
Uint32 GAL_GetBoxSize (GAL_Surface *surface, Uint32 w, Uint32 h, Uint32* pitch_p)
{
    Uint32 pitch;

    /* Box should be 4-byte aligned for speed */
    pitch = w * surface->format->BytesPerPixel;
    switch (surface->format->BitsPerPixel) {
        case 1:
            pitch = (pitch+7)/8;
            break;
        case 4:
            pitch = (pitch+1)/2;
            break;
        default:
            break;
    }

    pitch = (pitch + 3) & ~3;    /* 4-byte aligning */

    if (pitch_p)
        *pitch_p = pitch;

    return pitch * h;
}

/*
 * This function performs a fast HW -> SW box copying.
 */
int GAL_GetBox (GAL_Surface *src, const GAL_Rect *rect, BITMAP* box)
{
    Sint32 dst_x, dst_y, off_x, off_y, y;
    Uint8 *srcrow, *dstrow;
    Uint32 linelen;
    GAL_Rect srcrect = {0, 0, 0, 0};
    srcrect.w = src->w;
    srcrect.h = src->h;

    if (rect->h <= 0 || rect->w <= 0)
        return -1;

    box->bmType = BMP_TYPE_NORMAL;
    box->bmBitsPerPixel = src->format->BitsPerPixel;
    box->bmBytesPerPixel = src->format->BytesPerPixel;
    box->bmWidth = rect->w;
    box->bmHeight = rect->h;
    box->bmAlphaMask = NULL;
    box->bmAlphaPitch = 0;

    dst_x = rect->x;
    dst_y = rect->y;

    /* Perform clipping */
    if (!GAL_IntersectRect (rect, &srcrect, &srcrect)) {
        return 0;
    }

    off_x = srcrect.x - dst_x;
    off_y = srcrect.y - dst_y;

    if ((box->bmPitch == 0) || (box->bmBits == NULL)) {
        box->bmBits = malloc (GAL_GetBoxSize (src, box->bmWidth, box->bmHeight, &box->bmPitch));
        if (box->bmBits == NULL) {
            GAL_OutOfMemory ();
            return -1;
        }
    }

    linelen = srcrect.w * box->bmBytesPerPixel;
    srcrow = (Uint8 *)src->pixels + srcrect.y * src->pitch +
            srcrect.x * src->format->BytesPerPixel;
    dstrow = box->bmBits + off_y * box->bmPitch +
            off_x * box->bmBytesPerPixel;

    switch (box->bmBytesPerPixel) {
        case 1:
            if (((DWORD)srcrow & 3) || ((DWORD)dstrow & 3)
                    || (linelen & 3) || (box->bmPitch & 3) || (src->pitch & 3))
                goto slow_copy;
            else {
                int n = linelen >> 2;
                for (y = srcrect.h; y; --y) {
                    GAL_memcpy4 (dstrow, srcrow, n);
                    srcrow += src->pitch;
                    dstrow += box->bmPitch;
                }
            }
            break;

        case 2:
            if ((((DWORD)srcrow & 3) != ((DWORD)dstrow & 3))
                    || (box->bmPitch & 3) || (src->pitch & 3))
                goto slow_copy;

            for (y = srcrect.h; y; --y) {
                Uint16 *dstpixels = (Uint16 *)dstrow;
                Uint16 *srcpixels = (Uint16 *)srcrow;
                int n = srcrect.w;
                if ((DWORD)dstpixels & 3) {
                    *dstpixels = *srcpixels;
                    dstpixels++;
                    srcpixels++;
                    n--;
                }

                if (n >> 1)
                    GAL_memcpy4 (dstpixels, srcpixels, n >> 1);
                if (n & 1)
                    dstpixels [n - 1] = srcpixels [n - 1];

                srcrow += src->pitch;
                dstrow += box->bmPitch;
            }
            break;

        case 3:
            goto slow_copy;

        case 4:
            for (y = srcrect.h; y; --y) {
                GAL_memcpy4 (dstrow, srcrow, srcrect.w);
                srcrow += src->pitch;
                dstrow += box->bmPitch;
            }
            break;
    }

    return(0);

slow_copy:
    for (y = srcrect.h; y; --y) {
        GAL_memcpy (dstrow, srcrow, linelen);
        srcrow += src->pitch;
        dstrow += box->bmPitch;
    }

    return(0);
}

/*
 * This function performs a fast SW -> HW box copying.
 */

// helpers

#include "blit.h"

/* aligned 32-bit bound for alpha mask array index  */
#define GET_ALPHA_MASK_INDEX(srcrow, box, pitch, row)           \
do {                                                            \
    int x = (srcrow - (box)->bmBits) % (box)->bmPitch;          \
    int y = (srcrow - (box)->bmBits) / (box)->bmPitch;          \
    (pitch) = (box)->bmAlphaPitch;                              \
    (row) = (y * (pitch)) + (x / (box)->bmBytesPerPixel);       \
} while(0)

static int _PutBoxAlphaChannelEx (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = box->bmAlpha;


    if (bpp == 1)
        return -1;

    if (!(box->bmType & BMP_TYPE_ALPHA_MASK)) {
        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            DUFFS_LOOP(
                {
                    Uint32 pixel;
                    unsigned sR;
                    unsigned sG;
                    unsigned sB;
                    unsigned sA;
                    unsigned dR;
                    unsigned dG;
                    unsigned dB;
                    DISEMBLE_RGBA (srcpixels, bpp, dstfmt, pixel, sR, sG, sB, sA);
                    DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                    ALPHA_BLEND (sR, sG, sB, (alpha*sA)/255, dR, dG, dB);
                    ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                    dstpixels += bpp;
                    srcpixels += bpp;
                },
                w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
        }
    } else {
        /* for aligned 32-bits bound */
        int alpha_mask_index;
        int alpha_mask_pitch;
        int alpha_mask_row;
        GET_ALPHA_MASK_INDEX (srcrow, box, alpha_mask_pitch, alpha_mask_row);

        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            alpha_mask_index = alpha_mask_row;
            DUFFS_LOOP(
                {
                    Uint32 pixel;
                    unsigned sR;
                    unsigned sG;
                    unsigned sB;
                    unsigned sA;
                    unsigned dR;
                    unsigned dG;
                    unsigned dB;
                    DISEMBLE_RGB (srcpixels, bpp, dstfmt, pixel, sR, sG, sB);
                    DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                    sA = box->bmAlphaMask[alpha_mask_index];
                    ALPHA_BLEND (sR, sG, sB, (alpha*sA)/255, dR, dG, dB);
                    ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                    dstpixels += bpp;
                    srcpixels += bpp;
                    alpha_mask_index++;
                },
                w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
            alpha_mask_row += alpha_mask_pitch;
        }
    }

    return 0;
}

static int _PutBoxAlpha (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = dstfmt->Amask ? GAL_ALPHA_OPAQUE : 0;


    if (bpp == 1)
        return -1;

    if (!(box->bmType & BMP_TYPE_ALPHA_MASK)) {
        if (dstfmt->Amask == 0xff000000 && dstfmt->BitsPerPixel == 32)
        {
            while(h--) {
                Uint32 *srcp = (Uint32 *)srcrow;
                Uint32 *dstp = (Uint32 *)dstrow;
                unsigned char alpha;
                DUFFS_LOOP4(
                        {
                        Uint32 s;
                        Uint32 d;
                        Uint32 s1;
                        Uint32 d1;
                        Uint32 sA;
                        Uint32 dA;
                        s = *srcp;
                        d = *dstp;
                        sA = s >> 24;
                        dA = d >> 24;
                        dA = sA + dA - ((sA * dA) >> 8);
                        if(dA > 255) dA = 255; //alpha may be greater than 255
                        alpha = s >> 24;
                        s1 = s & 0xff00ff;
                        d1 = d & 0xff00ff;
                        d1 = (d1 + (((s1 - d1) * alpha) >> 8))
                        & 0xff00ff;
                        s &= 0xff00;
                        d &= 0xff00;
                        d = (d + (((s - d) * alpha) >> 8)) & 0xff00;
                        *dstp = d1 | d | (dA << 24);
                        ++srcp;
                        ++dstp;
                        }, w);
                srcrow += box->bmPitch;
                dstrow += dst->pitch;
            }
        }
        else
        {
            while ( h-- ) {
                dstpixels = dstrow;
                srcpixels = srcrow;
                DUFFS_LOOP(
                        {
                        Uint32 pixel;
                        unsigned sR = 0;
                        unsigned sG = 0;
                        unsigned sB = 0;
                        unsigned sA = 0;
                        unsigned dR = 0;
                        unsigned dG = 0;
                        unsigned dB = 0;
                        unsigned dA = 0;
                        DISEMBLE_RGBA (srcpixels, bpp, dstfmt, pixel, sR, sG, sB, sA);
                        DISEMBLE_RGBA (dstpixels, bpp, dstfmt, pixel, dR, dG, dB, dA);
                        ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
                        dA = sA + dA - ((sA * dA) >> 8);
                        if(dA > 255) dA = 255; //alpha may be greater than 255
                        ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, dA);
                        dstpixels += bpp;
                        srcpixels += bpp;
                        },
                        w);
                srcrow += box->bmPitch;
                dstrow += dst->pitch;
            }
        }
    } else {
        /* for aligned 32-bits bound */
        int alpha_mask_index;
        int alpha_mask_pitch;
        int alpha_mask_row;
        GET_ALPHA_MASK_INDEX (srcrow, box, alpha_mask_pitch, alpha_mask_row);

        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            alpha_mask_index = alpha_mask_row;
            DUFFS_LOOP(
            {
                Uint32 pixel;
                unsigned sR;
                unsigned sG;
                unsigned sB;
                unsigned sA;
                unsigned dR;
                unsigned dG;
                unsigned dB;
                DISEMBLE_RGB (srcpixels, bpp, dstfmt, pixel, sR, sG, sB);
                DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                sA = box->bmAlphaMask[alpha_mask_index];
                ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
                ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                dstpixels += bpp;
                srcpixels += bpp;
                alpha_mask_index++;
            },
            w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
            alpha_mask_row += alpha_mask_pitch;
        }
    }

    return 0;
}

static int _PutBoxAlphaChannel (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = box->bmAlpha;

    if (bpp == 1)
        return -1;

    while ( h-- ) {
        dstpixels = dstrow;
        srcpixels = srcrow;
        DUFFS_LOOP(
        {
            Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            unsigned dR;
            unsigned dG;
            unsigned dB;
            DISEMBLE_RGB (srcpixels, bpp, dstfmt, pixel, sR, sG, sB);
            DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
            ALPHA_BLEND (sR, sG, sB, alpha, dR, dG, dB);
            ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
            dstpixels += bpp;
            srcpixels += bpp;
        },
        w);
        srcrow += box->bmPitch;
        dstrow += dst->pitch;
    }

    return 0;
}

static int _PutBoxKeyAlphaChannelEx (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    Uint32 ckey = box->bmColorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    Uint32 rgbmask = ~dstfmt->Amask;
    unsigned alpha = box->bmAlpha;


    if (bpp == 1)
        return -1;

    if (!(box->bmType & BMP_TYPE_ALPHA_MASK)) {
        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            DUFFS_LOOP(
            {
                Uint32 pixel;
                unsigned sR;
                unsigned sG;
                unsigned sB;
                unsigned sA;
                unsigned dR;
                unsigned dG;
                unsigned dB;
                RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
                if ((pixel & rgbmask) != ckey ) {
                    RGBA_FROM_PIXEL (pixel, dstfmt, sR, sG, sB, sA);
                    DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                    ALPHA_BLEND (sR, sG, sB, (alpha*sA)/255, dR, dG, dB);
                    ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                }
                dstpixels += bpp;
                srcpixels += bpp;
            },
            w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
        }
    } else {
        /* for aligned 32-bits bound */
        int alpha_mask_index;
        int alpha_mask_pitch;
        int alpha_mask_row;
        GET_ALPHA_MASK_INDEX (srcrow, box, alpha_mask_pitch, alpha_mask_row);

        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            alpha_mask_index = alpha_mask_row;
            DUFFS_LOOP(
            {
                Uint32 pixel;
                unsigned sR;
                unsigned sG;
                unsigned sB;
                unsigned sA;
                unsigned dR;
                unsigned dG;
                unsigned dB;
                RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
                if ((pixel & rgbmask) != ckey ) {
                    RGB_FROM_PIXEL (pixel, dstfmt, sR, sG, sB);
                    DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                    sA = box->bmAlphaMask[alpha_mask_index];
                    ALPHA_BLEND (sR, sG, sB, (alpha*sA)/255, dR, dG, dB);
                    ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                }
                dstpixels += bpp;
                srcpixels += bpp;
                alpha_mask_index++;
            },
            w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
            alpha_mask_row += alpha_mask_pitch;
        }
    }

    return 0;
}

static int _PutBoxKeyAlpha (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    Uint32 ckey = box->bmColorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    Uint32 rgbmask = ~dstfmt->Amask;
    unsigned alpha = dstfmt->Amask ? GAL_ALPHA_OPAQUE : 0;


    if (bpp == 1)
        return -1;

    if (!(box->bmType & BMP_TYPE_ALPHA_MASK)) {
        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            DUFFS_LOOP(
            {
                Uint32 pixel;
                unsigned sR;
                unsigned sG;
                unsigned sB;
                unsigned sA;
                unsigned dR;
                unsigned dG;
                unsigned dB;
                RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
                if ((pixel & rgbmask) != ckey ) {
                    RGBA_FROM_PIXEL (pixel, dstfmt, sR, sG, sB, sA);
                    DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                    ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
                    ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                }
                dstpixels += bpp;
                srcpixels += bpp;
            },
            w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
        }
    } else {
        /* for aligned 32-bits bound */
        int alpha_mask_index;
        int alpha_mask_pitch;
        int alpha_mask_row;
        GET_ALPHA_MASK_INDEX (srcrow, box, alpha_mask_pitch, alpha_mask_row);

        while ( h-- ) {
            dstpixels = dstrow;
            srcpixels = srcrow;
            alpha_mask_index = alpha_mask_row;
            DUFFS_LOOP(
            {
                Uint32 pixel;
                unsigned sR;
                unsigned sG;
                unsigned sB;
                unsigned sA;
                unsigned dR;
                unsigned dG;
                unsigned dB;
                RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
                if ((pixel & rgbmask) != ckey ) {
                    RGB_FROM_PIXEL (pixel, dstfmt, sR, sG, sB);
                    DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                    sA = box->bmAlphaMask[alpha_mask_index];
                    ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
                    ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
                }
                dstpixels += bpp;
                srcpixels += bpp;
                alpha_mask_index++;
            },
            w);
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
            alpha_mask_row += alpha_mask_pitch;
         }
    }

    return 0;
}

static int _PutBoxKey (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    Uint32 ckey = box->bmColorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = dstfmt->Amask ? GAL_ALPHA_OPAQUE : 0;

    if (bpp == 1) {
        while (h--) {
            unsigned int i;
            dstpixels = dstrow;
            srcpixels = srcrow;
            for (i = 0; i < w; i++) {
                if (*srcpixels != ckey)
                    *dstpixels = *srcpixels;
                dstpixels += bpp;
                srcpixels += bpp;
            }
            srcrow += box->bmPitch;
            dstrow += dst->pitch;
        }
    }
    else while ( h-- ) {
        dstpixels = dstrow;
        srcpixels = srcrow;
        DUFFS_LOOP(
        {
            Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
            if (pixel != ckey || (pixel == ckey && box->bmType & BMP_TYPE_REPLACEKEY)) {
                if (pixel == ckey)
                    pixel = box->bmColorRep;
                RGB_FROM_PIXEL (pixel, dstfmt, sR, sG, sB);
                ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, sR, sG, sB, alpha);
            }
            dstpixels += bpp;
            srcpixels += bpp;
        },
        w);
        srcrow += box->bmPitch;
        dstrow += dst->pitch;
    }

    return 0;
}

static int _PutBoxKeyAlphaChannel (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
    BYTE* dstpixels = dstrow;
    BYTE* srcpixels = srcrow;
    Uint32 ckey = box->bmColorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = box->bmAlpha;

    if (bpp == 1)
        return -1;

    while ( h-- ) {
        dstpixels = dstrow;
        srcpixels = srcrow;
        DUFFS_LOOP(
        {
            Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            unsigned dR;
            unsigned dG;
            unsigned dB;
            RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
            if ( pixel != ckey ) {
                RGB_FROM_PIXEL (pixel, dstfmt, sR, sG, sB);
                DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
                ALPHA_BLEND (sR, sG, sB, alpha, dR, dG, dB);
                ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
            }
            dstpixels += bpp;
            srcpixels += bpp;
        },
        w);
        srcrow += box->bmPitch;
        dstrow += dst->pitch;
    }

    return 0;
}

int GAL_PutBox (GAL_Surface *dst, const GAL_Rect *dstrect, BITMAP* box)
{
    Uint32 box_x, box_y, off_x, off_y;
    int y, w, h;
    Uint8 *srcrow, *dstrow;
    Uint32 linelen;
    GAL_Rect my_dstrect;

    /* If 'dstrect' == NULL, then put to (0, 0) */
    if (dstrect) {
        box_x = dstrect->x;
        box_y = dstrect->y;

        /* Perform clipping */
        if (!GAL_IntersectRect (dstrect, &dst->clip_rect, &my_dstrect)) {
            return 0;
        }
    } else {
        box_x = 0;
        box_y = 0;

        my_dstrect = dst->clip_rect;
    }

    off_x = my_dstrect.x - box_x;
    off_y = my_dstrect.y - box_y;
    if (off_x >= box->bmWidth || off_y >= box->bmHeight ||
            box->bmBytesPerPixel != dst->format->BytesPerPixel) {
        return 0;
    }

    dstrow = (Uint8 *)dst->pixels + my_dstrect.y * dst->pitch +
            my_dstrect.x * dst->format->BytesPerPixel;
    srcrow = (Uint8 *)box->bmBits + off_y * box->bmPitch +
            off_x * box->bmBytesPerPixel;
    w = MIN (my_dstrect.w, box->bmWidth);
    h = MIN (my_dstrect.h, box->bmHeight);
    linelen = w * box->bmBytesPerPixel;

    /* TODO: Check for hardware acceleration here */

    if (box->bmType & BMP_TYPE_ALPHA) {
        if ((box->bmType & BMP_TYPE_ALPHACHANNEL) && (box->bmType & BMP_TYPE_COLORKEY)) {
            return _PutBoxKeyAlphaChannelEx (dst, dstrow, srcrow, w, h, box);
        }
        if ((box->bmType & BMP_TYPE_ALPHACHANNEL)) {
            return _PutBoxAlphaChannelEx (dst, dstrow, srcrow, w, h, box);
        }
        else if ((box->bmType & BMP_TYPE_ALPHA) && (box->bmType & BMP_TYPE_COLORKEY)) {
            return _PutBoxKeyAlpha (dst, dstrow, srcrow, w, h, box);
        }
        else if (box->bmType & BMP_TYPE_ALPHA) {
            return _PutBoxAlpha (dst, dstrow, srcrow, w, h, box);
        }
    }
    else if ((box->bmType & BMP_TYPE_ALPHACHANNEL) && (box->bmType & BMP_TYPE_COLORKEY)) {
        return _PutBoxKeyAlphaChannel (dst, dstrow, srcrow, w, h, box);
    }
    else if (box->bmType & BMP_TYPE_COLORKEY) {
        return _PutBoxKey (dst, dstrow, srcrow, w, h, box);
    }
    else if (box->bmType & BMP_TYPE_ALPHACHANNEL) {
        return _PutBoxAlphaChannel (dst, dstrow, srcrow, w, h, box);
    }


    switch (dst->format->BytesPerPixel) {
        case 1:
            if (((DWORD)srcrow & 3) || ((DWORD)dstrow & 3)
                    || (linelen & 3) || (box->bmPitch & 3) || (dst->pitch & 3))
                goto slow_copy;
            else {
                int n = linelen >> 2;
                for (y = h; y; --y) {
                    GAL_memcpy4 (dstrow, srcrow, n);
                    srcrow += box->bmPitch;
                    dstrow += dst->pitch;
                }
            }
            break;

        case 2:
            if ((((DWORD)dstrow & 3) != ((DWORD)srcrow & 3))
                    || (box->bmPitch & 3) || (dst->pitch & 3))
                goto slow_copy;

            for (y = h; y; --y) {
                Uint16 *dstpixels = (Uint16 *)dstrow;
                Uint16 *srcpixels = (Uint16 *)srcrow;
                int n = w;
                if ((DWORD)dstpixels & 3) {
                    *dstpixels = *srcpixels;
                    dstpixels++;
                    srcpixels++;
                    n--;
                }

                if (n >> 1)
                    GAL_memcpy4 (dstpixels, srcpixels, n >> 1);
                if (n & 1)
                    dstpixels [n - 1] = srcpixels [n - 1];

                srcrow += box->bmPitch;
                dstrow += dst->pitch;
            }
            break;

        case 3:
            goto slow_copy;

        case 4:
            for (y = h; y; --y) {
                GAL_memcpy4 (dstrow, srcrow, w);
                srcrow += box->bmPitch;
                dstrow += dst->pitch;
            }
            break;
    }

    return 0;

slow_copy:
    for (y = h; y; --y) {
        GAL_memcpy (dstrow, srcrow, linelen);
        srcrow += box->bmPitch;
        dstrow += dst->pitch;
    }

    return 0;
}

/*
 * Convert a surface into the specified pixel format.
 */
GAL_Surface * GAL_ConvertSurface (GAL_Surface *surface,
                    GAL_PixelFormat *format, Uint32 flags)
{
    GAL_Surface *convert;
    Uint32 colorkey = 0;
    Uint8 alpha = 0;
    Uint32 surface_flags;
    GAL_Rect bounds;

    /* Check for empty destination palette! (results in empty image) */
    if ( format->palette != NULL ) {
        int i;
        for ( i=0; i<format->palette->ncolors; ++i ) {
            if ( (format->palette->colors[i].r != 0) ||
                 (format->palette->colors[i].g != 0) ||
                 (format->palette->colors[i].b != 0) )
                break;
        }
        if ( i == format->palette->ncolors ) {
            GAL_SetError("NEWGAL: Empty destination palette.\n");
            return(NULL);
        }
    }

    /* Create a new surface with the desired format */
    convert = GAL_CreateRGBSurface(flags,
                surface->w, surface->h, format->BitsPerPixel,
        format->Rmask, format->Gmask, format->Bmask, format->Amask);
    if ( convert == NULL ) {
        return(NULL);
    }

    /* Copy the palette if any */
    if ( format->palette && convert->format->palette ) {
        memcpy(convert->format->palette->colors,
                format->palette->colors,
                format->palette->ncolors*sizeof(GAL_Color));
        convert->format->palette->ncolors = format->palette->ncolors;
    }

    /* Save the original surface color key and alpha */
    surface_flags = surface->flags;
    if ( (surface_flags & GAL_SRCCOLORKEY) == GAL_SRCCOLORKEY ) {
        /* Convert colourkeyed surfaces to RGBA if requested */
        if((flags & GAL_SRCCOLORKEY) != GAL_SRCCOLORKEY
           && format->Amask) {
            surface_flags &= ~GAL_SRCCOLORKEY;
        } else {
            colorkey = surface->format->colorkey;
            GAL_SetColorKey(surface, 0, 0);
        }
    }
    if ( (surface_flags & GAL_SRCALPHA) == GAL_SRCALPHA ) {
        alpha = surface->format->alpha;
        GAL_SetAlpha(surface, 0, 0);
    }

    /* Copy over the image data */
    bounds.x = 0;
    bounds.y = 0;
    bounds.w = surface->w;
    bounds.h = surface->h;
    GAL_LowerBlit(surface, &bounds, convert, &bounds);

    /* Clean up the original surface, and update converted surface */
    if ( convert != NULL ) {
        GAL_SetClipRect(convert, &surface->clip_rect);
    }
    if ( (surface_flags & GAL_SRCCOLORKEY) == GAL_SRCCOLORKEY ) {
        Uint32 cflags = surface_flags&(GAL_SRCCOLORKEY|GAL_RLEACCELOK);
        if ( convert != NULL ) {
            Uint8 keyR, keyG, keyB;

            GAL_GetRGB(colorkey,surface->format,&keyR,&keyG,&keyB);
            GAL_SetColorKey(convert, cflags|(flags&GAL_RLEACCELOK),
                GAL_MapRGB(convert->format, keyR, keyG, keyB));
        }
        GAL_SetColorKey(surface, cflags, colorkey);
    }
    if ( (surface_flags & GAL_SRCALPHA) == GAL_SRCALPHA ) {
        Uint32 aflags = surface_flags&(GAL_SRCALPHA|GAL_RLEACCELOK);
        if ( convert != NULL ) {
                GAL_SetAlpha(convert, aflags|(flags&GAL_RLEACCELOK),
                alpha);
        }
        GAL_SetAlpha(surface, aflags, alpha);
    }

    /* We're ready to go! */
    return(convert);
}

/*
 * Free a surface created by the above function.
 */
void GAL_FreeSurface (GAL_Surface *surface)
{
    /* Free anything that's not NULL, and not the screen surface */
    if ((surface == NULL) ||
        (current_video && (surface == GAL_VideoSurface))) {
        return;
    }

    if (--surface->refcount > 0) {
        return;
    }

#ifdef _MGUSE_SYNC_UPDATE
    EmptyClipRgn (&surface->update_region);
#endif

    if ( (surface->flags & GAL_RLEACCEL) == GAL_RLEACCEL ) {
            GAL_UnRLESurface(surface, 0);
    }
    if ( surface->format ) {
        GAL_FreeFormat(surface->format);
        surface->format = NULL;
    }
    if ( surface->map != NULL ) {
        GAL_FreeBlitMap(surface->map);
        surface->map = NULL;
    }
    if ( (surface->flags & GAL_HWSURFACE) == GAL_HWSURFACE ) {
        GAL_VideoDevice *video = current_video;
        GAL_VideoDevice *this  = current_video;
        video->FreeHWSurface(this, surface);
    }
    if ( surface->pixels &&
         ((surface->flags & GAL_PREALLOC) != GAL_PREALLOC) ) {
        free(surface->pixels);
    }
    free(surface);
#ifdef CHECK_LEAKS
    --surfaces_allocated;
#endif
}

#ifdef _MGRM_PROCESSES
void GAL_RequestHWSurface (const REQ_HWSURFACE* request, REP_HWSURFACE* reply)
{
    if (current_video->RequestHWSurface) {
        current_video->RequestHWSurface (current_video, request, reply);
    } else if (request->offset == 0) {
        reply->offset = 0;
    } else {
        /* NULL */
    }
}

#endif

BYTE*  gal_PutPixelKeyAlpha (GAL_Surface* dst, BYTE* dstrow,
                Uint32 pixel, MYBITMAP_CONTXT* mybmp)
{
    //BYTE* dstpixels = dstrow;
    //BYTE* srcpixels = srcrow;
    Uint32 ckey = mybmp->colorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    GAL_PixelFormat *srcfmt = mybmp->AlphaPixelFormat;
    int bpp = dstfmt->BytesPerPixel;
    Uint32 rgbmask = ~srcfmt->Amask;
    unsigned alpha = dstfmt->Amask ? GAL_ALPHA_OPAQUE : 0;

    if (bpp == 1)
        return dstrow;

    //while ( h-- ) {
        //dstpixels = dstrow;
        //srcpixels = srcrow;
        //DUFFS_LOOP(
        {
            //Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            unsigned sA;
            unsigned dR;
            unsigned dG;
            unsigned dB;
            //RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
            if ((pixel & rgbmask) != ckey ) {
                RGBA_FROM_PIXEL (pixel, srcfmt, sR, sG, sB, sA);
                DISEMBLE_RGB (dstrow, bpp, dstfmt, pixel, dR, dG, dB);
                ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
                ASSEMBLE_RGBA (dstrow, bpp, dstfmt, dR, dG, dB, alpha);
            }
            dstrow += bpp;
            //srcpixels += bpp;
        }
        //w);
        //srcrow += box->bmPitch;
        //dstrow += dst->pitch;
    //}

    return dstrow;
}

BYTE* gal_PutPixelAlpha (GAL_Surface* dst, BYTE* dstrow,
                Uint32 pixel, MYBITMAP_CONTXT* mybmp)
{
    //BYTE* dstpixels = dstrow;
    //BYTE* srcpixels = srcrow;
    GAL_PixelFormat *dstfmt = dst->format;
    GAL_PixelFormat *srcfmt = mybmp->AlphaPixelFormat;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = dstfmt->Amask ? GAL_ALPHA_OPAQUE : 0;

    if (bpp == 1)
        return dstrow;

    //while ( h-- ) {
        //dstpixels = dstrow;
        //srcpixels = srcrow;
        //DUFFS_LOOP(
        {
            //Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            unsigned sA;
            unsigned dR;
            unsigned dG;
            unsigned dB;
            //RGB_FROM_PIXEL (pixel, srcfmt, sR, sG, sB);
            RGBA_FROM_PIXEL(pixel, srcfmt, sR, sG, sB, sA);
            pixel &= ~srcfmt->Amask;
            //DISEMBLE_RGBA (srcpixels, bpp, srcfmt, pixel, sR, sG, sB, sA);
            DISEMBLE_RGB (dstrow, bpp, dstfmt, pixel, dR, dG, dB);
            ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
            ASSEMBLE_RGBA (dstrow, bpp, dstfmt, dR, dG, dB, alpha);
            dstrow += bpp;
            //srcpixels += bpp;
        }
        //w);
        //srcrow += box->bmPitch;
        //dstrow += dst->pitch;
    //}

    return dstrow;
}

BYTE*  gal_PutPixelKeyAlphaChannel (GAL_Surface* dst, BYTE* dstrow,
                Uint32 pixel, MYBITMAP_CONTXT* mybmp)
{
    //BYTE* dstpixels = dstrow;
    //BYTE* srcpixels = srcrow;
    Uint32 ckey = mybmp->colorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = mybmp->mybmp->alpha;

    if (bpp == 1)
        return dstrow;

    //while ( h-- ) {
        //dstpixels = dstrow;
        //srcpixels = srcrow;
        //DUFFS_LOOP(
        {
            //Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            unsigned dR;
            unsigned dG;
            unsigned dB;
            //RETRIEVE_RGB_PIXEL (srcpixels, bpp, pixel);
            if ( pixel != ckey ) {
                RGB_FROM_PIXEL (pixel, dstfmt, sR, sG, sB);
                DISEMBLE_RGB (dstrow, bpp, dstfmt, pixel, dR, dG, dB);
                ALPHA_BLEND (sR, sG, sB, alpha, dR, dG, dB);
                ASSEMBLE_RGBA (dstrow, bpp, dstfmt, dR, dG, dB, alpha);
            }
            dstrow += bpp;
            //srcpixels += bpp;
        }//,
        //w);
        //srcrow += box->bmPitch;
        //dstrow += dst->pitch;
    //}

    return dstrow;
}

BYTE*  gal_PutPixelKey (GAL_Surface* dst, BYTE* dstrow,
            Uint32 pixel, MYBITMAP_CONTXT* mybmp)
{
    Uint32 ckey = mybmp->colorKey;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = dstfmt->Amask ? GAL_ALPHA_OPAQUE : 0;

    if (bpp == 1) {
        if (pixel != ckey)
            *dstrow = pixel;
        dstrow += bpp;
    }
    else {
        unsigned sR;
        unsigned sG;
        unsigned sB;
        if (pixel != ckey) {
            RGB_FROM_PIXEL (pixel, dstfmt, sR, sG, sB);
            ASSEMBLE_RGBA (dstrow, bpp, dstfmt, sR, sG, sB, alpha);
        }
        dstrow += bpp;
    }

    return dstrow;
}

BYTE* gal_PutPixelAlphaChannel (GAL_Surface* dst, BYTE* dstrow,
        Uint32 pixel, MYBITMAP_CONTXT* mybmp)
{
    //BYTE* dstpixels = dstrow;
    //BYTE* srcpixels = srcrow;
    GAL_PixelFormat *dstfmt = dst->format;
    int bpp = dstfmt->BytesPerPixel;
    unsigned alpha = mybmp->mybmp->alpha;

    if (bpp == 1)
        return dstrow;

    //while ( h-- ) {
        //dstpixels = dstrow;
        //srcpixels = srcrow;
        //DUFFS_LOOP(
        {
            //Uint32 pixel;
            unsigned sR;
            unsigned sG;
            unsigned sB;
            unsigned dR;
            unsigned dG;
            unsigned dB;
            RGB_FROM_PIXEL(pixel, dstfmt, sR, sG, sB);
            //DISEMBLE_RGB (srcpixels, bpp, dstfmt, pixel, sR, sG, sB);
            DISEMBLE_RGB (dstrow, bpp, dstfmt, pixel, dR, dG, dB);
            ALPHA_BLEND (sR, sG, sB, alpha, dR, dG, dB);
            ASSEMBLE_RGBA (dstrow, bpp, dstfmt, dR, dG, dB, alpha);
            dstrow += bpp;
            //srcpixels += bpp;
        }//,
        //w);
        //srcrow += box->bmPitch;
        //dstrow += dst->pitch;
    //}

    return dstrow;
}

