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

page
 
 
        ALIGN   4

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

        DebugMsg <ExtTextOut STRBLT CLIFFL>

ifdef DBCS  ; DBCS device font support     ;IBMJ
; Here we will check cache depth and string length if the currently
; selected font segment is cached fontseg and font is huge font.
; If the specified string is too long to fit in the cached fontseg,
; we will devide the string into sub-strings and draw them in order.

; enter 0,0   ; start accessing parameters

; cmp npAttrs,ddc_ma  ; are we drawing marker?
; je draw_at_once  ; yes...

; CHARACTERS ONLY:
; Following ca_* fields are defined only for characters.

 mov ebx,lp_DDC  ; EBX --> ddc

 ASSUME ebx:PTR DDC

ifdef TMP
; Temporary Fix
; This portion is equivalent to following two instructions.
;
; cmp npAttrs,ddc_ma  ; are we drawing marker?
; je draw_at_once  ; yes...
;
 lea ecx,[ebx].ddc_ma ; ECX --> ddc_ma
 mov edx,ebx   ; EDX --> ddc
 add edx,npAttrs  ; EDX --> ddc_ca or ddc_ma ...
 cmp edx,ecx   ; are we drawing marker ?
 jz draw_at_once  ; yes...
endif;TMP

 test [ebx].ddc_ca.ca_fsFont,NLSCA_FONT_HUGE
 jz draw_at_once  ; font is not a cached huge font
 mov ecx,count
 or ecx,ecx
 jns @f
 neg ecx
@@: cmp [ebx].ddc_ca.ca_cCache,ecx
 jae draw_at_once  ; the string fits in ths cache

devide_string:
; leave    ; end accessing paramaters
 push esi
 push edi
 call SubStrExtTextOut ; devide the string into parts...
 pop edi
 pop esi
 jmp exit_strblt_with_extents

draw_at_once:
; leave    ; end accessing paramaters
endif;DBCS        ;IBMJ

        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

        ALIGN   4

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

        ALIGN   4

 
