;*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
;*   03/04/88                     Mike Harringont [mikehar] Re-Added correlation.
;*   02/18/88                     Hock Lee [hockl] Implemented DCR 23814.
;*   01/22/88                     Martin Picha [martinpi] Implemented DCR 23388
;*   11/30/87                     Hock Lee [hockl] Added bounds.
;*   11/07/87                     Hock Lee [hockl] Added lpAttrs and lpXY support.
;*   10/06/87                     Hock Lee [hockl] Added correlation.
;*   07/16/87                     Hock Lee [hockl] Modified for new DDI.
;*   11/26/92                     John Batty. Added DCAF changes.
;*   12/04/92              57695  David A Kerr [dkerr] Check grim reaper flag
;*                                for destination DCs.
;*   07/01/93              68509  Convert points from world to screen before
;*                                coorelation.
;*
;*****************************************************************************/

        OPTION  OLDSTRUCTS

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


        .386

        .xlist

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_GPIERRORS          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
DINCL_BITMAP            equ     1

        include driver.inc
        include oemblt.inc
        include assert.mac
        include extern.inc
        include protos.inc
        include tune.inc
        .list

        .MODEL FLAT

        ASSUME  CS:FLAT,SS:FLAT,DS:FLAT,ES:FLAT

        .DATA

        .CODE

_TUNE SEGMENT DWORD FLAT PUBLIC 'CODE'
        EXTERN  MakePassedColorsValid:NEAR


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
;*
;**************************************************************************/

                OPTION  PROLOGUE:None
                OPTION  EPILOGUE:None
ALIGN 4
CharMarkerPos   PROC    SYSCALL,        hdc     :DWORD, lpXY    :DWORD,
                                        lpRect  :DWORD, Options :DWORD,
                                        N       :DWORD, lpCh    :DWORD,
                                        lpDx    :DWORD, lpAttrs :DWORD,
                                        hddc    :DWORD, FunN    :DWORD


        DebugMsg <CharMarkerPos, WINSTR, CL >

        mov     ecx,OFFSET DDC.ddc_ma

        jmp     CharGenericPos

CharMarkerPos   ENDP

ALIGN 4
CharStringPos   PROC    SYSCALL         hdc     :DWORD, lpXY    :DWORD,
                                        lpRect  :DWORD, Options :DWORD,
                                        N       :DWORD, lpCh    :DWORD,
                                        lpDx    :DWORD, lpAttrs :DWORD,
                                        hddc    :DWORD, FunN    :DWORD

        DebugMsg <CharStringPos, WINSTR, CL >

        mov     ecx,OFFSET DDC.ddc_ca

        jmp     CharGenericPos

CharStringPos   ENDP

                OPTION  PROLOGUE:PROLOGUEDEF
                OPTION  EPILOGUE:EPILOGUEDEF
;/***************************************************************************
;*
;* FUNCTION NAME = CharGenericPos
;*
;* DESCRIPTION   = Write a char with the specified attributes
;*
;* INPUT         = hdc     :DWORD
;*                 lpXY    :DWORD
;*                 lpRect  :DWORD
;*                 N       :DWORD
;*                 lpDx    :DWORD
;*                 hddc    :DWORD
;*                 Options :DWORD
;*                 lpCh    :DWORD
;*                 lpAttrs :DWORD
;*                 FunN    :DWORD
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = EAX <> 0
;* RETURN-ERROR  = EAX = 0
;*
;**************************************************************************/
ALIGN 4
CharGenericPos  PROC    SYSCALL USES edi esi EBX, hdc     :DWORD, lpXY    :DWORD,
                                                lpRect  :DWORD, Options :DWORD,
                                                N       :DWORD, lpCh    :DWORD,
                                                lpDx    :DWORD, lpAttrs :DWORD,
                                                hddc    :DWORD, FunN    :DWORD
        LOCAL   nChars          :DWORD
        LOCAL   wOptions        :DWORD
        LOCAL   ddc_screen_x    :DWORD
        LOCAL   ddc_screen_y    :DWORD
        LOCAL   ddc_world_x     :DWORD
        LOCAL   ddc_world_y     :DWORD
        LOCAL   pXY             :DWORD
        LOCAL   cs_width        :DWORD
        LOCAL   cs_concat       :DWORD
        LOCAL   cs_height       :DWORD
        LOCAL   exttextout_args :eto_args
        LOCAL   StartXY         :POINTL
        LOCAL   TempRect        :RECTL
        LOCAL   OpaqueRect      :RECTL
        LOCAL   npAttrs         :DWORD  ;which attribute bundle to use
        LOCAL   old_sp          :DWORD
        LOCAL   return_code     :DWORD  ;Return code in case correlation
        LOCAL   mytemprect      :DWORD  
                                        
                                        
                                        
                                        
                                        
        LOCAL   rectangle[2]    :RECTL
