/****************************************************************************/
/*                                                                          */
/* SAMPMAIN.C: Sample Codec .DLL main file.                                 */
/*                                                                          */
/* Copyright (c) IBM Corporation 1991,1992,1993        All Rights Reserved  */
/*                                                                          */
/****************************************************************************/

/* OS/2 and Multimedia Includes */
#define INCL_WIN
#define INCL_GPI
#define INCL_DOSSEMAPHORES
#define INCL_DOSEXCEPTIONS
#define INCL_DOSERRORS
#define INCL_DOSPROCESS
#define INCL_OS2MM
#define INCL_MMIO_CODEC
#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <os2me.h>
#include <video.h>
#include "svsh.h"
#include "hhpheap.h"
#include "codecsrv.h"
#include "samplutg.h"
#include "sampinst.h"
#include "sampdeco.h"
#include "sampenco.h"


/* Define local prototypes to functions called prior to their definitions.  */
ULONG DeallocInstData     ( PCODEC_INST                                );

/* Define local clipping prototypes.                                        */
VOID  CalcClipMask        ( ULONG, PRECTL, RECTL1, PDECODE_INST, ULONG );
VOID  ConstrainRectToDest ( PRECTL, PRECTL );
VOID  RepairHolesInMask   ( ULONG, PRECTL, PRECTL, PDECODE_INST );




/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: QryNameLength                                           */
/*     This procedure is a standard interface to the system to which        */
/*     the system calls to find out the length of your CODEC name.          */
/*                                                                          */
/****************************************************************************/

RC QryNameLength ( PVOID ppcodec_inst, WORD wMessage,
                   PULONG pulLength, LONG lParam2 )
   {
   /* Put your codec name length here, plus two for the terminating chars.  */
   /* Example: "Sample Codec" is 6+1+5+2= 14;                               */
   *pulLength= 14L;

   /* This always succeeds.                                                 */
   return MMIO_SUCCESS;
   }




/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: QryNameString                                           */
/*     This procedure is a standard interface to the system to which        */
/*     the system calls to find out the your CODEC name.                    */
/*                                                                          */
/****************************************************************************/

RC QryNameString ( PVOID ppcodec_inst, WORD wMessage,
                   PSZ pszCODECNameNew, PULONG pulLength )
   {
   /* Put your codec name length here, plus two for the terminating chars.  */
   /* Example: "Sample Codec" is 6+1+5+2= 14;                               */
   *pulLength= 14L;

   /* Use standard string function "strcpy" to copy over your codec name.   */
   strcpy(pszCODECNameNew,"Sample Codec");

   /* This always succeeds.                                                 */
   return MMIO_SUCCESS;
   }




/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: AllocInstData                                           */
/*     This routine is called once per instance allocation.  Here is where  */
/*     you initialize either your compression parameters, your              */
/*     decompression parameters, or both (one instance can do both at       */
/*     once) based on the passed open flags.                                */
/*                                                                          */
/****************************************************************************/

