/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = CONVFUNS                                       */
/*                                                                    */
/*   Description     = Display Device Driver functions used for       */
/*                     converting between internal and external       */
/*                     formats                                        */
/*                                                                    */
/*                                                                    */
/**********************************************************************/

#define INCL_DDIMISC
#define INCL_WINSYS

#include "eddinclt.h"

#include "eddbcone.h"
#include "eddccone.h"

#include "eddbtypt.h"
#include "edddtypt.h"

#include "bitmaps.h"

#include "eddbextf.h"
#include "eddcextf.h"
#include "eddgextf.h"

#include "eddhcone.h"
#include "eddhtype.h"
#include "eddhmacr.h"

#include "convfuns.h"

/**********************************************************************/
/* Macro for setting RGB and RGB2 structures and incrementing the     */
/* pointer to it                                                      */
/* SetRGBAndIncEither(pRGB, bRed, bGreen, bBlue)                      */
/**********************************************************************/
#define SetRGBAndIncEither(p,r,g,b)                                    \
    {                                                                  \
        (p)->bRed=(BYTE)(r);                                           \
        (p)->bGreen=(BYTE)(g);                                         \
        (p)->bBlue=(BYTE)(b);                                          \
        (p)++;                                                         \
    }

/**********************************************************************/
/* SetRGBAndInc                                                       */
/*                                                                    */
/* ppExtRGB pointer to the pointer to the external colour table in    */
/*          old format                                                */
/* bRed     red component of the colour to set                        */
/* bGreen   green component of the colour to set                      */
/* bBlue    blue component of the colour to set                       */
/*                                                                    */
/* Sets one entry in the old format external colour table, ppExtRGB,  */
/* and increments the pointer to point to the next entry.             */
/**********************************************************************/
VOID SetRGBAndInc(PRGB * ppExtRGB,
                         BYTE   bRed,
                         BYTE   bGreen,
                         BYTE   bBlue)
{
   SetRGBAndIncEither(*ppExtRGB, bRed, bGreen, bBlue)
}


/**********************************************************************/
/* SetRGB2AndInc                                                      */
/*                                                                    */
/* ppExtRGB pointer to the pointer to the external colour table in    */
/*          new format                                                */
/* bRed     red component of the colour to set                        */
/* bGreen   green component of the colour to set                      */
/* bBlue    blue component of the colour to set                       */
/*                                                                    */
/* Sets one entry in the new format external colour table, ppExtRGB,  */
/* and increments the pointer to point to the next entry.             */
/**********************************************************************/
VOID SetRGB2AndInc(PRGB * ppExtRGB,
                          BYTE   bRed,
                          BYTE   bGreen,
                          BYTE   bBlue)
{
   ((PRGB2)*ppExtRGB)->fcOptions = 0;
   SetRGBAndIncEither((PRGB2)*ppExtRGB, bRed, bGreen, bBlue)
}


/**********************************************************************/
/* BytesPerExternalLine                                               */
/*                                                                    */
/* Calculates and returns the number of bytes in a single row of a    */
/* external bitmap, given a pointer to the external bitmap header,    */
/* and the number of pels in the bitmap line.                         */
/* (We cannot use the bitmap size from the external header because    */
/* we are sometimes passed an external header with just the planes    */
/* and bitcount fields filled in - the idea seems to be that we know  */
/* the size of our internal bitmap so we can get the size information */
/* from that)                                                         */
/* External lines are padded to a 32-bit boundary.                    */
/**********************************************************************/
ULONG BytesPerExternalLine(PBITMAPINFO2  pExt,
                           ULONG         cpels)
{
    if (OldFormatBitmap(pExt))
    {
        return (4 * ((
          (cpels *
               (ULONG)( ((PBITMAPINFO)pExt)->cBitCount) ) + 31) / 32));
    }
    else
    {
        return (4 * (( (cpels * pExt->cBitCount) + 31) / 32));
    }
}

