/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* 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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME =  BITMAPS.C
 *
 * DESCRIPTIVE NAME =  PRINTER DRIVER SOURCE
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION   Contains bitmap related routines.
 *
 *
 * FUNCTIONS     This file contains BitBlt, SetPel, GetPel and support routines.
 *
 *               prdb_SetPel
 *               GpiBitBlt
 *               prdb_GetBitmapHandle
 *               DrawImage
 *               PalGray
 *               PrintColor
 *               DefinePal
 *               BeginImage
 *               ImageScan
 *               ImageProcToChannel
 *               prdb_ImageData
 *
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#pragma pack(1)
#define  INCL_GPIBITMAPS
#include "inc\prdinclt.h"
#define  INCL_DEV
#include <pmdev.h>
#include <string.h>
#include "inc\utl.h"
#include "inc\prdgextf.h"
#include "inc\prdlextf.h"
#include "inc\prdcextf.h"
#include "inc\prdeextf.h"
#include "inc\image.h"
#include "inc\config.h"
#include "inc\prdmath.h"
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#define  INCL_GENPLIB_MEMORY
#define  INCL_GENPLIB_GAMMA  /* @V3.0GAMMA1 */
#include <genplib.h>


#define  FIXED_255     0x00ff0000
#define  LHBITMAP      0X4000000L
#define  OD_MEMORY     8L

extern PFNL    pfnlBitBlt;
extern ULONG   ps_patfill(HDC,PDDC,ULONG,BOOL,PBITBLTATTRS,BOOL);
extern HMODULE pscript_module;

/*                     */
/*
** Added ULONG to the arguments.
*/
ULONG DrawImage( PDDC, HDC, HDC, ULONG, PRECTL, PRECTL, PBITBLTATTRS, ULONG );

BOOL ImageProcToChannel( PDDC,BOOL,int,int );
int  PalGray( RGB *, PBYTE);  /* @V3.0GAMMA1 */
void DefinePal( PDDC, RGB *, int );    /* D74609 - parameter for color   */
                                       /* not needed now.  Soon function */
                                       /* will not be needed.            */

/*               */
/*
** Added ULONG to the arguments.
*/
void BeginImage( PDDC, int, BOOL, int, int, int, int, int, int, ULONG );

/*              */
/*
** Added two SHORTs to the arguments.
*/
VOID ImageScan( PDDC, PB, RGB *, int, BOOL, int, int, SHORT, SHORT );

USHORT  CompressMode2Out( PBYTE, PBYTE, USHORT);       /* D74609 */
USHORT  CompressAscii85( PBYTE, PBYTE, USHORT, PDDC);  /* D74609 */
LONG _Optlink GammaAdjust( PBYTE, LONG ); /* @V3.0GAMMA1 */

#define BIT16OBJ short int    /* D74609 */
#define MAXBLOCKSIZE 127      /* D74609 */

/* #include "j:\osdd\src\genplib\gplassrt.h" worklatter */

/*            */
extern ULONG ULGreVersion;


/***************************************************************************
 *
 * FUNCTION NAME = prdb_SetPel
 *
 * DESCRIPTION   = This call sets the color value for a given pixel.
 *
 * INPUT         = hdc, pptl, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 *
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/

ULONG prdb_SetPel( HDC hdc, PPOINTL pptl, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle        */
 /* PPOINTL pptl;                         Ptr to the pixel's coordinates    */
 /* PDDC pddc;                            Ptr to the DC instance data       */
 /* ULONG FunN;                           The engine's function number      */
{
  SHORT usCommand;
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( (PSZ)"prdb_SetPel(%lp, %lp, %lp, %lp)", ((PB)&hdc)+sizeof(hdc));
  #endif                                 /* DEBUG */

  PrintLog( (PB) "*pptl = {%ld, %ld}\n", pptl->x, pptl->y);

  /*
  ** call gre function if od_memory
  */
  if (pddc->iType == OD_MEMORY)
  {
    tl = GreSetPel( pddc->hdcMemory, pptl );
    ExitDriver( pddc );
    return( tl );
  }

  /*
  ** this call is illegal if a path or area is already defined
  */
  if (pddc->pddcb->path.fPathOrArea)
  {
    GplErrSetError(  PMERR_INV_NESTED_FIGURES );
    ExitDriver( pddc );
    return ( GPI_ERROR );
  }

  /*
  ** set the linecap to round, since it is the only one which
  ** will draw anything with a zero width line.
  */
  ps_setlinecap( pddc, 1 );

  /*
  ** set zero line width.  we want one device pixel to be printed
  */
  ps_setlinewidth( pddc, 0L, TRUE );

  /*
  ** now draw a zero length line at the specified point
  */
  ps_movetoCPx( pddc, pptl );
  ps_lineto( pddc, pptl );
  ps_stroke( pddc );
  ExitDriver( pddc );
  return (GPI_OK);
}

/***************************************************************************
 *
 * FUNCTION NAME =  LONG GpiBitBlt
 *
 * DESCRIPTION   =  The GpiBitBlt function copies a bitmap from one
 *                  presentation space to another.
 *
 *                  HPS hpsTarg;           target presentation-space handle
 *                  HPS hpsSrc;            source presentation-space handle
 *                  LONG cPoints;          number of points in array
 *                  PPOINTL aptl;          address of array
 *                  LONG lRop;             mixing method
 *                  ULONG flOptions;       line/column-compression flag
 *
 * The GpiBitBlt function copies a bitmap from one
 * presentation space to another.  It can also
 * modify the bitmap within a rectangle in a
 * presentation space.  The exact operation
 * carried out by GpiBitBlt depends on the raster
 * operation specified by the lRop parameter.
 *
 * If lRop directs GpiBitBlt to copy a bitmap, the
 * function copies the bitmap from a source
 * presentation space specified by hpsSrc to a
 * target presentation space specified by hpsTarg.
 * Each presentation space must be associated with
 * a device context for the display, for memory, or
 * for some other suitable raster device.  The
 * target and source presentation spaces can be the
 * same if desired.  The aptl parameter points to
 * an array of points that specify the corners of a
 * rectangle containing the bitmap in the source
 * presentation space as well as the corners of the
 * rectangle in the target presentation space to
 * receive the bitmap.  If the source and target
 * rectangles are not the same, GpiBitBlt stretches
 * or compresses the bitmap to fit the target rectangle.
 *
 * If lRop directs GpiBitBlt to modify a bitmap,
 * the function uses the raster operation to
 * determine how to alter the bits in a rectangle
 * in the target presentation space.  Raster
 * operations include changes such as inverting
 * target bits, replacing target bits with pattern
 * bits, and mixing target and pattern bits to
 * create new colors.  For some raster operations,
 * the function mixes the bits of a bitmap from a
 * source presentation space with the target and/or
 * pattern bits.
 *
 * Parameter  Description
 * ----------------------------------------------------------------------------
 * hpsTarg    Identifies the target presentation space.
 *
 * hpsSrc     Identifies the source presentation space.
 *
 * cPoints    Specifies the number of points pointed to by the aptl parameter.
 *
 *            It may be one of the following values:
 *
 *            Value  Meaning
 *            -----------------------------------------------------------------
 *            2      The points specify the lower-left and upper-right corners
 *                   of the target rectangle. If 2 is given, the raster
 *                   operation specified by the lRop parameter must not include
 *                   a source.
 *
 *            3      The points specify the lower-left and upper-right corners
 *                   of the target rectangle, and the lower-left corner of the
 *                   source rectangle. The upper-right corner of the source
 *                   rectangle is computed such that the target and source
 *                   rectangles have equal width and height. Any raster
 *                   operation may be used. If the operation does not include a
 *                   source, the third point is ignored.
 *
 *            4      The points specify the lower-left and upper-right corners
 *                   of the target and the source rectangles. If the rectangles
 *                   do not have equal width and height, the source bitmap is
 *                   stretched or compressed to fit the target rectangle.
 *                   GpiBitBlt uses the flOptions parameter to determine how
 *                   the bitmap should be compressed. If the raster operation
 *                   does not include a source, the source coordinates are
 *                   ignored.
 *
 * aptl       Points to an array of POINTL structures containing the number of
 *            points specified in the cPoints parameter. The points must be
 *            given in the following order:
 *
 *            Element index  Coordinate
 *            -----------------------------------------------------------------
 *            0              Specifies the lower-left corner of the target
 *                           rectangle.
 *
 *            1              Specifies the upper-right corner of the target
 *                           rectangle.
 *
 *            2              Specifies the lower-left corner of the source
 *                           rectangle.
 *
 *            3              Specifies the upper-right corner of the source
 *                           rectangle.
 *
 *            All points must be in device coordinates.
 *
 * lRop       Specifies the raster operation for the function. It can be any
 *            value in the range 0 through 255 or one of the following values,
 *            which represent common raster operations:
 *
 *            Value            Meaning
 *            -----------------------------------------------------------------
 *            ROP_DSTINVERT    Inverts the target.
 *
 *            ROP_MERGECOPY    Combines the source and the pattern using the
 *                             bitwise AND operator.
 *
 *            ROP_MERGEPAINT   Combines the inverse of the source and the
 *                             target using the bitwise OR operator.
 *
 *            ROP_NOTSRCCOPY   Copies the inverse of the source to the target.
 *
 *            ROP_NOTSRCERASE  Combines the inverse of the source and the
 *                             inverse of the target bitmaps using the bitwise
 *                             AND operator.
 *
 *            ROP_ONE          Sets all target pels to 1.
 *
 *            ROP_PATCOPY      Copies the pattern to the target.
 *
 *            ROP_PATINVERT    Combines the target and the pattern using the
 *                             bitwise exclusive XOR operator.
 *
 *            ROP_PATPAINT     Combines the inverse of the source, the pattern,
 *                             and target using the bitwise OR operator.
 *
 *            ROP_SRCAND       Combines the source and target bitmaps using the
 *                             bitwise AND operator.
 *
 *            ROP_SRCCOPY      Copies the source bitmap to the target.
 *
 *            ROP_SRCERASE     Combines the source and the inverse of the
 *                             target bitmaps using the bitwise AND operator.
 *
 *            ROP_SRCINVERT    Combines the source and target bitmaps using the
 *                             bitwise exclusive OR operator.
 *
 *            ROP_SRCPAINT     Combines the source and target bitmaps using the
 *                             bitwise OR operator.
 *
 *            ROP_ZERO         Sets all target pels to 0.
 *
 * flOptions  Specifies how to compress a bitmap if the target rectangle is
 *            smaller than the source. It can be one of the following values:
 *
 *            Value       Meaning
 *            -----------------------------------------------------------------
 *            BBO_AND     Compresses two rows or columns into one by combining
 *                        them with the bitwise AND operator. This value is
 *                        useful for compressing bitmaps
 *
 *            BBO_IGNORE  Compresses two rows or columns by throwing one out.
 *                        This value is useful for compressing color bitmaps.
 *                        that have black images on a white background.
 *
 *            BBO_OR      Compresses two rows or columns into one by combining
 *                        them with the bitwise OR operator. This value is the
 *                        default and is useful for compressing bitmaps that
 *                        have white images on a black background.
 *
 *            All values in the range 0x0100 through 0xFF00 are reserved for
 *            privately supported modes for particular devices.
 *
 *
 *
 * Comments
 *
 * The source and target presentation spaces may be associated with any device
 * context having raster capabilities. Some raster devices, such as banded
 * printers, can receive bitmaps but cannot supply them. These devices cannot
 * be used as a source.
 *
 * GpiBitBlt does not affect the pels in the upper and right boundaries of the
 * target rectangle. This means the function draws up to but does not include
 * those pels.
 *
 * If lRop includes a pattern, GpiBitBlt uses the current area color, area
 * background color, pattern set, and pattern symbol of the target presentation
 * space. Although the function may stretch or compress the bitmap, it never
 * stretches or compresses the pattern.
 *
 * If the target and source presentation spaces are associated with device
 * contexts that have different color formats, GpiBitBlt converts the bitmap
 * color format as it copies the bitmap. This applies to bitmaps copied to or
 * from a device context having a monochrome format. To convert a monochrome
 * bitmap to a color bitmap, GpiBitBlt converts 1 pels to the target's
 * foreground color, and 0 pels to the current area background color. To
 * convert a color bitmap to a monochrome bitmap, GpiBitBlt converts pels with
 * the source's background color to the target's background color, and all
 * other pels to the target's foreground color.
 *
 * The bitmap associated with a source presentation space is always a finite
 * size. Although GpiBitBlt will copy a bitmap when given a source rectangle
 * that is larger than the source bitmap or extends past the boundaries of the
 * source bitmap, any pels not associated with the source bitmap are
 * undefined.
 *
 * Example
 *
 * This example uses GpiBitBlt to copy and compress a bitmap in a presentation
 * space. The function copies the bitmap that is 100 pels wide and 100 pels
 * high into a 50-by-50-pel rectangle at the location (300,400). Since the
 * raster operation is ROP_SRCCOPY, GpiBitBlt replaces the image previously in
 * the target rectangle. The function compresses the bitmap to fit the new
 * rectangle by discarding extra rows and columns as specified by the
 * BBO_IGNORE option.
 *
 * HPS hps;
 * POINTL aptl[4] = {
 *     300, 400,            lower-left corner of target
 *     350, 450,            upper-right corner of target
 *     0, 0,                lower-left corner of source
 *     100, 100 };          upper-right corner of source
 *
 * GpiBitBlt(hps,           target presentation space
 *     hps,                 source presentation space
 *     4L,                  four points needed to compress
 *     aptl,                points to source and target
 *     ROP_SRCCOPY,         copy source replacing target
 *     BBO_IGNORE);         discard extra rows and columns
 *
 * INPUT         = hdcDst, hdcSrc, nptl, pptl, ulMix, ulStyle,
 *                    pbbAttrs, pddc, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  GPI_OK
 *
 * RETURN-ERROR  = GPI_ERROR
 *
 * Use the WinGetLastError function to retrieve the error value, which may be
 * one of the following:
 *
 *      PMERR_BASE_ERROR
 *      PMERR_BITBLT_LENGTH_EXCEEDED
 *      PMERR_BITMAP_NOT_SELECTED
 *      PMERR_INCOMPATIBLE_BITMAP
 *      PMERR_INCORRECT_DC_TYPE
 *      PMERR_INV_BITBLT_MIX
 *      PMERR_INV_BITBLT_STYLE
 *      PMERR_INV_COORDINATE
 *      PMERR_INV_DC_TYPE
 *      PMERR_INV_HBITMAP
 *      PMERR_INV_HDC
 *      PMERR_INV_HPS
 *      PMERR_INV_IN_AREA
 *      PMERR_INV_IN_PATH
 *      PMERR_INV_LENGTH_OR_COUNT
 *      PMERR_NO_BITMAP_SELECTED
 *      PMERR_PS_BUSY
 *
 **************************************************************************/