ULONG AllocInstData ( PCODEC_INST pCodecInst )
   {
   PMAIN_INST        pMainInst;
   PENCODE_INST      pEncodeInst;
   PDECODE_INST      pDecodeInst;
   PCODECVIDEOHEADER pcodecvideoheaderDst;
   PCODECVIDEOHEADER pcodecvideoheaderSrc;

   /* Allocate a main instance block.                                       */
   if ( !(pMainInst= (PMAIN_INST) HhpAllocMem ( hHeap, sizeof(MAIN_INST) )) )
      {
      DeallocInstData ( pCodecInst );
      return ERROR_ALLOC_RESOURCES;
      }

   /* Save a copy of the main instance address in the stream inst struct.   */
   pCodecInst->pCodecPlugInstData= pMainInst;

   /* Grab a local copy of the codec open structure pointer.                */
   pcodecvideoheaderSrc = (PCODECVIDEOHEADER) pCodecInst->
                                              pe_movie_inst->
                                              codecopen.pSrcHdr;
   pcodecvideoheaderDst = (PCODECVIDEOHEADER) pCodecInst->
                                              pe_movie_inst->
                                              codecopen.pDstHdr;


   /* Check to see if we need to initialize any compression storage.        */
   if ( pCodecInst->pe_movie_inst->codecopen.ulFlags & CODEC_COMPRESS )
      {
      /* Allocate the compression parameters structure and link it to the   */
      /* instance structure.  Set up the parameters based on the open.      */
      if ( !( pEncodeInst= (PENCODE_INST) HhpAllocMem ( hHeap,
                                                  sizeof(ENCODE_INST) ) ) )
         return MMIOERR_OUTOFMEMORY;
      pMainInst->pEncodeInst= pEncodeInst;

      /* Make sure that the source and dest buffer sizes are the same       */
      /* since this compression does not support any pre-scaling.           */
      if (((pcodecvideoheaderSrc->cx != pcodecvideoheaderDst->cx) ||
           (pcodecvideoheaderSrc->cy != pcodecvideoheaderDst->cy)))
         return MMIO_ERROR;

      /* This is where to fill in the fields.                               */
      pEncodeInst->ulWidth= pcodecvideoheaderSrc->cx;
      pEncodeInst->ulHeight= pcodecvideoheaderSrc->cy;
      pEncodeInst->ulColorEncoding= pcodecvideoheaderSrc->ulColorEncoding;
      pEncodeInst->pbLastFrame= NULL;
      pEncodeInst->ulKeyFrameInterval= ((PMMVIDEOOPEN)(pCodecInst->
                     pe_movie_inst->codecopen.pOtherInfo))->ulKeyFrameRate;
      pEncodeInst->ulKeyFrameCounter= 0L;

      /* Call to initialize the compression routine.                        */
      if ( Compressor_Init ( pEncodeInst ) )
         return MMIO_ERROR;
      }
   else
      pMainInst->pEncodeInst= NULL;



   /* Check to see if we need to initialize any decompression storage.      */
   if ( pCodecInst->pe_movie_inst->codecopen.ulFlags & CODEC_DECOMPRESS )
      {
      /* Allocate the decompression parameters structure and link it to the */
      /* instance structure.  Set up the parameters based on the open.      */
      if ( !( pDecodeInst= (PDECODE_INST) HhpAllocMem ( hHeap,
                                                  sizeof(DECODE_INST) ) ) )
         return MMIOERR_OUTOFMEMORY;
      pMainInst->pDecodeInst= pDecodeInst;

      /* Grab a global copy of the decode bit depth.                        */
      pDecodeInst->ulNumColors= pcodecvideoheaderDst->genpal.ulNumColors;

      /* Build the color lookup/conversion table.                           */
      codecfns.pfnPaletteHandler ( &pcodecvideoheaderDst->genpal );

      /* SuperVGA support requires a flag to determine whether it is being  */
      /* called for the first time to allow initialization or stripping of  */
      /* possible frame headers.                                            */
      pDecodeInst->fFirstTime = TRUE;

      /* Allocate a clipping mask, and note that it is stored in inst data. */
      /* See the clipping mask generator for details about this size, which */
      /* is effectively ( height / 8 + 1 ) * 256.                           */
      if ( !(pDecodeInst->pbClipMask = (PBYTE)HhpAllocMem( hHeap,
                           (pcodecvideoheaderSrc->cy << 5) + 256 )) )
         {
         DeallocInstData ( pCodecInst );
         return ERROR_ALLOC_RESOURCES;
         }

      /* Allocate space for the clipping mask.  In this case, the mask is a */
      /* byte mask of 8x8 blocks.  That is, each 8x8 in the image will map  */
      /* to a byte in this mask.  0= 8x8 is showing !0= 8x8 is covered.     */
      /* In this scenereo, the decoder will not clip to pixel boundries.    */
      /* For addressing speed in the decompressor, the mask is always 256   */
      /* 8x8's wide.  Note the the default output size is the image size,   */
      /* and that it will by default be decoding upside-down.               */
      pDecodeInst->ulMovieWidth = pcodecvideoheaderSrc->cx;
      pDecodeInst->ulMovieHeight= pcodecvideoheaderSrc->cy;
      }
   else
      pMainInst->pDecodeInst= NULL;

   /* If this routine fails, it will return from the failing point.         */
   return MMIO_SUCCESS;
   }