/**********************************************************************/
/* BytesInOneBMRow                                                    */
/*                                                                    */
/* usBitsPerPel bits per pel of the bitmap in question                */
/* ulPels       number of pels in one scanline of the bitmap          */
/*                                                                    */
/* Calculates and returns the number of bytes in a single row of a    */
/* bitmap. (Can be either an internal or an external bitmap).         */
/* This is the number of bytes without padding to a 32-bit            */
/* boundary.                                                          */
/**********************************************************************/
ULONG BytesInOneBMRow(USHORT usBitsPerPel,
                      ULONG  ulPels)
{
    switch (usBitsPerPel)
    {
        case 24:
            return(ulPels * 3);

        case 16:
            return(ulPels * 2);

        case 8:
            return(ulPels);

        case 4:
            return((ulPels + 1) / 2);

        case 1:
            return((ulPels + 7) / 8);
    }
} /* BytesInOneBMRow */


/**********************************************************************/
/* BytesTo32BitPadBMRow                                               */
/*                                                                    */
/* ulBytesPerRow  number of bytes containing data in each bitmap row. */
/*                                                                    */
/* Calculates and returns the number of bytes needed to pad the end   */
/* of each bitmap row to make it finish on a 32-bit boundary.         */
/**********************************************************************/
#define BytesTo32BitPadBMRow(ulBytesPerRow)                            \
   ((4 - (USHORT)(ulBytesPerRow % 4)) & 3)


/**********************************************************************/
/* ConvertPMInt                                                       */
/*                                                                    */
/* pfnvInnerConvert   pointer to the function to convert a scanline   */
/* ppbInternalPointer pointer to the variable used in the inner       */
/*                    convert function as a pointer to the internal   */
/*                    bitmap                                          */
/* ppbExternalPointer pointer to the variable used in the inner       */
/*                    convert function as a pointer to the external   */
/*                    bitmap                                          */
/*                                                                    */
/* Does the conversion between PM and XGA formats in both directions. */
/* Each scanline is processed by the function pointed to by           */
/* pfnvInnerConvert.                                                  */
/**********************************************************************/
VOID ConvertPMInt(PFNV     pfnvInnerConvert,
                  PBYTE *  ppbInternalPointer,
                  PBYTE *  ppbExternalPointer)
{
    ULONG       ulInternalBytesPerLine;
    ULONG       ulExternalBytesPerLine;
    PBYTE       pbInternalRowStart;
    PBYTE       pbExternalRowStart;

    /******************************************************************/
    /* Calculate the bytes in each internal and external bitmap line. */
    /******************************************************************/
    ulInternalBytesPerLine = SPad.pbhInternalBitmap->BytesPerLine;
    ulExternalBytesPerLine =
                     BytesPerExternalLine(SPad.pExternalHeader,SPad.cx);


    /******************************************************************/
    /* Initialize the external pointer to the start of the external   */
    /* bitmap bits.                                                   */
    /******************************************************************/
    pbExternalRowStart  = SPad.pbExternalBitmap;

    /******************************************************************/
    /* The internal format is stored upside down compared to the      */
    /* external format so find the start of the last row of the       */
    /* internal bitmap less the start position.                       */
    /******************************************************************/
    pbInternalRowStart = SPad.pbhInternalBitmap->Bitmap +
                         ulInternalBytesPerLine *
                         (SPad.pbhInternalBitmap->Info.Height -
                          SPad.pptlScanStart->y - 1);

    /******************************************************************/
    /* for each of the rows to be converted                           */
    /******************************************************************/
    while(SPad.lScanLines--)
    {
        /**********************************************************/
        /* Initialise internal pointer to the start of each row   */
        /**********************************************************/
        *ppbExternalPointer = pbExternalRowStart;
        *ppbInternalPointer = pbInternalRowStart;

        /**************************************************************/
        /* Call the specialist routine to process this scanline.      */
        /**************************************************************/
        pfnvInnerConvert();

        /**************************************************************/
        /* Move back a row in the internal bitmap.                    */
        /* and on a line in the external bitmap.                      */
        /**************************************************************/
        pbExternalRowStart += ulExternalBytesPerLine;
        pbInternalRowStart -= ulInternalBytesPerLine;

    } /* for each row of the bitmap */

} /* ConvertPMToInt */


