;*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 strblt function and the ExtendedTextOut                                         
;*              function.                                                        
;*
;* FUNCTIONS    ExtTextOut
;*              InternalExtTextOut
;*              get_clip proc
;*              color_bitmap_opaque
;*              worst_case_ext
;*              abc_extent
;*
;* 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
;*   12/05/89                     John Colleran [johnc] If a DX vector is present
;*                                then don't add in a and c space as it was doing
;*                                before.  This should be rewritten slightly but
;*                                there is no time now.
;*   06/01/88                     Mitchell McLain [gssc!mmm] Cloned for the 8514.
;*****************************************************************************/

        .xlist
        include cmacros.inc
INCL_GRE_CLIP           equ                      1
INCL_FONTFILEFORMAT     equ                      1
INCL_GRE_FONTS          equ                      1
        include pmgre.inc
DINCL_ROPS              equ                      1
        include driver.inc
        include 8514.inc
        include 8514mem.inc
        include strblt8.inc
        include fontseg.inc
        .list
        .286p

        ??_out  strblt

sBegin  PtrData
        externB screen_busy
sEnd

        externFP far_unexclude                        ;Clear excluded area

sBegin  Code
        assumes cs,Code

        externW MyPtrCodeData                     ; for access to pointer data segment

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


        externNP get_device_data
        externNP output_o_rect
        externNP cached_font_strblt              ; Cached font display code
        externNP build_to_screen                 ; Uncached font display code
        externNP build_string                    ; All other font bitmap code

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

        externNP get_mode_and_font
        externNP translate_string
        externNP comp_extent
        externNP quick_clip

page

;/***************************************************************************
;*
;* FUNCTION NAME = ExtTextOut     
;*
;* DESCRIPTION   = 
;*
;*                 Registers Preserved:             
;*                       SI,DI,DS,BP                
;*                 Registers Destroyed:             
;*                       AX,BX,CX,DX,ES,FLAGS       
;*                 Calls:
;*                       unexclude
;*                       ......
;*
;* 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
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        define_frame ExtTextOut                  ;Define frame
cBegin  <nogen>
        xor     cx,cx                             ;Must translate string
        errn$   InternalExtTextOut
cEnd    <nogen>
page

;/***************************************************************************
;*
;* FUNCTION NAME = InternalExtTextOut
;*
;* DESCRIPTION   = 
;*
;*                 Registers Preserved:             
;*                       SI,DI,DS,BP                
;*                 Registers Destroyed:             
;*                       AX,BX,CX,DX,ES,FLAGS       
;*                 Calls:
;*                       unexclude
;*                       ......
;*
;* 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
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        define_frame InternalExtTextOut ;Define frame

cBegin
        mov     fbTranslate,cl                    ;Save string translation flag
        xor     ax,ax                             ;Assume there is no string to output
        mov     lhs,ax
        mov     rhs,ax
        mov     concat,ax
        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 the opaquing rectangle.
;*/

        mov     cx,count
        jcxz    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     cx,count
        or      cx,cx
        jns     short draw_char_string
        mov     bx,di                             ;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_relay                ;No opaque rectangle
        call    get_device_data                  ;Get device specific parameters
        jcxz    exit_strblt                       ;Recursed and are done
        jmp     short 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
        jcxz    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    ax,bx                             ;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:

;/*
;** Draw the string.
;**   Call cached_font_strblt in the following case:
;**       - The font is cached.  This can only happen when screen is destination.
;**
;**   Call build_to_screen in the following case:
;**       - Destination is the screen and the font is not cached.
;**
;**   Call build_string is the following case:
;**       - Destination is a bitmap
;*/

        mov     bh,excel
        mov     ax,CodeOFFSET cached_font_strblt
        test    bh,CACHED_FONT                    ; Font cached?
        jnz     invoke_string_code               ; Yes

        mov     ax,CodeOFFSET build_to_screen
        test    bh,IS_DEVICE                      ; Going to the screen?
        jnz     invoke_string_code               ; Yes

        mov     ax,CodeOFFSET build_string

invoke_string_code:
        call    ax                                ;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,
exit_strblt_relay:
        jz      exit_strblt                       ;  then some cleanup is in order
        ReleaseScreen  strblt                     ; cursor can now interfere again
        call    far_unexclude

exit_strblt:
        mov     ax,rhs
        mov     bx,concat
        mov     cx,lhs
        mov     dx,lfd.font_height

exit_strblt_with_extents:

cEnd

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

        assumes ds,nothing
        assumes es,nothing

        public  get_clip
get_clip proc   near

        cld
        lds     si,lp_clip_rect
        assumes ds,nothing
        mov     ax,eto_options
        and     ax,ETO_OPAQUE_CLIP+ETO_OPAQUE_FILL
        jnz     opaque_rect_needed

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

        mov     ax,ss                             
        mov     es,ax                             
        assumes es,nothing
        lea     di,clip
        movsw                                     ;We assume that we will never
        movsw                                     ;  get passed a null rectangle
        movsw
        movsw
        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     bx                                ;Remove return address
        jmp     exit_strblt