ULONG prdb_Bitblt( HDC hdcDst, HDC hdcSrc, LONG nptl, PPOINTL pptl, ULONG ulMix, ULONG ulStyle, PBITBLTATTRS pbbAttrs,
                   PDDC pddc, ULONG FunN )
 /* HDC hdcDst;                           The destination display context   */
 /* HDC hdcSrc;                           The source display context        */
 /* LONG nptl;                            The number of points in the array */
 /* PPOINTL pptl;                         Ptr to the src and dst rectangle  */
 /*                                       info                              */
 /* ULONG ulMix;                          The mix mode                      */
 /* ULONG ulStyle;                        The bitblt style                  */
 /* PBITBLTATTRS pbbAttrs;                Ptr to the bitblt attributes      */
 /* PDDC pddc;                            Ptr to destination DC's instance  */
 /*                                       data                              */
 /* ULONG FunN;                           The engine function number        */
{
  POINTL      ptl;                     /* A point                           */
  POINTL      orgptl;                  /* original position                 */
  RECTL       rclDst;                  /* The destination rectangle         */
  RECTL       rclSrc;                  /* The source rectangle              */
  PBITMAPINFO pbmi;                    /* Ptr to the bitmap info structure  */
  long        lColorFg;                /* The foreground color              */
  long        lColorBg;                /* The background color              */
  ULONG       ulTmpColor;
  ULONG       ulTmpBkColor;
  SHORT       usTmpMix;
  BYTE        bMyROP;                  /* my temp copy of the ROP.          */
  union                                /* Union for converting an RGB to a  */
  {
    RGB  rgb;
    long lColor;
  }
  u;
  ULONG ulRet = GPI_OK;                                          /* @LMM */

  EnterDriver( pddc );

  /*
  ** ASSERT( sizeof( pddc->pddcb->image.bmi) == (sizeof( BITMAPINFOHEADER ) + 8 ) );
  */

  #if      DEBUG
    LogCall( (PSZ) "prdb_Bitblt(%lp, %lp, %ld, %lp, %08lx, %ld, %lp, %lp, %lp)",
             ((PB) &hdcDst) + sizeof( hdcDst ) );
  #endif                                 /* DEBUG */

  /*
  ** do nothing if DRAW bit not set
  */
  if (!(FunN&COM_DRAW))
  {
    ExitDriver( pddc );
    return (GPI_OK);
  }

  /*
  ** Only the least signifigant 8 bits can be set in a
  ** valid raster op.  If any other bits are set, then
  ** report an error.
  */
  if (ulMix & ~0xFFL)
  {
    RIP( "BitBlt: invalid mix mode." );
    GplErrSetError(  PMERR_INV_BITBLT_MIX );
    ExitDriver( pddc );
    return (GPI_ERROR);
  }

  /* @V3.1109317
  ** Init to not ours
  */
  CLEARFLAG ( pddc->pdv->ulGenFlags, IS_OUR_BITMAP );

  if (hdcSrc)
  {
    if (ulStyle & BLTMODE_SRC_BITMAP)
    {
      /*
      ** Source is a bitmap
      */
      HBITMAP hbmDisplay;

      hbmDisplay = (HBITMAP) GetDriverInfo( (HBITMAP) hdcSrc, 1L, hdcDst );

      if (hbmDisplay != GPI_ALTERROR)
      { /*
        ** It is one of ours.
        */

        /* @V3.1109317
        ** record we own it
        */
        SETFLAG ( pddc->pdv->ulGenFlags, IS_OUR_BITMAP );

        /*           
        ** Handle is ready to go on 2.2+
        */
        if ( ULGreVersion < RASTER_ENGINE_22 )
        {
          hdcSrc = (HDC) hbmDisplay;
        }
      }
    }
    else
    if ( ULGreVersion < RASTER_ENGINE_22 )  /*            */
    {
      /*
      ** Source is a DC
      */
      PDDC pddcSrc;

      pddcSrc = (PDDC) GetDriverInfo( hdcSrc, 0L, hdcDst );

      if ((ULONG) pddcSrc != GPI_ALTERROR &&
          (pddcSrc->iType == OD_MEMORY) )
      {
        hdcSrc = pddcSrc->hdcMemory;
      }
    }  /* if (ulStyle & BLTMODE_SRC_BITMAP) */
  }    /* if(hdcSrc)                        */

  /*
  ** Switch on the DC type so that the body of this function
  ** is executed only if it is OD_DIRECT or OD_QUEUED.
  */
  switch ((SHORT)pddc->iType)
  {
  /*
  ** in the case of an info dc, just return GPI_OK.
  */
  case OD_INFO:
       PrintLog( (PSZ) "BitBlt: iType = OD_INFO\n" );
       ExitDriver( pddc );
       return (GPI_OK);

  case OD_MEMORY:
       if (FunN & (COM_PATH | COM_AREA))
       {
         RIP( "BitBlt: invalid in path or area" );
         GplErrSetError(  PMERR_INV_IN_PATH );
         ulRet = GPI_ERROR;
       }
       else
       {
         ulRet = InnerGreBitblt( pddc->hdcMemory, hdcSrc, nptl, pptl,
                                 ulMix, ulStyle, pbbAttrs, FunN );
       }
       ExitDriver( pddc );
       return (ulRet);

  case OD_DIRECT:
  case OD_QUEUED:
       /*
       ** These cases fall through to the body of the function
       */
       break;

  default:
       RIP( "BitBlt: incorrect DC type." );
       GplErrSetError(  PMERR_INCORRECT_DC_TYPE );
       ExitDriver( pddc );
       return (GPI_ERROR);
  }

  if (pddc->pddcb->path.fPathIsOpen)
  {
    RIP( "BitBlt: invalid in path" );
    GplErrSetError(  PMERR_INV_IN_PATH );
    ExitDriver( pddc );
    return (GPI_ERROR);
  }

  if (pddc->pddcb->path.fAreaIsOpen)
  {
    RIP( "BitBlt: invalid in area" );
    GplErrSetError(  PMERR_INV_IN_AREA );
    ExitDriver( pddc );
    return (GPI_ERROR);
  }

  /*                */
  /*
  ** Destination coordinates will converted
  ** from world to device if the BBO_TAGWORLD is set in the usStyle.
  ** Hence, copy them from pptl to rclDst,
  ** so that the driver will sent back the points as it received.
  */
  rclDst.xLeft   = pptl[0].x;
  rclDst.yBottom = pptl[0].y;
  rclDst.xRight  = pptl[1].x;
  rclDst.yTop    = pptl[1].y;

  /*
  ** if the world coordinate flag is set, change the destination
  ** coordinates from world to device coordinates, since we ouput
  ** the image command using device coordinates. Sides are
  ** inclusive/inclusive in world coords.  In device coords the
  ** top and right should be exclusive (thats the + 1s are for)
  */
  /*                        */
  /*
  ** Convert the rclDst.
  */
  if (ulStyle & BBO_TARGWORLD)
  {
    GreConvert( hdcDst, CVTC_WORLD, CVTC_DEVICE, (PPOINTL) &rclDst, 2L );
    rclDst.xRight += 1;
    rclDst.yTop += 1;
  }

  /*
  ** Compute the source and destination rectangles.
  */
  switch ((SHORT) nptl)
  {
  case 2:
       /*
       ** If there are only two points, then there is only
       ** one rectangle so this must be a PATBLT
       ** except for ROP_DSTINVERT, ~ROP_DSTINVERT,
       ** ROP_ZERO and ROP_ONE.
       */
       /*                             */
       if (ulMix != ROP_DSTINVERT && ulMix != (0x00FF & ~ROP_DSTINVERT) && ulMix
           != ROP_ZERO && ulMix != ROP_ONE)
       {
         ulMix = ROP_PATCOPY;
       }
       break;

  case 3:
       /*
       ** With three points, the source rectangle is
       ** incomplete, so the driver assumes that its the
       ** same size as the destination rectangle.
       */
       rclSrc.xLeft = pptl[2].x;
       rclSrc.yBottom = pptl[2].y;
       rclSrc.xRight = rclSrc.xLeft + (rclDst.xRight - rclDst.xLeft);
       rclSrc.yTop = rclSrc.yBottom + (rclDst.yTop - rclDst.yBottom);
       break;

  case 4:
       /*
       ** With four points, there are two complete rectangle
       ** specifications.  Therefore, these two rectangles
       ** May be different sizes and a stretchblt will
       ** occur.
       */
       rclSrc.xLeft = pptl[2].x;
       rclSrc.yBottom = pptl[2].y;
       rclSrc.xRight = pptl[3].x;
       rclSrc.yTop = pptl[3].y;
       PrintLog( (PSZ) "rclSrc = {%ld, %ld, %ld, %ld}\n", rclSrc.xLeft,
                 rclSrc.yBottom, rclSrc.xRight, rclSrc.yTop );
       PrintLog( (PSZ) "rclDst = {%ld, %ld, %ld, %ld}\n", rclDst.xLeft,
                 rclDst.yBottom, rclDst.xRight, rclDst.yTop );
       break;

  default:
       RIP( "BitBlt: invalid bitmap dimension." );
       GplErrSetError(  PMERR_INV_BITMAP_DIMENSION );
       ExitDriver( pddc );
       return (FAILURE);
  }

  /*                                                                       */
  /*
  ** If the device coordinates system is set inside a function,
  ** then function should reset before it returns , even if it returns
  ** prematurely (On FALIURE).
  ** Hence, this is moved from top of the function to here,
  ** so that there is only one return after this.
  ** Also, the device coordinates system will be set for both
  ** !(ulStyle & BBO_TARGWORLD) and (ulStyle & BBO_TARGWORLD)
  **
  ** Since, BitBlt does not "draw" anything,
  ** there is no need to set wc for the change of CM.
  */
  ps_savematrix( pddc );
  PrintChannel( pddc, (PSZ) "md sm\n" );

  /*
  ** Now we shall do the bitblt operation. Since Postscript doesn't
  ** allow us to do anything but overpaint and doesn't allow us
  ** to look at the image on the printer, we can only support SRCCOPY
  ** and PATCOPY. All ROPs will be mapped to SRCCOPY, PATCOPY or NOP.
  ** The rule is: If it has a source, it's SRCCOPY. If it doesn't have
  ** a source but it does involve a pattern, it's PATCOPY. If it doesn't
  ** involve a source or a pattern, it's a NOP.
  **
  ** It would be       to return an error for the ROPs that we don't
  ** directly support. This would mess  up meta files and suprise
  **             some apps. The driver should just do the best that it
  ** can with what it's got.
  */

  /*                                                                       */
  /*
  ** Since Postscript is a vector device, it doesn't allow to do anything
  ** but overpaint and doesn't allow to look at the image on the printer,
  ** we can only support SRCCOPY, PATCOPY, ROP_ZERO and ROP_ONE. All other
  ** ROPs will be mapped to SRCCOPY, PATCOPY or NOP.
  **
  ** The rule is:
  **
  **     If the rop is ROP_DSTINVERT or ~ROP_DSTINVERT, then it is a NOP.
  **
  **      else If the rop is ROP_ZERO, then paint the destination
  **      with the color value 0x000000L (CLR_BLACK).
  **
  **      else If the rop is ROP_ONE, paint then the destination
  **      with the color value 0x0FFFFFL (CLR_WHITE).
  **
  **      else if the rop has a source, then it's a SRCCOPY.
  **
  **      else if it doesn't have a source but it does involve a pattern,
  **      then it's a PATCOPY.
  **
  **      else if it doesn't involve a source or a pattern, then it's a NOP.
  **
  */
  orgptl = pddc->pddcb->pen.ptlCur;    /* save current position */
  bMyROP = (BYTE) (ulMix & 0x00FF);    /* this is done for speed, the rotates   */
                                       /* on a byte are much faster and cleaner */
                                       /* than those done on a long. */

  /*                                                                       */
  /*
  ** Support for ROP_DSTINVERT.
  */
  if (bMyROP == (BYTE) (0x00FF & ROP_DSTINVERT))      /* ROP_DSTINVERT 0x0055 */
  {
    /*
    ** since there is no operator in postscript for inverting
    ** destination bitmaps, no action is carried out
    */
    PrintLog( (PSZ) "ROP_DSTINVERT\n" );
  }

  /*                                                                       */
  /*
  ** Support for ~ROP_DSTINVERT.                     0x00AA
  ** Leave the destination as it is.
  */
  else if (bMyROP == (BYTE)(0x00FF & ~ROP_DSTINVERT))
  {
    /*
    ** Leave the destinaton as it is, no action carried out.
    */
    PrintLog( (PSZ)"~ROP_DSTINVERT\n" );
  }

  /*                                                                       */
  /*
  ** Support for ROP_ZERO.
  */
  else if (bMyROP == ROP_ZERO)          /* ROP_ZERO 0x0000 */
  {
    PrintLog( (PSZ) "ROP_ZERO\n" );

    /*
    ** All the destination pels are zero.
    ** The rgb color value of all the pels will be zero.
    ** Hence paint the destination with the color value 0x000000L.
    ** The color index CLR_BLACK will map to Black color in the system
    ** color table.
    */
    ps_newpath( pddc );
    pddc->pddcb->cgs.fPathExists = TRUE;
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld box\n", rclDst.xLeft,
                  rclDst.yBottom, (rclDst.xRight - 1), (rclDst.yTop - 1) );

    /*
    ** BitBlt is supposed to pay attention to only the foreground
    ** mix mode.  We will, therefore, set the background mix mode
    ** to BM_OVERPAINT.
    */
    usTmpMix = pddc->pddcb->pat.usBgMix;
    pddc->pddcb->pat.usBgMix = BM_OVERPAINT;

    /*
    ** we will call ps_patfill to handle the pattern filling.
    ** Foreground color:
    ** The color index CLR_BLACK will map to Black color in the system
    ** color table.
    */
    ulTmpColor = pddc->pddcb->pat.ulFgColor;
    pddc->pddcb->pat.ulFgColor = CLR_BLACK;

    /*                                                                      */
    /*
    ** For pattern filling, use the pattern colors.
    ** For background set the pattern colors to the image colors.
    ** ulTmpBkColor = pddc->pddcb->pat.ulBgColor;
    ** pddc->pddcb->pat.ulBgColor = pddc->pddcb->image.ulBgColor;
    */
    /*   Added gsave and grestore around patfill.*/ /*            */
    /*                                                                      */
    /*
    ** Removed gsave and grestore around patfill.
    ** Patfill function will do this if necessary.
    */
    ps_patfill( hdcDst, pddc, (pddc->pddcb->path.usAreaFlags&BA_WINDING) ?
                BA_WINDING : BA_ALTERNATE, (ulStyle & BLTMODE_ATTRS_PRES) ?
                PF_BLT_ATTRS:
                FALSE, pbbAttrs, FALSE);
    pddc->pddcb->pat.usBgMix = usTmpMix;
    pddc->pddcb->pat.ulFgColor = ulTmpColor;

    /*                                                                       */
    /*
    ** For pattern filling, use the pattern colors.
    */
    /*
    ** pddc->pddcb->pat.ulBgColor = ulTmpBkColor;
    */
  }

  /*                                                                         */
  /*
  ** Support for ROP_ONE.
  */
  else if (bMyROP == (BYTE)(0x00FF & ROP_ONE))      /* ROP_ONE 0xFF */
  {
    PrintLog( (PSZ) "ROP_ONE\n" );

    /*
    ** All the destination pels are one.
    ** The rgb color value of all the pels will be one.
    ** Hence paint the destination with the color value 0x0ffffffL.
    ** The color index CLR_WHITE will map to White color in the system
    ** color table.
    */
    ps_newpath( pddc );
    pddc->pddcb->cgs.fPathExists = TRUE;
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld box\n", rclDst.xLeft,
                  rclDst.yBottom, (rclDst.xRight - 1), (rclDst.yTop - 1) );

    /*
    ** BitBlt is supposed to pay attention to only the foreground
    ** mix mode.  We will, therefore, set the background mix mode
    ** to BM_OVERPAINT.
    */
    usTmpMix = pddc->pddcb->pat.usBgMix;
    pddc->pddcb->pat.usBgMix = BM_OVERPAINT;

    /*
    ** we will call ps_patfill to handle the pattern filling.
    ** Foreground color:
    ** The color index CLR_WHITE will map to White color in the system
    ** color table.
    */
    ulTmpColor = pddc->pddcb->pat.ulFgColor;
    pddc->pddcb->pat.ulFgColor = CLR_WHITE;

    /*                                                                      */
    /*
    ** For pattern filling, use the pattern colors.
    ** For background set the pattern colors to the image colors.
    ** ulTmpBkColor = pddc->pddcb->pat.ulBgColor;
    ** pddc->pddcb->pat.ulBgColor = pddc->pddcb->image.ulBgColor;
    */
    /*
    **  Added gsave and grestore around patfill.                       
    **
    */
    /*
    ** Removed gsave and grestore around patfill.
    ** Patfill function will do this if necessary.
    */
    ps_patfill( hdcDst, pddc, (pddc->pddcb->path.usAreaFlags & BA_WINDING) ?
                BA_WINDING : BA_ALTERNATE, (ulStyle & BLTMODE_ATTRS_PRES) ?
                PF_BLT_ATTRS : FALSE, pbbAttrs, FALSE);
    pddc->pddcb->pat.usBgMix = usTmpMix;
    pddc->pddcb->pat.ulFgColor = ulTmpColor;

    /*                                                                       */
    /*
    ** For pattern filling, use the pattern colors.
    ** pddc->pddcb->pat.ulBgColor = ulTmpBkColor;
    */
  }
  else if ((bMyROP & 0x33) == ((bMyROP >> 2) &0x33)) /* No src, Pattern */
  {
    /*
    ** If the raster op if PATCOPY, then a rectangular
    ** path is created and filled with the current
    ** pattern attributes.
    */
    ps_newpath( pddc );
    pddc->pddcb->cgs.fPathExists = TRUE;
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld box\n", rclDst.xLeft,
                  rclDst.yBottom, (rclDst.xRight - 1), (rclDst.yTop - 1));

    /*
    ** BitBlt is supposed to pay attention to only the foreground
    ** mix mode.  We will, therefore, set the background mix mode
    ** to BM_OVERPAINT.
    */
    usTmpMix = pddc->pddcb->pat.usBgMix;
    pddc->pddcb->pat.usBgMix = BM_OVERPAINT;

    /*
    ** we will call ps_patfill to handle the pattern filling.  we
    ** must set the pattern colors to the image colors.
    */
    /*                                                                  */
    /*
    ** For pattern filling, use the pattern colors.
    ** ulTmpColor = pddc->pddcb->pat.ulFgColor;
    ** pddc->pddcb->pat.ulFgColor = pddc->pddcb->image.ulFgColor;
    ** ulTmpBkColor = pddc->pddcb->pat.ulBgColor;
    ** pddc->pddcb->pat.ulBgColor = pddc->pddcb->image.ulBgColor;
    */
    /*
    ** Added gsave and grestore around patfill.                
    */
    /*                                                                      */
    /*
    ** Removed gsave and grestore around patfill.
    ** Patfill function will do this if necessary.
    */
    ps_patfill( hdcDst, pddc, (pddc->pddcb->path.usAreaFlags & BA_WINDING) ?
                BA_WINDING : BA_ALTERNATE, (ulStyle & BLTMODE_ATTRS_PRES) ?
                PF_BLT_ATTRS : FALSE, pbbAttrs, FALSE);
    pddc->pddcb->pat.usBgMix = usTmpMix;

    /*                                                                    */
    /*
    ** For pattern filling, use the pattern colors.
    ** pddc->pddcb->pat.ulFgColor = ulTmpColor;
    ** pddc->pddcb->pat.ulBgColor = ulTmpBkColor;
    */
  }
  else                         /* Possible Pattern, Source */
  {
    PrintLog((PSZ)"ROP_SRCPAINT\n");
    /*                                                                      */
    /*
    ** Added ulMix to the arguments.
    */
    /* We need to listen to what DrawImage returns because it might
    ** return an error which we would then have to propogate back
    ** up the call stack...                                @LMM
    **/

    ulRet = DrawImage( pddc, hdcDst, hdcSrc, ulStyle, &rclDst, &rclSrc, pbbAttrs,
                       ulMix );
  }

  /*                                                                      */
  /*
  ** restore the matrix.
  */
  ps_restorematrix( pddc );
  ps_moveto( pddc, &orgptl );            /* restore org cp */
  ExitDriver( pddc );
  return (ulRet);
}

