/******************************************************************************
*
*   Source Control
*   --------------
*   $Header: algorithm.c,v 35.11 88/04/22 15:17:07 bart Exp $
*
*   by Bart Whitebook
*
* Copyright (c) 1988 Commodore-Amiga, Inc.
*
* Executables based on this information may be used in software
* for Commodore Amiga computers.  All other rights reserved.
*
* This information is provided "as is"; no warranties are made.
* All use is at your own risk, and no liability or responsibility is assumed.
*
******************************************************************************/


#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/rastport.h>

/* scale_rastport.c -- scale a portion of one rastport to fit into another */

dispose_temprp(temprp)
struct RastPort *temprp;
{
    if(temprp)
    {
        struct BitMap *tempbm = temprp->BitMap;

        if(tempbm)
        {
            int numbytes = (tempbm->Depth * tempbm->BytesPerRow);

            if((numbytes)&&(tempbm->Planes[0]))
            {
                FreeMem(tempbm->Planes[0],numbytes);    
            }
            FreeMem(tempbm,sizeof(*tempbm));    
        }
        FreeMem(temprp,sizeof(*temprp));    
    }
}

struct RastPort *get_temprp(srcrp)
struct RastPort *srcrp;
{
    struct RastPort *temprp = NULL;

    if(srcrp)
    {
        /* allocate temprp */

        if ((temprp = (struct RastPort *)
        AllocMem(sizeof(*temprp),MEMF_PUBLIC)) != NULL)
        {
            struct BitMap *tempbm;
            UBYTE   *mem;

            /* copy srcrp */

            *temprp = *srcrp;

            /* null layer pointer */

            temprp->Layer = NULL;

            /* allocate tempbm */

            if ((tempbm = (struct BitMap *)
                AllocMem(sizeof(*tempbm),MEMF_PUBLIC)) == NULL)
            {
                FreeMem(temprp,sizeof(*temprp));
                temprp = NULL;
            }
            else
            {
                int width;
                int bytesperrow;
                int numbytes;

                /* copy srcrp bitmap */ 

                *tempbm = *(srcrp->BitMap);

                /* force copy of bitmap to one line high */

                tempbm->Rows = 1;

                /* allocate temp memory for one line high bitmap */

                width = srcrp->BitMap->BytesPerRow << 3;

                if(width < 1)
                {
                    FreeMem(tempbm,sizeof(*tempbm));
                    FreeMem(temprp,sizeof(*temprp));
                    temprp = NULL;
                }
                else
                {
                    bytesperrow = (((width+0xf)>>4)<<1);

                    tempbm->BytesPerRow = bytesperrow;

                    numbytes = (tempbm->Depth * bytesperrow); 

                    if ((mem = (UBYTE *)
                        AllocMem(numbytes,MEMF_CHIP)) == NULL)
                    {
                        FreeMem(tempbm,sizeof(*tempbm));
                        FreeMem(temprp,sizeof(*temprp));
                        temprp = NULL;
                    }
                    else
                    {
                        int i;

                        /* initialize plane pointers for temp bitmap */

                        for(i=0; i<tempbm->Depth; i++)
                        {
                            tempbm->Planes[i] = mem+(tempbm->BytesPerRow * i);
                        }

                        /* link tempbm to temprp */

                        temprp->BitMap = tempbm;

                        /* exit */
                    }
                }
            }
        }
    }
    return(temprp);
}

scale_rastport(srcrp,srcr,dstrp,dstr)
struct RastPort  *srcrp;
struct Rectangle *srcr;
struct RastPort  *dstrp;
struct Rectangle *dstr;
{
    LONG error = FALSE;

    /* origin of source rectangle */

    WORD x = srcr->MinX;
    WORD y = srcr->MinY;
    WORD X = dstr->MinX;
    WORD Y = dstr->MinY;

    /* scale from m x n src rectangle to M x N dst rectangle */

    WORD m = (srcr->MaxX - x) + 1;
    WORD n = (srcr->MaxY - y) + 1;
    WORD M = (dstr->MaxX - X) + 1;
    WORD N = (dstr->MaxY - Y) + 1;

