;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME =  STRBLT.ASM
;*
;* DESCRIPTIVE NAME = Strblt function
;*
;*
;* VERSION      V2.0
;*
;* DATE         03/17/87
;*
;* DESCRIPTION  This module contains the the ExtTextOut function.
;*
;* FUNCTIONS    ExtTextOut
;*
;* 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/17/87                     Written by Walt Moore
;*   05/20/94              82193  Improper conversion from mono byte
;*                                width to color byte width.
;*
;*****************************************************************************/


        .386

        .xlist

INCL_GRE_CLIP           equ    1
INCL_FONTFILEFORMAT     equ    1
INCL_GRE_FONTS          equ    1

        include pmgre.inc

DINCL_ROPS              equ    1
DINCL_ENABLE            equ    1
DINCL_BITMAP            equ    1

        include driver.inc
        include fontmap.inc
        include egafam.inc
        include strblt.inc
        include extern.inc
        include protos.inc

        .list

        .MODEL FLAT

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

        .DATA

;/*
;**       Functions found in STRBLTC.ASM:
;*/

        EXTERN  get_mode_and_font       :NEAR
        EXTERN  translate_string        :NEAR
        EXTERN  comp_extent             :NEAR
        EXTERN  quick_clip              :NEAR
        EXTERN  bitmap_opaque           :NEAR

;/*
;**       Other functions required for ExtTextOut
;*/

        EXTERN  get_device_data         :NEAR
        EXTERN  reset_ega               :NEAR




        .CODE
page

;/***************************************************************************
;*
;* FUNCTION NAME = ExtTextOut
;*
;* DESCRIPTION   =
;*
;*                 Registers Preserved:
;*                       SI,DI,DS,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,ES,FLAGS
;*
;* INPUT         = EGA registers in default state
;* OUTPUT        = AX = rhs of string (zero based)
;*                 BX = concatenation point (zero based)
;*                 CX = lhs of string (zero based)
;*                 DX = Y extent of string  if extent call
;*                 EGA registers in default state
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/



ExtTextOut         PROC SYSCALL USES esi edi,
        lp_DDC        :DWORD, lpDestDev     :DWORD,
        x             :DWORD, y             :DWORD,
        lp_clip_rect  :DWORD, lp_string     :DWORD,
        count         :DWORD, npAttrs       :DWORD,
        lp_dx         :DWORD, lp_opaque_rect:DWORD,
        eto_options   :DWORD
        LOCAL   accel           :BYTE
        LOCAL   excel           :BYTE
        LOCAL   wc_flags        :BYTE
        LOCAL   fbTranslate     :BYTE
        LOCAL   colors          :DWORD
        LOCAL   char_extra      :DWORD
        LOCAL   num_null_pixels :DWORD
        LOCAL   null_char_offset:DWORD
        LOCAL   lhs             :DWORD
        LOCAL   rhs             :DWORD
        LOCAL   concat          :DWORD
        LOCAL   pFont           :DWORD
        LOCAL   lfd             :local_font_def
        LOCAL   clip            :RECTL
        LOCAL   text_bbox       :RECTL
        LOCAL   o_rect          :RECTL
        LOCAL   smart_flags            :BYTE
        LOCAL   special_bm_opaque_color:BYTE
        LOCAL   num_planes             :BYTE
        LOCAL   left_clip_mask         :BYTE
        LOCAL   right_clip_mask        :BYTE
        LOCAL   cell_adjust            :DWORD
        LOCAL   buffer                 :DWORD
        LOCAL   fix_total_bytes        :DWORD
        LOCAL   next_scan              :DWORD
        LOCAL   next_plane             :DWORD
        LOCAL   next_scan_xor          :DWORD
        LOCAL   opaque_routine         :DWORD
        LOCAL   opaque_height          :DWORD
        LOCAL   lp_surface             :DWORD
        LOCAL   temp_off_lp_bits       :DWORD
        LOCAL   scan_start             :DWORD
        LOCAL   clipped_font_height    :DWORD
        LOCAL   amt_clipped_on_top     :DWORD
        LOCAL   clear_stack            :DWORD
        LOCAL   min_stack              :DWORD
        LOCAL   build_proc             :DWORD
        LOCAL   phase                  :DWORD
        LOCAL   x2                     :DWORD
        LOCAL   inner_loop             :DWORD
        LOCAL   clipped_table          :DWORD
        LOCAL   non_clipped_table      :DWORD
        LOCAL   inner_byte_count       :DWORD
        LOCAL   current_lhs            :DWORD
        LOCAL   current_rhs            :DWORD
        LOCAL   wc_opaque_lhs          :DWORD
        LOCAL   wc_opaque_rhs          :DWORD
        LOCAL   string_start_bias      :DWORD
        LOCAL   ret_addr               :DWORD
        LOCAL   unrolled_entry_point   :DWORD
        LOCAL   vect_one_in_first      :DWORD
        LOCAL   vect_two_in_first      :DWORD
        LOCAL   vect_one_in_middle     :DWORD
        LOCAL   vect_two_in_middle     :DWORD
        LOCAL   vect_one_in_last       :DWORD
        LOCAL   vect_two_in_last       :DWORD
        LOCAL   ret_addr_2             :DWORD
        LOCAL   ega_oc_saved_data      :DWORD
        LOCAL   current_banks                           :byte
        LOCAL   bitmap_type                             :byte

        DebugMsg <ExtTextOut STRBLT CLIFFL>

        mov     bitmap_type,0

        xor     ecx,ecx
        mov     fbTranslate,cl                    ;Save string translation flag
        xor     eax,eax                           ;Assume there is no string to output
        mov     lhs,eax
        mov     rhs,eax
        mov     concat,eax
        mov     wc_flags,al
        call    get_mode_and_font

;/*
;** If the character count is negative, then the extent of the string
;** should be calculated.  If positive, then the string should be drawn.
;** If zero, then only output then opaquing rectangle.
;*/

        mov     ecx,count
        jecxz   only_draw_o_rect                 ;No chars, might have opaque rect
        cmp     fbTranslate,0
        jne     strings_been_translated
        call    translate_string                 ;Handle multi-codepage fonts