/***************************************************************************
 *
 * FUNCTION NAME = prdb_GetBitmapHandle
 *
 * DESCRIPTION   = Returns the bitmap handle.
 *
 * INPUT         = (hdc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

HBITMAP prdb_GetBitmapHandle( HDC hdc )
{
  LONG    lResult;
  HBITMAP hbm;                         /* Source bitmap handle              */

  /*
  ** !!!CR What happens if hdc > LHBITMAP?
  */
  if ((ULONG)hdc < LHBITMAP)
  {
    /*
    ** Pop the source bitmap handle out of the source
    ** DC by selecting in a null bitmap handle and
    ** then reselecting the origional bitmap handle.
    */
    hbm = GreSelectBitmap(hdc, 0L );

    if (((LONG) hbm) <= 0)
    {
      RIP( "GetBitmapHandle: GreSelectBitmap failed." );
      GplErrSetError( PMERR_BITMAP_NOT_FOUND );
      return( (HBITMAP)0 );
    }
    lResult = (LONG) GreSelectBitmap( hdc, hbm );

    if (lResult < 0)
    {
      RIP( "GetBitmapHandle: 2nd GreSelectBitmap failed." );
      GplErrSetError( PMERR_BITMAP_NOT_FOUND );
      return( (HBITMAP) 0 );
    }
  }
  else
  {
    hbm = (HBITMAP) 0;
  }
  return( hbm );
}

/***************************************************************************
 *
 * FUNCTION NAME = DrawImage
 *
 * DESCRIPTION   = This routine projects an image of a rectangle on
 *                 the source DC's bitmap onto the printer.
 *
 * INPUT         = pddc, hdcDst, hdcSrc, ulStyle, prclDst, prclSrc,
 *                 pbbAttrs, ulMix)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
*/ /*                                                                        */
 /*
 ** Added ulMix to the arguments.
 */