/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: DeallocInstData                                         */
/*     This routine will be called once per instance at deallocation time.  */
/*     Be sure to deallocate any private RAM allocated from the heap or     */
/*     through DosAllocMem in either a compression instance, decompression  */
/*     instance, or both.                                                   */
/*                                                                          */
/****************************************************************************/

ULONG DeallocInstData ( PCODEC_INST pcodec_inst )
   {
   PMAIN_INST pMainInst= pcodec_inst->pCodecPlugInstData;

   /* Check to make sure that the instance is valid.                        */
   if ( pMainInst )
      {
      /* Check to see if there is an encoding structure to deallocate.      */
      if ( pMainInst->pEncodeInst )
         {
         /* Let the compressor do any clean-up.                             */
         Compressor_End ( pMainInst->pEncodeInst );
         HhpFreeMem ( hHeap, pMainInst->pEncodeInst );
         }

      /* Check to see if there is a decoding structure to deallocate.       */
      if ( pMainInst->pDecodeInst )
         {
         /* Check to see if there is a clipping mask to deallocate.         */
         if ( pMainInst->pDecodeInst->pbClipMask )
            HhpFreeMem ( hHeap, pMainInst->pDecodeInst->pbClipMask );
         HhpFreeMem ( hHeap, pMainInst->pDecodeInst );
         }

      /* Deallocate the main instance structure.                            */
      HhpFreeMem ( hHeap, pMainInst );

      /* Null the instance data pointer.                                    */
      pcodec_inst->pCodecPlugInstData = NULL;
      }

   /* No errors are checked for in this routine.                            */
   return NO_ERROR;
   }




/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: CompressBuffer                                          */
/*     This routine will be called once per frame to compress.  This may    */
/*     be a symmetric call or an asymmetric call (or even an asymmetric     */
/*     call to a symmetric algorithm) if your compression has set the       */
/*     symmetric flag.  Otherwise the asymmetric flag must be set and       */
/*     the call to the compression can take as long as desired.             */
/*                                                                          */
/****************************************************************************/

ULONG CompressBuffer ( PMMCOMPRESS pmmCompress, PCODEC_INST pCodecInst )
   {
   PENCODE_INST pEncodeInst;

   /* Check for NULL input parameters before trying to do anything.         */
   if ( !pmmCompress || !pCodecInst )
      return MMIO_ERROR;

   /* Check for an associated compression instance.                         */
   if ( ! ( pEncodeInst= ( (PMAIN_INST)pCodecInst->pCodecPlugInstData )
                                                            ->pEncodeInst ) )
      return MMIO_ERROR;

   /* Call to the compressor.                                               */
   pmmCompress->ulDstBufLen= Compressor_CompressFrame ( pEncodeInst,
                        pmmCompress->pSrcBuf, pmmCompress->pDstBuf );

   /* A zero-length frame is an indication of a compression error.          */
   if ( pmmCompress->ulDstBufLen )
      return MMIO_SUCCESS;
   else
      return MMIO_ERROR;
   }




/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: DecompressBuffer                                        */
/*     This routine will be called **at least once per frame**.  On SVGA    */
/*     systems with small aperture sizes (like 64KBytes), the stream        */
/*     handler must call the decompressor to decode only what fits in       */
/*     each bank (aperture).  For a 240 line high movie, this routine may   */
/*     be called four or five times *per frame*.  Be careful here, because  */
/*     your decompression code must update decompress buffer structures     */
/*     giving progress to the stream handler.  Specifically, the stream     */
/*     handler will stop calling the decompress code when it finds          */
/*     pmmDecompress->pSrcBuf zero.  If you don't update this variable,     */
/*     the stream handler will continue to call decompress buffer at        */
/*     ring zero in an infinite loop (big red switch time).  Also, the      */
/*     avio (.AVI Input/Outout) procedure is expecting to see               */
/*     pmmDecompress->ulSrcBufLen as zero to know when it can free the      */
/*     spot for the next frame.  On system with large apertures (like       */
/*     one or more MByte) like the XGA or XGA-2 cards, this procedure       */
/*     will be called one time only.                                        */
/*                                                                          */
/****************************************************************************/