ifdef DCAF                                                           ;          
        LOCAL   rclBounds       :RECTL  ; use this when calculating  ;          
                                        ; screen bounds              ;          
endif ; DCAF                                                         ;          


        ddc?    hddc

        mov     npAttrs,ecx              ;Save attribute bundle pointer

        errnz   OFFSET CHAR_ATTRS.ca_ba-OFFSET MARKER_ATTRS.ma_ba

        errnz   OFFSET CHAR_ATTRS.ca_pFont-OFFSET MARKER_ATTRS.ma_pFont

        errnz   OFFSET CHAR_ATTRS.ca_fs-OFFSET MARKER_ATTRS.ma_fs

        cld
        mov     esi,hddc
        mov     edx,esi
        ENTER_DRIVER2
        jc      csp_exit_no_lock        ; DX:AX = 0 on error

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

        no_path_area    CharStringPos_error,area,error

        ASSUME  esi:PTR DDC

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

;/*            Check grim reaper flag if device is our screen. We can't
;** allow drawing to the screen when we don't own the glass.
;*/
        test    [esi].DDC.ddc_fb,DDC_DEVICE ;Check destination   ;          
        jz      reaper_check_done           ;Not our screen      ;          
        cmp     fGrimReaper,WE_BE_DEAD      ;If we're dead, then ;          
        jne     reaper_check_done           ;clear COM_DRAW flag ;          
        and     FunN, NOT COM_DRAW                               ;          
                                                                 ;          
reaper_check_done:                                               ;          

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

        mov     eax,PMERR_INV_LENGTH_OR_COUNT
        cmp     N,(MAX_CHARSTRING_LENGTH+1) AND 0FFFFh
        jge     CharStringPos_error_relay

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

        mov     ebx,npAttrs
        test    FunN,COM_PATH
        jz      its_ok

        ASSUME  ebx:PTR CHAR_ATTRS

        test    [esi][ebx].ca_fs,CA_VECTOR
        jz      its_not_ok
        test    Options,CHS_CLIP+CHS_OPAQUE     ; no rectangle allowed
        jz      its_ok

its_not_ok:

        mov     eax,PMERR_INV_IN_PATH

CharStringPos_error_relay:

        jmp     CharStringPos_error
ALIGN 4

its_ok:
        test    FunN,COM_DEVICE
        jnz     normal                  ; we are forced to go through with it.
        test    [esi][ebx].ca_fs,CA_VECTOR+CA_PREC_NOT_1+CA_STRIKEOUT+CA_UNDERSCORE+CA_ITALIC+CA_BOLD+CA_DIRECTION
        jnz     goto_engine
        cmp     ebx,OFFSET DDC.ddc_ma
        jz      @F                      ; no spacing or alignment for markers

        assert  ebx,E,OFFSET DDC.ddc_ca

        mov     eax,[esi].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

        mov     eax,[esi].ddc_ca.ca_fxExtra
        or      eax,[esi].ddc_ca.ca_fxBreakExtra
        jnz     goto_engine             ; ask engine to do text spacings
@@:
        test    Options,NOT OK_CHS_FLAGS
        jnz     goto_engine
        test    WORD PTR Options[2],NOT 0
        jz      normal

goto_engine:

        call    leave_driver

        pop     ebx
        pop     esi
        pop     edi
        leave
        jmp     pfnDefCharStringPos
ALIGN 4

normal:

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

        mov     ebx,[esi].ddc_prddc

        ASSUME  ebx:PTR RDDC

        mov     eax,[ebx].rddc_ptsCurPos.ptl_x
        mov     ecx,[ebx].rddc_ptsCurPos.ptl_y
        test    Options,CHS_START_XY
        jz      no_start_xy
        mov     ebx,lpXY

        ASSUME  ebx:PTR POINTL

        mov     eax,[ebx].ptl_x
        mov     ecx,[ebx].ptl_y

no_start_xy:

        mov     StartXY.ptl_x,eax
        mov     StartXY.ptl_y,ecx

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

        ASSUME  esi:PTR DDC, edi:PTR DDC

        mov     eax,[esi].ddc_iSysClr
        lea     edi,ddcInit

        cmp     eax,[edi].ddc_iSysClr
        je      @F

        INVOKE  PropagateSysClrChange
@@:

        mov     ebx,npAttrs

        ASSUME  ebx:PTR CHAR_ATTRS

        test    Options,CHS_ATTR_INFO
        jnz     csp_passed_attrs
        test    [esi][ebx].ca_ba.ba_fb,BA_CLR_INVALID or BA_CLR_BACK_INV
        jz      csp_colors_are_valid
        lea     ebx,[esi][ebx].ca_ba

        INVOKE  MakeColorsValid

        jns     csp_colors_are_valid

csp_invalid_colors:

        jmp     CharStringPos_bad_exit  ;Error code has been logged
ALIGN 4