    /* map pixels from source to destination bitmap */

    LONG I = 0;
    LONG J = 0;

    LONG II = 0;
    LONG JJ = 0;

    /* readpixelarray */

    struct RastPort *temprp = get_temprp(srcrp);

    if(temprp)
    {
        UWORD *array = (UWORD *) AllocMem((m*2),MEMF_PUBLIC|MEMF_CLEAR);
        WORD n_err_N = 0; 

        if(array) 
        {
            WORD rectflag = FALSE;
            WORD M_div_m = 0;
            WORD N_div_n = 0;
            WORD m_mod_M;
            WORD n_mod_N;
            WORD m_neg_M;
            WORD n_neg_N;
            LONG iii;
            LONG jjj;
            LONG ii;
            LONG jj;

            /* initialize static variables */

            /* M_div_m = M/m; */
            /* N_div_n = N/n; */

            m_mod_M = m%M;
            n_mod_N = n%N;

            /* increments to write by */

            iii = (M_div_m < 1) ? 1: M_div_m;
            jjj = (N_div_n < 1) ? 1: N_div_n;

            /* write pixels or rectangles? */

            if((iii>1)||(jjj>1)) rectflag = TRUE;

            /* scaling variables */

            m_neg_M = (m_mod_M-m) * iii;

            n_neg_N = (n_mod_N-n) * jjj;

            /* for all pixels in the destination rectangle */

            kprintf("scale_rastport: M == %ld, N == %ld\n",M,N);

            for(J=0; J<N; J+=jjj)
            { 
                WORD m_err_M = 0; 

                /* map line to destination */

                II = 0;

                jj = JJ/N;

                /* determine first most popular j */
                {
                    WORD n_ERR_N = (n_err_N)%N; 
                    LONG j = jj;
                    WORD n_last_N = -n_ERR_N;
                    WORD n_next_N = N;

                    n_ERR_N += n; 

                    /* until next dest pixel */

                    n_ERR_N -= N; 

                    while(!(n_ERR_N < 0))
                    {
                        if(n_next_N > n_last_N)
                        {
                            n_last_N = n_next_N;
                            j++;
                        }
                        n_ERR_N -= N; 
                    }
                    n_next_N += n_ERR_N;

                    if(n_next_N > n_last_N)
                    {
                        n_last_N = n_next_N;
                        j++;
                    }

                    ReadPixelLine(srcrp,x,y+j,m,array,NULL,temprp);
                }


                for(I=0; I<M; I+=iii)
                {

                    ii = II/M;

                    /* map pixel to destination */
                    {
                        /* determine first most popular i */

                        WORD m_ERR_M = (m_err_M)%M;
                        LONG i = ii;
                        WORD m_last_M = -m_ERR_M;
                        WORD m_next_M = M;

                        m_ERR_M += m; 

                        /* until next dest pixel */

                        m_ERR_M -= M; 

                        while(!(m_ERR_M < 0))
                        {
                            if(m_next_M > m_last_M)
                            {
                                m_last_M = m_next_M;
                                i++;
                            }
                            m_ERR_M -= M; 
                        }
                        m_next_M += m_ERR_M;

                        if(m_next_M > m_last_M)
                        {
                            m_last_M = m_next_M;
                            i++;
                        }

                        /* map pixel */

                        SetAPen(dstrp,*(array+i));

                        if(rectflag)
                        {
                            RectFill(dstrp,X+I,Y+J,X+I+iii-1,Y+J+jjj-1);
                        }
                        else
                        {
                            WritePixel(dstrp,X+I,Y+J);
                        }

                    }

                    m_err_M += m_neg_M;

                    II += m * iii;

                    if(error) break;
                }

                n_err_N += n_neg_N;

                JJ += n * jjj;

                if(error) break;
            }

            FreeMem(array,(m*2));
        }
        else
        {
            error = TRUE;
        }

        dispose_temprp(temprp);
    }
    else
    {
        error = TRUE;
    }

    return(error);
}