ULONG DecompressBuffer ( PMMDECOMPRESS pmmDecompress, PCODEC_INST pCodecInst )
   {
   ULONG                rc= MMIO_SUCCESS;
   PMMVIDEODECOMPRESS   pmmvideodecompress;
   PDECODE_INST         pDecodeInst;
   PCODECOPEN           pcodecopen;
   ULONG                ulRowsToDecode;
   ULONG                ulLocalSkipLength;

   /* Check for NULL input parameters before trying to do anything.         */
   if ( !pmmDecompress || !pCodecInst )
      return MMIOERR_INVALID_PARAMETER;

   /* Check for an associated decompression instance, and get a local copy. */
   if ( ! ( pDecodeInst= ( (PMAIN_INST)pCodecInst->pCodecPlugInstData )
                                                            ->pDecodeInst ) )
      return MMIOERR_INVALID_PARAMETER;

   /* Check the videodecompress parameter for validity.                     */
   if ( !(pmmvideodecompress=(PMMVIDEODECOMPRESS)pmmDecompress->pRunTimeInfo))
      return MMIOERR_INVALID_PARAMETER;

   /* Grab a local copy pointer to the initial codec open structure.        */
   pcodecopen= &pCodecInst->pe_movie_inst->codecopen;

   /* Skip length is the line width (in bytes) of the screen.               */
   ulLocalSkipLength= pmmvideodecompress->ulSkipLength;

   /* Check for the presence of system palette change.  Note that           */
   /* there is still a video frame to be decodec.                           */
   if ( pmmDecompress->ulFlags & MMIO_PALETTE_CHANGE )
      {
      /* Call the routine to handle palette changes.                        */
      rc= codecfns.pfnPaletteHandler (&pmmvideodecompress->genpalPhysical);

      /* Signify that we have handled the message.                          */
      pmmDecompress->ulFlags &= ~MMIO_PALETTE_CHANGE;
      }

   /* Check to see if our visible region has changed.                       */
   if ( pmmDecompress->ulFlags  & MMIO_RECTL_CHANGE )
      {
      /* Go and calculate the clipping mask for the region changes.         */
      /* Also consider that the scaling flag may have changed.              */
      CalcClipMask ( pmmvideodecompress->ulRectlCount,
                     (PRECTL)pmmvideodecompress->prectl,
                     pmmvideodecompress->rectlDst,
                     pDecodeInst, ulLocalSkipLength );

      /* Signify that we have handled the message.                          */
      pmmDecompress->ulFlags &= ~MMIO_RECTL_CHANGE;
      }

   /* Check the number of lines to be decoded these must be converted       */
   /* to blocks based on the scaling factor, the destination address        */
   /* must then be modified to compensate for the imaginary (CLIPPED)       */
   /* areas above the visable rectangle.                                    */
   if ( pDecodeInst->fFirstTime )
      {
      if ( pmmDecompress->ulFlags & MMIO_ORIGIN_LOWERLEFT )
         {
         /* Remember, rows to decode is actually 8x8's in this example.     */
         /* Don't worry about ulDecodeLines here, the decoder won't be      */
         /* called to decode upside down in blocks.  Only full image        */
         /* upside down decode is required for PM blitting.                 */
         ulRowsToDecode= pDecodeInst->ulMovieHeight >> 3;

         /* If we are decoding upside down, let the decoder know by         */
         /* negating the local copy of the screen width.  Your decoder      */
         /* can get this information via parameters if desired.             */
         ulLocalSkipLength= -ulLocalSkipLength;
         }
      else
         /* Remember, rows to decode is actually 8x8's in this example.     */
         ulRowsToDecode= pmmvideodecompress->ulDecodeLines >> 3;

      /* Set up the pointers for first call of data movement.               */
      pDecodeInst->pbCompressedData = (PBYTE) (pmmDecompress->pSrcBuf);
      pmmDecompress->pSrcBuf= NULL;

      /* Note: you may get called multiple times to *display* in multiple   */
      /* aperture mode cards, but sometimes it's more efficient to do a     */
      /* complete decode and do the display by request.  This is not what   */
      /* was done for this example.  If this were to be done, the entire    */
      /* frame decode would go here, and the DecodeAndDisplayxxx routines   */
      /* below would become simply Dispalyxxx routines.                     */
      }

   else
      /* We are on a succeeding call in a multiple aperture type card.      */
      ulRowsToDecode = pmmvideodecompress->ulDecodeLines >> 3;

   /* Move the frame (or part of the frame) to the video display.  Note     */
   /* that the video stream handler will continue to call the decoder as    */
   /* long as pmmDecompress->pSrcBuf is not zero.  The Handler assumes that */
   /* it's between bank switches and there is more data to be processed.    */
   /* If you do not update this variable, your system will go into an       */
   /* infinite loop and you will not regain control of your system.         */
   if ( pDecodeInst->ulNumColors==65536 )
      DecodeAndDisplayToRGB16 (
            &pDecodeInst->pbCompressedData,              /* ppSource.       */
            pmmDecompress->pDstBuf,                      /* pDestination.   */
            (PULONG)&pmmDecompress->pSrcBuf,             /* pCounters.      */
            ((PCODECVIDEOHEADER)pcodecopen->pSrcHdr)->cx,/* Image width.    */
            ((PCODECVIDEOHEADER)pcodecopen->pSrcHdr)->cy,/* Image height.   */
            ulLocalSkipLength,                  /* Screen width in bytes.   */
            ulRowsToDecode,                     /* 8x8 Blocks to decode.    */
            pDecodeInst->pbClipMask );          /* Pointer to cliping mask. */

   else
      DecodeAndDisplayToPalette (
            &pDecodeInst->pbCompressedData,              /* ppSource.       */
            pmmDecompress->pDstBuf,                      /* pDestination.   */
            (PULONG)&pmmDecompress->pSrcBuf,             /* pCounters.      */
            ((PCODECVIDEOHEADER)pcodecopen->pSrcHdr)->cx,/* Image width.    */
            ((PCODECVIDEOHEADER)pcodecopen->pSrcHdr)->cy,/* Image height.   */
            ulLocalSkipLength,                  /* Screen width in bytes.   */
            ulRowsToDecode,                     /* 8x8 Blocks to decode.    */
            pDecodeInst->pbClipMask );          /* Pointer to cliping mask. */

   /* Check if the entire frame has been completely decoded.                */
   if ( pmmDecompress->pSrcBuf )
      {
      /* There is more to decode, we must be on an SVGA bank switch.        */
      pDecodeInst->fFirstTime= FALSE;
      pmmDecompress->ulSrcBufLen= (ULONG)pmmDecompress->pSrcBuf;
      pmmDecompress->ulFlags&= ~MMIO_IS_KEY_FRAME;
      }
   else
      {
      /* The frame has be completely decoded, reset a few variables.        */
      pDecodeInst->fFirstTime= TRUE;
      pmmDecompress->ulSrcBufLen= 0;
      }

   /* Return any errors found in this routine.  The streams will halt in    */
   /* the middle of playback if an error is returned here.                  */
   return rc;
   }