csp_passed_attrs:

        mov     edi,lpAttrs             ;ES:DI --> passed colors
        lea     ebx,[esi][ebx].ca_ba    ;BX = attribute bundle in ddc
        call    MakePassedColorsValid
        js      csp_invalid_colors

csp_colors_are_valid:

;/*
;** Do Correlation
;*/

        test    FunN,COM_CORRELATE
        jnz     csp_corr
        jmp     csp_no_correlation
ALIGN 4

        public  csp_corr

csp_corr::


        DebugMsg <csp_corr, WINSTR, CL >

        lea     edi,rectangle
        mov     mytemprect,edi

        test    Options,CHS_CLIP+CHS_OPAQUE
        jz      csp_corr_check_box

;/*
;** make a copy of lpRect
;*/

        mov     edx,esi
        mov     ecx,size RECTL/4
        mov     esi,lpRect
        rep     movsd
        mov     esi,edx

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

csp_rectl_converted:

        mov     edi,FunN

        INVOKE  intersect_with_corr, mytemprect

        or      eax,eax
        jz      csp_corr_check_clip

        INVOKE  PFNDefRectVisible PTR pfnDefRectVisible,
                hdc, mytemprect, hddc, NGreRectVisible

        and     eax,2
        jnz     csp_save_rc

csp_corr_check_clip:

        test    Options,CHS_CLIP
        jnz     csp_no_correlation_sp

csp_corr_check_box:

;*   07/01/93              68509    BEGIN

        test    FunN,COM_TRANSFORM
        jz      @F
        test    Options,CHS_START_XY
        jz      @F
        call    xform_passed_xy
        or      eax,eax
        jz      csp_save_rc
@@:

;*   07/01/93              68509    END

        mov     eax,FunN
        and     eax,(NOT COM_TRANSFORM) OR COM_DEVICE
        or      eax,NGreQueryTextBox

        INVOKE  PFNDefQueryTextBox PTR pfnDefQueryTextBox,
                hdc, N, lpCh, 3, mytemprect,
                hddc, eax

        mov     edi,mytemprect

        ASSUME  edi:PTR POINTL

        add     edi,size POINTL

;/*
;** make coordinates cp relative
;*/

        mov     eax,StartXY.ptl_x
        mov     ecx,StartXY.ptl_y

        add     [edi].ptl_x,eax
        add     [edi].ptl_y,ecx
        add     edi,size POINTL
        add     [edi].ptl_x,eax
        add     [edi].ptl_y,ecx

        sub     edi,size POINTL
        mov     ebx,edi
        mov     edi,FunN                        ;pass on COM_* flags

        INVOKE  intersect_with_corr, ebx

        or      eax,eax
        jz      csp_no_correlation_sp
        add     eax,eax

csp_save_rc:

        mov     return_code,eax

csp_no_correlation_sp:

csp_no_correlation:

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

        test    [esi].ddc_fb,DDC_PRESENT         ; If no surface, then
        jnz     csp_have_surface
        mov     eax,PMERR_BITMAP_NOT_SELECTED   ; bail out with error
        jmp     CharStringPos_error
ALIGN 4

csp_have_surface:

        mov     ebx,[esi].ddc_npsd                ; DS:BX -> surface

        ASSUME  ebx:PTR SURFACE

        test    [ebx].sd_fb,SD_DEVICE            ; is it our screen?
        jnz     csp_have_dev                    ;  yes    CR!!!

csp_have_dev:

        mov     exttextout_args.eto_lpDestDev,ebx

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

        test    FunN,COM_TRANSFORM              ; FunN - MSW is commands
        jz      no_starting_xy
        test    Options,CHS_START_XY
        jz      no_starting_xy
        call    xform_passed_xy
        or      eax,eax
        jz      some_label

no_starting_xy:

        test    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.
;*/

        push    esi

        mov     esi,lpRect
        lea     edi,TempRect
        mov     lpRect,edi
        mov     ecx,(SIZE RECTL) / 4
        rep     movsd                   ; fill in temporary rectangle.
        pop     esi

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

some_label:

        jmp     CharStringPos_exit
ALIGN 4

no_increment_vector:

        xor     eax,eax
        jmp     fill_eto_width_vector
ALIGN 4

get_increment_vector:

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

        test    Options,CHS_VECTOR
        jz      no_increment_vector             ; no increment vector

        mov     ecx,N
        jecxz   no_increment_vector             ; no increment vector


        shl     ecx,2
        sub     esp,ecx                         ; allocate space for widths
        mov     edi,esp
        shr     ecx,2                           ; CX = count

        test    [esi].ddc_fb,DDC_UNIT_XFORM
        jz      cnvlst_complex_xform            ; not a identity transform

        push    esi
        push    edi

        mov     esi,lpDx

        assert  ecx,NE,0

