;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = WINSTR.ASM
;*
;* DESCRIPTIVE NAME = String Output
;*
;*
;* VERSION      V2.0
;*
;* DATE         03/12/87
;*
;* DESCRIPTION  Handles string output 
;*              
;* FUNCTIONS    CharMarkerPos 
;*              CharStringPos 
;*              CharGenericPos
;*              CharString    
;*              ConvertList32 
;*                            
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   03/12/87                     Written by Charles Whitmer
;*   05/11/87                     Hock Lee [hockl]
;*   05/11/87                     Changed the passed argument structure.
;*   06/18/87                     Martin Picha [martinpi] Wrote ConvertList32
;*   06/19/87                     Hock Lee [hockl] Massive changes to ConvertList32
;*   06/20/87                     Hock Lee [hockl] Wrote CharStringPos
;*   07/16/87                     Hock Lee [hockl] Modified for new DDI.
;*   09/02/87                     Hock Lee [hockl] Modified to update current
;*   09/02/87                     position for simple case.
;*   09/02/87                     Tony Pisculli [tonyp] Wrote CharString
;*   10/06/87                     Hock Lee [hockl] Added correlation.
;*   10/06/87                     Hock Lee [hockl] Rewrote to call CharStringPos!
;*   11/07/87                     Hock Lee [hockl] Added lpAttrs and lpXY support.
;*   11/30/87                     Hock Lee [hockl] Added bounds.
;*   01/22/88                     Martin Picha [martinpi] Implemented DCR 23388
;*   02/18/88                     Hock Lee [hockl] Implemented DCR 23814.
;*   03/04/88                     Mike Harringont [mikehar] Re-Added correlation.
;*
;*****************************************************************************/

CVTC_SCREEN     equ     6               

;/*
;**       Definitions for the eto_options parameters
;*/

ETO_OPAQUE_FILL equ     00000010b       ;Fill opaque rect with background color
ETO_OPAQUE_CLIP equ     00000100b       ;Clip to opaque rectangle

MAX_CHARSTRING_LENGTH   equ     512     ;Character strings mustn't be longer
                                        ;than this many characters.

OK_CHS_FLAGS    equ     CHS_OPAQUE+CHS_VECTOR+CHS_LEAVEPOS+CHS_CLIP+CHS_START_XY+CHS_ATTR_INFO

;/*
;** FONT_ENTRIES is the format of the individual character records in
;** a fixed or proportional font.
;*/

FONT_ENTRIES    struc
fe_dBits        dd      ?
fe_width        dw      ?
FONT_ENTRIES    ends

;/*
;** ABC_FONT_ENTRIES is the format of the individual character records in
;** an ABC spaced font.
;*/

ABC_FONT_ENTRIES struc
abc_fe_dBits     dd     ?
abc_a_space      dw     ?
abc_b_space      dw     ?
abc_c_space      dw     ?
ABC_FONT_ENTRIES ends


        .286p
        .xlist
        include cmacros.inc
INCL_GRE_FONTS          equ     1
INCL_GRE_XFORMS         equ     1
INCL_GRE_BITMAPS        equ     1
INCL_GRE_STRINGS        equ     1
INCL_GRE_DEVSUPPORT     equ     1
INCL_GRE_DEVMISC3       equ     1
INCL_FONTFILEFORMAT     equ     1
INCL_DDIMISC            equ     1
INCL_DEV                equ     1
INCL_DDIBUNDLES         equ     1
INCL_DDICOMFLAGS        equ     1
INCL_GPITRANSFORMS      equ     1
INCL_GRE_XFORMS         equ     1
INCL_GRE_CLIP           equ     1
INCL_GPIREGIONS         equ     1
        include pmgre.inc
DINCL_ENABLE            equ     1
DINCL_VIO               equ     1
        include driver.inc
        include oemblt.inc
        include fontseg.inc
        include njmp.mac
        include assert.mac
        .list

        ??_out  winstr

        externFP ExtTextOut

        errcode <INV_IN_PATH,BITMAP_NOT_SELECTED,INV_CHAR_MODE_ATTR,INV_RECT>
        errcode <INV_IN_AREA,COORDINATE_OVERFLOW,INV_LENGTH_OR_COUNT>

sBegin  Data
        externB         ddcInit
        externD         pfnDefCharStringPos ;Default charstringpos handler
        externD         pfnDefQueryTextBox  ;Default querytextbox  handler
        externD         pfnDefRectVisible
        externD         pfnDefQueryCharPositions
        externD         pfnDefConvert
sEnd    Data


sBegin  FarCode
        externNP PropagateSysClrChange
        externNP intersect_with_corr
sEnd

sBegin  Code
        assumes cs,Code

        externW  CodeData
        externNP convert_screen_world
        externNP convert_world_screen
        externNP enumerate_clip_rects
        externNP do_exttextout
        externNP enter_driver
        externNP leave_driver

        externNP InnerAccumulateBounds
        externNP MakePassedColorsValid
        externNP MakeColorsValid
        externNP validate_rectl
        externNP convert_rectl
page

