text: /******************************Module*Header*******************************\
  * Module Name: drawstream.c                                                *
  *                                                                          *
  * Client side draw stream support.  Handles metafiling if primary          *
  * is a metafile.                                                           *
  *                                                                          *
  * Created: 03-Mar-2001                                                     *
  * Author: Barton House [bhouse]                                            *
  *                                                                          *
  * Copyright (c) 1991-2001 Microsoft Corporation                            *
  \**************************************************************************/
  
  #include "precomp.h"
  #pragma hdrstop
  /******************************Public*Routine******************************\
  * GdiDrawStream
  *
  *
  * Arguments:
  *
  *
  *
  * Return Value:
  *
  *
  *
  * History:
  *
  *    3/21/2001 Barton House
  *
  \**************************************************************************/
  
  BOOL
  GdiDrawStream(
                   HDC   hdcDst,
                   ULONG cjIn,
                   VOID *pvIn)
  {
      BOOL bRet = FALSE;
      PDC_ATTR pdca;
  
      FIXUP_HANDLE(hdcDst);
      
      if (IS_ALTDC_TYPE(hdcDst))
      {
          HBITMAP hbmSource = NULL;
          HRGN    hrgnSaved  = NULL;
          HBITMAP hbmScratch = NULL;
          HDC     hdcScratch = NULL;
          int     iDstClip = -1;
          ULONG * pul = (ULONG *) pvIn;
      
          if(cjIn < sizeof(ULONG))
              return FALSE;
      
          if(*pul++ != 'DrwS')
              return FALSE;
      
          cjIn -= sizeof(ULONG);
      
          while(cjIn >= sizeof(ULONG))
          {
              ULONG   command = *pul;
              ULONG   commandSize;
              RECTL   rclDstClip;
      
              switch(command)
              {
              case DS_SETTARGETID: // set target
                  {
                      DS_SETTARGET *  cmd = (DS_SETTARGET *) pul;
      
                      commandSize = sizeof(*cmd);
      
                      if(cjIn < commandSize)
                          goto altExit;
      
                      if((HDC) ULongToHandle(cmd->hdc) != hdcDst)
                      {
                          // NOTE: This restriction is only in place for the
                          //       initial implementation of GdiDrawStream.
  
                          WARNING("GdiDrawStream: target must match primary target");
                          goto altExit;
                      }
      
                      rclDstClip = cmd->rclDstClip;
  
                      if(hrgnSaved == NULL)
                      {
                          int iRet;
  
                          hrgnSaved = CreateRectRgn(0,0,0,0);
                          
                          if(hrgnSaved == NULL)
                          {
                              WARNING("GdiDrawStream: unable to create saved region");
                              goto altExit;
                          }
  
                          iDstClip = GetClipRgn(hdcDst, hrgnSaved);
  
                          if(iDstClip == -1)
                          {
                              WARNING("GdiDrawStream: failed to get DC application clip");
                              goto altExit;
                          }
  
                      }
                      else
                      {
                          // need to restore target clip
  
                          if(iDstClip)
                          {
                              SelectClipRgn(hdcDst, hrgnSaved);
                          }
                          else
                          {
                              SelectClipRgn(hdcDst, NULL);
                          }
                      }
      
                      IntersectClipRect(hdcDst, rclDstClip.left, rclDstClip.top, rclDstClip.right, rclDstClip.bottom);
                  }
                  break;
      
              case DS_SETSOURCEID: // set source
      
                  {
                      DS_SETSOURCE *  cmd = (DS_SETSOURCE *) pul;
      
                      commandSize = sizeof(*cmd);
      
                      if(cjIn < commandSize)
                          goto altExit;
      
                      hbmSource = (HBITMAP) ULongToHandle(cmd->hbm);
      
                  }
                  break;
      
              case DS_NINEGRIDID:
                  {
                      DS_NINEGRID * cmd = (DS_NINEGRID *) pul;
                      LONG  lSrcWidth = cmd->rclSrc.right - cmd->rclSrc.left;
                      LONG  lSrcHeight = cmd->rclSrc.bottom - cmd->rclSrc.top;
                      LONG  lDstWidth = cmd->rclDst.right - cmd->rclDst.left;
                      LONG  lDstHeight = cmd->rclDst.bottom - cmd->rclDst.top;
                      BOOL  bRenderRet;
                      RECTL rclDst = cmd->rclDst;
  
                      struct {
                          BITMAPINFOHEADER    bmih;
                          ULONG               masks[3];
                      } bmi;
      
                      struct {
                          DS_HEADER       hdr;
                          DS_SETTARGET    setTarget;
                          DS_SETSOURCE    setSource;
                          DS_NINEGRID     ng;
                      } scratchStream;
      
                      RECTL   rclScratch;
      
                      commandSize = sizeof(DS_NINEGRID);
      
                      // validate nine grid
      
                      #define DSDNG_MASK  0x007F      // move to wingdip.h
      
                      if(cmd->ngi.flFlags & ~DSDNG_MASK)
                      {
                          WARNING("GreDrawStream: unrecognized nine grid flags set\n");
                          goto altExit;
                      }
      
                      if(lSrcWidth < 0 || lSrcHeight < 0)
                      {
                          WARNING("GreDrawStream: nine grid rclSrc is not well ordered\n");
                          goto altExit;
                      }
      
                      if(cmd->ngi.flFlags & DSDNG_TRUESIZE)
                      {
                          if(lDstWidth > lSrcWidth) 
                          {
                              lDstWidth = lSrcWidth;
                              rclDst.right = rclDst.left + lDstWidth;
                          }
  
                          if(lDstHeight > lSrcHeight)
                          {
                              lDstHeight = lSrcHeight;
                              rclDst.bottom = rclDst.top + lDstHeight;
                          }
                      }
                      else
                      {
                          // NOTE: we have to check individual first then sum due to possible
                          //       numerical overflows that could occur in the sum that might
                          //       not be detected otherwise.
      
                          if(cmd->ngi.ulLeftWidth < 0 ||
                             cmd->ngi.ulRightWidth < 0 ||
                             cmd->ngi.ulTopHeight < 0 ||
                             cmd->ngi.ulBottomHeight < 0 ||
                             cmd->ngi.ulLeftWidth > lSrcWidth ||
                             cmd->ngi.ulRightWidth > lSrcWidth ||
                             cmd->ngi.ulTopHeight > lSrcHeight ||
                             cmd->ngi.ulBottomHeight > lSrcHeight ||
                             cmd->ngi.ulLeftWidth + cmd->ngi.ulRightWidth > lSrcWidth ||
                             cmd->ngi.ulTopHeight + cmd->ngi.ulBottomHeight > lSrcHeight)
                          {
                              WARNING("GreDrawStream: nine grid width is greater then rclSrc width or negative\n");
                              goto altExit;
                          }
                      }
      
                      if((cmd->ngi.flFlags & (DSDNG_TRANSPARENT | DSDNG_PERPIXELALPHA)) == (DSDNG_TRANSPARENT | DSDNG_PERPIXELALPHA))
                      {
                          WARNING("GreDrawStream: nine grid attempt to set both transparency and per pixel alpha\n");
                          goto altExit;
                      }
      
                      // create temporary to render nine grid into
      
                      bmi.bmih.biSize = sizeof(bmi.bmih);
                      bmi.bmih.biWidth = lDstWidth;
                      bmi.bmih.biHeight = lDstHeight;
                      bmi.bmih.biPlanes = 1;
                      bmi.bmih.biBitCount = 32;
                      bmi.bmih.biCompression = BI_BITFIELDS;
                      bmi.bmih.biSizeImage = 0;
                      bmi.bmih.biXPelsPerMeter = 0;
                      bmi.bmih.biYPelsPerMeter = 0;
                      bmi.bmih.biClrUsed = 3;
                      bmi.bmih.biClrImportant = 0;
                      bmi.masks[0] = 0xff0000;    // red
                      bmi.masks[1] = 0x00ff00;    // green
                      bmi.masks[2] = 0x0000ff;    // blue
      
                      if(hbmScratch != NULL)
                          DeleteObject(hbmScratch);
      
                      hbmScratch = CreateDIBitmap(hdcDst, &bmi.bmih, CBM_CREATEDIB , NULL, (BITMAPINFO*)&bmi.bmih, DIB_RGB_COLORS);
                      
                      if(hbmScratch == NULL)
                      {
                          WARNING("GdiDrawStream: unable to create temporary\n");
                          goto altExit;
                      }
                      
                      if(hdcScratch == NULL)
                      {
                          hdcScratch = CreateCompatibleDC(hdcDst);
  
                          if(hdcScratch == NULL)
                          {
                              WARNING("GdiDrawStream: unable to create temporary dc\n");
                              goto altExit;
                          }
                      }
  
                      SelectObject(hdcScratch, hbmScratch);
      
                      rclScratch.left = 0;
                      rclScratch.top = 0;
                      rclScratch.right = lDstWidth;
                      rclScratch.bottom = lDstHeight;
                      
                      scratchStream.hdr.magic = DS_MAGIC;
                      scratchStream.setTarget.ulCmdID = DS_SETTARGETID;
                      scratchStream.setTarget.hdc = HandleToULong(hdcScratch);
                      scratchStream.setTarget.rclDstClip = rclScratch;
                      scratchStream.setSource.ulCmdID = DS_SETSOURCEID;
                      scratchStream.setSource.hbm = HandleToULong(hbmSource);
                      scratchStream.ng.ulCmdID = DS_NINEGRIDID;
                      scratchStream.ng.rclDst = rclScratch;
                      scratchStream.ng.rclSrc = cmd->rclSrc;
                      scratchStream.ng.ngi = cmd->ngi;
                      scratchStream.ng.ngi.flFlags &= ~(DSDNG_TRANSPARENT | DSDNG_PERPIXELALPHA);
      
                      NtGdiDrawStream(hdcScratch, sizeof(scratchStream), &scratchStream);
  
                      if(cmd->ngi.flFlags & DSDNG_TRANSPARENT)
                      {
                          bRenderRet = GdiTransparentBlt(hdcDst,
                                            rclDst.left, 
                                            rclDst.top,
                                            lDstWidth,
                                            lDstHeight,
                                            hdcScratch,
                                            0,
                                            0,
                                            lDstWidth,
                                            lDstHeight,
                                            cmd->ngi.crTransparent);
                      }
                      else if(cmd->ngi.flFlags & DSDNG_PERPIXELALPHA)
                      {
                          // alpha blend
                          BLENDFUNCTION   bfx;
  
                          bfx.AlphaFormat = AC_SRC_ALPHA;
                          bfx.BlendFlags = 0;
                          bfx.BlendOp = AC_SRC_OVER;
                          bfx.SourceConstantAlpha = 255;
  
                          bRenderRet = GdiAlphaBlend(hdcDst,
                                            rclDst.left, 
                                            rclDst.top,
                                            lDstWidth,
                                            lDstHeight,
                                            hdcScratch,
                                            0,
                                            0,
                                            lDstWidth,
                                            lDstHeight,
                                            bfx);
                      }
                      else
                      {
                          // bitblt
  
                          bRenderRet = BitBlt(hdcDst,
                                            rclDst.left, 
                                            rclDst.top,
                                            lDstWidth,
                                            lDstHeight,
                                            hdcScratch,
                                            0,
                                            0,
                                    SRCCOPY);
                      }
  
                      if(!bRenderRet)
                      {
                          WARNING("GdiDrawStream: failed to render temporary to destination");
                          goto altExit;
                      }
                  }
                  break;
      
              default:
                  WARNING("GdiDrawStream: unrecognized command");
                  goto altExit;
              }
  
              pul += commandSize  / sizeof(ULONG);
              cjIn -= commandSize;
  
          }
  
          bRet = TRUE;
  
      altExit:
  
          if(iDstClip == 1)
          {
              SelectClipRgn(hdcDst, hrgnSaved);
          }
          else if(iDstClip == 0)
          {
              SelectClipRgn(hdcDst, NULL);
          }
          
          if(hbmScratch != NULL)
              DeleteObject(hbmScratch);
  
          if(hdcScratch != NULL)
              DeleteDC(hdcScratch);
  
          if(hrgnSaved != NULL)
              DeleteObject(hrgnSaved);
  
          return bRet;
      }
  
      RESETUSERPOLLCOUNT();
  
      bRet = NtGdiDrawStream(
                        hdcDst,
                        cjIn,
                        pvIn);
      return(bRet);
  }
  
  
  