Cnvlst_loop:

        lodsd
        movsx   EDX,AX
        cmp     EDX,EAX
        jne     cnvlst_overflow
        and     eax,0000ffffh           ; width cannot be less than zero
        stosd                           ; make it zero
        loop    Cnvlst_loop
        jmp     cnvlst_restore_registers
ALIGN 4

cnvlst_overflow:

        mov     eax,PMERR_COORDINATE_OVERFLOW

        save_error_code

        mov     return_code,0                   ; error

        errn$   cnvlst_restore_registers

cnvlst_restore_registers:

        pop     edi                             ; restore registers
        pop     esi
        jmp     cnvlst_done
ALIGN 4

cnvlst_complex_xform:

        INVOKE  ConvertList32, lpDx, edi, esi, ecx

        or      eax,eax
        jnz     cnvlst_done
        mov     return_code,eax                 ; error

cnvlst_done:

        ddc?    esi

        mov     eax,edi

fill_eto_width_vector:

        mov     exttextout_args.eto_lpDX,eax

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

        mov     eax,lpCh
        mov     exttextout_args.eto_lpString,eax
        mov     eax,N                           ; Count
        mov     exttextout_args.eto_count,eax

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

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

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

        mov     edi,exttextout_args.eto_lpDestDev    ; ES:SI -> SD

        ASSUME  edi:PTR SURFACE

        mov     edx,[edi].sd_cy                ; DX = height
        mov     eax,edx                         ; flip target point
        sub     eax,StartXY.ptl_y

;/*
;** 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     ebx,[esi].ddc_ca.ca_usTextAlign
        cmp     bh,(TA_TOP shr 8)
        jne     baseline_align
        test    FunN,COM_DEVICE
        jz      no_baseline_align

baseline_align:

        mov     ebx,npAttrs

        ASSUME  ebx:PTR CHAR_ATTRS

        mov     edi,[esi][ebx].ca_pFont             ; ES:DI -> font

        ASSUME  edi:PTR FOCAFONT

        movsx   ebx,[edi].ff_fmMetrics.foca_yMaxAscender
        sub     eax,ebx
                                                ; make it baseline relative!
no_baseline_align:

        mov     exttextout_args.eto_y,eax

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

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

        xor     eax,eax
        xor     ebx,ebx

        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
        mov     ebx,lpRect

        ASSUME  ebx:PTR RECTL

        mov     eax,[ebx].rcl_xLeft             ; X1
        mov     OpaqueRect.rcl_xLeft,eax
        mov     eax,[ebx].rcl_xRight            ; X2
        mov     OpaqueRect.rcl_xRight,eax

        mov     eax,edx                         ; flip opaque rectangle
        ;SEL 1-27-92
        sub     ax,WORD PTR [ebx].rcl_yBottom[0]        ;   y1' = (ymax - y2)
        movsx   EAX,AX
        jno     @f                              ; check possibility of
        mov     eax,MAXSHORT                    ; overflow after flipping
@@:

        sub     edx,[ebx].rcl_yTop
        mov     OpaqueRect.rcl_yTop,eax         ;   y2' = (ymax - y1)
        mov     OpaqueRect.rcl_yBottom,edx
        lea     ebx,OpaqueRect

no_opaque_rect:

        mov     exttextout_args.eto_lpOpaqueRect,ebx

ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** At this point ebx holds either the address of OpaqueRect         ;          
;** if an opaque rectangle is present,                               ;          
;** or NULL if there was no opaque rectangle.                        ;          
;**                                                                  ;          
;** The following lines are omitted because they are moved later     ;          
;** in the file.                                                     ;          
;*/                                                                  ;          
else ; DCAF                                                          ;          
        xor     eax,eax
        xor     ebx,ebx
        test    ch,CHS_CLIP                     ; is clip rectangle present?
        jz      no_clip_rect                    ;   no
        mov     ebx,lpRect

no_clip_rect:
endif ; DCAF                                                         ;          

        movzx   ecx,cl
        mov     exttextout_args.eto_Options,ecx

        mov     ecx,npAttrs
        mov     exttextout_args.eto_npAttrs,ecx
        mov     exttextout_args.eto_lpDDC,esi

ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** There are two possible approaches to when we could calculate     ;          
;** the bounds - either before or after we do the drawing.           ;          
;** The non-DCAF version of the driver chose to do the bounds        ;          
;** after the drawing.                                               ;          
;** In the DCAF version we would like to have the bounding           ;          
;** rectangle available whilst we are doing the actual drawing       ;          
;** (since we accumulate SCREEN bounds in the enum_clip_rect         ;          
;** loop), so we want to do bounds before drawing.                   ;          
;** Thus we omit a section of code here, and re-include it after     ;          
;** we have done the bounds.                                         ;          
;*/                                                                  ;          
else ; DCAF                                                          ;          
;/*
;**  check COM_DRAW bit
;*/

        mov     cl,[esi].ddc_fb
        and     ecx,DDC_VISIBLE
        and     cx,WORD PTR FunN[2]     ; FunN - MSW is commands
        jz      csp_drawn               ; skip draw, bounds, and cp update!

        .errnz  COM_DRAW SHR 16 - DDC_VISIBLE