;/*
;** 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
ifndef DBCS ; DBCS device font support     ;IBMJ
        xor     bl,WIDTH_IS_8
        test    bl,HAVE_WIDTH_VECT+WIDTH_IS_8+HAVE_CHAR_EXTRA
        jnz     invoke_string_code
        mov     eax,OFFSET fixed_pitch_strblt
endif;DBCS        ;IBMJ
 
invoke_string_code:

        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


        ALIGN   4

get_clip::
 
        DebugMsg <get_clip STRBLT CLIFFL>

        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

        ALIGN   4

 
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

        ALIGN   4

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


        ALIGN   4

color_bitmap_opaque::
 
        DebugMsg <color_bitmap_opaque STRBLT CLIFFL>

        mov     cl,num_planes                     ;Loop count
        mov     ch,byte ptr colors[BACKGROUND]
 
color_bitmap_opaque_loop:

        shr     ch,1                              ;Set color
        dec     cl                                ;DEC preserves 'C'
        jnz     @F                                ;...because NJZ doesn't wanna
                                                  ;   work all of a sudden!
        jmp     bitmap_opaque                     ;If last plane, just jump to it

        ALIGN   4

@@:
        push    ecx
        push    eax
        push    ebx
        push    edx
        push    esi
        push    edi
        call    bitmap_opaque
        pop     edi
        pop     esi
        pop     edx
        pop     ebx
        pop     eax
        pop     ecx
        add     edi,next_plane
        jmp     color_bitmap_opaque_loop

        ALIGN   4

 
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


        ALIGN   4

worst_case_ext:: 

        DebugMsg <worst_case_ext STRBLT CLIFFL>

        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     ax,di                             ;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      dx,dx
        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

        ALIGN   4

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

        ALIGN   4

abc_extent::

        DebugMsg <abc_extent STRBLT CLIFFL>

        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
        add     ebx,4
        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     ax,di                             ;AX = this rhs
        ;!!!SEL 1-27-92:Check this
        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         ;!!!SEL 1-27-92:
                                ;Check this, the jno below might
                                ;not work right b/c this used to be a 16 bit
                                ;add!!!
@@:
        jecxz   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

        ALIGN   4

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

jno     abc_exit        ;!!!SEL 1-27-92:Check this
 
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 

ifdef DBCS  ; DBCS device font support     ;IBMJ

page

;--------------------------Exported-Routine-----------------------------;
; SubStrExtTextOut
;
;   Devide string into some sub-strings according to the cache size,
;   draw them, and construct total string extent values.
;
; Entry:
; Returns:
; EAX = rhs of string (zero based)
; EBX = concatenation point (zero based)
; ECX = lhs of string (zero based)
; EDX = Y extent of string  if extent call
; Error Returns:
; Registers Preserved:
; ESI,EDI,EBP
; Registers Destroyed:
; EAX,EBX,ECX,EDX,FLAGS
; Calls:
; History:
;   12-Nov-1990 by Soh Ohta [jl09057 at ymtvm3]
; Created.
;-----------------------------------------------------------------------;

SubStrExtTextOut PROC SYSCALL,
  lp_DDC  :DWORD,
  lp_device :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 lhs  :DWORD,
  rhs  :DWORD,
  concat  :DWORD,
  yext  :DWORD,
  cbCache  :DWORD,
  cbString :DWORD,
  cbSubString :DWORD,
  opaqRhs  :DWORD,
  fsNls  :DWORD

 ASSUME ebx:PTR DDC

; Assume there was no string to output.

 xor eax,eax
 mov lhs,eax
 mov rhs,eax
 mov concat,eax
 mov yext,eax

; If it is opaque draw call, save original opaque rect rhs.

 test eto_options,ETO_OPAQUE_FILL
 jz transparent_call ; transparent call...
 mov ebx,lp_opaque_rect

 ASSUME ebx:PTR RECTL

 mov eax,[ebx].rcl_xRight
 mov opaqRhs,eax  ; save original opaque rect rhs

; Get max sub-string length which fits in cache size.
; The string will be chopped by the cache size.

transparent_call:
 mov ebx,lp_DDC  ; EBX --> ddc

 ASSUME ebx:PTR DDC

 mov ecx,[ebx].ddc_ca.ca_cCache
 mov cbCache,ecx  ; max sub-string length
 mov eax,count
 or eax,eax
 jns @f
 neg eax
@@: mov cbString,eax  ; remaining string length


; Sub-string drawing loop.
; Devide given string into sub-strings and process them in order.
; We will draw the sub-strings with "truncation check". With this option,
; the string division at invalid position (i.e, just after DBCS 1st byte)
; will be reported after drawing through NLSCA_CONCAT flag in ddc.

sub_string_loop:
 mov eax,[ebx].ddc_ca.ca_fsNls
 or eax,NLSCA_CONCAT ; assume not the last sub-string
 mov ecx,cbCache  ; max length, with truncation check.
 cmp cbString,ecx
 ja @f
 and eax,not NLSCA_CONCAT ; this is the last sub-string
 mov ecx,cbString  ; last length, w/o truncation check.

@@: mov [ebx].ddc_ca.ca_fsNls,eax
 mov fsNls,eax  ; save parsing option flags

 mov cbSubString,ecx  ; CX = current sub-string length
 xchg count,ecx
 or ecx,ecx
 jns @f
 neg count   ; set string length parameter
@@:     ; (be sure count is signed value)

; If this is opaque draw call, we should know the sub-string extents
; to device the opaque rect into sub-rects.

 test eto_options,ETO_OPAQUE_FILL
 jz draw_sub_string  ; transparent call...
 mov ecx,count
 or ecx,ecx
 js draw_sub_string  ; extent call...

; Opaque draw call. Here we will make a extent call for this sub-string.

 neg count   ; make a extent call (count<0)
 xor ecx,ecx   ; CX=0 means string translation needed

 INVOKE ExtTextOut,
  lp_DDC,
  lp_device,
  x,
  y,
  lp_clip_rect,
  lp_string,
  count,
  npAttrs,
  lp_dx,
  lp_opaque_rect,
  eto_options

 neg count   ; restore count>0
 mov ecx,ebx
 add ecx,x   ; CX = string concatination point

; Modify opaque rect size according to the sub-string extents.

 mov ebx,lp_DDC  ; EBX --> ddc

 ASSUME ebx:PTR DDC

 mov eax,fsNls  ; restore parsing flag
 mov [ebx].ddc_ca.ca_fsNls,eax
 test eax,NLSCA_CONCAT ; last sub-string ?
 jnz @f   ; no...
 mov ecx,opaqRhs  ; ECX = original opaque rect rhs
@@: mov ebx,lp_opaque_rect ; set this point as opaque rect rhs

 ASSUME ebx:PTR RECTL

 mov [ebx].rcl_xRight,ecx

; Draw a sub-string (and corresponding opaque rectangle),
; and get extents for it.

draw_sub_string:
 xor ecx,ecx   ; CX=0 means string translation needed

 INVOKE ExtTextOut,
  lp_DDC,
  lp_device,
  x,
  y,
  lp_clip_rect,
  lp_string,
  count,
  npAttrs,
  lp_dx,
  lp_opaque_rect,
  eto_options

; Update string extents. InternalExtTextOut returnes:
; 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

 add x,ebx   ; update next sub-string start position

 add eax,concat  ; make these extents
 add ebx,concat  ;   concatination point based
 add ecx,concat

 mov concat,ebx  ; update concatination point
 cmp rhs,eax
 jge @f   ; (signed value)
 mov rhs,eax   ; update rhs point
@@: cmp lhs,ecx
 jle @f   ; (signed value)
 mov lhs,ecx   ; update lhs point
@@: cmp yext,edx
 jae @f   ; (unsigned value)
 mov yext,edx  ; update y extent
@@:

; If this is opaque draw call, Update next sub-opaque rect lhs.

 test eto_options,ETO_OPAQUE_FILL
 jz next_sub_string  ; transparent call...
 mov ecx,count
 or ecx,ecx
 js next_sub_string  ; extent call...
 mov ebx,lp_opaque_rect ; set next opaque rect lhs = prev rhs

 mov eax,[ebx].rcl_xRight
 mov [ebx].rcl_xLeft,eax

; Get the next sub-string.
; If the last byte of sub-string was truncated during parsing, We will
; start the next sub-string from the last byte of current sub-string.

next_sub_string:
 mov ebx,lp_DDC  ; EBX --> ddc

 ASSUME ebx:PTR DDC

 mov ecx,cbSubString
 test [ebx].ddc_ca.ca_fsNls,NLSCA_CONCAT
 jz @f   ; last byte was not truncated
 dec ecx   ; truncated. last byte is not processed
@@:
 add lp_string,ecx  ; next sub-string address
 sub cbString,ecx  ; remaining string length
 ja sub_string_loop

; Return computed extents of whole string.

 mov eax,rhs
 mov ebx,concat
 mov ecx,lhs
 mov edx,yext

 ret
SubStrExtTextOut   ENDP

endif;DBCS        ;IBMJ

        end
