/**
 ** pblitv2v.c ---- video to video bitblt routine for packed (8, 16, 24, 32 bpp) modes
 **
 ** Copyright (c) 1995 Csaba Biegl, 820 Stirrup Dr, Nashville, TN 37221
 ** [e-mail: csaba@vuse.vanderbilt.edu].
 **
 ** This file is part of the GRX graphics library.
 **
 ** The GRX graphics library is free software; you can redistribute it
 ** and/or modify it under some conditions; see the "copying.grx" file
 ** for details.
 **
 ** This library 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.
 **
 **/

#include "libgrx.h"
#include "grdriver.h"
#include "arith.h"
#include "mempeek.h"

/* frame offset address calculation */
#define FOFS(x,y,lo) umuladd32((y),(lo),(x))

#ifndef __XWIN__
static INLINE
void dualpageblt(GrFrame *dst,int dx,int dy,
                 GrFrame *src,int sx,int sy,
                 int w,int h)
{
    unsigned long doff,soff;
    int   dskip,sskip;
    int   rb,rbb;
    int   wb,wbb;
    
    GRX_ENTER();
    if(dy > sy) {
        dy   += (h - 1);
        sy   += (h - 1);
        dskip = -(dst->gf_lineoffset + w);
        sskip = -(src->gf_lineoffset + w);
    }
    else {
        dskip = dst->gf_lineoffset - w;
        sskip = src->gf_lineoffset - w;
    }
    doff = FOFS(dx,dy,dst->gf_lineoffset);
    soff = FOFS(sx,sy,src->gf_lineoffset);
    wbb  = (-1);
    rbb  = (-1);
    setup_far_selector(dst->gf_selector);
    do {
        unsigned w1 = BANKLFT(doff);
        unsigned w2 = BANKLFT(soff);
        unsigned w3,ww;
        w1 = umin(w,w1);
        w2 = umin(w,w2);
        usort(w1,w2);
        w3 = w  - w2;
        w2 = w2 - w1;
        if(w2 == 0) w2=w3 , w3=0;
        do {
            char *dptr = &dst->gf_baseaddr[0][BANKPOS(doff)];
            char *sptr = &src->gf_baseaddr[0][BANKPOS(soff)];
            wb = BANKNUM(doff);
            rb = BANKNUM(soff);
            if((rbb - rb) | (wbb - wb)) SRWBANK((rbb = rb),(wbb = wb));
            doff += w1;
            soff += w1;
            #ifndef MISALIGNED_16bit_OK
            do {
                poke_b_f(dptr,peek_b_f(sptr));
                dptr++; sptr++;
            } while (w1--);
            #else
            if(w1 >= 3) {
                if((GR_PtrInt)(dptr) & 1) {
                    poke_b_f(dptr,peek_b_f(sptr));
                    dptr++; sptr++; w1--;
                }
                if((GR_PtrInt)(dptr) & 2) {
                    poke_w_f(dptr,peek_w_f(sptr));
                    dptr += 2; sptr += 2; w1 -= 2;
                }
                if((ww = w1 >> 2) > 0) do {
                    poke_l_f(dptr,peek_l_f(sptr));
                    dptr += 4; sptr += 4;
                } while(--ww != 0);
            }
            if(w1 & 2) {
                poke_w_f(dptr,peek_w_f(sptr));
                dptr += 2; sptr += 2;
            }
            if(w1 & 1) {
                poke_b_f(dptr,peek_b_f(sptr));
            }
            #endif
            w1 = w2;
            w2 = w3;
            w3 = 0;
        } while(w1 != 0);
        doff += dskip;
        soff += sskip;
    } while(--h != 0);
    GRX_LEAVE();
}
#endif /* !__XWIN__ */

#define TMPSIZE 16384

void _GrFrDrvPackedBitBltV2V(GrFrame *dst,int dx,int dy,
                             GrFrame *src,int sx,int sy,
                             int w,int h,GrColor op)
{
    
    GRX_ENTER();
    #ifndef __XWIN__
    if((C_OPER(op) == C_WRITE) && DRVINFO->splitbanks &&
        ((dy != sy) || (sx >= dx))) {
        dualpageblt(dst,dx,dy,src,sx,sy,w,h);
        } else
    #endif
        {
            GrFrame tmp;
            int tmpx,tmpn;
            tmp.gf_lineoffset = (w + 7) & ~3;
            tmpn = umax(umin(h,(TMPSIZE / tmp.gf_lineoffset)),1);
            tmpx = tmp.gf_lineoffset * tmpn;
            tmp.gf_baseaddr[0] = malloc((size_t)tmpx);
            if(tmp.gf_baseaddr[0]) {
                int ydir = 0;
                tmpx = sx & 3;
                if(dy > sy) {
                    dy  += h;
                    sy  += h;
                    ydir = ~0;
                }
                do {
                    int cnt = umin(h,tmpn);
                    dy -= (ydir & cnt);
                    sy -= (ydir & cnt);
                    _GrFrDrvPackedBitBltV2R(&tmp,tmpx,0,src,sx,sy,w,cnt,GrWRITE);
                    _GrFrDrvPackedBitBltR2V(dst,dx,dy,&tmp,tmpx,0,w,cnt,op);
                    dy += (~ydir & cnt);
                    sy += (~ydir & cnt);
                    h  -= cnt;
                } while(h != 0);
                free(tmp.gf_baseaddr[0]);
            }
        }
        GRX_LEAVE();
}