opaque_rect_needed:
        cmp     ax,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:

        les     di,lp_opaque_rect
        assumes es,nothing
        test    ax,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.
;*/

        mov     ax,ds:[si].rcs_pts1.pts_x
        mov     cx,es:[di].rcs_pts1.pts_x
        smax_ax cx
        mov     o_rect.rcs_pts1.pts_x,ax
        mov     clip.rcs_pts1.pts_x,ax

        mov     ax,ds:[si].rcs_pts1.pts_y
        mov     cx,es:[di].rcs_pts1.pts_y
        smax_ax cx
        mov     o_rect.rcs_pts1.pts_y,ax
        mov     clip.rcs_pts1.pts_y,ax

        mov     ax,ds:[si].rcs_pts2.pts_x
        mov     cx,es:[di].rcs_pts2.pts_x
        smin_ax cx
        cmp     ax,clip.rcs_pts1.pts_x
        jle     null_clip_rect                    ;Right side <= left side, doesn't show
        mov     o_rect.rcs_pts2.pts_x,ax
        mov     clip.rcs_pts2.pts_x,ax

        mov     ax,ds:[si].rcs_pts2.pts_y
        mov     cx,es:[di].rcs_pts2.pts_y
        smin_ax cx
        cmp     ax,clip.rcs_pts1.pts_y
        jle     null_clip_rect                    ;Bottom <= top, doesn't show
        mov     o_rect.rcs_pts2.pts_y,ax
        mov     clip.rcs_pts2.pts_y,ax
        jmp     short 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     ax,ds:[si].rcs_pts1.pts_x
        mov     clip.rcs_pts1.pts_x,ax
        mov     cx,es:[di].rcs_pts1.pts_x
        smax_ax cx
        mov     o_rect.rcs_pts1.pts_x,ax

        mov     ax,ds:[si].rcs_pts1.pts_y
        mov     clip.rcs_pts1.pts_y,ax
        mov     cx,es:[di].rcs_pts1.pts_y
        smax_ax cx
        mov     o_rect.rcs_pts1.pts_y,ax

        mov     ax,ds:[si].rcs_pts2.pts_x
        mov     clip.rcs_pts2.pts_x,ax
        mov     cx,es:[di].rcs_pts2.pts_x
        smin_ax cx
        mov     o_rect.rcs_pts2.pts_x,ax