ULONG DrawImage( PDDC pddc, HDC hdcDst, HDC hdcSrc, ULONG ulStyle, PRECTL prclDst, PRECTL prclSrc, PBITBLTATTRS pbbAttrs, ULONG ulMix )
  /*   ULONG ulStyle;                       The bitblt style               */
  /*                                                                       */
  /*
  ** Added ULONG ulMix to the arguments.
  */
  /* ULONG ulMix; */
{
  /*                                                                          */
  #define  SCAN_BUFFER_SIZE (0x00004000L)  /* 16KB */
  /*  #define  SCAN_BUFFER_SIZE  (0x00000000L)        No buffering. */

  int              nBitsPerSample;
  int              nColors;
  BITMAPINFOHEADER bmi;
  PBITMAPINFO      pbmi;
  HBITMAP          hbmSrc;
  PB               pbScan;
  int              iyScan;
  ULONG            ulSuccess = GPI_OK;
  BOOL             fIsColorDevice = pddc->pdv->jobProperties.fIsColorDevice;
  ULONG            ulTmpColor;
  RGB              rgb[2];
  FIXED            fxRed, fxGreen, fxBlue, fxGray, fxTmp;
  ULONG            ulLinesScanned;                             /* @LMM */
  /*                                                                        */
  SHORT usBytesPerScan,usScanPerBuf;
  SHORT usLanguageLevel;
  PDV   pdv = pddc->pdv;
  PBYTE pbGammaTable = pddc->pdv->pbGammaTable; /* @V3.0GAMMA1 */



  /*
  ** !!!CR check out GetBitmapHandle
  */
  if (!(ulStyle&BLTMODE_SRC_BITMAP))
  {
    if (!(hbmSrc = prdb_GetBitmapHandle(hdcSrc)))
    {
      RIP( "DrawImage: GetBitmapHandle failed." );
      PrintLog( (PSZ)"GetBitmapHandle failed\n" );
      return( GPI_ERROR );
    }
  }
  else
  {
    hbmSrc = (HBITMAP)hdcSrc;         /* hbmSrc will be validated by  */
    /*            */
    if ( ULGreVersion < RASTER_ENGINE_22 )
    {
      hdcSrc = pddc->hdcMemory ;
      GreSelectBitmap(hdcSrc, hbmSrc );
    }
    else
    {
      PDV pdv = pddc->pdv;

      hdcSrc = pddc->hdc;

      /* @V3.1109317
      ** Select bitmap into scratch DC
      */
      if ( ! CHECKFLAG ( pdv->ulGenFlags, IS_OUR_BITMAP ) ) /* Not our bitmap? */
      {
        if ( ! pdv->hdcScratch )  /* No scratch DC? */
        {                         /* Create one     */
          pdv->hdcScratch = GreOpenDC ((HDC) NULL, OD_MEMORY, "*", 0L,
                                             (PVOID)NULL );
        }

        hdcSrc = pdv->hdcScratch;
        GreSelectBitmap( pdv->hdcScratch, hbmSrc );
      }
    }

  }

  /*
  ** Determine the bitmap format
  */
  if (!GreGetBitmapParameters( hbmSrc, &bmi))
  {
    RIP( "DrawImage: GreGetBitmapParameters failed." );
    return( GPI_ERROR );
  }

  /* D74609
  ** If compat set set local level to 1
  */
  if ( pdv->usPSLevel1 == 1 )
  {
    usLanguageLevel = 1;
  }
  else
  {
    usLanguageLevel = pdv->usLanguageLevel;
  }

  /*
  ** Decide on which standard bitmap format to use.
  ** nBitsPerSample will be set to 1,4,8, or 24
  */
  if (bmi.cPlanes == 24 || bmi.cBitCount == 24)
  {
    if ( usLanguageLevel >= 2 )   /* 24BPP */
    {
      fIsColorDevice = TRUE;
    }

    if (fIsColorDevice)
    {
      nBitsPerSample = 24;
    }
    else
    {
      nBitsPerSample = 8;
    }
  }
  else if (bmi.cPlanes >= 8 || bmi.cBitCount >= 8)
  {
    nBitsPerSample = 8;
  }
  else if (bmi.cPlanes >= 4 || bmi.cBitCount >= 4)
  {
    nBitsPerSample = 4;
  }
  else
  {
    nBitsPerSample = 1;
  }

  /*
  ** Compute the number of color table entries.
  */
  if (nBitsPerSample != 24)
  {
    nColors = 2 << nBitsPerSample;
  }
  else
  {
    nColors = 0;
  }

  /*
  ** Allocate storage for the scan line
  */
  /*                                                                        */
  /*
  ** Find the number of scan lines in one buffer.
  ** The lines are in 32 bit boundary.
  */
  usBytesPerScan = ((nBitsPerSample * bmi.cx+31) >> 5) << 2;
  usScanPerBuf = (SHORT) (SCAN_BUFFER_SIZE / usBytesPerScan );

  /*
  ** Check for minima and maxima.
  */
  if (usScanPerBuf == 0)
  {
    usScanPerBuf = 1;
  }
  else if (usScanPerBuf >= (SHORT)(prclSrc->yTop-prclSrc->yBottom))
  {
    usScanPerBuf = (SHORT)(prclSrc->yTop-prclSrc->yBottom );
  }

  /*
  ** Allocate memory for "usScanPerBuf" lines.
  */
  if (!(pbScan = GplMemoryAlloc(pdv->pDCHeap, usBytesPerScan *usScanPerBuf)))
  {
    RIP( "DrawImage: AllocMem for scanline failed." );
    return( GPI_ERROR );
  }

  /*
  ** Allocate storage for the bitmap info structure
  */
  if (!(pbmi = (PBITMAPINFO)GplMemoryAlloc(pdv->pDCHeap,
                                   sizeof(BITMAPINFO)+nColors *sizeof (LONG))))
  {
    /* "DrawImage: Allocmem for bitmap info struc failed." */
    GplMemoryFree((PB)pbScan);
    return( GPI_ERROR );
  }
  pbmi->cbFix = sizeof( BITMAPINFOHEADER );
  pbmi->cPlanes = 1;
  pbmi->cBitCount = nBitsPerSample;

  /*
  ** Output the appropriate PostScript image operator.
  */
  PrintChannel( pddc, (PSZ)"\nsave\n" );

  /*                                                                        */
  /*
  ** Output the color info.
  */


  if (nBitsPerSample == 1)
  {
    /*
    ** if this is a monochrome source bitmap then we need to use
    ** the destination's image data colors to do the color conversion
    ** (as per the rules given for bitblt)
    */
    ulTmpColor = prdc_GetColor( pddc, pddc->pddcb->image.ulBgColor );
    ulTmpColor = GammaAdjust( pbGammaTable, ulTmpColor ); /* @V3.0GAMMA1 */
    rgb[0].bBlue = (BYTE)( ulTmpColor & 0xFF );
    rgb[0].bGreen = (BYTE)( (ulTmpColor >> 8) & 0xFF );
    rgb[0].bRed = (BYTE)( (ulTmpColor >> 16) & 0xFF );
    ulTmpColor = prdc_GetColor( pddc, pddc->pddcb->image.ulFgColor );
    ulTmpColor = GammaAdjust( pbGammaTable, ulTmpColor ); /* @V3.0GAMMA1 */
    rgb[1].bBlue = (BYTE) ( ulTmpColor & 0xFF );
    rgb[1].bGreen = (BYTE)( (ulTmpColor >> 8) & 0xFF );
    rgb[1].bRed = (BYTE)( (ulTmpColor >> 16) & 0xFF );

    /*
    ** These color parameters can be overwritten
    */
    if (ulStyle & BLTMODE_ATTRS_PRES)
    {
      switch ((SHORT) pbbAttrs->cSize)
      {
      /*
      ** pbbAttrs specifies both fore and back
      */
      case 12:
           ulTmpColor = prdc_GetColor( pddc, pbbAttrs->lBackColor );
           ulTmpColor = GammaAdjust( pbGammaTable, ulTmpColor ); /* @V3.0GAMMA1 */
           rgb[0].bBlue = (BYTE)( ulTmpColor & 0xFF );
           rgb[0].bGreen = (BYTE)( (ulTmpColor >> 8) & 0xFF );
           rgb[0].bRed = (BYTE)( (ulTmpColor >> 16) & 0xFF );
           /*
           ** NOTE: falls through
           */

      /*
      ** pbbAttrs only specifies foreground
      */
      case 8:
           ulTmpColor = prdc_GetColor( pddc, pbbAttrs->lColor );
           ulTmpColor = GammaAdjust( pbGammaTable, ulTmpColor ); /* @V3.0GAMMA1 */
           rgb[1].bBlue = (BYTE)( ulTmpColor & 0xFF );
           rgb[1].bGreen = (BYTE)( (ulTmpColor >> 8) & 0xFF );
           rgb[1].bRed = (BYTE)( (ulTmpColor >> 16) & 0xFF );
           break;

      default:
           GplErrSetError( PMERR_INV_LENGTH_OR_COUNT );
           ulSuccess = GPI_ERROR;
           goto DrawImageFreeMem;
           break;
      }
    }

    if (fIsColorDevice)
    {
      /*
      ** output the rgb colors for the foreground, and then
      ** the background colors.
      */
      fxTmp = (LONG) rgb[1].bBlue << 16;
      fxBlue = frdiv( fxTmp, FIXED_255 );
      fxTmp = (LONG) rgb[1].bGreen << 16;
      fxGreen = frdiv( fxTmp, FIXED_255 );
      fxTmp = (LONG) rgb[1].bRed << 16;
      fxRed = frdiv( fxTmp, FIXED_255 );
      PrintChannel( pddc, (PSZ)"%f %f %f ", fxRed, fxGreen, fxBlue );
      fxTmp = (LONG) rgb[0].bBlue << 16;
      fxBlue = frdiv( fxTmp, FIXED_255 );
      fxTmp = (LONG) rgb[0].bGreen << 16;
      fxGreen = frdiv( fxTmp, FIXED_255 );
      fxTmp = (LONG) rgb[0].bRed << 16;
      fxRed = frdiv( fxTmp, FIXED_255 );
      PrintChannel( pddc, (PSZ)"%f %f %f\n", fxRed, fxGreen, fxBlue );
    }
    else
    {
      /*
      ** output the gray shades for the foreground, then
      ** the background colors.
      */

      fxTmp = (LONG) PalGray( &rgb[1], NULL ) << 16;
      fxTmp = frdiv( fxTmp, FIXED_255 );
      PrintChannel( pddc, (PSZ)"%f ", fxTmp );
      fxTmp = (LONG) PalGray( &rgb[0], NULL ) << 16;
      fxTmp = frdiv( fxTmp, FIXED_255 );
      PrintChannel( pddc, (PSZ)"%f ", fxTmp );
    }

    /*
    ** fill the rectangle behind the image with the
    ** background color.
    */
    ps_newpath( pddc );
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld box\n", prclDst->xLeft,
       prclDst->yBottom, (prclDst->xRight-1), (prclDst->yTop-1) );

    /*
    ** fill the box with the background color, then set the
    ** foreground color.
    */

    if ( fIsColorDevice == FALSE)
    {
      PrintChannel( pddc, (PSZ)"g f g\n" );
    }
    else
    {
      PrintChannel( pddc, (PSZ)"r f r\n" );
    }
  }
  else
  {
    /*
    ** Color bitmap.
    ** Get the colors from the bitmap.
    */
    ulLinesScanned = (ULONG) GreGetBitmapBits (hdcSrc, hbmSrc,
                                          (ULONG) (prclSrc->yTop-1), 1L,
                                          pbScan, (PBITMAPINFO) pbmi );

    if (ulSuccess != 1)
    {
      RIP( "DrawImage: GetBitmapBits failed." );
      ulSuccess = GPI_ERROR;
      goto DrawImageFreeMem;
    }

    /*
    ** Output the colors.
    */

    /* D74609 */
    if (fIsColorDevice && ( usLanguageLevel >= 2 || nBitsPerSample == 4 ))
      DefinePal( pddc, pbmi->argbColor, (int)nBitsPerSample );

  }

  if ( usLanguageLevel < 2 )   /* D74609 */
  {
    ImageProcToChannel( pddc, fIsColorDevice, nBitsPerSample, (int)
                                            (prclSrc->xRight-prclSrc->xLeft) );
  }

  /*
  ** Output the appropriate image command.
  */
  BeginImage( pddc, nBitsPerSample, fIsColorDevice, (int)(prclSrc->xRight-
              prclSrc->xLeft), (int)(prclSrc->yTop-prclSrc->yBottom), (int)
              (prclDst->xRight-prclDst->xLeft), (int)(prclDst->yTop-prclDst->yBottom),
              (int) prclDst->xLeft, (int)prclDst->yBottom, ulMix );

  /*
  ** Call GetBitmapBits to load the the scan buffer.
  */
  /*                                                                        */
  /*
  ** Read the bitmaps from bottom to top.
  */
  if ( usLanguageLevel >= 2 )   /* D74609 */
  {
    FlushChannel( pddc );   /* Clear buffer first - get data out start with */
                            /* a clear channel buffer that is a multiple of */
                            /* 4.  This is needed because CompressAscii85   */
                            /* expands 4 inputs in to 5 bytes out.          */
                            /* Add extra long for flag                      */

    if (!(pdv->Compress85 = GplMemoryAlloc( pdv->pDCHeap,
                                            (5*ABBUF_SIZE/4)+sizeof(LONG))))
    {
     RIP( "DrawImage: AllocMem for Compress85 failed." );
     return( GPI_ERROR );
    }
    else
    {
      pdv->Compress85 += sizeof(LONG); /* First long is a flag for CompressAscii85 */
    }
  }


  for (iyScan = (int) prclSrc->yBottom ; iyScan <=
       (int) prclSrc->yTop - usScanPerBuf ; iyScan += usScanPerBuf)
  {
    /*
    ** Let's go DIRECTLY to the display driver to get them bits!!
    */
    /*
    ** Replace ulSuccess with ulLinesScanned.  This function is checked for
    ** a return code of OK or fail.  If ulSuccess is used here, it will be
    ** used as the return code.  if an app is checking OK or FAIL only, than
    ** it is possible that the app will act as if it is failing, when it
    ** really returns OK, only because the app won't know what kind of return
    ** code it is.
    ** @LMM
    */
    ulLinesScanned = (ULONG) GreGetBitmapBits (hdcSrc, hbmSrc, (ULONG) iyScan,
                                               (ULONG) usScanPerBuf, pbScan,
                                               (PBITMAPINFO)pbmi );

    if (ulLinesScanned != (ULONG) usScanPerBuf)
    {
      RIP( "DrawImage: GetBitmapBits failed." );
      ulSuccess = GPI_ERROR;
      goto DrawImageFreeMem;
    }


    ImageScan (pddc, pbScan, (RGB *)pbmi->argbColor, (int)nBitsPerSample,
       fIsColorDevice, (int)prclSrc->xLeft, (int)(prclSrc->xRight-
       prclSrc->xLeft), usBytesPerScan, usScanPerBuf );
  }

  /*             */

  if (iyScan < (int) prclSrc->yTop)
  {
    /*
    ** Read the remaining lines.
    */
    /*
    ** Save the number of lines read in a different variable other than
    ** ulSuccess.  ulSuccess should contain OK or FAIL *ONLY*.
    ** @LMM
    */
    ulLinesScanned = (ULONG) GreGetBitmapBits( hdcSrc, hbmSrc, (ULONG) iyScan,
                                          (ULONG) (prclSrc->yTop-iyScan),
                                          pbScan, (PBITMAPINFO) pbmi );

    if (ulLinesScanned != (prclSrc->yTop-iyScan))
    {
      RIP( "DrawImage: GetBitmapBits failed." );
      ulSuccess = GPI_ERROR;
      goto DrawImageFreeMem;
    }

    ImageScan( pddc, pbScan, (RGB  *)pbmi->argbColor, (int)nBitsPerSample,
               fIsColorDevice, (int)prclSrc->xLeft, (int)(prclSrc->xRight-
               prclSrc->xLeft), usBytesPerScan, (SHORT) (prclSrc->yTop-iyScan) );
  }