strings_been_translated:

;/*
;** Since there is some text involved in the operation, compute the extent
;** of the string.  !!! at some point improve on this.  We should only
;** compute the extent if its really needed.  This may be hard to determine
;*/

        mov     accel,bh                          ;Some extent routines will need
        mov     excel,bl                          ;  these values on the frame
        call    comp_extent
        mov     ecx,count
        or      ecx,ecx
        jns     draw_char_string
        mov     ebx,edi                           ;Set concatenation into BX
        jmp     exit_strblt_with_extents

;/*
;** There are no characters to output.  If there is an opaquing rectangle
;** to be filled with the background color, we still must do that.
;*/

only_draw_o_rect:

        call    get_clip                          ;Aborts if NULL clip region
        mov     accel,bh
        mov     excel,bl

text_completely_clipped:

        test    bl,OPAQUE_RECT
        jz      exit_strblt                       ;No opaque rectangle
        call    get_device_data                  ;Get device specific parameters
        jecxz   exit_strblt                       ;Recursed and are done
        jmp     maybe_output_o_rect

;/*
;** Normal text output is to occur.  Perform a quick rejection on the
;** string.  If the string is clipped then we can abort now.
;*/

draw_char_string:

        call    get_clip                          ;Aborts if NULL clip region
        call    quick_clip
        mov     accel,bh
        mov     excel,bl
        test    bl,TEXT_VISIBLE
        jz      text_completely_clipped ;Nothing shows, see about opaque rect
        call    get_device_data                  ;Get device specific parameters
        jecxz   exit_strblt                       ;Recursed and are done

;/*
;** Based on the characteristics of the strblt operation, dispatch to the
;** correct handler.
;*/

        mov     bl,accel                          ;If transparent mode and
        test    bl,IS_OPAQUE                      ;  there is an opaque rectangle
        jnz     draw_dispatch                     ;  then it must be output now.
        mov     al,excel
        test    al,OPAQUE_RECT
        jz      draw_dispatch
        and     al,not OPAQUE_RECT
        mov     excel,al
        xchg    eax,ebx                           ;Need excel in BL
        call    output_o_rect
        mov     bl,accel

draw_dispatch:

        test    excel,OPAQUE_RECT                ;if we have an opaque rect, we
        jnz     have_opaque_rect                 ;  must go through the text drawing code
        test    wc_flags,ROP_IS_NOP              ;if not, and if both mixes are leavealone,
        jnz     strblt_clean_up                  ;  then we can exit now