;/*
;**  enumerate the clip rectangles
;*/

        mov     ecx,RECTDIR_LFRT_BOTTOP-1 ;Favored direction
        lea     edi,exttextout_args

        INVOKE  enumerate_clip_rects, esi, ebx, ADDR do_exttextout

csp_drawn:
endif ; DCAF                                                         ;          

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

ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** We need to save these options since they get overwritten when    ;          
;** we work out the width and height                                 ;          
;*/                                                                  ;          
        push    exttextout_args.eto_Options                          ;          
endif ; DCAF                                                         ;          
        test    Options,CHS_LEAVEPOS
        jz      get_width_height
ifdef DCAF                                                           ;          
        test    FunN, COM_BOUND+COM_ALT_BOUND+COM_SCR_BOUND          ;          
else ; DCAF                                                          ;          
        test    FunN,COM_BOUND+COM_ALT_BOUND
endif ; DCAF                                                         ;          
        jz      got_extent                      ; no need to get extent

get_width_height:

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

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

        sub     esp,SIZE eto_args
        mov     edi,esp
        lea     esi,exttextout_args

        .errnz  (SIZE eto_args) and 3

        mov     ecx,(SIZE eto_args) / 4
        cld
        rep     movsd

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

        call    ExtTextOut                      ; DX = Height, AX = Extent
        ADD     ESP,SIZE eto_args               ; SEL 1-26-92: Added this line
        pop     esi                             ; b/c it was hosing the
        mov     cs_width,eax                    ; stack!!
        mov     cs_concat,ebx
        mov     cs_height,edx
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Re-negate the count, to get back to a positive number.           ;          
;** Note that the path through this code when the count is zero      ;          
;** only does a single negate.                                       ;          
;*/                                                                  ;          
        neg     exttextout_args.eto_count       ; restore positive count ;          
endif ; DCAF                                                         ;          

got_extent:
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Restore this value that was saved above                          ;          
;*/                                                                  ;          
        pop     exttextout_args.eto_Options                          ;          
endif ; DCAF                                                         ;          

        public  csp_check_bounds

csp_check_bounds::


        DebugMsg <csp_check_bounds, WINSTR, CL >

ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Check again to see if it was because of the need to              ;          
;** update the current position, or because we are calculating       ;          
;** bounds that we determined the text bounding rectangle.           ;          
;*/                                                                  ;          
        test    FunN,COM_BOUND+COM_ALT_BOUND+COM_SCR_BOUND           ;          
else ; DCAF                                                          ;          
        test    FunN,COM_BOUND+COM_ALT_BOUND
endif ; DCAF                                                         ;          
        jnz     csp_do_bounds           ; do bounds
ifdef DCAF                                                           ;          
        jmp     csp_draw                ; skip bounds                ;          
else ; DCAF                                                          ;          
        jmp     csp_update_position     ; skip bounds
endif ; DCAF                                                         ;          
ALIGN 4

csp_do_bounds:

ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** For DCAF we require a single bounding rectangle.                 ;          
;** The non DCAF case took the easier route of calculating the       ;          
;** bound for each component drawn (ie opaque rect and text string)  ;          
;** and if necessary making two calls to the accumulation function   ;          
;**                                                                  ;          
;** Thus for DCAF we need to reorder code here again.                ;          
;** We calculate the complex bit first (the text bound), then        ;          
;** simply add the opaque rectangle to it later if needed.           ;          
;**                                                                  ;          
;** Thus omit this bit of opaque rectangle code here and include     ;          
;** it again later.                                                  ;          
;*/                                                                  ;          
else ; DCAF                                                          ;          
        test    Options,CHS_OPAQUE
        jz      csp_do_char_bounds      ; no opaque rectangle for bounds
        mov     edi,lpRect

        ASSUME  edi:PTR RECTL

        mov     eax,[edi].rcl_xLeft
        mov     ebx,[edi].rcl_yBottom
        mov     ecx,[edi].rcl_xRight
        dec     ecx                     ;Bounds are accumulated inclusive
        mov     edx,[edi].rcl_yTop
        dec     edx
        mov     edi,FunN
        call    InnerAccumulateBounds   ;DS:SI == ddc, DI = Command flags
        or      eax,eax
        jz      CharStringPos_exit_relay

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

csp_do_char_bounds:

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

        mov     ebx,npAttrs
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Save esi.                                                        ;          
;*/                                                                  ;          
        push    esi                                                  ;          