DrawImageFreeMem:


  if ( usLanguageLevel >= 2 )   /* D74609 */
  {
    LONG lFlag;

    FlushChannel(pddc);  /* Flush any remaining encoded bytes out. */

    pdv->Compress85 -= sizeof(LONG);
    lFlag = *((PLONG)pdv->Compress85);
    GplMemoryFree((PB)pdv->Compress85);
    pdv->Compress85 = NULL;

    if ( lFlag == 1 )                  /* Check to see if termination byte  */
    {                                  /* for CompressMode2Out was sent.    */
                                       /* output to the printer.            */
      PrintChannel( pddc, (PSZ) "\n~>restore\n");  /* Termination code sent */
    }                      /* for CompressMode2Out so just send ~> which is */
    else                   /* termination for CompressAscii85().            */
    {
      PrintChannel( pddc, (PSZ) "J,\n~>restore\n"); /* Need termination code */
    }                                               /* for CompressMode2Out  */
                                                    /* and CompresssAscii85. */
        /* Note that J, is the CompressAscii85 for the 128 which is the else */
        /* termination for CompressMode2Out.                                 */
  }
  else
  {
    PrintChannel( pddc, (PSZ) "\nrestore\n");
  }

  /*            */
  if (ulStyle & BLTMODE_SRC_BITMAP)
  {
    GreSelectBitmap( hdcSrc, (HBITMAP)NULL );
    /*
    ** GreCloseDC( hdcSrc );
    */
  }

  if (pbmi)
  {
    GplMemoryFree((PB)pbmi);
  }

  /*                                                                        */
  /*
  ** if (pbmi)      The pbScan will never be freed. Fixed this BUG.
  */
  if (pbScan)
  {
    GplMemoryFree((PB)pbScan);
  }


  return( ulSuccess );
}

/***************************************************************************
 *
 * FUNCTION NAME = PalGray
 *
 * DESCRIPTION   = This routine converts an RGB color value into
 *                 an 8 bit PostScript gray-shade image value.
 *                 Image values range from 0 to 255 where zero
 *                 corresponds to black and 255 corresponds to
 *                 white.  Returns the grayshade value.
 *
 * INPUT         = prgb
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/
 /* @V3.0GAMMA1
 ** Modified routine to apply gamma
 */

int PalGray( RGB *prgb, PBYTE pbGammaTable )
{
  int iBlue = prgb->bBlue & 0x0ff;
  int iGreen = prgb->bGreen & 0x0ff;
  int iRed = prgb->bRed & 0x0ff;

  if ( pbGammaTable )
  {
    iRed   = pbGammaTable[ iRed   + RED_OFFSET ];
    iGreen = pbGammaTable[ iGreen + GREEN_OFFSET ];
    iBlue  = pbGammaTable[ iBlue  + BLUE_OFFSET ];
  }

  /*
  ** The gray value is computed by applying the weights
  ** corresponding the response of the human eye to each of
  ** the primary  colors.  The weights are:
  **
  ** Blue = 0.15,    Green = 0.60,      Red = 0.25
  --
  ** @V3.084696
  ** Changed the weighted values.  These values are modified in the
  ** following.
  ** 0.11 Blue weight
  ** 0.30 Red weight
  ** 0.59 Green weight
  */

  return( (iBlue * 11 + iRed * 30 + iGreen * 59) / 100 );
}

/***************************************************************************
 *
 * FUNCTION NAME = PrintColor
 *
 * DESCRIPTION   = This routine outputs the RGB color in hex format on
 *                 the output channel.
 *
 * INPUT         = pddc, prgb
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void _Optlink PrintColor( PDDC pddc, RGB  *prgb, PBYTE pbGammaTable )
{
  /* @V3.0GAMMA1
  ** Do gamma adjust if needed
  */

  if ( pbGammaTable )
  {
    prgb->bRed   = pbGammaTable[ (BYTE)prgb->bRed   + RED_OFFSET ];
    prgb->bGreen = pbGammaTable[ (BYTE)prgb->bGreen + GREEN_OFFSET ];
    prgb->bBlue  = pbGammaTable[ (BYTE)prgb->bBlue  + BLUE_OFFSET ];
  }

  PrintChannel( pddc, (PSZ)"%02x%02x%02x", prgb->bRed & 0x0ff, prgb->bGreen
      & 0x0ff, prgb->bBlue & 0x0ff );
}

/***************************************************************************
 *
 * FUNCTION NAME = DefinePal
 *
 * DESCRIPTION   =
 *                 This routine defines the color table in PostScript
 *                 that is used to translate between the sample
 *                 indexes and the color values.
 *
 * INPUT         = pddc, prgb,  nBitsPerSample
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
*/