;/***************************************************************************
;*
;* FUNCTION NAME = CharStringPos 
;*
;* DESCRIPTION   = Draws  a character string starting at the current x,y position,
;*                 with user controlled spacing.  The character string is formatted 
;*                 according to the format control flags passed.
;*
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,ES
;*
;* INPUT         =
;*      P32_CH Long pointer to the string of character codepoints.
;*
;*      S32_N Specifies the number of bytes in the character string.
;*
;*      U32_FORMAT  Series  of  flags controlling left, right, center,
;*                  horizontal and vertical justification.
;*
;*                   Flags for char string control format are
;*
;*                   Bit 0   = 0 Opaque rectangle not present
;*                           = 1 Opaque rectangle present
;*
;*                   Bit 1   = 0 Position or delta vector not present
;*                           = 1 Position or delta vector present
;*
;*                   Bit 2   = 0 Normal Text
;*                           = 1 Grayed Text
;*
;*                   Bit 3   = 0 Update current position
;*                           = 1 Don't update current position
;*
;*                   Bit 4   = 0 Don't clip to opaque rectangle
;*                           = 1 Clip to opaque rectangle
;*
;*                   Bit 5   = 0 start postion not present
;*                           = 1 start postion present
;*
;*                   Bit 6   = 0 Attribute info not present
;*                           = 1 Attribute info present
;*
;*
;*      P32_RECT    a optional rectangle which will be filled with the
;*                  character background color before the text is layed
;*                  down. The coordinates are in DOCS.
;*
;*      P32_VECTOR  an optional vector of fixed point deltas or
;*                  positions. All information is in DOCS.
;*
;*      P32_XY      an optional starting position.
;*
;*      P32_ATTRS   an optional structure of attributes to be used with
;*                  the characters.  The structure looks like
;*                      csp_info struc
;*                              cSize   dd ?    ; size of structure in bytes
;*                              clrFore dd ?    ; foreground color to use
;*                              clrBack dd ?    ; background color to use
;*                      csp_info ends
;*
;*
;* OUTPUT        =
;*
;* RETURN-NORMAL = AX = 1 
;* RETURN-ERROR  = AX = 0
;*
;**************************************************************************/

        check   CharStringPos,<hdc,pptlStart,prclOpaque,flCmd,cch,pch,pdx,pcspAttrs,hddc,ulFunN>

        assumes ds,nothing
        assumes es,nothing



labelFP <PUBLIC,CharMarkerPos>
        mov     cx,ddc_ma
        jmp     short CharGenericPos

labelFP <PUBLIC,CharStringPos>
        mov     cx,ddc_ca
        errn$   CharGenericPos

cProc   CharGenericPos,<PUBLIC,FAR,NODATA>,<di,si>

        parmD   hdc
        parmD   lpXY
        parmD   lpRect
        parmD   Options
        parmD   N
        parmD   lpCh
        parmD   lpDx
        parmD   lpAttrs
        parmD   hddc
        parmD   FunN

        localW  nChars
        localW  wOptions
        localW  ddc_screen_x
        localW  ddc_screen_y
        localD  ddc_world_x
        localD  ddc_world_y
        localW  pXY
        localW  cs_width
        localW  cs_concat
        localW  cs_height
        localV  exttextout_args,<size eto_args>
        localV  StartXY,%(size POINTL)
        localV  TempRect,%(size RECTL)
        localV  OpaqueRect,%(size RECTS)
        localW  npAttrs                 ;which attribute bundle to use
        localW  old_sp
        localW  return_code             ;Return code in case correlation
        localW  mytemprect              
                                        
                                        
                                        
                                        
                                        
        localV  rectangle,%((size RECTL)*2)


cBegin
        ddc?    hddc
        mov     npAttrs,cx              ;Save attribute bundle pointer
        errnz   ca_ba-ma_ba
        errnz   ca_pFont-ma_pFont
        errnz   ca_fs-ma_fs

        cld
        mov     ds,CodeData
        assumes ds,Data
        mov     si,hddc.lo
        mov     dx,si
        call    enter_driver
        njc     csp_exit_no_lock        ; DX:AX = 0 on error

;/*
;** illegal to draw text in area bracket
;*/

        no_path_area    CharStringPos_error,area,error

        mov     return_code,1           ;Return code for success
        mov     ax,PMERR_BITMAP_NOT_SELECTED
        test    [si].ddc_fb,DDC_PRESENT
        jz      CharStringPos_error_relay

;/*
;** Strings longer than MAX_CHARSTRING_LENGTH cannot be
;** processed in this version.
;** The parameter N is unsigned.
;*/

        mov     ax,PMERR_INV_LENGTH_OR_COUNT
        mov     dx,N.hi
        cmp     N.lo,(MAX_CHARSTRING_LENGTH+1) AND 0FFFFh
        sbb     dx,(MAX_CHARSTRING_LENGTH+1) SHR 16
        jae     CharStringPos_error_relay

;/*
;** illegal to draw RASTER text in path bracket
;*/

        mov     bx,npAttrs
        test    FunN.hi,COM_PATH
        jz      its_ok
        test    [si][bx].ca_fs,CA_VECTOR
        jz      its_not_ok
        test    Options.lo,CHS_CLIP+CHS_OPAQUE  ; no rectangle allowed
        jz      its_ok
its_not_ok:
        mov     ax,PMERR_INV_IN_PATH
CharStringPos_error_relay:
        jmp     CharStringPos_error