have_opaque_rect:

        mov     eax,OFFSET build_string
        call    eax                               ;This guy does the real work

maybe_output_o_rect:

        mov     bl,excel
        test    bl,OPAQUE_RECT
        jz      strblt_clean_up
        call    output_o_rect

strblt_clean_up:

        test    excel,IS_DEVICE                  ;If this is the device,
        jz      exit_strblt                       ;  then some cleanup is in order
        call    far_unexclude
        call    reset_ega

exit_strblt:

        mov     eax,rhs
        mov     ebx,concat
        mov     ecx,lhs
        movzx   edx,lfd.font_height

exit_strblt_with_extents:

        ret
page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_clip
;*
;* DESCRIPTION   = Get a clipping rectangle
;*
;*                 Registers Preserved:
;*                       None
;*                 Registers Destroyed:
;*                       AX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = BH = accel flags
;*                 BL = excel flags
;* OUTPUT        = BH = accel flags
;*                 BL = excel flags
;*                 clip   structure initialized
;*                 o_rect structure initialized if opaque rectangle given
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

        PUBLIC  get_clip

get_clip::

        cld
        mov     esi,lp_clip_rect
        mov     eax,eto_options
        and     eax,ETO_OPAQUE_CLIP+ETO_OPAQUE_FILL
        jnz     opaque_rect_needed

;/*
;** Only a clip rectangle is needed.  Simply copy it to the frame and exit.
;*/

        lea     edi,clip
        movsd                                     ;We assume that we will never
        movsd                                     ;  get passed a null rectangle
        movsd
        movsd
        ret

;/*
;** The intersection of the clipping rectangle and the opaque rectangle
;** was null, and we were asked to use the result as the real clipping
;** rectangle.  Nothing to do now but exit.
;*/

null_clip_rect:

        pop     ebx                               ;Remove return address
        jmp     exit_strblt

opaque_rect_needed:

        cmp     eax,ETO_OPAQUE_CLIP+ETO_OPAQUE_FILL ; Opaque rect & clipped?
        jnz     dont_draw_text_opaque           ; No, continue.
        or      bh,IS_OPAQUE                    ; Yes, do fast opaque text

dont_draw_text_opaque:

        mov     edi,lp_opaque_rect
        test    eax,ETO_OPAQUE_CLIP
        jz      opaque_isnt_clip

;/*
;** The opaque rectangle is to be intersected with the clipping region,
;** and the result used as the actual clip region.
;*/

        ASSUME  esi:PTR RECTL, edi:PTR RECTL

        mov     eax,[esi].rcl_xLeft
        mov     ecx,[edi].rcl_xLeft
        smax_ax ecx
        mov     o_rect.rcl_xLeft,eax
        mov     clip.rcl_xLeft,eax

        mov     eax,[esi].rcl_yBottom
        mov     ecx,[edi].rcl_yBottom
        smax_ax ecx
        mov     o_rect.rcl_yBottom,eax
        mov     clip.rcl_yBottom,eax

        mov     eax,[esi].rcl_xRight
        mov     ecx,[edi].rcl_xRight
        smin_ax ecx
        cmp     eax,clip.rcl_xLeft
        jle     null_clip_rect                    ;Right side <= left side, doesn't show
        mov     o_rect.rcl_xRight,eax
        mov     clip.rcl_xRight,eax

        mov     eax,[esi].rcl_yTop
        mov     ecx,[edi].rcl_yTop
        smin_ax ecx
        cmp     eax,clip.rcl_yBottom
        jle     null_clip_rect                    ;Bottom <= top, doesn't show
        mov     o_rect.rcl_yTop,eax
        mov     clip.rcl_yTop,eax
        jmp     see_if_opaque_is_filled

;/*
;** The opaque rectangle is to be intersected with the clipping region.
;** It will not be used as the actual clip rectangle.
;*/

opaque_isnt_clip:

        mov     eax,[esi].rcl_xLeft
        mov     clip.rcl_xLeft,eax
        mov     ecx,[edi].rcl_xLeft
        smax_ax ecx
        mov     o_rect.rcl_xLeft,eax

        mov     eax,[esi].rcl_yBottom
        mov     clip.rcl_yBottom,eax
        mov     ecx,[edi].rcl_yBottom
        smax_ax ecx
        mov     o_rect.rcl_yBottom,eax

        mov     eax,[esi].rcl_xRight
        mov     clip.rcl_xRight,eax
        mov     ecx,[edi].rcl_xRight
        smin_ax ecx
        mov     o_rect.rcl_xRight,eax