/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: CalcClippingMask                                        */
/*     This function will calculate the clipping mask specific to the       */
/*     sample decoder, that is, non-sizeable, 8x8 block clipping regions,   */
/*     and 256 block wide lookup.  See the decoder as an example of how     */
/*     it uses this method of clipping mask generation.                     */
/*                                                                          */
/****************************************************************************/

VOID CalcClipMask ( ULONG ulNumValidRects, PRECTL prectlArray,
                RECTL1 rectlDst, PDECODE_INST pDecodeInst, ULONG ulSkipAddr )
   {
   PBYTE  pbMask;
   PBYTE  pbLastMaskStart;
   PRECTL prectl;
   RECTL  rectlVIS;
   LONG   lVisRectWidth;
   LONG   lVisRectHeight;
   ULONG  ulRectIndex;
   ULONG  ulMaskOffset;
   ULONG  ulXindex;
   ULONG  ulYindex;
   ULONG  ulVisRectFromBottom;
   ULONG  ulVisRectFromTop;
   ULONG  ulVisRectFromRHEdge;
   ULONG  ulVisRectFromLHEdge;

   /* Map the rectangle to the origin; all clip rects are relative to this. */
   rectlDst.xRight -= rectlDst.xLeft;
   rectlDst.yTop   -= rectlDst.yBottom;
   rectlDst.xLeft   = 0;
   rectlDst.yBottom = 0;

   /* Start with the mask fully covered, and work towads uncovered with     */
   /* the visible region notification message rectangles.                   */
   memset ( pDecodeInst->pbClipMask, 1, 256 + (pDecodeInst->ulMovieHeight << 5) );

   /* Note that all rectangles are INclusive at the bottom and left, and    */
   /* EXclusive at the top and right.                                       */
   for ( ulRectIndex=0, prectl = prectlArray;
         ulRectIndex < ulNumValidRects;
         ulRectIndex++, prectl++ )
      {
      /* You will, on occasion, get valid regions which may not necessarily */
      /* be in the region you would normally blit to, so clamp to that area.*/
      rectlVIS= *prectl;
      ConstrainRectToDest ( (PRECTL)&rectlDst, &rectlVIS );

      /* Round to the nearest 8x8 boundries.                                */
      ulVisRectFromBottom = ( rectlVIS.yBottom + 8 - 1 ) / 8;
      ulVisRectFromTop    = ( rectlDst.yTop   - rectlVIS.yTop + 8 - 1 ) / 8;
      ulVisRectFromRHEdge = ( rectlDst.xRight - rectlVIS.xRight + 8 - 1 ) / 8;
      ulVisRectFromLHEdge = ( rectlVIS.xLeft   + 8 - 1 ) / 8;

      /* Calculate the visible region rectangle width.  Note that this may  */
      /* be negative one if the region is smaller than the block size (8).  */
      lVisRectWidth= ( rectlDst.xRight / 8 ) -
                            ( ulVisRectFromRHEdge + ulVisRectFromLHEdge );

      /* Calculate the visible region rectangle height.  Note that this may */
      /* be negative one if the region is smaller than the block size (8).  */
      lVisRectHeight= ( rectlDst.yTop / 8 ) -
                            ( ulVisRectFromTop + ulVisRectFromBottom  );

      /* Check here to make sure we are dealing with an actual rectangle.   */
      if ( (lVisRectWidth  > 0) && (lVisRectHeight > 0) )
         {
         /* Calculate the offset into the mask.  The is a little tricky     */
         /* here... the clipping mask is a mask of bytes, with each byte    */
         /* representing clipped (1) or visible (0) 8x8 block of image data.*/
         /* What the decoder is going to do is have the 8x8 width and       */
         /* height counters in one variable (width in the lsb and height in */
         /* the msb).  When it needs to check the mask, it simply does a    */
         /* lookup into the mask with the counters as the offset.  This is  */
         /* why the width is 256, not image width.  Note that this also     */
         /* restricts the image to be divisible by 8 in both directions     */
         /* and no larger than 2048 in either. Lower left is (1,1).         */
         ulMaskOffset= ( ( ulVisRectFromBottom + 1 ) * 256 ) +
                           ulVisRectFromRHEdge + 1;

         /* Store the pointer to the previous line down.                    */
         pbLastMaskStart= (PBYTE)( (ULONG)(pDecodeInst->pbClipMask) +
                                 ulMaskOffset );

         /* Fill the rectangular region with zeros signifing visible.       */
         for ( ulYindex= 0; ulYindex < lVisRectHeight;
                                 ulYindex++, pbLastMaskStart += 256 )
            for ( ulXindex= 0, pbMask= pbLastMaskStart;
                   ulXindex < lVisRectWidth; ulXindex++ )
                *pbMask++= 0;

         } /* endfor ulXindex */
      } /* endfor ulRectlIndex */

   /* At this point the mask may not be completely correct.  Two visible    */
   /* regions next to each other will create a clipped line (because of     */
   /* the rounding down to 8x8's).  These "holes" need repair.              */
   RepairHolesInMask ( ulNumValidRects, prectlArray,
                             (PRECTL)(&rectlDst), pDecodeInst );

   return;
   }