its_ok:
        test    FunN.hi,COM_DEVICE
        jnz     normal                  ; we are forced to go through with it.
        test    [si][bx].ca_fs,CA_VECTOR+CA_PREC_NOT_1+CA_STRIKEOUT+CA_UNDERSCORE+CA_ITALIC+CA_BOLD+CA_DIRECTION
        jnz     goto_engine
        cmp     bx,ddc_ma
        jz      @F                      ; no spacing or alignment for markers
        assert  bx,E,ddc_ca
        mov     ax,[si].ddc_ca.ca_usTextAlign
        cmp     al,TA_CENTER
        je      goto_engine
        cmp     al,TA_RIGHT
        je      goto_engine
        cmp     ah,(TA_HALF shr 8)
        je      goto_engine
        cmp     ah,(TA_BOTTOM shr 8)
        je      goto_engine
        cmp     ah,(TA_STANDARD_VERT shr 8)
        je      goto_engine
.errnz TA_NORMAL_HORIZ   - 0001h
.errnz TA_LEFT           - 0002h
.errnz TA_CENTER         - 0003h
.errnz TA_RIGHT          - 0004h
.errnz TA_STANDARD_HORIZ - 0005h
.errnz TA_NORMAL_VERT    - 0100h
.errnz TA_TOP            - 0200h
.errnz TA_HALF           - 0300h
.errnz TA_BASE           - 0400h
.errnz TA_BOTTOM         - 0500h
.errnz TA_STANDARD_VERT  - 0600h
        CPUMode 386
        mov     eax,[si].ddc_ca.ca_fxExtra
        or      eax,[si].ddc_ca.ca_fxBreakExtra
        CPUMode 286
        jnz     goto_engine             ; ask engine to do text spacings
@@:
        test    Options.lo,NOT OK_CHS_FLAGS
        jnz     goto_engine
        test    Options.hi,NOT 0
        jz      normal

goto_engine:
        call    leave_driver
        mov     ax,ds
        mov     es,ax
        assumes es,Data
        cleanframe
        assumes ds,nothing
        jmp     pfnDefCharStringPos     ;Pass it back to the engine

        assumes ds,Data
        assumes es,nothing

normal:

;/*
;** Place the current position on the stack so we can overwrite
;** it if the caller passed us a starting (X,Y).
;*/

        les     bx,[si].ddc_prddc
        assumes es,nothing
        mov     ax,es:[bx].rddc_ptsCurPos.pts_x
        mov     cx,es:[bx].rddc_ptsCurPos.pts_y
        test    off_Options,CHS_START_XY
        jz      no_start_xy
        les     bx,lpXY
        assumes es,nothing
        mov     ax,es:[bx].ptl_x.lo
        mov     cx,es:[bx].ptl_y.lo
        fw_zero <es>
no_start_xy:
        cwd
        mov     StartXY.ptl_x.lo,ax
        mov     StartXY.ptl_x.hi,dx
        xchg    ax,cx
        cwd
        mov     StartXY.ptl_y.lo,ax
        mov     StartXY.ptl_y.hi,dx

;/*
;** Get correct colors
;*/

        mov     ax,[si].ddc_iSysClr
        lea     di,ddcInit
        cmp     ax,[di].ddc_iSysClr
        je      @F
        cCall   PropagateSysClrChange
@@:

        mov     bx,npAttrs
        test    off_Options,CHS_ATTR_INFO
        jnz     csp_passed_attrs
        test    [si][bx].ca_ba.ba_fb,BA_CLR_INVALID or BA_CLR_BACK_INV
        jz      csp_colors_are_valid
        lea     bx,[si][bx].ca_ba
        call    MakeColorsValid
        jns     csp_colors_are_valid
csp_invalid_colors:
        jmp     CharStringPos_bad_exit  ;Error code has been logged

csp_passed_attrs:
        les     di,lpAttrs              ;ES:DI --> passed colors
        assumes es,nothing              ;DS:SI --> DDC
        lea     bx,[si][bx].ca_ba       ;BX = attribute bundle in ddc
        call    MakePassedColorsValid
        js      csp_invalid_colors
csp_colors_are_valid:

;/*
;** Do Correlation
;*/
        test    FunN.hi,COM_CORRELATE
        jnz     csp_corr
        jmp     csp_no_correlation

        public  csp_corr
csp_corr:
        lea     di,rectangle
        mov     mytemprect,di

        test    off_Options,CHS_CLIP+CHS_OPAQUE
        jz      csp_corr_check_box

;/*
;** make a copy of lpRect
;*/
        mov     dx,si
        mov     cx,size RECTL/2
        mov     ax,ss
        mov     es,ax
        assumes es,nothing
        lds     si,lpRect
        assumes ds,nothing
        rep     movsw
        mov     ds,CodeData
        assumes ds,Data
        mov     si,dx

        lea     di,rectangle            ;ES:DI --> rectl to convert
        call    validate_rectl          ;make sure rectangle is valid
        njc     CharStringPos_error
        mov     ah,CVTC_WORLD           ;AH = source space
        mov     al,CVTC_SCREEN          ;AL = dest space
        test    FunN.hi,COM_TRANSFORM
        jnz     @F
        mov     ah,al                   ;Source is device, just check ordering
@@:
        call    convert_rectl
        jnc     csp_rectl_converted
        jmp     CharStringPos_exit