/**********************************************************************/
/* FlipTheBitmap                                                      */
/*                                                                    */
/* Flips the bitmap (moves the origin from the internal - top left -  */
/* position to the external - bottom left - position) without doing   */
/* any conversions between bitmap bits per pixel.                     */
/**********************************************************************/
VOID FlipTheBitmap(USHORT  usDirection)
{
    ULONG            ulInternalBytesPerLine;
    ULONG            ulExternalBytesPerLine;
    PBYTE            pbInternalPointer;
    PBYTE            pbExternalPointer;

    /******************************************************************/
    /* There are (possibly) different numbers of bytes per line       */
    /* internally and externally because scanlines in internal        */
    /* bitmaps are padded to byte boundaries, but in external bitmaps */
    /* they are padded to dword boundaries.                           */
    /******************************************************************/
    ulInternalBytesPerLine = SPad.pbhInternalBitmap->BytesPerLine;
    ulExternalBytesPerLine =
                     BytesPerExternalLine(SPad.pExternalHeader,SPad.cx);

    /******************************************************************/
    /* The internal format is stored upside down compared to the      */
    /* external format so find the start of the last row of the       */
    /* internal bitmap less the start position.                       */
    /******************************************************************/
    pbInternalPointer = SPad.pbhInternalBitmap->Bitmap +
                        ulInternalBytesPerLine *
                        (SPad.pbhInternalBitmap->Info.Height -
                         SPad.pptlScanStart->y - 1);

    /******************************************************************/
    /* Initialize the external pointer                                */
    /******************************************************************/
    pbExternalPointer = SPad.pbExternalBitmap;

    /******************************************************************/
    /* for each of the rows to be converted                           */
    /******************************************************************/
    while(SPad.lScanLines--)
    {
        /**************************************************************/
        /* Copy one scanline.                                         */
        /* (internal bytes per line is only byte padded compared to   */
        /* dword padding externally, so is likely to be a smaller     */
        /* amount of data to move - but it would be more efficient if */
        /* we knew we were always able to move a dword multiple of    */
        /* data).                                                     */
        /**************************************************************/
        if (usDirection == INT_TO_EXT_COPY)
        {
            /**********************************************************/
            /* Copy internal to external.                             */
            /**********************************************************/
            memcpy(pbExternalPointer,
                   pbInternalPointer,
                   ulInternalBytesPerLine);
        }
        else
        {
            /**********************************************************/
            /* Copy external to internal.                             */
            /**********************************************************/
            memcpy(pbInternalPointer,
                   pbExternalPointer,
                   ulInternalBytesPerLine);
        }

        /**************************************************************/
        /* Adjust internal and external pointers to the next line.    */
        /* Remember these are stored upside down to each other (hence */
        /* the + and the - here).                                     */
        /**************************************************************/
        pbInternalPointer -= ulInternalBytesPerLine;
        pbExternalPointer += ulExternalBytesPerLine;
    }
} /* FlipTheBitmap */