endif ; DCAF                                                         ;          

        ASSUME  ebx:PTR CHAR_ATTRS

        mov     edi,[esi][ebx].ca_pFont         ; ES:DI -> font

        ASSUME  edi:PTR FOCAFONT

        movsx   ebx,[edi].ff_fmMetrics.foca_yMaxDescender
        neg     ebx
        mov     eax,StartXY.ptl_x               ; left
        add     ebx,StartXY.ptl_y               ; bottom
        mov     ecx,eax                         ; right
        add     ecx,cs_width
        mov     edx,ebx                         ; top
        add     edx,cs_height

ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** If opaqueing is required expand existing bounds to include       ;          
;** opaque rectangle.                                                ;          
;**                                                                  ;          
;*/                                                                  ;          
        test    Options,CHS_OPAQUE                                   ;          
        jz      consider_clipping                                    ;          
       ;jz      csp_bounds_clipped                                   ;          
        mov     esi,lpRect                                           ;          
        ASSUME  esi:PTR RECTL                                        ;          
        cmp     eax,[esi].rcl_xLeft                                  ;          
        jle     @F                                                   ;          
        mov     eax,[esi].rcl_xLeft                                  ;          
@@:                                                                  ;          
        cmp     ebx,[esi].rcl_yBottom                                ;          
        jle     @F                                                   ;          
        mov     ebx,[esi].rcl_yBottom                                ;          
@@:                                                                  ;          
        cmp     ecx,[esi].rcl_xRight                                 ;          
        jge     @F                                                   ;          
        mov     ecx,[esi].rcl_xRight                                 ;          
@@:                                                                  ;          
        cmp     edx,[esi].rcl_yTop                                   ;          
        jge     @F                                                   ;          
        mov     edx,[esi].rcl_yTop                                   ;          
@@:                                                                  ;          
consider_clipping:                                                   ;          
endif ; DCAF                                                         ;          
                                                                     ;          
;/*
;** The size of the character rectangle must be clipped against the
;** clipping rectangle if it was given.
;*/

        test    Options,CHS_CLIP
        jz      csp_bounds_clipped
        mov     edi,lpRect

        ASSUME  edi:PTR RECTL

        cmp     eax,[edi].rcl_xLeft
        jg      @F
        mov     eax,[edi].rcl_xLeft
@@:
        cmp     ebx,[edi].rcl_yBottom
        jg      @F
        mov     ebx,[edi].rcl_yBottom
@@:
        cmp     ecx,[edi].rcl_xRight
        jl      @F
        mov     ecx,[edi].rcl_xRight
@@:
        cmp     edx,[edi].rcl_yTop
        jl      @F
        mov     edx,[edi].rcl_yTop
@@:

;/*
;** 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:
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Restore esi.                                                     ;          
;*/                                                                  ;          
        pop     esi                                                  ;          
endif ; DCAF                                                         ;          

        cmp     eax,ecx                 ;Make sure we still have a rectangle
        jge     csp_done_bounds
        cmp     ebx,edx
        jge     csp_done_bounds
                                                                     ;          
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Save bounds rectangle for passing to enum_clip_rects later.      ;          
;** We only call enum_clip_rects if the draw bit is actually set!    ;          
;*/                                                                  ;          
        test    FunN, COM_DRAW                                       ;          
        jz      rcl_saved                                            ;          
;/*                                                                  ;          
;** Rectangle is currently in (eax,ebx) (ecx,edx)                    ;          
;*/                                                                  ;          
        mov     rclBounds.rcl_xLeft,eax                              ;          
        mov     rclBounds.rcl_yBottom,ebx                            ;          
        mov     rclBounds.rcl_xRight,ecx                             ;          
        mov     rclBounds.rcl_yTop,edx                               ;          
rcl_saved:                                                           ;          
                                                                     ;          
                                                                     ;          
;/*                                                                  ;          
;** Accumulate USER and GPI bounds if requested                      ;          
;*/                                                                  ;          
        mov     edi,FunN                                             ;          
        test    edi,COM_BOUND+COM_ALT_BOUND                          ;          
        jz      csp_draw                                             ;          
endif ; DCAF                                                         ;          
        dec     ecx                     ;Bounds are accumulated inclusive
        dec     edx
        mov     edi,FunN
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Since the real drawing is performed under the control of         ;          
;** enumerate_clip_rects, SCREEN bounds will be accumulated          ;          
;** there (after clipping).  Thus we dont want to accumulate the     ;          
;** unclipped bounds here, so turn off the COM_SCR_BOUND bit.        ;          
;*/                                                                  ;          
        and     edi, not COM_SCR_BOUND                               ;          
endif ; DCAF                                                         ;          
        call    InnerAccumulateBounds   ;DS:SI == ddc, DI = Command flags
        or      eax,eax
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** We have now moved drawing after bounds accumulation              ;          
;*/                                                                  ;          
        jnz     csp_draw                                             ;          
else ; DCAF                                                          ;          
        jnz     csp_update_position
endif ; DCAF                                                         ;          

CharStringPos_exit_relay:

        jmp     CharStringPos_exit