;/*
;** We set the condition code for a null oapque rectangle now, but we
;** cannot make the jump until we've saved the bottom of the clip region.
;*/

        cmp     eax,o_rect.rcl_xLeft

        mov     eax,[esi].rcl_yTop
        mov     clip.rcl_yTop,eax
        jle     null_opaque_rect                 ;Right side <= left side, doesn't show
        mov     ecx,[edi].rcl_yTop
        smin_ax ecx
        cmp     eax,o_rect.rcl_yBottom
        jle     null_opaque_rect                 ;Bottom <= top, doesn't show
        mov     o_rect.rcl_yTop,eax

;/*
;** If the user has specified that the opaque rectangle is to be filled,
;** then show that the rectangle is present.
;*/

see_if_opaque_is_filled:

        test    eto_options,ETO_OPAQUE_FILL
        jz      null_opaque_rect
        or      bl,OPAQUE_RECT                    ;Show it really exists

null_opaque_rect:

        ret
page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  color_bitmap_opaque
;*
;* DESCRIPTION   = Fill the given bitmap with all the background color
;*
;*                 Registers Preserved:
;*                       DX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,DI,FLAGS
;*
;* INPUT         = AL = first byte mask
;*                 AH = last byte mask
;*                 BX = height
;*                 DX = next scan increment
;*                 SI = inner loop count
;*                 ES:DI --> destination
;*                 DS:DI --> destination
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        PUBLIC  color_bitmap_opaque

color_bitmap_opaque::

        push    esi                               ; do not disturb...
        ;           shl     esi,3                 ; multiply the length by 8

;/*
;** convert the mask to a bit count
;*/

        movzx   ebx,bx
        call    convert_mask_to_count2
        add     esi,eax

        mov     al,byte ptr colors[BACKGROUND]   ; fetch from the stack
        mov     edx,esi                           ; dx holds full length
        mov     ecx,esi                           ; load length
        movzx   ecx,cx
        jcxz    cobiop_done_2                     ; exit if nop

cobiop_loop_2:

        push    edi
        REP     STOSB                   ; store the new background
        pop     edi
        add     edi,next_scan                     ; advance to next scan line
        mov     ecx,edx                           ; reload length count
        dec     ebx                               ; check for end of fill
        jnz     cobiop_loop_2                     ; loop if not done...

cobiop_done_2:
        pop     esi

        ret

public  convert_mask_to_count2
convert_mask_to_count2::
        push    ebx

        xor     ebx,ebx
        mov     bl,ah                             ;get left first
        add     ebx,offset cnv_msk_table_2
        mov     ah,byte ptr cs:[ebx]
        xor     ebx,ebx
        mov     bl,al                             ;get right first
        add     ebx,offset cnv_msk_table_2
        mov     al,byte ptr cs:[ebx]
        add     al,ah

        pop     ebx
        ret

cnv_msk_table_2 label   byte
        db 0,1,0,2,0,0,0,3,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5
        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6
        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7
        db 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        db 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        db 3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,5,0,0,0,6,0,7,8

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  worst_case_ext
;*
;* DESCRIPTION   = Compute the extent for a string which has a width vector.
;*                 Since it is possible for characters to overlap, the extent
;*                 of the string may be greater than where the next character
;*                 would start (think about a "w" and an "i" which started at
;*                 the same location).
;*
;*                 The actual extent code is a subroutine which can be called
;*                 by the drawing code at output time or the extent code.
;*
;*                 This routine could be called for either a fix/proportional
;*                 font It will not be called for a font with ABC spacing.
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,ES,DS,FLAGS
;*
;* INPUT         = CX = character count, > 0
;* OUTPUT        = AX = rhs (right hand side), zero based
;*                 CX = lhs (left hand side).  zero based
;*                 DI = concatenation point,  zero based
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


                PUBLIC  worst_case_ext

worst_case_ext::

        push    lp_dx                             ;This routine alters this
        mov     esi,lp_string

        xor     edi,edi                           ;Start is 0
        mov     lhs,edi
        mov     rhs,edi