/**********************************************************************/
/* CopyTheBitmap                                                      */
/*                                                                    */
/* Simply copies the callers bitmap bits to our internal data area.   */
/*                                                                    */
/* This routine was added for Defect 54391. With this defect we       */
/* are adding support for 16 bpp bitmaps, only when running at        */
/* 16 bpp.  This is really not a defect, it is a performance          */
/* enhancement.  The rationale is as follows;                         */
/*                                                                    */
/* Why should the app convert from 16 to 24 and then we convert       */
/* from 24 back to 16.                                                */
/*                                                                    */
/*                                                                    */
/* Joe Celi (12/2/92) - Defect 58015                                  */
/**********************************************************************/
VOID CopyTheBitmap(VOID)
{
    ULONG            ulInternalBytesPerLine;
    ULONG            ulExternalBytesPerLine;
    PBYTE            pbInternalPointer;
    PBYTE            pbExternalPointer;

    /******************************************************************/
    /* There are (possibly) different numbers of bytes per line       */
    /* internally and externally because scanlines in internal        */
    /* bitmaps are padded to byte boundaries, but in external bitmaps */
    /* they are padded to dword boundaries.                           */
    /******************************************************************/
    ulInternalBytesPerLine = SPad.pbhInternalBitmap->BytesPerLine;
    ulExternalBytesPerLine =
                     BytesPerExternalLine(SPad.pExternalHeader,SPad.cx);

    /******************************************************************/
    /* The internal format is stored upside down compared to the      */
    /* external format so find the start of the last row of the       */
    /* internal bitmap less the start position.                       */
    /******************************************************************/
    pbInternalPointer = SPad.pbhInternalBitmap->Bitmap;

    /******************************************************************/
    /* Initialize the external pointer                                */
    /******************************************************************/
    pbExternalPointer = SPad.pbExternalBitmap;

    /******************************************************************/
    /* for each of the rows to be converted                           */
    /******************************************************************/
    while(SPad.lScanLines--)
    {

       /**********************************************************/
       /* Copy external to internal.                             */
       /**********************************************************/
       memcpy(pbInternalPointer,
              pbExternalPointer,
              ulInternalBytesPerLine);

        /**************************************************************/
        /* Adjust internal and external pointers to the next line.    */
        /* Remember these are stored upside down to each other (hence */
        /* the + and the - here).                                     */
        /**************************************************************/
        pbInternalPointer += ulInternalBytesPerLine;
        pbExternalPointer += ulExternalBytesPerLine;
    }
} /* CopyTheBitmap */

/**********************************************************************/
/* Convert1To4                                                        */
/*                                                                    */
/* Converts a scanline in 1 bpp format to a scanline in 4 bpp format. */
/**********************************************************************/
VOID Convert1To4(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;

    /**************************************************************/
    /* each source byte of eight pixels maps to 4 target bytes    */
    /**************************************************************/
    for ( j = SPad.cx / 8; j--; )
    {
       /**************************************************************/
       /* make up the four target bytes described by a single source */
       /* byte                                                       */
       /**************************************************************/
       bSrcByte = *SPad.pbSrcPointer++;

       *SPad.pbTrgPointer++ =
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x80)>>7] << 4) |
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x40)>>6]     )  ;

       *SPad.pbTrgPointer++ =
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x20)>>5] << 4) |
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x10)>>4]     )  ;

       *SPad.pbTrgPointer++ =
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x08)>>3] << 4) |
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x04)>>2]     )  ;

       *SPad.pbTrgPointer++ =
               (BYTE)(SPad.ausConvertTable[(bSrcByte & 0x02)>>1] << 4) |
               (BYTE)(SPad.ausConvertTable[ bSrcByte & 0x01    ]     )  ;

    } /* for each byte on the row */

    /******************************************************************/
    /* finish off any incomplete bytes at the end of the row          */
    /* SrcPointer already points to the correct source byte           */
    /******************************************************************/
    j = ((SPad.cx & 7)+1) / 2;
    if ( j )
    {
        bSrcByte = *SPad.pbSrcPointer++;
        while (j--)
        {
            *SPad.pbTrgPointer++ = (BYTE)
                   ((SPad.ausConvertTable[(bSrcByte & 0x80)>>7] << 4) |
                    (SPad.ausConvertTable[(bSrcByte & 0x40)>>6]     ) );
            bSrcByte <<= 2;
        }
    }
} /* Convert1To4 */