csp_rectl_converted:
        mov     di,FunN.hi
        farPtr  mylpRect,ss,mytemprect
        cCall   intersect_with_corr,<mylpRect>
        or      ax,ax
        jz      csp_corr_check_clip

        cCall   pfnDefRectVisible,<hdc,mylpRect,hddc,GreRectVisible>
        and     ax,2
        njnz     csp_save_rc

csp_corr_check_clip:
        test    off_Options,CHS_CLIP
        njnz     csp_no_correlation_sp

csp_corr_check_box:
        test    seg_FunN,COM_TRANSFORM          ; FunN - MSW is commands
        jz      csp_corr_no_starting_xy
        test    off_Options,CHS_START_XY
        jz      csp_corr_no_starting_xy
        call    xform_passed_xy
        or      ax,ax
        jz      csp_save_rc

csp_corr_no_starting_xy:
        mov     ax,FunN.hi
        and     ax,(NOT COM_TRANSFORM) OR COM_DEVICE
        farPtr  mylpRect,ss,mytemprect
        farPtr  mynum,0,3
        farPtr  myFunN,ax,off_QueryTextBox
        cCall   pfnDefQueryTextBox,<hdc,N,lpCh,mynum,mylpRect,hddc,myFunN>
        mov     di,mytemprect
        add     di,size POINTL

;/*
;** make coordinates cp relative
;*/
        mov     ax,StartXY.ptl_x.lo
        mov     dx,StartXY.ptl_x.hi
        mov     cx,StartXY.ptl_y.lo
        mov     bx,StartXY.ptl_y.hi

        add     ss:[di].ptl_x.lo,ax
        adc     ss:[di].ptl_x.hi,dx
        add     ss:[di].ptl_y.lo,cx
        adc     ss:[di].ptl_y.hi,bx
        add     di,size POINTL
        add     ss:[di].ptl_x.lo,ax
        adc     ss:[di].ptl_x.hi,dx
        add     ss:[di].ptl_y.lo,cx
        adc     ss:[di].ptl_y.hi,bx

        sub     di,size POINTL
        mov     bx,di
        mov     di,FunN.hi                      ;pass on COM_* flags
        farPtr  myrect,ss,bx
        cCall   intersect_with_corr,<myrect>
        or      ax,ax
        jz      csp_no_correlation_sp
        add     ax,ax
csp_save_rc:
        mov     return_code,ax
csp_no_correlation_sp:
csp_no_correlation:

;/*
;** locate the bitmap
;*/

        test    [si].ddc_fb,DDC_PRESENT         ; If no surface, then
        jnz     csp_have_surface
        mov     ax,PMERR_BITMAP_NOT_SELECTED    ; bail out with error
        jmp     CharStringPos_error
csp_have_surface:
        mov     bx,[si].ddc_npsd                ; DS:BX -> surface
        test    [bx].sd_fb,SD_DEVICE            ; is it our screen?
        jnz     csp_have_dev                    ;  yes    CR!!!
csp_have_dev:
        mov     word ptr exttextout_args.eto_lpDestDev[2],ds
        mov     word ptr exttextout_args.eto_lpDestDev[0],bx

;/*
;** transform the rectangle
;*/

        test    seg_FunN,COM_TRANSFORM          ; FunN - MSW is commands
        jz      no_starting_xy
        test    off_Options,CHS_START_XY
        jz      no_starting_xy
        call    xform_passed_xy
        or      ax,ax
        jz      some_dummy_label
no_starting_xy:
        test    off_Options,CHS_CLIP OR CHS_OPAQUE
        jz      get_increment_vector            ; no rectangle to transform

;/*
;** if we are going to perform the transformation, do it in local space
;** so we don't wipe out what we were passed in.
;*/

        mov     bx,ds
        push    si

        lds     si,lpRect
        assumes ds,nothing
        lea     di,TempRect
        mov     ax,ss                   ; SS = seg_TempRect
        mov     es,ax
        mov     seg_lpRect,ax           ; point to temporary rectangle.
        mov     off_lpRect,di
        assumes es,nothing
        mov     cx,(SIZE RECTL) / 2
        rep     movsw                   ; fill in temporary rectangle.
        pop     si

        mov     ds,bx
        assumes ds,Data

        lea     di,TempRect             ;ES:DI --> rectl to convert
        call    validate_rectl          ;make sure rectangle is valid
        njc     CharStringPos_error     ;duplicate code!!!
        mov     ah,CVTC_WORLD           ;AH = source space
        mov     al,CVTC_SCREEN          ;AL = dest space
        test    FunN.hi,COM_TRANSFORM
        jnz     @F
        mov     ah,al                   ;Source is device, just check ordering
@@:
        call    convert_rectl
        jnc     get_increment_vector

some_dummy_label:
        jmp     CharStringPos_exit

no_increment_vector:
        xor     ax,ax
        cwd
        jmp     fill_eto_width_vector

get_increment_vector:

;/*
;** reserve space for widths
;*/

        test    off_Options,CHS_VECTOR
        jz      no_increment_vector             ; no increment vector

        mov     cx,off_N
        jcxz    no_increment_vector             ; no increment vector

        shl     cx,1
        sub     sp,cx                           ; allocate space for widths
        mov     di,sp
        push    ss                              ; ES:DI = lpWx
        pop     es
        assumes es,nothing
        shr     cx,1                            ; CX = count

        test    [si].ddc_fb,DDC_UNIT_XFORM
        jz      cnvlst_complex_xform            ; not an identity transform

        push    ds                              ; save registers
        push    es
        push    si
        push    di

        lds     si,lpDx
        assumes ds,nothing
        assert  cx,NE,0
Cnvlst_loop:
        lodsw
        stosw
        cwd
        lodsw
        cmp     ax,dx
        jnz     cnvlst_overflow
        or      ax,ax
        jz      @F                              ; width cannot be less than zero
        mov     word ptr es:[di][-2],0          ; make it zero
@@:
        loop    Cnvlst_loop
        jmp     short cnvlst_restore_registers

cnvlst_overflow:
        mov     ax,PMERR_COORDINATE_OVERFLOW
        save_error_code
        mov     return_code,0                   ; error
        errn$   cnvlst_restore_registers

cnvlst_restore_registers:
        pop     di                              ; restore registers
        pop     si
        pop     es
        assumes es,nothing
        pop     ds
        assumes ds,Data
        jmp     short cnvlst_done

cnvlst_complex_xform:
        farPtr  target16,es,di
        cCall   ConvertList32,<lpDx,target16,si,cx>
        or      ax,ax
        jnz     cnvlst_done
        mov     return_code,ax                  ; error
cnvlst_done:

        ddc?    si
        mov     dx,es                           ; DX:AX = lpWx
        mov     ax,di

fill_eto_width_vector:

        mov     word ptr exttextout_args.eto_lpDX[2],dx
        mov     word ptr exttextout_args.eto_lpDX[0],ax

;/*
;**  fill arguments to ExtTextOut
;*/

        mov     dx,seg_lpCh                     ; lpString
        mov     ax,off_lpCh
        mov     word ptr exttextout_args.eto_lpString[2],dx
        mov     word ptr exttextout_args.eto_lpString[0],ax
        mov     ax,off_N                        ; Count
        mov     exttextout_args.eto_count,ax

;/*
;** get x position
;*/

        mov     dx,StartXY.ptl_x.lo                 ; DX = starting position
        mov     exttextout_args.eto_x,dx

;/*
;** get y position
;** flip the coordinate system over for the new scheme
;*/

        les     di,exttextout_args.eto_lpDestDev    ; ES:SI -> SD
        assumes es,nothing
        mov     dx,es:[di].sd_cy                ; DX = height
        mov     ax,dx                           ; flip target point
        sub     ax,StartXY.ptl_y.lo

;/*
;** If the vertical text align is TA_TOP then we want to skip the step
;** which aligns the text with the baseline, unless this call has already
;** gone through the engine simulation, in which case the position will
;** already have been adjusted to compensate for the alignment.
;*/

        mov     bx,[si].ddc_ca.ca_usTextAlign
        cmp     bh,(TA_TOP shr 8)
        jne     baseline_align
        test    FunN.hi,COM_DEVICE
        jz      no_baseline_align

baseline_align:
        mov     bx,npAttrs
        les     di,[si+bx].ca_pFont             ; ES:DI -> font
        assumes es,nothing
        sub     ax,es:[di].fsMetrics.foca_yMaxAscender
                                                ; make it baseline relative!
no_baseline_align:
        mov     exttextout_args.eto_y,ax

        mov     ch,BYTE PTR Options[0]          ; CH = csp_Options
        xor     cl,cl                           ; CL = eto_Options

;/*
;** handle opaque rectangle
;*/

        xor     ax,ax
        xor     bx,bx

        test    ch,CHS_OPAQUE                   ; is opaque rectangle present?
        jz      no_opaque_rect                  ;   no

;/*
;** make a wrect in the first point
;*/

make_opaque_wrect:

        test    ch,CHS_CLIP                 ; Set accelerator if CHS_CLIP
        jz      @f                          ; bit is set.
        or      cl,ETO_OPAQUE_CLIP
@@:

        or      cl,ETO_OPAQUE_FILL
        les     bx,lpRect
        assumes es,nothing
        mov     ax,es:[bx].rcl_xLeft.lo         ; X1
        mov     OpaqueRect.rcs_pts1.pts_x,ax
        mov     ax,es:[bx].rcl_xRight.lo        ; X2
        mov     OpaqueRect.rcs_pts2.pts_x,ax

        mov     ax,dx                           ; flip opaque rectangle
        sub     ax,es:[bx].rcl_yBottom.lo       ;   y1' = (ymax - y2)

        jno     @f                              ; check possibility of
        mov     ax,MAXSHORT                     ; overflow after flipping
@@:

        sub     dx,es:[bx].rcl_yTop.lo
        mov     OpaqueRect.rcs_pts2.pts_y,ax    ;   y2' = (ymax - y1)
        mov     OpaqueRect.rcs_pts1.pts_y,dx
        mov     ax,ss                           ; AX:BX = CHS_OPAQUE
        lea     bx,OpaqueRect

no_opaque_rect:
        mov     word ptr exttextout_args.eto_lpOpaqueRect[2],ax
        mov     word ptr exttextout_args.eto_lpOpaqueRect[0],bx

        xor     ax,ax
        xor     bx,bx
        test    ch,CHS_CLIP                     ; is clip rectangle present?
        jz      no_clip_rect                    ;   no
        mov     ax,seg_lpRect
        mov     bx,off_lpRect