wce_next_char_loop:

        lodsd
        add     eax,eax
        mov     ebx,eax
        add     eax,eax
        add     ebx,eax

        .errnz  (size FONT_ENTRIES) - 6

        mov     eax,pFont

        ASSUME  eax:PTR FONTMAP, ebx:PTR FONT_ENTRIES

        movzx   eax,[eax].fsCharOffset[ebx].fe_width
        mov     edx,eax
        add     edx,char_extra
        test    accel,HAVE_WIDTH_VECT
        jz      wce_have_width

;/*
;** not needed
;*/

        mov     ebx,lp_dx
        mov     edx,DWORD PTR [ebx]               ;Move in user's width
        add     ebx,4
        mov     lp_dx,ebx

wce_have_width:

;/*
;** Just so you know:
;**       AX = real width
;**       CX = character count
;**       DX = desired width
;**       DI = x coordinate for character
;*/

        cmp     edx,eax
        jl      wce_concat_is_smaller
        mov     eax,edx                           ;Make concat == desired

;/*
;** Concatenation point is less than width, will be going backwards
;** unless this is the last character.
;*/

wce_concat_is_smaller:

        add     eax,edi                           ;AX = this rhs
        jo      wce_exit_with_max                ;Overflow, peg rhs
        cmp     eax,rhs
        jl      @F
        mov     rhs,eax                           ;This is a new rhs
@@:
        or      edx,edx
        js      @F                                ;Negative, don't update x
        add     edi,edx
@@:
        dec     ecx
        jz      wce_last_char                     ;This is the last character
        cmp     edx,eax                           ;If concat < real width
        jge     wce_next_char_loop               ;  then we stepped back
        or      wc_flags,STEPPED_BACK
        jmp     wce_next_char_loop


;/*
;** This is the last character, so don't check for going backwards
;*/

wce_last_char:

        jno     wce_exit

wce_exit_with_max:

        mov     edi,MAXSHORT
        mov     rhs,edi