ALIGN 4
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** This is the relocated (and dramatically changed) version         ;          
;** of the actual drawing call code                                  ;          
;*/                                                                  ;          
csp_draw:                                                            ;          
;/*                                                                  ;          
;** Check COM_DRAW bit                                               ;          
;**                                                                  ;          
;*/                                                                  ;          
        mov     cl,[esi].ddc_fb                                      ;          
        and     ecx,DDC_VISIBLE                                      ;          
        and     cx,WORD PTR FunN[2]     ; FunN - MSW is commands     ;          
        jz      csp_drawn               ; skip drawing               ;          
                                                                     ;          
        .errnz  COM_DRAW SHR 16 - DDC_VISIBLE                        ;          
                                                                     ;          
;/*                                                                  ;          
;** Enumerate the clip rectangles                                    ;          
;** We now pass in the correct bounding rectangle here if we have    ;          
;** actually calculated it.                                          ;          
;**                                                                  ;          
;*/                                                                  ;          
        xor     ebx,ebx             ; Assume a NULL bounding rect    ;          
        test    FunN,COM_BOUND+COM_ALT_BOUND+COM_SCR_BOUND           ;          
        jz      no_minimal_bound                                     ;          
                                                                     ;          
        lea     ebx, rclBounds                                       ;          
        jmp     pbound_in_ebx                                        ;          
                                                                     ;          
no_minimal_bound:                                                    ;          
;/*                                                                  ;          
;** If we didn't calculate a bounding rectangle, but did get         ;          
;** passed a clip rectangle to the CharStringPos call then           ;          
;** use that.                                                        ;          
;*/                                                                  ;          
        test    Options,CHS_CLIP          ; Is clip rect present?    ;          
        jz      pbound_in_ebx             ; Jump if not              ;          
        mov     ebx,lpRect                                           ;          
                                                                     ;          
pbound_in_ebx:                                                       ;          
        mov     ecx,RECTDIR_LFRT_BOTTOP-1 ;Favored direction         ;          
        lea     edi,exttextout_args                                  ;          
                                                                     ;          
ifdef DCAF                                                           ;          
        ; Store the FunN in a global variable so that                ;          
        ; enumerate_clip_rects can check the COM_SCR_BOUNDS flag.    ;          
        mov     eax,FunN                                             ;          
        mov     ulGlobalCOMFlags,eax                                 ;          
endif ;DCAF                                                          ;          
                                                                     ;          
        INVOKE  enumerate_clip_rects, esi, ebx, ADDR do_exttextout   ;          
                                                                     ;          
ifdef DCAF                                                           ;          
        ; Reset the global COM flags so that other (non-DCAF)        ;          
        ; calls to enumerate_clip_rects work OK.                     ;          
        mov     ulGlobalCOMFlags,0                                   ;          
endif ;DCAF                                                          ;          
csp_drawn:                                                           ;          
endif ; DCAF                                                         ;          

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

csp_done_bounds:

csp_update_position:

        test    Options,CHS_LEAVEPOS
        jnz     no_update

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

        mov     eax,cs_concat
        ;SEL 1-27-92
        add     ax,WORD PTR StartXY.ptl_x[0]    ;StartXY is +/- 32K
        MOVSX   EAX,AX
        jno     @F
        mov     eax,PMERR_COORDINATE_OVERFLOW
        jmp     CharStringPos_error
ALIGN 4
@@:
        push    eax                     ;Save ddc_ptsCurPos.pts_x
        push    StartXY.ptl_y           ;Save ddc_ptsCurPos.pts_y
        mov     StartXY.ptl_x,eax

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

        INVOKE  convert_screen_world, ADDR StartXY, 1   ; ds:si = ddc

        or      eax,eax
        jnz     update_cp_in_ddc        ; success
        mov     return_code,eax         ; error
        add     esp,size POINTS         ;Remove cp from stack
        jmp     no_update
ALIGN 4

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

update_cp_in_ddc:

        mov     ebx,[esi].ddc_prddc

        rddc?   ebx

        ASSUME  ebx:PTR RDDC

        pop     [ebx].rddc_ptsCurPos.ptl_y
        pop     [ebx].rddc_ptsCurPos.ptl_x
        mov     eax,StartXY.ptl_x
        mov     [ebx].rddc_ptlWorldPos.ptl_x,eax
        mov     eax,StartXY.ptl_y
        mov     [ebx].rddc_ptlWorldPos.ptl_y,eax

no_update:

        test    Options,CHS_VECTOR
        jz      no_vector_to_free               ; no increment vector

        mov     ecx,N
        shl     ecx,2
        add     esp,ecx                         ; free space for widths!

no_vector_to_free:

CharStringPos_good_exit:

        mov     eax,return_code
        jmp     CharStringPos_exit
ALIGN 4

CharStringPos_error:

        save_error_code

CharStringPos_bad_exit:

        xor     eax,eax