no_clip_rect:

        xor     ch,ch
        mov     exttextout_args.eto_Options,cx

        mov     cx,npAttrs
        mov     exttextout_args.eto_npAttrs,cx
        mov     word ptr exttextout_args.eto_lpDDC[2],ds
        mov     word ptr exttextout_args.eto_lpDDC[0],si

;/*
;** check COM_DRAW bit
;*/

        mov     cl,[si].ddc_fb
        and     cx,DDC_VISIBLE
        and     cx,FunN.hi              ; FunN - MSW is commands
        jz      csp_drawn               ; skip draw, bounds, and cp update!
        .errnz  COM_DRAW-DDC_VISIBLE
;/*
;** enumerate the clip rectangles
;*/

        mov     cx,RECTDIR_LFRT_BOTTOP-1 ;Favored direction
        lea     di,exttextout_args
        farPtr  MyRect,ax,bx
        cCall   enumerate_clip_rects,<si,MyRect,CodeOFFSET do_exttextout>

csp_drawn:

;/*
;** need string width and height for bounds and current position update
;*/

        test    off_Options,CHS_LEAVEPOS
        jz      get_width_height
        test    seg_FunN,COM_BOUND+COM_ALT_BOUND
        jz      got_extent                      ; no need to get extent

get_width_height:

        xor     ax,ax                           ; assume zero extent
        mov     cs_width,ax
        mov     cs_concat,ax
        mov     cs_height,ax
        mov     exttextout_args.eto_Options,0   ; no rectangles
        neg     exttextout_args.eto_count       ; negative count to get extent
        jz      got_extent
        push    si

;/*
;** get the arguments onto our stack
;*/

        sub     sp,SIZE eto_args
        mov     di,sp
        lea     si,exttextout_args
        push    ss
        pop     es
        assumes es,nothing
        .errnz  (SIZE eto_args) and 1
        mov     cx,(SIZE eto_args) / 2
        cld
        rep     movs word ptr es:[di],word ptr ss:[si]

;/*
;** make the exttextout call
;*/

        call    ExtTextOut                      ; DX = Height, AX = Extent
        pop     si
        mov     cs_width,ax
        mov     cs_concat,bx
        mov     cs_height,dx

got_extent:

        public  csp_check_bounds
csp_check_bounds:
        test    seg_FunN,COM_BOUND+COM_ALT_BOUND
        jnz     csp_do_bounds           ; do bounds
        jmp     csp_update_position     ; skip bounds

csp_do_bounds:
        test    off_Options,CHS_OPAQUE
        jz      csp_do_char_bounds      ; no opaque rectangle for bounds
        les     di,lpRect
        assumes es,nothing
        mov     ax,es:[di].rcl_xLeft.lo
        mov     bx,es:[di].rcl_yBottom.lo
        mov     cx,es:[di].rcl_xRight.lo
        dec     cx                      ;Bounds are accumulated inclusive
        mov     dx,es:[di].rcl_yTop.lo
        dec     dx
        mov     di,FunN.hi
        call    InnerAccumulateBounds   ;DS:SI == ddc, DI = Command flags
        or      ax,ax
        jz      CharStringPos_exit_relay

;/*
;** do bounds for character string
;*/

csp_do_char_bounds:

;/*
;** direction must be L->R in this call
;** take care of direction for one character case!!!
;*/

        mov     bx,npAttrs
        les     di,[si][bx].ca_pFont            ; ES:DI -> font
        assumes es,nothing
        mov     bx,es:[di].fsMetrics.foca_yMaxDescender
        neg     bx
        mov     ax,StartXY.ptl_x.lo             ; left
        add     bx,StartXY.ptl_y.lo             ; bottom
        mov     cx,ax                           ; right
        add     cx,cs_width
        mov     dx,bx                           ; top
        add     dx,cs_height

;/*
;** The size of the character rectangle must be clipped against the
;** clipping rectangle if it was given.
;*/

        test    Options.lo,CHS_CLIP
        jz      csp_bounds_clipped
        les     di,lpRect
        assumes es,nothing
        cmp     ax,es:[di].rcl_xLeft.lo
        jg      @F
        mov     ax,es:[di].rcl_xLeft.lo
@@:
        cmp     bx,es:[di].rcl_yBottom.lo
        jg      @F
        mov     bx,es:[di].rcl_yBottom.lo
@@:
        cmp     cx,es:[di].rcl_xRight.lo
        jl      @F
        mov     cx,es:[di].rcl_xRight.lo
@@:
        cmp     dx,es:[di].rcl_yTop.lo
        jl      @F
        mov     dx,es:[di].rcl_yTop.lo
@@:

;/*
;** Even if we didn't clip, we have to handle the case where the string
;** had a length of zero.  !!! In the future, blow out of here in that case
;*/

csp_bounds_clipped:
        cmp     ax,cx                   ;Make sure we still have a rectangle
        jge     csp_done_bounds
        cmp     bx,dx
        jge     csp_done_bounds
        dec     cx                      ;Bounds are accumulated inclusive
        dec     dx
        mov     di,FunN.hi
        call    InnerAccumulateBounds   ;DS:SI == ddc, DI = Command flags
        or      ax,ax
        CPUMode 386
        jnz     csp_update_position
        CPUMode 286