/**********************************************************************/
/* Convert8To1                                                        */
/*                                                                    */
/* Converts a scanline in 8 bpp format to a scanline in 1 bpp format. */
/**********************************************************************/
VOID Convert8To1(VOID)
{
    ULONG   j;
    BYTE    bTrgByte;
    USHORT  usExtraBits;

    /**************************************************************/
    /* eight source bytes map to each target byte                 */
    /**************************************************************/
    for ( j = SPad.cx / 8; j--; )
    {
        /**********************************************************/
        /* get the target index corresponding to each of eight    */
        /* source bytes, shifting each left into a byte to be     */
        /* written to the target                                  */
        /**********************************************************/
        bTrgByte = (BYTE)(SPad.ausConvertTable[*SPad.pbSrcPointer++] << 7);
        bTrgByte |= SPad.ausConvertTable[*SPad.pbSrcPointer++] << 6;
        bTrgByte |= SPad.ausConvertTable[*SPad.pbSrcPointer++] << 5;
        bTrgByte |= SPad.ausConvertTable[*SPad.pbSrcPointer++] << 4;
        bTrgByte |= SPad.ausConvertTable[*SPad.pbSrcPointer++] << 3;
        bTrgByte |= SPad.ausConvertTable[*SPad.pbSrcPointer++] << 2;
        bTrgByte |= SPad.ausConvertTable[*SPad.pbSrcPointer++] << 1;
        *SPad.pbTrgPointer++ =
               bTrgByte | (BYTE)SPad.ausConvertTable[*SPad.pbSrcPointer++];

    } /* for each pel on the row */

    /**************************************************************/
    /* if there are extra bits at the end of the row, handle them */
    /**************************************************************/
    usExtraBits = (USHORT)(SPad.cx & 7);
    if (usExtraBits)
    {
        bTrgByte = 0;
        for ( j = usExtraBits; j--; )
        {
            bTrgByte |= (BYTE)SPad.ausConvertTable[*SPad.pbSrcPointer++];
            bTrgByte <<= 1;
        }
        *SPad.pbTrgPointer++ = (BYTE)(bTrgByte << (7 - usExtraBits));
    }
} /* Convert8To1 */


/**********************************************************************/
/* Convert4To1                                                        */
/*                                                                    */
/* Converts a scanline in 4 bpp format to a scanline in 1 bpp format. */
/**********************************************************************/
VOID Convert4To1(VOID)
{
    ULONG   j;
    BYTE    bTrgByte;
    BYTE    bSrcByte;
    USHORT  usExtraBits;

    /******************************************************************/
    /* for each byte in the target there are 8 pixels                 */
    /******************************************************************/
    for ( j = SPad.cx / 8; j--; )
    {
        /**************************************************************/
        /* make up on target byte - this comprises 4 source bytes     */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;
        bTrgByte = (BYTE)
               (((SPad.ausConvertTable[(bSrcByte & 0xF0)>>4] << 1) |
                  SPad.ausConvertTable[(bSrcByte & 0x0F)   ]        ) << 2);

        bSrcByte = *SPad.pbSrcPointer++;
        bTrgByte = (BYTE)((bTrgByte |
             (SPad.ausConvertTable[(bSrcByte & 0xF0)>>4] << 1) |
              SPad.ausConvertTable[(bSrcByte & 0x0F)   ]        ) << 2);

        bSrcByte = *SPad.pbSrcPointer++;
        bTrgByte = (BYTE)((bTrgByte |
             (SPad.ausConvertTable[(bSrcByte & 0xF0)>>4] << 1) |
              SPad.ausConvertTable[(bSrcByte & 0x0F)   ]        ) << 2);

        bSrcByte = *SPad.pbSrcPointer++;
        bTrgByte = (BYTE)((bTrgByte |
             (SPad.ausConvertTable[(bSrcByte & 0xF0)>>4] << 1) |
              SPad.ausConvertTable[(bSrcByte & 0x0F)   ]        ));

        *SPad.pbTrgPointer++ = bTrgByte;

    } /* for each target byte */

    /******************************************************************/
    /* finish off any incomplete bytes at the end of the row          */
    /* SrcPointer already points to the correct source byte           */
    /******************************************************************/
    usExtraBits = (USHORT)(((SPad.cx & 7) + 1) / 2);
    if (usExtraBits)
    {
        for ( j = usExtraBits; j--; )
        {
            bSrcByte = *SPad.pbSrcPointer++;
            bTrgByte = (BYTE)((bTrgByte << 2) |
                         (SPad.ausConvertTable[(bSrcByte & 0xF0)>>4] << 1) |
                                    SPad.ausConvertTable[(bSrcByte & 0x0F)]);
        }
        *SPad.pbTrgPointer++ = (BYTE)(bTrgByte << (8 - usExtraBits * 2));
    }

} /* Convert4To1 */