;/*
;** 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     ax,o_rect.rcs_pts1.pts_x

        mov     ax,ds:[si].rcs_pts2.pts_y
        mov     clip.rcs_pts2.pts_y,ax
        jle     null_opaque_rect                 ;Right side <= left side, doesn't show
        mov     cx,es:[di].rcs_pts2.pts_y
        smin_ax cx
        cmp     ax,o_rect.rcs_pts1.pts_y
        jle     null_opaque_rect                 ;Bottom <= top, doesn't show
        mov     o_rect.rcs_pts2.pts_y,ax

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

get_clip        endp
page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  comp_color_byte_interval
;*
;* DESCRIPTION   = A interval will be computed for the string extent.
;*                 A first mask and a last mask will be calculated.
;*
;*                 Registers Preserved:        
;*                       ES,BP                
;*                 Registers Destroyed:        
;*                       AX,BX,CX,DX,SI,FLAGS  
;*
;* INPUT         = BX    = right coordinate (exclusive) 
;*                 DX    = left coordinate  (inclusive) 
;* OUTPUT        = 'C' clear 
;*                 SI    = inner loop count
;*                 AL    = first byte mask (possibly 0)
;*                 AH    = last  byte mask (possibly 0)
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = 'C' if no characters to display 
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  comp_color_byte_interval
comp_color_byte_interval proc near
        cmp     bx,dx                             ; Anything visible?
        jle     ccbi_null_interval               ; No.

        mov     di,dx                             ; Return destination in DI
        xor     si,si                             ; Assume no inner byte count
        xor     ax,ax                             ; Assume no clip masks
        mov     cx,dx                             ; CX = left edge
        sub     cx,x                              ; Anything clipped on left?
        jcxz    ccbi_check_right_side            ; No.

        and     cx,7                              ; Left mask is determined by shifting
        mov     al,0ffh                           ;   0ffh right by the phasing implied
        shr     al,cl                             ;   by the partial left byte.

        add     dx,8                              ; The new left side is now the leftmost
        sub     dx,cx                             ;   bit of the next byte.
        cmp     dx,bx                             ; Left side now > right side?
        jle     ccbi_check_right_side            ; No.

ccbi_non_null_interval:
        clc                                       ; There is something to display,
        ret                                       ;   so go display it.

ccbi_check_right_side:
        mov     si,bx                             ; SI = right edge
        sub     si,dx                             ; SI = string extent
        mov     cx,si
        shr     si,3                              ; Number of whole bytes in extent
        and     cx,7                              ; Any partial right byte?
        jcxz    ccbi_non_null_interval           ; No.

        mov     ah,080h                           ; At least one bit is visible
        sar     ah,cl                             ; Now have right side mask
        jmp     short ccbi_non_null_interval

ccbi_null_interval:
        stc                                       ; Nothing to display
        ret

comp_color_byte_interval    endp
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         = BX = height               
;*                 DX = next scan increment  
;*                 SI = inner loop count     
;*                 ES:DI --> destination     
;*                 DS:DI --> destination     
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  color_bitmap_opaque
color_bitmap_opaque     proc near
        mov     al,byte ptr colors[BACKGROUND] ; The color to fill with
        cld                                       ; Work up in direction
        sub     dx,si                             ; delta to next scan line

cbo_fill_loop:
        mov     cx,si                             ; Fill this many bytes
        rep     stosb                             ; Fill the scan line
        add     di,dx                             ; Offset to next scan line
        dec     bx
        jnz     cbo_fill_loop

        ret

color_bitmap_opaque     endp

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

        assumes ds,nothing
        assumes es,nothing

cProc   worst_case_ext,<NEAR,PUBLIC>
cBegin
        push    lp_dx.off                         ;This routine alters this
        lds     si,lp_string
        assumes ds,nothing
        mov     es,selFont
        assumes es,FontSeg

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

wce_next_char_loop:
        lodsw
        add     ax,ax
        mov     bx,ax
        add     ax,ax
        add     bx,ax
        .errnz  (size FONT_ENTRIES) - 6
        mov     ax,fsCharOffset[bx].fe_width
        mov     dx,ax
        add     dx,char_extra
        test    accel,HAVE_WIDTH_VECT
        jz      wce_have_width
        les     bx,lp_dx
        assumes es,nothing
        mov     dx,es:[bx]                        ;move in user's width
        inc     bx
        inc     bx
        mov     lp_dx.lo,bx
        mov     es,selFont
        assumes es,FontSeg
wce_have_width:

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

        cmp     dx,ax
        jl      wce_concat_is_smaller
        mov     ax,dx                             ;Make concat == desired

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

wce_concat_is_smaller:
        add     ax,di                             ;AX = this rhs
        jo      wce_exit_with_max                ;Overflow, peg rhs
        cmp     ax,rhs
        jl      @F
        mov     rhs,ax                            ;This is a new rhs
@@:
        dec     cx
        jz      wce_last_char                     ;This is the last character
        or      dx,dx
        js      @F                                ;Negative, don't update x
        add     di,dx
@@:
        cmp     dx,ax                             ;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:
        add     di,dx                             ;Set final concatenation point
        jno     wce_exit

wce_exit_with_max:
        mov     di,MAXSHORT
        mov     rhs,di

wce_exit:
        mov     ax,rhs
        mov     cx,lhs
        mov     concat,di
        pop     lp_dx.off
cEnd

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


        assumes ds,nothing
        assumes es,nothing

cProc   abc_extent,<NEAR,PUBLIC>
cBegin
        push    lp_dx.off                         ;This routine alters this
        lds     si,lp_string
        assumes ds,nothing
        mov     es,selFont
        assumes es,FontSeg

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

        lodsw
        imul    bx,ax,size ABC_FONT_ENTRIES
        mov     di,fsCharOffset[bx].abc_a_space
        or      di,di
        jns     @F                                ;Start is negative
        xor     di,di
@@:
        mov     lhs,di
        mov     rhs,di

abc_next_char_loop:
        mov     ax,fsCharOffset[bx].abc_b_space
        mov     dx,ax
        add     dx,char_extra
        add     dx,fsCharOffset[bx].abc_c_space
        test    accel,HAVE_WIDTH_VECT
        jz      abc_still_collecting_width
        les     bx,lp_dx
        assumes es,nothing
        mov     dx,es:[bx]                        ;Move in user's width
        inc     bx
        inc     bx
        mov     lp_dx.lo,bx
        mov     es,selFont
        assumes es,FontSeg
abc_still_collecting_width:
        dec     cx                                ;Ignore next char's "a" space if
        jz      abc_have_c_space                 ;  this is the last character
        xchg    ax,bx                             ;Save real width in BX
        lodsw                                     ;--> next char's data
        imul    ax,size ABC_FONT_ENTRIES; 
        xchg    ax,bx

        test    accel,HAVE_WIDTH_VECT
        jnz     abc_have_c_space
        add     dx,fsCharOffset[bx].abc_a_space
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     dx,ax
        jl      abc_concat_is_smaller
        mov     ax,dx                             ;Make concat == desired

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

abc_concat_is_smaller:
        add     ax,di                             ;AX = this rhs
        jo      abc_exit_with_max                ;Overflow, peg rhs
        cmp     ax,rhs
        jl      @F
        mov     rhs,ax                            ;This is a new rhs
@@:
        jcxz    abc_last_char                     ;This is the last character
        or      dx,dx
        js      @F                                ;Negative, don't update x
        add     di,dx
@@:
        cmp     dx,ax                             ;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:
        add     di,dx                             ;Set final concatenation point
        jno     abc_exit

abc_exit_with_max:
        mov     di,MAXSHORT
        mov     rhs,di

abc_exit:
        mov     ax,rhs
        mov     cx,lhs
        mov     concat,di
        pop     lp_dx.off
cEnd


sEnd    Code

        end