CharStringPos_exit:

        call    leave_driver

csp_exit_no_lock:

        mov     ecx,eax

        RET


ALIGN 4
xform_passed_xy:



        DebugMsg <xform_passed_xy, WINSTR, CL >

        push    esi

        mov     esi,lpXY                        
        lea     edi,StartXY
        mov     ecx,(SIZE POINTL / 4)

        .errnz  SIZE POINTL AND 3

        rep     movsd                   ; fill in temporary rectangle.
        pop     esi

        INVOKE  convert_world_screen, ADDR StartXY, 1   ; ds:si = ddc

exit_xform_passed_xy:

        OPTION  EPILOGUE:None

        ret

CharGenericPos  ENDP
_TUNE ENDS
        OPTION  EPILOGUE:EPILOGUEDEF

;/***************************************************************************
;*
;* 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
;*
;**************************************************************************/

ALIGN 4
CharString      PROC    SYSCALL USES EBX esi edi,   hdc     :DWORD,
                                                N       :DWORD,
                                                lpChars :DWORD,
                                                hddc    :DWORD,
                                                FunN    :DWORD

        INVOKE  CharStringPos, hdc,0, 0, 0, N, lpChars, 0, 0, hddc, FunN

        RET

CharString      ENDP

;/***************************************************************************
;*
;* 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
;*
;**************************************************************************/

CONVERT_BATCH_SIZE      equ     8
CVL_HAS_DEV_TOTAL       equ     00001h          ; for myflags

ALIGN 4
ConvertList32   PROC    SYSCALL USES esi edi,   lpSrcList       :DWORD,
                                                lpDstList       :DWORD,
                                                ddc             :DWORD,
                                                Count           :DWORD
        LOCAL   aptl[CONVERT_BATCH_SIZE+1]      :POINTL
        LOCAL   lDeviceTotal                    :DWORD
        LOCAL   lWorldTotal                     :DWORD
        LOCAL   nWidths                         :DWORD
        LOCAL   myflags                         :DWORD



        DebugMsg <ConvertList32, WINSTR, CL >

        cld
        xor     eax,eax
        mov     myflags,eax
        mov     lWorldTotal,eax

convert_widths_loop:

        lea     edi,aptl
        mov     esi,lpSrcList
        stosd                           ; transform relative to (0,0)
        stosd

        mov     ecx,Count               ; do at most CONVERT_BATCH_SIZE widths
        cmp     ecx,CONVERT_BATCH_SIZE  ; at a time
        jb      @F
        mov     ecx,CONVERT_BATCH_SIZE
@@:
        assert  ecx,NE,0

        mov     nWidths,ecx             ; remember number of widths
        sub     Count,ecx
        mov     ebx,lWorldTotal         ; DX:BX = width running total

get_width_running_total:

        lodsd                           ; calculate width running total
        add     ebx,eax
        mov     eax,ebx                 ; store [width running total,0] vector
        stosd
        xor     eax,eax
        stosd
        loop    get_width_running_total
        mov     lpSrcList,esi           ; remember pointer for next loop
        mov     lWorldTotal,ebx         ; remember width running total

        mov     ecx,nWidths             ; number of widths
        inc     ecx                     ; include (0,0) vector
        mov     esi,ddc
        lea     edi,aptl
        xor     eax,eax

        INVOKE  PFNDefConvert PTR pfnDefConvert,
                [esi].ddc_hdc, CVTC_WORLD, CVTC_DEVICE,
                ADDR aptl, ecx, 0, NGreConvert

        or      eax,eax
        jz      convert_widths_exit

        lea     esi,aptl                        ; SS:SI -> aptl

        ASSUME  esi:PTR POINTL

        mov     edi,lpDstList
        test    myflags,CVL_HAS_DEV_TOTAL
        jnz     @F
        or      myflags,CVL_HAS_DEV_TOTAL
        mov     eax,[esi].ptl_x
        mov     lDeviceTotal,eax        ; initialize previous width running
@@:

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

get_device_widths:

        add     esi,SIZE POINTL
        mov     eax,[esi].ptl_x
        mov     ebx,eax
        xchg    ebx,lDeviceTotal        ; get previous width running total
        sub     eax,ebx                 ; get device width

        or      eax,eax

        jns     @F                      ; widths cannot be less than zero
        xor     eax,eax                 ; make it zero
@@:
        stosd                           ; store the width
        dec     nWidths
        jnz     get_device_widths
        mov     lpDstList,edi           ; remember pointer for next loop

        mov     eax,1                   ; assume done
        cmp     Count,0
        jz      convert_widths_exit     ; return success
        jmp     convert_widths_loop
ALIGN 4


convert_widths_overflow:

        mov     eax,PMERR_COORDINATE_OVERFLOW

        save_error_code

        xor     eax,eax

convert_widths_exit:

        assert  eax,BE,1

        RET

ConvertList32   ENDP

        end