CharStringPos_exit_relay:
        jmp     CharStringPos_exit

;/*
;** Update current position
;*/

csp_done_bounds:
csp_update_position:
        test    Options.lo,CHS_LEAVEPOS
        jnz     no_update

;/*
;** direction must be L->R in this call
;** take care of direction for one character case!!!
;*/

        mov     ax,cs_concat
        add     ax,StartXY.ptl_x.lo     ;StartXY is +/- 32K
        jno     @F
        mov     ax,PMERR_COORDINATE_OVERFLOW
        jmp     short CharStringPos_error
@@:
        push    ax                      ;Save ddc_ptsCurPos.pts_x
        push    StartXY.ptl_y.lo        ;Save ddc_ptsCurPos.pts_y
        mov     StartXY.ptl_x.lo,ax
        cwd
        mov     StartXY.ptl_x.hi,dx

;/*
;** transform new position to WORLD coordinates
;*/

        lea     bx,StartXY
        farPtr  lp_point,ss,bx
        cCall   convert_screen_world,<lp_point,1>       ; ds:si = ddc
        or      ax,ax
        jnz     update_cp_in_ddc        ; success
        mov     return_code,ax          ; error
        add     sp,size POINTS          ;Remove cp from stack
        jmp     short no_update

;/*
;** update WORLD position in ddc
;*/

update_cp_in_ddc:
        les     bx,[si].ddc_prddc
        assumes es,nothing
        rddc?   es,bx
        pop     es:[bx].rddc_ptsCurPos.pts_y
        pop     es:[bx].rddc_ptsCurPos.pts_x
        CPUMode 386
        mov     eax,StartXY.ptl_x
        mov     es:[bx].rddc_ptlWorldPos.ptl_x,eax
        mov     eax,StartXY.ptl_y
        mov     es:[bx].rddc_ptlWorldPos.ptl_y,eax
        fw_zero <es>
        CPUMode 286
no_update:

        test    off_Options,CHS_VECTOR
        jz      no_vector_to_free               ; no increment vector

        mov     cx,off_N
        shl     cx,1
        add     sp,cx                           ; free space for widths!

no_vector_to_free:

CharStringPos_good_exit:
        mov     ax,return_code
        jmp     short CharStringPos_exit

CharStringPos_error:
        save_error_code

CharStringPos_bad_exit:
        xor     ax,ax

CharStringPos_exit:
        cwd
        call    leave_driver
csp_exit_no_lock:
        mov     cx,ax

cEnd


        assumes ds,Data

;/*
;**       'Z' set if convert error
;**       'Z' clear of convert ok
;*/

xform_passed_xy proc near


        mov     bx,ds
        push    si

        lds     si,lpXY                 
        assumes ds,nothing
        lea     di,StartXY
        mov     ax,ss                   ; SS = seg_StartXY
        mov     es,ax
        assumes es,nothing
        mov     cx,(SIZE POINTL / 2)
        .errnz  SIZE POINTL AND 1
        rep     movsw                   ; fill in temporary rectangle.
        pop     si

        mov     ds,bx
        assumes ds,Data

        lea     dx,StartXY
        farPtr  lp_point,ss,dx          ; SS = seg_StartXY
        cCall   convert_world_screen,<lp_point,1>       ; ds:si = ddc

exit_xform_passed_xy:
        ret

xform_passed_xy endp


;/***************************************************************************
;*
;* FUNCTION NAME = CharString 
;*
;* DESCRIPTION   = Draws  a character string starting at the current x,y position.
;*
;* INPUT         = lpChars    Long pointer to the string of character codepoints.  
;*                 N          Specifies the number of bytes in the character string
;* OUTPUT        =
;*
;* RETURN-NORMAL = AX = 1 if the function executed successfully 
;* RETURN-ERROR  = AX = 0 error
;*
;**************************************************************************/

        check   CharString,<hdc,cch,pch,hddc,ulFunN>

        assumes ds,nothing
        assumes es,nothing

cProc   CharString,<PUBLIC,FAR,NODATA>,<di,si>

        parmD   hdc
        parmD   N
        parmD   lpChars
        parmD   hddc
        parmD   FunN

cBegin

        check   CharStringPos,<hdc,pptlStart,prclOpaque,flCmd,cch,pch,pdx,pcspAttrs,hddc,ulFunN>

        xor     ax,ax
        farPtr  MyXY,ax,ax
        farPtr  MyRect,ax,ax
        farPtr  MyOptions,ax,ax
        farPtr  MyDx,ax,ax
        farPtr  MyAttrs,ax,ax
        farPtr  FuncNum,<FunN.hi>,<off_CharStringPos>
        cCall   CharStringPos,<hdc,MyXY,MyRect,MyOptions,N,lpChars,MyDx,MyAttrs,hddc,FuncNum>
cEnd