wce_exit:
        mov     eax,rhs
        mov     ecx,lhs
        mov     concat,edi
        pop     lp_dx

        ret

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  abc_extent
;*
;* DESCRIPTION   = Compute the extent for an abc spaced font.
;*
;*                 Since it is possible for characters to overlap, the extent
;*                 of the string may be greater than where the next character
;*                 would start (think about a "w" and an "i" which started at
;*                 the same location).
;*
;*                 The actual extent code is a subroutine since which can be
;*                 called by the drawing code at output time or the extent code.
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,ES,DS,FLAGS
;*
;* INPUT         = CX = character count, > 0
;*
;* OUTPUT        = AX = rhs (right hand side), zero based
;*                 CX = lhs (left hand side).  zero based
;*                 DI = concatenation point,  zero based
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                       ------Pseudo-Code----
;*  {
;*
;*  // ABC spaced text extent.  As we compute the extent of the string, we
;*  // also want to keep track to see if we ever step back over the previous
;*  // character.  If we do, we will remember this and inform the caller
;*  // that we did.  This is required when opaquing is involved.
;*
;*
;*  // lhs is the left hand side of the string.  The only way this can be
;*  // negative is if the first character's A space is negative.  Otherwise
;*  // it will be zero.
;*
;*  fbStepBack = FALSE;
;*  lhs = min(0,aSpace[char[0]]);
;*
;*
;*  // x is the starting position of the current character.  rhs is the
;*  // rightmost side of the string.  This can be greater than the current x.
;*
;*  x = rhs = lhs;
;*
;*
;*  // Loop through all the characters computing the width of the character
;*  // and the next character's staring location.  Two values are of interest
;*  // to us.  The first is the actual width of the character, as defined by
;*  // the "b" space.  This many pels will always be drawn regardless of any
;*  // other width adjustments.
;*  //
;*  // The second is the concatenation point.  This is a combination of any
;*  // character extra, any user supplied width, this character's "c" space,
;*  // and the next character's "a" space (if there is a next character).
;*  // When a user width is given, it overrides the width of the character.
;*  //
;*  // We cheat a little and fold the next charcater's "a" space into the
;*  // current character's "c" space.  This will generally be a positive
;*  // number so we will stand a better chance of avoiding the overlapping
;*  // code.
;*  //
;*  // One restriction is that the next character cannot start before the
;*  // current character.  This restriction is mostly for clipping purposes,
;*  // and we ignore it when computing the concatenation point for the very
;*  // last character.
;*
;*  for (n=0;n<count;n++)
;*  {
;*      concat = width = bSpace[char[n]];   // Always positive
;*      concat += char_extra + cSpace[char[n]];
;*      if (WIDTH_VECTOR)
;*          concat = pdx[n] - width;
;*      if (n < count-1)
;*          concat += aSpace[char[n+1]];
;*
;*  // If the concatenation point is greater than or equal to the "b" space
;*  // of the character, then we won't be stepping backwards.  If it is less
;*  // than the width, then the next character will overlap this one.
;*
;*      if (concat >= width)                        // Concat must be + since width is +
;*          rhs = max(rhs,(x += concat));
;*      else
;*      {
;*          rhs = max(rhs,(x + width));
;*          if (n < count-1)                          // If not the last character
;*          {                                         // want to remember we stepped back
;*              fbStepback = TRUE;                   //  and limit next char's start
;*              x += max(0,concat);
;*          }
;*          else
;*              x += concat;
;*      }
;*  }
;*
;**************************************************************************/

                PUBLIC  abc_extent
abc_extent::

        push    lp_dx                             ;This routine alters this
        mov     esi,lp_string

;/*
;** Compute starting X coordinate which is also initial lhs and rhs
;*/

        lodsd
        imul    ebx,eax,size ABC_FONT_ENTRIES
        mov     edi,pFont

        ASSUME  edi:PTR FONTMAP, ebx:PTR ABC_FONT_ENTRIES

        movzx   edi,[edi].fsCharOffset[ebx].abc_a_space
        or      di,di
        jns     @F                                ;Start is negative
        xor     edi,edi
@@:
        mov     lhs,edi
        mov     rhs,edi

abc_next_char_loop:

        push    edi
        mov     edi,pFont
        movzx   eax,[edi].fsCharOffset[ebx].abc_b_space
        mov     edx,eax
        add     edx,char_extra
        movzx   edi,[edi].fsCharOffset[ebx].abc_c_space
        add     edx,edi
        test    accel,HAVE_WIDTH_VECT
        jz      abc_still_collecting_width
        mov     ebx,lp_dx
        mov     edx,DWORD PTR [ebx]               ;move in user's width
        inc     ebx
        inc     ebx
        mov     lp_dx,ebx

abc_still_collecting_width:

        dec     ecx                               ;Ignore next char's "a" space if
        jz      abc_have_c_space                 ;  this is the last character
        xchg    eax,ebx                           ;Save real width in BX
        lodsd                                     ;--> next char's data
        imul    eax,size ABC_FONT_ENTRIES; 
        xchg    eax,ebx

        pop     edi
        test    accel,HAVE_WIDTH_VECT
        jnz     abc_have_c_space
        push    edi
        mov     edi,pFont
        movzx   edi,[edi].fsCharOffset[ebx].abc_a_space
        add     edx,edi
        pop     edi

abc_have_c_space:

;/*
;** Just so you know:
;**       AX = real width
;**       BX = offset for the next character
;**       CX = character count -1
;**       DX = desired width
;**       DI = x coordinate for character
;*/

        cmp     edx,eax
        jl      abc_concat_is_smaller
        mov     eax,edx                           ;Make concat == desired

;/*
;** Concatenation point is less than width, will be going backwards
;** unless this is the last character.
;*/

abc_concat_is_smaller:

        add     eax,edi                           ;AX = this rhs
        jo      abc_exit_with_max                ;Overflow, peg rhs
        cmp     eax,rhs
        jl      @F
        mov     rhs,eax                           ;This is a new rhs
@@:
        or      dx,dx
        js      @F                                ;Negative, don't update x
        add     edi,edx
@@:
        jcxz    abc_last_char                     ;This is the last character
        cmp     edx,eax                           ;If concat < real width
        jge     abc_next_char_loop               ;  then we stepped back
        or      wc_flags,STEPPED_BACK
        jmp     abc_next_char_loop


;/*
;** This is the last character, so don't check for going backwards
;*/

abc_last_char:

        jno     abc_exit

abc_exit_with_max:

        mov     edi,MAXSHORT
        mov     rhs,edi

abc_exit:

        mov     eax,rhs
        mov     ecx,lhs
        mov     concat,edi
        pop     lp_dx

        ret

ExtTextOut      ENDP

        end