void DefinePal( PDDC pddc, RGB  *prgb, int nBitsPerSample )
  /* PDDC pddc;                            Pointer to the DC instance data   */
  /* RGB  *prgb;                           Pointer to the color table        */
  /* int nBitsPerSample;                   The number of bits per sample.    */
  /* BOOL fIsColorDevice;                  TRUE is this is a color device    */
{
  int i;
  int j;
  SHORT usLanguageLevel;
  PBYTE pbGammaTable = pddc->pdv->pbGammaTable; /* @V3.0GAMMA1 add gamma to */
                                                /* PrintColor call          */
  /* D74609
  ** If compat set set local level to 1
  */
  if ( pddc->pdv->usPSLevel1 == 1 )
  {
    usLanguageLevel = 1;
  }
  else
  {
    usLanguageLevel = pddc->pdv->usLanguageLevel;
  }

  switch (nBitsPerSample)
  {
  case 24:
       break;

  case 8:   /* D74609 */

       /*
       ** There is no color table for gray-scale because the
       ** samples are converted to a gray value and sent down
       ** directly as 8 bit sample values.
       */

         PrintChannel( pddc, (PSZ)"[/Indexed /DeviceRGB 255 <\n" );
         i = 0;

         while (i < 256)
         {
           for (j = 0 ; j < 8 ; ++j, ++i)
           {
             PrintColor( pddc, &prgb[i], pbGammaTable );
           }
           PrintChannel( pddc, (PSZ)"\n" );
         }
         PrintChannel( pddc, (PSZ)">] setcolorspace\n" );
       break;

    case 4:

         if ( usLanguageLevel >= 2)
         {
            PrintChannel( pddc, (PSZ)"[/Indexed /DeviceRGB 15 <\n" );
            i = 0;

            while (i < 16)
            {
              for (j = 0; j < 8; ++j, ++i)
                  PrintColor( pddc, &prgb[i], pbGammaTable );

              PrintChannel( pddc, (PSZ)"\n" );
            }
            PrintChannel( pddc, (PSZ)">] setcolorspace\n" );
         }
         else
         {
            /* Print out a pal array that is used as a lookup that includes */
            /* the lookup on two nibbles that is the bitmap data output. */
            /* This simplifies the postscript algorithm when doing subsitutions */
            /* on the index data. See imageproctochannel() */
            PrintChannel( pddc, (PSZ)"/pal <\n");    /* D74609 */
            for (i = 0; i < 16; i++)
              for (j = 0; j < 16; j++)
              {
                PrintColor( pddc, &prgb[i], pbGammaTable );
                PrintColor( pddc, &prgb[j], pbGammaTable );
              }
            PrintChannel( pddc, (PSZ)"> def\n");
         }
       break;

    default:
      break;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = BeginImage
 *
 * DESCRIPTION   = This routine outputs the PostScript code for the
 *                 image command appropriate for the bitmap format.
 *
 * INPUT         = pddc, nBitsPerSample, fIsColorDevice,
 *                 nSamplesPerScan, nScans, iWidth, iHeight, x, y, ulMix
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
*/ /*                                                                         */
 /*
 ** Added ulMix to the arguments.
 */

void BeginImage( PDDC pddc, int nBitsPerSample, BOOL fIsColorDevice, int nSamplesPerScan, int nScans,
                 int iWidth, int iHeight, int x, int y, ULONG ulMix )
  /*  PDDC pddc;                            Pointer to the DC instance data   */
  /*  int nBitsPerSample;                   The number of bits per sample: 1, */
  /*                                        4, 8, 24                          */
  /*  BOOL fIsColorDevice;                  TRUE if this is a color device    */
  /*  int nSamplesPerScan;                  The number of samples per scan    */
  /*  int nScans;                           The number of scanlines in the    */
  /*                                        image                             */
  /*  int iWidth;                           The destination rectangle width   */
  /*  int iHeight;                          The destination rectangle height  */
  /*  int x;                                The x coordinate of bottom-left   */
  /*                                        corner                            */
  /*  int y;                                The y coordinate of the           */
  /*                                        bottom-left corner                */
  /*             */
  /*
  ** Added ULONG ulMix to the arguments.
  */
  /* ULONG ulMix; */
{

  int cDecode;              /* D74609 */
  SHORT usLanguageLevel;


  /* D74609
  ** If compat set set local level to 1
  */
  if ( pddc->pdv->usPSLevel1 == 1 )
  {
    usLanguageLevel = 1;
  }
  else
  {
    usLanguageLevel = pddc->pdv->usLanguageLevel;
  }

  /*
  ** Set the device coordinate system.
  */
  PrintChannel( pddc, (PSZ)"md sm\n" );

  /*
  ** On a monochrome printer, 24 bit samples will be
  ** gray converted and downloaded as 8 bit samples.
  */
  if (nBitsPerSample == 24 && !fIsColorDevice)
  {
    nBitsPerSample = 8;
  }

  /*
  ** If more than one sample can fit in a byte, then the
  ** image code needs know how many samples are in the byte
  ** so that it can trim the excess samples off the end of
  ** the scanline.
  */
  /*
  ** if (nBitsPerSample < 8)
  ** PrintChannel( pddc, (PSZ) "/nSamp %d def /ns %d def\n", nSamplesPerScan,
  ** nSamplesPerScan );
  */
  /*
  ** position the image on the page.
  */
  PrintChannel( pddc, (PSZ)"%d %d translate ", x, y );

  /*
  ** do to the fact that area fills and imagemasks seem to fill
  ** the same rectangle differently, we need to shift the bitmap
  ** to the left one pixel.
  */
  PrintChannel( pddc, (PSZ)"-1 0 _snap translate " );

  /*
  ** scale the image.
  */
  PrintChannel( pddc, (PSZ)"%d %d scale\n", iWidth, iHeight );

  /*
  ** Output the image operator and the scan data.
  */

  /* D74609
  ** Check if Level 2 and color and nBitsPerSample != 24. We output the
  ** dictionary indexed method.
  */
  if ( usLanguageLevel >= 2 && fIsColorDevice &&  nBitsPerSample != 24 )
  {
    PrintChannel( pddc, (PSZ)"<<\n/ImageType 1\n");
    PrintChannel( pddc, (PSZ)"/Width %d /Height %d\n", nSamplesPerScan, nScans);
    PrintChannel( pddc, (PSZ)"/BitsPerComponent %d\n", nBitsPerSample);
    if ( nBitsPerSample == 1 )
    {
      if ( ulMix == ROP_NOTSRCCOPY )
        PrintChannel( pddc, (PSZ)"/Decode [0 1]\n");
      else
        PrintChannel( pddc, (PSZ)"/Decode [1 0]\n");
    }
    else
    {
      if ( nBitsPerSample == 8 )   /* 8 color mode */
        PrintChannel( pddc, (PSZ)"/Decode [0 255]\n");
      else                         /* must be 4 color mode */
        PrintChannel( pddc, (PSZ)"/Decode [0 15]\n");
    }
    PrintChannel( pddc, (PSZ)"/ImageMatrix [%d 0 0 %d 0 0]\n", nSamplesPerScan, nScans );
    PrintChannel( pddc, (PSZ)"/DataSource currentfile /ASCII85Decode filter /RunLengthDecode filter\n>>\n");
    if ( nBitsPerSample == 1 )
      PrintChannel( pddc, (PSZ)"imagemask\n");
    else
      PrintChannel( pddc, (PSZ)"image\n");
    return;
  }

  PrintChannel( pddc, (PSZ)"%d %d ", nSamplesPerScan, nScans );

  /*
  ** if there is one bit per sample,we will be using the imagemask command.
  ** otherwise, we will be using either the image or color image command.
  ** the imagemask command has the following parameters: width height invert
  ** matrix proc, where invert is a boolean.  in this case, true means
  ** to paint the '1' bits with the foreground color.  On the other
  ** hand, the image and colorimage commands have the same first five
  ** parameters: width height bits/sample matrix proc, where we are
  ** outputting the bits/sample in this block of code.  this will
  ** always be 8, as we have defined it.
  */
  if (nBitsPerSample == 1)
  {
    /*                                                                     */
    /*
    ** If the bitmap is monochrome and the
    ** ulMix is ROP_NOTSRCCOPY, then invert it.
    */
    if (ulMix == ROP_NOTSRCCOPY)
    {
      PrintChannel( pddc, (PSZ)"false " );
    }
    else
    {
      PrintChannel( pddc, (PSZ)"true " );
    }
  }
  else
  {
    PrintChannel( pddc, (PSZ)"8 " );
  }

  /*                                                                        */
  /*
  ** The bitmap has been read from bottom to top.
  ** PrintChannel( pddc, (PSZ) "[%d 0 0 %d 0 %d] ", nSamplesPerScan, -nScans, nScans );
  */
  PrintChannel( pddc, (PSZ)"[%d 0 0 %d 0 0] ", nSamplesPerScan, nScans );


  if (nBitsPerSample == 1)    /* uses imagemask */
  {
    if ( usLanguageLevel >= 2 )
    {
      PrintChannel( pddc, (PSZ) "\ncurrentfile\n/ASCII85Decode filter");
      PrintChannel( pddc, (PSZ) "\n/RunLengthDecode filter imagemask\n");
    }
    else
      PrintChannel( pddc, (PSZ)"{read_scan_line} imagemask\n" );
  }
  else
  if ( usLanguageLevel >= 2 )
  {
    if (fIsColorDevice)
    { /* only do if fIsColorDevice is true and nBitsPerSample is 24 */
      PrintChannel( pddc, (PSZ)"\ncurrentfile\n/ASCII85Decode filter");
      PrintChannel( pddc, (PSZ)"\n/RunLengthDecode filter\nfalse 3 colorimage\n");
    }
    else  /* done for mono Level 2 */
      PrintChannel( pddc, (PSZ)"\ncurrentfile\n/ASCII85Decode filter\n/RunLengthDecode filter\nimage\n");
  }
  else
  {
    if ( fIsColorDevice )
      PrintChannel( pddc, (PSZ)"{read_scan_line} false 3 colorimage\n",
                    nBitsPerSample );
    else
      PrintChannel( pddc, (PSZ)"{read_scan_line} image\n", nBitsPerSample );
  }

}

/***************************************************************************
 *
 * FUNCTION NAME = ImageScan
 *
 * DESCRIPTION   = This routine outputs the scanline data for the
 *                 appropriate bitmap format.
 *
 * INPUT         = (pddc, pbScan, prgb, nBitsPerSample, fIsColorDevice,
 *                  iFirst, nSamples, usBytesPerScan, usScan)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Pointer to the next scan line in the buffer if the buffer is not
 *                 scanned completely otherwise NULL.
 *
 * RETURN-ERROR  = NONE
 *
*/
VOID ImageScan( PDDC pddc, PB pbScan, RGB  *prgb, int nBitsPerSample, BOOL fIsColorDevice, int iFirst,
                int nSamples, SHORT usBytesPerScan, SHORT usScan )
  /* PDDC pddc;                            Pointer to the DC instance data   */
  /* PB pbScan;                            Pointer to the scanline data      */
  /* RGB  *prgb;                        Pointer to the color table           */
  /* int nBitsPerSample;                   The number of bits per sample:    */
  /*                                       1,4,8, 24                         */
  /* BOOL fIsColorDevice;                  TRUE if this is a color device    */
  /* int iFirst;                           Index of the first sample 0...N-1 */
  /* int nSamples;                         The number of samples             */
  /* SHORT usBytesPerScan;                Total bytes in one scan line       */
  /* SHORT usScan;                        Number of lines to be scanned      */
{
  int   nBytes;
  int   iShiftCount;
  int   iShiftCountCompliment;
  PB    pbScanStart = pbScan;
  SHORT scScan = usScan;
  RGB   rgb;
  register BYTE  bTempColor;
  LONG  ncBytes;
  register PBYTE pbScanOut;
  LONG  i;
  PDV   pdv = pddc->pdv;

  /* D74609
  ** needed for level 1 and level 2 changes. This holds the expanded output
  ** with the color info.
  */
  PB Compressed = NULL;
  /* D74609
  ** needed for level 2 to hold the CompressMode2Out() output.
  */
  PB Compressed2 = NULL;
  USHORT nOut;    /* D74609 */
  SHORT usLanguageLevel;
  PBYTE pbGammaTable = pddc->pdv->pbGammaTable; /* @V3.0GAMMA1 */

  /*
  ** The extracts the input scandata and shifts it to align it
  ** on byte boundaries and then prints it in hex format.
  */

  /* D74609
  ** If compat set set local level to 1
  */
  if ( pdv->usPSLevel1 == 1 )
  {
    usLanguageLevel = 1;
  }
  else
  {
    usLanguageLevel = pdv->usLanguageLevel;
  }


  switch (nBitsPerSample)
  {
  case 1:      /* D74609 This case is for both color and mono */
       /*
       ** Find the number of bytes.
       */
       nBytes = (nSamples + 7) >> 3;

       if ( usLanguageLevel >= 2 )     /* Level 2 pscript */
       {
         if ( !(Compressed =  GplMemoryAlloc( pdv->pDCHeap, 2*nBytes)))
         {
            RIP( "ScanImage:GplMemoryAlloc for Compressed failed." );
            return;
         }
       }

       if ((iFirst&7) != 0)
       {
         /*
         ** Source left is not divisible by 8.
         ** Hence bit shifting is required.
         */
         int ncBytes;

         iShiftCount = iFirst & 7;
         iShiftCountCompliment = 8 - iShiftCount;

         if (((iFirst + nSamples) & 7) == 0 || ((iFirst + nSamples) & 7) >
             iShiftCount)
         {
           iFirst = iFirst >> 3;

           for (pbScan = pbScanStart + iFirst ; --scScan >= 0 ; pbScanStart +=
                usBytesPerScan, pbScan = pbScanStart + iFirst)
           {
             /*
             ** pbScan++ not to exceed the bounds.
             */
             for (ncBytes = 1 ; ncBytes < nBytes ; ncBytes++)
             {
               *pbScan = (*pbScan << iShiftCount)|((*(pbScan+1) & 0x00ff) >>
                          iShiftCountCompliment );
               pbScan++;
             }

             /*
             ** The last byte
             */
             *pbScan = *pbScan << iShiftCount;

             /*
             ** Output to channel.
             */

             if ( usLanguageLevel >= 2)    /* D74609 */
             {
               nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
               WriteChannel( pddc, Compressed, nOut );
             }
             else
               HexToChannel( pddc, pbScanStart+iFirst, nBytes );

           }
         }
         else
         {
           iFirst = iFirst >> 3;

           for (pbScan = pbScanStart + iFirst ; --scScan >= 0 ; pbScanStart +=
              usBytesPerScan, pbScan = pbScanStart+iFirst)
           {
             for (ncBytes = 1; ncBytes <= nBytes; ncBytes++)
             {
               *pbScan = (*pbScan << iShiftCount) | ((*(pbScan + 1) & 0x00ff) >>
                  iShiftCountCompliment );
               pbScan++;
             }

             /*
             ** Output to channel.
             */

             if ( usLanguageLevel >= 2)   /* D74609 */
             {
               nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
               WriteChannel( pddc, Compressed, nOut );
             }
             else
               HexToChannel( pddc, pbScanStart+iFirst, nBytes );
           }
         }       /* End of if ((iFirst + nSamples) & 7 > iShiftCount) */
       }
       else
       {
         /*
         ** Source left is divisible by 8.
         ** Hence no bit shifting is required.
         */
         iFirst = iFirst >> 3;

         for ( ; --scScan >= 0 ; pbScanStart += usBytesPerScan)
         {
           /*
           ** Output to channel.
           */
           if ( usLanguageLevel >= 2)   /* D74609 */
           {
               nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
               WriteChannel( pddc, Compressed, nOut );
           }
           else
               HexToChannel( pddc, pbScanStart+iFirst, nBytes );
         }
       }
       break;

    case 4:

         /*
         ** Number of Bytes.
         */
         nBytes = (nSamples + 1) >> 1;

         if ( !fIsColorDevice )      /* D74609 */
         {
            if (!(Compressed =  GplMemoryAlloc( pdv->pDCHeap, 2*nBytes )))
            {
               RIP( "ScanImage:GplMemoryAlloc for Compressed2 failed." );
               return;
            }

            if ( usLanguageLevel >= 2 )
            {
               if (!(Compressed2 =  GplMemoryAlloc( pdv->pDCHeap, 4*nBytes )))
               {
                  RIP( "ScanImage:GplMemoryAlloc for Compressed2 failed." );
                  GplMemoryFree(Compressed);
                  return;
               }
            }
         }
          else
            if ( usLanguageLevel >= 2 )
            {
               if (!(Compressed =  GplMemoryAlloc( pdv->pDCHeap, 2*nBytes )))
               {
               RIP( "ScanImage:GplMemoryAlloc for Compressed2 failed." );
               return;
               }
            }


         if ((iFirst & 1) != 0)
         {

           /*
           ** Source left is not divisible by 2.
           ** Hence bit shifting is required.
           */

           if ((nSamples & 1) != 0)
           {
             /*
             ** Odd number of Samples.
             */

             iFirst = iFirst >> 1;

             for (pbScan = pbScanStart + iFirst ; --scScan >= 0 ; pbScanStart +=
                usBytesPerScan, pbScan = pbScanStart + iFirst)
             {
               /*
               ** pbScan++ not to exceed the bounds.
               */
               for (ncBytes = 1 ; ncBytes < nBytes; ncBytes++)
               {
                 *pbScan = (*pbScan << 4) | ((*(pbScan + 1) & 0x00ff) >> 4 );
                 pbScan++;
               }

               /*
               ** The last byte
               */
               *pbScan = *pbScan << 4;

               /*
               ** Output to channel.
               */

               if ( !fIsColorDevice )
               {
                 for (i=0; i<nBytes; i++)  /* unpack nibbles D74609 */
                 {
                    *(Compressed + 2*i) = (char) PalGray(&prgb[ *(pbScanStart+iFirst+i) >> 4], pbGammaTable );
                    *(Compressed + 2*i + 1) = (char) PalGray(&prgb[ *(pbScanStart+iFirst+i) & 0x0f], pbGammaTable );
                 }

                 if ( usLanguageLevel >= 2)   /* D74609 */
                 {
                    nOut = CompressMode2Out( Compressed, Compressed2, 2*nBytes);
                    WriteChannel( pddc, Compressed2, nOut );
                  }
                 else
                    HexToChannel( pddc, Compressed, 2*nBytes);
               }
               else
               {
               if ( usLanguageLevel >= 2)  /* D74609 */
                 {
                   nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
                   WriteChannel( pddc, Compressed, nOut );
                 }
               else
                   HexToChannel( pddc, pbScanStart+iFirst, nBytes);
               }
             }
           }
           else
           {
             /*
             ** Even number of Samples.
             */
             iFirst = iFirst >> 1;

             for( pbScan = pbScanStart + iFirst ; --scScan >= 0; pbScanStart +=
                usBytesPerScan, pbScan = pbScanStart + iFirst )
             {
               for (ncBytes = 1 ; ncBytes <= nBytes ; ncBytes++)
               {
                 *pbScan = (*pbScan << 4) | ((*(pbScan + 1) & 0x00ff) >> 4 );
                 pbScan++;
               }

               /*
               ** Output to channel.
               */

               if ( !fIsColorDevice )
               {
                 for (i=0; i<nBytes; i++)  /* unpack nibbles D74609 */
                 {
                    *(Compressed + 2*i) = (char) PalGray(&prgb[ *(pbScanStart+iFirst+i) >> 4], pbGammaTable );
                    *(Compressed + 2*i + 1) = (char) PalGray(&prgb[ *(pbScanStart+iFirst+i) & 0x0f], pbGammaTable );
                 }

                 if ( usLanguageLevel >= 2)   /* D74609 */
                 {
                    nOut = CompressMode2Out( Compressed, Compressed2, 2*nBytes);
                    WriteChannel( pddc, Compressed2, nOut );
                 }
                 else
                    HexToChannel( pddc, Compressed, 2*nBytes);
               }
               else
               {
                 if ( usLanguageLevel >= 2)  /* D74609 */
                 {
                   nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
                   WriteChannel( pddc, Compressed, nOut );
                 }
                 else
                   HexToChannel( pddc, pbScanStart+iFirst, nBytes);
               }
             }
           }                              /* Odd/Even samples */
         }
         else
         {

           /*
           ** Source left is divisible by 2.
           ** Hence no bit shifting is required.
           */

            iFirst = iFirst >> 1;

            for ( ; --scScan >= 0 ; pbScanStart += usBytesPerScan)
            {
               /*
               ** Output to channel.
               */
              if ( !fIsColorDevice )
              {
                for (i=0; i<nBytes; i++)  /* unpack nibbles D74609 */
                {
                  *(Compressed + 2*i) = (char) PalGray(&prgb[ *(pbScanStart+iFirst+i) >> 4], pbGammaTable );
                  *(Compressed + 2*i + 1) = (char) PalGray(&prgb[ *(pbScanStart+iFirst+i) & 0x0f], pbGammaTable );
                }

                if ( usLanguageLevel >= 2)  /* D74609 */
                {
                  if ((nSamples & 1) != 0)
                    nOut = CompressMode2Out( Compressed, Compressed2, 2*nBytes-1);
                  else
                    nOut = CompressMode2Out( Compressed, Compressed2, 2*nBytes);

                  WriteChannel( pddc, Compressed2, nOut );
                }
                else
                {

                  if ((nSamples & 1) != 0)
                    HexToChannel( pddc, Compressed, 2*nBytes - 1);
                  else
                    HexToChannel( pddc, Compressed, 2*nBytes );
                }

              }
              else
              {
                if ( usLanguageLevel >= 2)  /* D74609 */
                {
                  nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
                  WriteChannel( pddc, Compressed, nOut );
                }
                else
                  HexToChannel( pddc, pbScanStart+iFirst, nBytes);
              }
            }
         }
         break;

    case 8:

         nBytes = nSamples;


         if ( usLanguageLevel >= 2 )  /* For CompressMode2Out */
         {
           if ( !(Compressed =  GplMemoryAlloc( pdv->pDCHeap, 2*nBytes )))  /* D74609 */
           {
               RIP( "ScanImage:GplMemoryAlloc for Compressed failed." );
               return;
           }
         }
         else
           if (fIsColorDevice)    /* To inserted rgb byte values */
           {
             if ( !(Compressed =  GplMemoryAlloc( pdv->pDCHeap, 3*nBytes )))  /* D74609 */
             {
               RIP( "ScanImage:GplMemoryAlloc for Compressed failed." );
               return;
             }
           }


         if (!fIsColorDevice)
         {
           int ncBytes;


           for (pbScan = pbScanStart + iFirst ; --scScan >= 0 ; pbScanStart +=
                usBytesPerScan, pbScan = pbScanStart + iFirst)
           {
             for (ncBytes = 1 ; ncBytes <= nBytes ; ncBytes++)
             {
               *pbScan = (char) (PalGray( &prgb[*pbScan & 0x0ff], pbGammaTable ));
               pbScan++;
             }

             /*
             ** Output to channel.
             */

             if ( usLanguageLevel >= 2 )  /* D74609 */
             {
               nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
               WriteChannel( pddc, Compressed, nOut );
             }
             else
               HexToChannel( pddc, pbScanStart+iFirst, nBytes );

           }

         }
         else
         {

           for ( ; --scScan >= 0 ; pbScanStart += usBytesPerScan)
           {
             /*
             ** Output to channel.
             */
             if ( usLanguageLevel >= 2 ) /* D74609 */
             {
                nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
                WriteChannel( pddc, Compressed, nOut );
             }
             else
             {

                for (i = 0 ; i <  nBytes ; i++)  /* for level 1 place in color info D74609 */
                {
                *(Compressed + 3*i)     = (char) ( prgb[*(pbScanStart+iFirst+i)].bRed);
                *(Compressed + 3*i + 1) = (char) ( prgb[*(pbScanStart+iFirst+i)].bGreen);
                *(Compressed + 3*i + 2) = (char) ( prgb[*(pbScanStart+iFirst+i)].bBlue);
                }

                HexToChannel( pddc, Compressed, 3*nBytes );
             }
           }

         }
         break;

    case 24:

         if ( usLanguageLevel >= 2 )     /* Level 2 pscript */
           if ( !(Compressed =  GplMemoryAlloc( pdv->pDCHeap,
                                   fIsColorDevice ? 6*nSamples : 2*nSamples )))
           {
             RIP( "ScanImage:GplMemoryAlloc for Compressed failed." );
             return;
           }

         if (!fIsColorDevice)
         {

           iFirst = iFirst * 3;
           nBytes = nSamples;


           for (pbScanOut = pbScan = pbScanStart + iFirst ; --scScan >= 0 ;
                pbScanStart += usBytesPerScan, pbScanOut = pbScan = pbScanStart +
                iFirst)
           {
             for (ncBytes = 1 ; ncBytes <= nBytes ; ncBytes++)
             { /*
               ** The colors are actually stored as Blue - Green - Red in the
               ** rgb structure
               */
               rgb.bBlue    = *pbScan++;
               rgb.bGreen   = *pbScan++;
               rgb.bRed     = *pbScan++;
               *pbScanOut++ = (char) (PalGray( &rgb, pbGammaTable ));
             }

             /*
             ** Output to channel.
             */
             if ( usLanguageLevel >= 2 )  /* D74609 */
             {
                nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
                WriteChannel( pddc, Compressed, nOut );
             }
             else
                HexToChannel( pddc, pbScanStart+iFirst, nBytes );
           }

         }
         else
         {
           iFirst = iFirst * 3;
           nBytes = nSamples * 3;

           for ( ; --scScan >= 0 ; pbScanStart += usBytesPerScan)
           {

             pbScanOut = pbScanStart + iFirst;
             for ( i = 0; i < nSamples; i++, pbScanOut += 3 )
             {
               bTempColor = *pbScanOut;     /* Save the blue */
               *pbScanOut = *(pbScanOut+2); /* Put the red in the blue spot */
               *(pbScanOut+2) = bTempColor; /* Put the blue in the red spot */

               /* @V3.0GAMMA1
               ** Apply gamma correction
               */
               if ( pbGammaTable )
               {
                 *pbScanOut     = pbGammaTable[ *pbScanOut     + RED_OFFSET ];
                 *(pbScanOut+1) = pbGammaTable[ *(pbScanOut+1) + GREEN_OFFSET ];
                 *(pbScanOut+2) = pbGammaTable[ *(pbScanOut+2) + BLUE_OFFSET ];
               }
             }

             /*
             ** Output to channel.
             */

             if ( usLanguageLevel >= 2 )
             {
               nOut = CompressMode2Out( pbScanStart+iFirst, Compressed, nBytes);
               WriteChannel( pddc, Compressed, nOut );
             }
             else
               HexToChannel( pddc, pbScanStart + iFirst, nBytes );

           }
         }
         break;
  }                                    /* end switch */

  if ( Compressed )
    GplMemoryFree( Compressed );
  if ( Compressed2 )
    GplMemoryFree( Compressed2 );

}

/***************************************************************************
 *
 * FUNCTION NAME = ImageProcToChannel
 *
 * DESCRIPTION   = This routine outputs the scanline data for the
 *                 appropriate bitmap format.
 *
 * INPUT         = (pddc, fIsColorDevice, nBitsPerSample,
 *                  nSamplesPerScan)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
*/
BOOL ImageProcToChannel( PDDC pddc, BOOL fIsColorDevice, int nBitsPerSample, int nSamplesPerScan )
{
  SHORT        usModHandle;
  PVOID        pResource;
  IMAGE_PROCS *phdr;
  long         ofs;
  long         cb;
  SHORT        nSamp;
  char         bufout[256];   /* For output from the resource with format parameters - D74609 */

#if 0
//usModHandle = pscript_module;**/
//if (DosGetResource( usModHandle, IMAGEGRP, IMAGE_CODE, &pResource) )
//{
//  GplErrSetError( PMERR_BASE_ERROR );
//  return( FALSE );
//}
//phdr = (IMAGE_PROCS *) pResource;
#endif
  static CHAR achStringDef[] = "/scandata %d string def\n";
  static CHAR achReadScanLine[] = "/read_scan_line {currentfile scandata "
                                  "readhexstring pop} def\n";

  /*
  ** If the PostScript header wasn't sent then send it now.  The
  ** reason that it was delayed till now is to handle the case
  ** where the spooler is just copying raw data to the driver.
  ** If this is the case, then the driver must be careful not
  ** to send multiple headers.
  */
  if (!pddc->fHeaderSent)
  {
    ps_startdoc( pddc );
  }

  switch (nBitsPerSample)
  {
  case 1:
#if 0
//     if (fIsColorDevice)
//     {
//       ofs = phdr->lcColor1.ofs;
//       cb = phdr->lcColor1.cb;
//     }
//     else
//     {
//       ofs = phdr->lcMono1.ofs;
//       cb = phdr->lcMono1.cb;
//       strncpy( (PB) bufout, ((PB)phdr)+ofs, (int)cb );
//       bufout[cb] = 0;
//       PrintChannel( pddc, (PB)bufout, nSamplesPerScan );
//       return(TRUE);
//     }
#endif
         if ( ( pddc->pdv->usLanguageLevel == 1 ) ||  //          
              ( pddc->pdv->usPSLevel1 == 1 )       )
         {
           nSamplesPerScan = 1;
         }
         PrintChannel( pddc, (PB)achStringDef, nSamplesPerScan );
         PrintChannel( pddc, achReadScanLine );
         return TRUE;


       break;

  case 4:

       if (fIsColorDevice)
       {
         /*
         **  ofs = phdr->lcColor4.ofs;
         **  cb = phdr->lcColor4.cb;
         */
         /*
         ** CR!!! move this to the images.ps file later!!!
         */
         nSamp = (nSamplesPerScan + 1) >> 1;
         PrintChannel( pddc,
            (PSZ) "/scandata %d string def /scanout %d string def\n", nSamp,
            nSamplesPerScan *3 );

         /*
         ** handle the case where we have an uneven number of samples
         ** per scanline.
         */
         if ((nSamplesPerScan % 2) != 0)
         {
           nSamp -= 1;
         }

         /*
         ** read_4_color reads a scanline at a time of the image data.
         ** it reads the data into scandata, then parses each 4 bit
         ** section of it.  it then uses this value to look up the
         ** color table value, and puts that 8-bit value into
         ** scanout.  when this routine is done, scanout is put on the
         ** stack, containing the 8-bit version of the scanline.
         */
         /* Here we improve the performance by looking up two 4 bit values
         ** at once.  We increase the size of the lookup array to include
         ** the possible 8 bit lookup values.  D74609
         */
         PrintChannel( pddc,
            (PSZ) "/read_scan_line { currentfile scandata readhexstring pop pop\n" );
         PrintChannel( pddc, (PSZ)"0 1 %d { /ns exch def\n", (nSamp-1) );
/* begin D74609- changed the following */
         PrintChannel( pddc, (PSZ)
            "scanout ns 6 mul pal scandata ns get\n" );
         PrintChannel( pddc, (PSZ)"6 mul 6 getinterval putinterval\n" );
         PrintChannel( pddc, (PSZ)"} for\n" );
/* end D74609 - changed the following */
         /*
         ** this is done to handle the individual sample at the end of the
         ** scanline, when the number of samples per scanline is odd.
         */
         if ((nSamplesPerScan % 2) != 0)
         {
           PrintChannel( pddc, (PSZ)"/ns ns 1 add def\n" );
           PrintChannel( pddc, (PSZ)
              "scanout ns 6 mul pal scandata ns get -4 bitshift 15 and\n" );
           PrintChannel( pddc, (PSZ)"3 mul 3 getinterval putinterval\n" );
         }
         PrintChannel( pddc, (PSZ)"scanout }B\n" );
         return( TRUE );
       }
       else
       {
#if 0
//       ofs = phdr->lcMono4.ofs;
//       cb = phdr->lcMono4.cb;
//       strncpy( (PB) bufout, ((PB)phdr)+ofs, (int)cb );
//       bufout[cb] = 0;
//       PrintChannel( pddc, (PB)bufout, nSamplesPerScan );
#endif
         PrintChannel( pddc, (PB)achStringDef, nSamplesPerScan );
         PrintChannel( pddc, achReadScanLine );
         return( TRUE );
       }
       break;

  case 8:
#if 0
//     if (fIsColorDevice)
//     {
//       ofs = phdr->lcColor8.ofs;
//       cb = phdr->lcColor8.cb;
//       strncpy( (PB) bufout, ((PB)phdr)+ofs, (int)cb );
//       bufout[cb] = 0;
//       PrintChannel( pddc, (PB)bufout, nSamplesPerScan );
//     }
//     else
//     {
//       ofs = phdr->lcMono8.ofs;
//       cb = phdr->lcMono8.cb;
//       strncpy( (PB) bufout, ((PB)phdr)+ofs, (int)cb );
//       bufout[cb] = 0;
//       PrintChannel( pddc, (PB)bufout, nSamplesPerScan );
//     }
#endif
       PrintChannel( pddc, (PB)achStringDef, nSamplesPerScan );
       PrintChannel( pddc, achReadScanLine );
       return(TRUE);

  case 24:
#if 0
//     ofs = phdr->lcColor24.ofs;
//     cb = phdr->lcColor24.cb;
//     strncpy( (PB) bufout, ((PB)phdr)+ofs, (int)cb );
//     bufout[cb] = 0;
//     PrintChannel( pddc, (PB)bufout, nSamplesPerScan );
#endif
       PrintChannel( pddc, (PB)achStringDef, nSamplesPerScan );
       PrintChannel( pddc, achReadScanLine );
       return(TRUE);
  }

#if 0
//if (cb > 0)
//{
//  WriteChannel( pddc, ((PB)phdr)+ofs, (int)cb );
//}
#endif
  return( TRUE );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdb_ImageData
 *
 * DESCRIPTION   = Output a scanline from a bitmap.
 *
 * INPUT         = hdc, pbBits, nBits, iRow, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE.
 *
 **************************************************************************/

ULONG prdb_ImageData( HDC hdc, PB pbBits, LONG nBits, LONG iRow,
                      PDDC pddc, ULONG FunN)
  /* HDC hdc ;                             The display context handle        */
  /* PB pbBits;                            Ptr to the bits to download       */
  /* LONG nBits;                           The number of bits                */
  /* LONG iRow;                            The row number of this scanline   */
  /* PDDC pddc;                            Ptr to the dc instance data       */
  /* ULONG FunN;                           The engine function number        */
{
  BOOL fInvert;
  ULONG ulRet ;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdb_ImageData(%08lx, %lp, %ld, %ld, %lp, %08lx)", ((PB)&hdc)+
       sizeof( hdc) );
  #endif                                 /* DEBUG  */

  if (pddc->iType == OD_MEMORY)
  {
     ulRet = InnerGreImageData (pddc->hdcMemory , pbBits, nBits, iRow, FunN );
     ExitDriver(pddc );
     return(ulRet );
  }

  /*
  ** Select a color to draw the forground bits in
  */
  ps_setrgbcolor( pddc, prdc_GetColor( pddc, pddc->pddcb->image.ulFgColor) );

  /*
  ** PostScript can only paint the foreground color on the background
  ** so the only mix mode that we support is where the source bits are
  ** inverted before the painting is done.
  */
  fInvert = pddc->pddcb->image.usFgMix == FM_MERGENOTSRC;

  /*
  ** Call the PostScript interface to output the image.
  */
  ps_imagedata( pddc, fInvert, pddc->pddcb->pen.ptlCur.x,
                pddc->pddcb->pen.ptlCur.y-iRow, nBits, pbBits );
  ulRet = ps_status( pddc );
  ExitDriver( pddc );
  return( ulRet );
}

/**************************************************************************
 *                                                                        *
 * Function:       CompressMode2Out                                       *
 *                                                                        *
 * Description:    Graphics compression                                   *
 *                                                                        *
 * Concept:        This function compresses a scanline.  The compression  *
 *                 is done according to LJ IIP Mode 2 compression: mixed  *
 *                 binary and run-length encoding.                        *
 *                                                                        *
 *                 Once in a repeated run, we stop and emit a block if a  *
 *                 different byte is encountered.                         *
 *                                                                        *
 *                 Once in a literal run, we stop and emit a block if at  *
 *                 least 4 repeated bytes follow: the next block will be  *
 *                 a repeat block.                                        *
 *                                                                        *
 *                 Repeats of 2 or more instances of a byte b are         *
 *                 represented by -(n-1)  b                               *
 *                                                                        *
 *                 Literal runs of different bytes b1 b2 b3 .. bn are     *
 *                 represented by (n-1) b1 b2 b3 .. bn                    *
 *                                                                        *
 * Input(s):       char * pSource                                         *
 *                 char * usSourceLen                                     *
 *                                                                        *
 * Output(s):      char * pDest                                           *
 *                                                                        *
 * Return:         int Length in bytes of pDest                           *
 *                                                                        *
 *                                                                        *
 **************************************************************************/


USHORT  CompressMode2Out  ( PBYTE pSource,      /* source string           */
                            PBYTE pDest,        /* destination string      */
                            USHORT usSourceLen) /* length of source string */
{
  PBYTE pBeginDest=pDest;            /* save original address of destination */
  PBYTE pBeginSource=pSource;        /* save original address of source      */
  PBYTE p;                           /* working pointer                      */
  PBYTE temp1, temp2;                /* temp pointers                        */
  BOOL  fDone = FALSE;               /* flag                                 */

  p = pBeginSource;
  while ( !fDone )
  {
    /*
    ** 4 bytes left?
    ** if not, exit out of the loop.
    **/
    if ((p+4-pBeginSource) > usSourceLen)
    {
      break;
    }

    /*
    ** 2 bytes equal?
    ** ... and 4 bytes equal?
    **/
    if ((*p == *(p+1)) && ( *( BIT16OBJ *)(p) == *(BIT16OBJ *)(p+2) ))
    {
      /*
      ** YES, run of repeating byte.
      ** At this point, the first 4 bytes are equal.
      **/
      p = p + 3;
      while ((p-pSource) < MAXBLOCKSIZE)  /* 127 bytes?           */
      {
        p++;                              /* advance to next byte */

        if ((p-pBeginSource) >= usSourceLen)    /* end of input buffer? */
        {
          fDone = TRUE;
          break;
        }
        if (*p != *pSource)               /* still equal? */
        {
          break;
        }
      }
      *pDest++ = -(p-pSource-1);          /* -(n-1)                */
      *pDest++ = *pSource;                /* copy repeating byte   */
      pSource = p;                        /* advance to next block */
    }
    else
    {
      /*
      ** NO, literal run of differring bytes
      */
      temp1 = pSource;                    /* save start location */
      temp2 = pDest++;
      while ((p-temp1) < MAXBLOCKSIZE)    /* 127 bytes?          */
      {
        p++;                              /* advance to next byte*/

        if ((p-pBeginSource) >= usSourceLen)  /* end of input buffer?*/
        {
          fDone = TRUE;
          *pDest++ = *pSource;                /* copy the last byte */
          break;                              /*   before exit out  */
        }
        /*
        ** Remain in literal block (differing bytes) unless there are
        ** at least 4 bytes left and 4 consecutive bytes are equal.
        */
        if (*p == *pSource)                   /* 2 bytes equal? */
        {
          if ((pSource+4-pBeginSource) <= usSourceLen)  /* 4 bytes left? */
          {
            if (*(BIT16OBJ *)(pSource) == *(BIT16OBJ *)(pSource+2))
            {
              p--;                            /* 4 bytes are equal */
              break;
            }
          }
        }
        *pDest++ = *pSource++;                /* copy byte */
      }
      *temp2 = p-temp1-1;                     /* (n-1) */
    }

  } /* end of while(!fDone) */

  /*
  ** If not done yet, the block is less than 4 bytes:  literal run
  */
  if (!fDone)
  {
    temp2 = pDest++;
    while ((p-pBeginSource) < usSourceLen)
    {
      *pDest++ = *p++;
    }
    *temp2 = p-pSource-1;
  }

  return((int)(pDest-pBeginDest));
}
/**************************************************************************
*                                                                         *
* CompressAscii85 ( PBYTE pSource, PBYTE pDest, USHORT usSourceLen, PDDC )  *
*                                                                         *
*   Description:  Encodes 4 bytes in to 5 bytes out converting from a base*
*                 256 representation on input to a base 85 representation *
*                 on output.  The output is then output in ascii with     *
*                 base 85 ( zero digit = !) and base 85 ( 84 digit = u )  *
*                                                                         *
*   Input(s):       char * pSource                                        *
*                   char * usSourceLen                                    *
*                                                                         *
*   Output(s):      char * pDest                                          *
*                                                                         *
*   Return:         int Length in bytes of pDest                          *
*                                                                         *
***************************************************************************/

USHORT CompressAscii85 ( PBYTE  pSource,      /* Source input        */
                         PBYTE  pDest,        /* Destination output  */
                         USHORT usSourceLen,  /* Source input length */
                         PDDC   pddc )
{
  ULONG i, j = 0, m = 0;
  ULONG val, val2;

  register ULONG q;
  register ULONG r;
  register ULONG t;
  PLONG plFlag = (PLONG)pDest;
  ldiv_t ans;

  plFlag--;   /* The buffer has a flag long at the very begining */
  *plFlag = 0;

  ans = ldiv( usSourceLen, 4 );

  for( i=0; i < ans.quot; i++)
  {
    m = 4 * i;

    val = (((ULONG) ((((ULONG) *(pSource+ m)) << 8) |
          *(pSource + m + 1))) << 16) | ((((ULONG) *(pSource + m + 2)) << 8) |
          *(pSource + m + 3));

    if (val == 0 )
    {
      *(pDest + j++) = 'z';
    }
    else
    {
      q = val/7225;
      r = (ULONG)val - 7225*(ULONG)q;

      *(pDest + j + 3) = (t = r/85) + '!';
      *(pDest + j + 4) = r - 85*t + '!';
      *(pDest + j ) = (t = q/7225) + '!';
      r = (ULONG)q - 7225*t;
      *(pDest + j + 1 ) = (t = r/85) + '!';
      *(pDest + j + 2 ) = r - 85*t + '!';

      j += 5;
    }
  }

  if( ans.rem > 0 )        /* Get the remainder of bytes */
  {
    val = 0;

    *(pSource + usSourceLen) = (UCHAR)128;   /* Used by CompressMode2Out; */
    ans.rem += 1;

    for ( i=0; i < 4; i++)
    {
      val = val << 8 | ((i < ans.rem) ? *(pSource + 4*ans.quot + i ) : 0);
    }

    q = val/7225;
    r = (ULONG)val - 7225*(ULONG)q;

    *(pDest + j + 3) = (t = r/85) + '!';
    *(pDest + j + 4) = r - 85*t + '!';
    *(pDest + j ) = (t = q/7225) + '!';
    r = (ULONG)q - 7225*t;
    *(pDest + j + 1 ) = (t = r/85) + '!';
    *(pDest + j + 2 ) = r - 85*t + '!';

    *plFlag = 1;

    j += (ans.rem + 1);

  }

  return j;
}