/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: RepairHolesInMask                                       */
/*     This routine will repair any "holes" left in the mask by the above   */
/*     calculate clipping mask routine.  See comment above call.            */
/*                                                                          */
/****************************************************************************/

VOID RepairHolesInMask ( ULONG ulNumValidRects, PRECTL prectlArray,
                       PRECTL prectlDest, PDECODE_INST pDecodeInst )
   {
   ULONG    ulSearchIndex;
   ULONG    ulCurrentIndex;
   RECTL    rectlCurrent;
   RECTL    rectlSearch;
   PRECTL   prectlCurrent;
   PRECTL   prectlSearch;
   ULONG    ulValidAreaWidth;
   ULONG    ulMaskOffset;
   ULONG    ulBlksFromRHS;
   ULONG    ulRight;
   ULONG    ulLeft;

   /* Don't bother searching the array if there's less than two rectls.     */
   if ( ulNumValidRects < 2 )
      return;

   /* Search the rectangles for a bottom/top match which is the scenereo    */
   /* for the generated "holes."                                            */
   for ( ulCurrentIndex = 0, prectlCurrent = prectlArray;
         ulCurrentIndex < ulNumValidRects-1;
         ulCurrentIndex++, prectlCurrent++)
      {
      /* Again, insure that the rectangle envelops only the visible area.   */
      rectlCurrent = *prectlCurrent;
      ConstrainRectToDest (prectlDest, &rectlCurrent);

      for ( ulSearchIndex = ulCurrentIndex + 1,
            prectlSearch  = prectlArray + ulSearchIndex;
            ulSearchIndex < ulNumValidRects;
            ulSearchIndex++, prectlSearch++)
         {
         /* Ensure the search rectangle is in only the visible area.        */
         rectlSearch = *prectlSearch;
         ConstrainRectToDest (prectlDest, &rectlSearch);

         /* If it's not on a block boundry, then we need to handle it.      */
         if ( (rectlCurrent.yBottom == rectlSearch.yTop) &&
              (rectlCurrent.yBottom % 8 ) )
            {
            /* Determine the extent of the overlay.                         */
            if ( rectlCurrent.xRight >= rectlSearch.xRight )
               {
               ulRight = rectlSearch.xRight;

               /* Check for overlap condition.                           */
               if ( rectlSearch.xRight > rectlCurrent.xLeft )
                  if ( rectlCurrent.xLeft < rectlSearch.xLeft )
                     ulLeft = rectlSearch.xLeft;
                  else
                     ulLeft = rectlCurrent.xLeft;

               else
                  ulRight = ulLeft = 0;   /* No overlap.                 */
               }

            else
               {
               ulRight = rectlCurrent.xRight;
               if ( rectlCurrent.xRight > rectlSearch.xLeft )
                  if ( rectlCurrent.xLeft < rectlSearch.xLeft )
                     ulLeft = rectlSearch.xLeft;
                  else
                     ulLeft = rectlCurrent.xLeft;

               else /* no overlap */
                  ulRight = ulLeft = 0;
               }

            /* Snap ulLeft to the right as it can't overlay window to right.*/
            if ( ulLeft % 8 )
               ulLeft += 8 - ulLeft % 8;

            /* Snap ulRight to the left as it can't overlay window to right.*/
            if ( ulRight % 8 )
               ulRight -= ulRight % 8;

            ulValidAreaWidth = (ulRight - ulLeft) / 8;

            /* Check to see if the rectangle is actually a valid size.      */
            if ( (ulValidAreaWidth > 0) && (ulValidAreaWidth <= 256 ) )
               {
               /* "Hole" region: clear them as visible.                     */
               ulMaskOffset = ((rectlCurrent.yBottom / 8 ) + 1) * 256;
               ulBlksFromRHS = (((ULONG)prectlDest->xRight - ulRight) / 8 ) + 1;

               while ( ulValidAreaWidth-- )
                  {
                  *(PBYTE)( (ULONG)(pDecodeInst->pbClipMask) +
                          ulBlksFromRHS + ulMaskOffset ) = 0;

                  ulMaskOffset++;
                  }
               }
            }
         }
      } /* End of main loop traversing the array of rectangles.             */

   return;
   }





/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME: ConstrainRectToDest                                     */
/*     This small routine will simply modify the visible region message     */
/*     rectangle to be within the actual output region of the video.        */
/*                                                                          */
/****************************************************************************/

VOID ConstrainRectToDest ( PRECTL prectlDest, PRECTL prectlModify )
   {
   if ( prectlModify->xRight > prectlDest->xRight )
      prectlModify->xRight = prectlDest->xRight;

   if ( prectlModify->xLeft > prectlDest->xRight )
      prectlModify->xLeft = prectlDest->xRight;

   if ( prectlModify->yTop > prectlDest->yTop )
      prectlModify->yTop = prectlDest->yTop;

   return;
   }