;/***************************************************************************
;*
;* FUNCTION NAME = ConvertList32 
;*
;* DESCRIPTION   = This function takes a list of s32 real-world x-displacements
;*                 and converts them to a s16 list of device x-coordinates.    
;*                                                                             
;*                    Where rn = real world x-displacements,                   
;*                          dn = device x-displacements,                       
;*                                                                             
;*                 INPUT: (r1, r2, r3, ...)      [s32]                         
;*                 OUTPT: (d1, d2, d3, ...)      [s16]                         
;*                                                                             
;*                 Conversion is done at the accumulative level to prevent     
;*                 accumulative round-off errors.                              
;*                 The lists can in-fact be the same list causing replacement. 
;*                 
;*                 Registers Destroyed:
;*                   Flags,AX,BX,CX,DX 
;*
;* INPUT         = lpSrcList       :DWORD
;*                 lpDstList       :DWORD
;*                 ddc             :DWORD
;*                 Count           :DWORD
;*
;* OUTPUT        =
;*
;* RETURN-NORMAL = AX = 1 
;* RETURN-ERROR  = AX = 0 
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

CONVERT_BATCH_SIZE      equ     8
CVL_HAS_DEV_TOTAL       equ     00001h          ; for myflags

cProc   ConvertList32,<PUBLIC,NEAR>,<ds,es,si,di>
        parmD   lpSrcList
        parmD   lpDstList
        parmW   ddc
        parmW   Count
        localV  aptl,%((CONVERT_BATCH_SIZE+1)*(SIZE POINTL))
        localD  lDeviceTotal
        localD  lWorldTotal
        localW  nWidths
        localW  myflags
cBegin

        cld
        xor     ax,ax
        mov     myflags,ax
        mov     lWorldTotal.lo,ax
        mov     lWorldTotal.hi,ax

convert_widths_loop:
        lea     di,aptl
        push    ss
        pop     es
        assumes es,nothing
        lds     si,lpSrcList
        assumes ds,nothing
        stosw                           ; transform relative to (0,0)
        stosw
        stosw
        stosw

        mov     cx,Count                ; do at most CONVERT_BATCH_SIZE widths
        cmp     cx,CONVERT_BATCH_SIZE   ; at a time
        jb      @F
        mov     cx,CONVERT_BATCH_SIZE
@@:
        assert  cx,NE,0
        mov     nWidths,cx              ; remember number of widths
        sub     Count,cx
        mov     bx,lWorldTotal.lo       ; DX:BX = width running total
        mov     dx,lWorldTotal.hi
get_width_running_total:
        lodsw                           ; calculate width running total
        add     bx,ax
        lodsw
        adc     dx,ax
        mov     ax,bx                   ; store [width running total,0] vector
        stosw
        mov     ax,dx
        stosw
        xor     ax,ax
        stosw
        stosw
        loop    get_width_running_total
        mov     lpSrcList.off,si        ; remember pointer for next loop
        mov     lWorldTotal.lo,bx       ; remember width running total
        mov     lWorldTotal.hi,dx

        mov     cx,nWidths              ; number of widths
        inc     cx                      ; include (0,0) vector
        mov     ds,CodeData
        assumes ds,Data
        mov     si,ddc
        lea     di,aptl
        xor     ax,ax
        farPtr  myhdc,[si].ddc_hdc.hi,[si].ddc_hdc.lo
        farPtr  world,ax,CVTC_WORLD
        farPtr  device,ax,CVTC_DEVICE
        farPtr  mycount,ax,cx
        farPtr  widths,ss,di
        farPtr  hddcNull,ax,ax
        check   Convert,<hdc,lSrc,lTarg,pptl,cptl,hddc,ulFunN>
        cCall   pfnDefConvert,<myhdc,world,device,widths,mycount,hddcNull,GreConvert>
        or      ax,ax
        jz      convert_widths_exit

        lea     si,aptl                 ; SS:SI -> aptl
        les     di,lpDstList
        assumes es,nothing
        test    myflags,CVL_HAS_DEV_TOTAL
        jnz     @F
        or      myflags,CVL_HAS_DEV_TOTAL
        mov     ax,ss:[si].ptl_x.lo
        mov     dx,ss:[si].ptl_x.hi
        mov     lDeviceTotal.lo,ax      ; initialize previous width running
        mov     lDeviceTotal.hi,dx      ; total
@@:

;/*
;** now we can subtract each running total from the previous one to get
;** the device width
;*/

get_device_widths:
        add     si,SIZE POINTL
        mov     ax,ss:[si].ptl_x.lo
        mov     dx,ss:[si].ptl_x.hi
        mov     bx,ax
        mov     cx,dx
        xchg    bx,lDeviceTotal.lo      ; get previous width running total
        xchg    cx,lDeviceTotal.hi
        sub     ax,bx                   ; get device width
        sbb     dx,cx
        mov     cx,dx                   ; make sure it's in range
        cwd
        cmp     cx,dx
        jnz     convert_widths_overflow
        jcxz    @F                      ; widths cannot be less than zero
        xor     ax,ax                   ; make it zero
@F:
        stosw                           ; store the width
        dec     nWidths
        jnz     get_device_widths
        mov     lpDstList.off,di        ; remember pointer for next loop

        mov     ax,1                    ; assume done
        cmp     Count,0
        jz      convert_widths_exit     ; return success
        jmp     convert_widths_loop


convert_widths_overflow:
        mov     ax,PMERR_COORDINATE_OVERFLOW
        save_error_code
        xor     ax,ax
convert_widths_exit:
        assert  ax,BE,1

cEnd

sEnd    Code

        end
