;*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.;
;*****************************************************************************/
;------------------------------Module-Header-------------------------------;
; Module Name: FONTXFRM.ASM
;
; Created: Tuesday August 23, 1988
; Author: Greg Hitchcock [gregh]
;
;
; History:
;  Wed 24-Aug-1988 09:47:49 -by-  Greg Hitchcock [gregh]
;    Copied it from the engine.
;
;  21/2/91 Mark Berry - Data Connection
;    simplified the firewalling to use 'ifdef' rather than 'if'
;    this means we can more easily control firewalling from the command line
;
;--------------------------------------------------------------------------;

INCL_GPIBITMAPS EQU     1
INCL_FONTFILEFORMAT     equ     1
INCL_GRE_XFORMS         equ     1
INCL_GPITRANSFORMS      equ     1
include os2.inc
include eddinclt.inc

.386p                                                                   ;**
.xlist

?DF     equ     1       ; we dont want _TEXT segment defined            ;**
INCLUDE cmacros.inc

INCLUDE missing.inc

INCLUDE engine.inc
.list

ifdef FIREWALLS
INCL_WINP               equ     1
INCL_WINP_SEI           equ     1
INCLUDE os2p.inc

externFP    <WinSetErrorInfo>,C
endif ; FIREWALLS

extrn       InnerGre32Entry7 : near

DC_TYPE                 equ     100h

_TEXT           segment use32 dword public 'CODE'                       ;**
                assume  cs:FLAT, ds:FLAT, es:FLAT

ifdef FIREWALLS

;---------------------------Public-Routine------------------------------;
; perform_rip
;
; The given error message is passed to WinSetErrorInfo with the
; abort flag set
;
; Entry:
;       None
; Returns:
;       None
; Error Returns:
;       None
; Registers Destroyed:
;       AX,BX,CX,DX,ES
; History:
;       Sun 17-Jan-1988 17:19:07 -by-  Walt Moore [waltm]
;       Wrote it.
;-----------------------------------------------------------------------;

ErrorMsg        msg_Overflow,<Overflow flag should not be set>

SEIOptions = SEI_REGISTERS+SEI_STACKTRACE

        align   4
cProc   perform_rip,<PUBLIC,NODATA>                                     ;**
        parmD   pMsg                                                    ;**
cBegin
        farPtr  errorid,SEVERITY_SEVERE,1
        cCall   WinSetErrorInfo,<errorid,SEIOptions,pMsg>,<C>
cEnd


endif ; FIREWALLS

;---------------------------Internal-Routine----------------------------;
;   WORD FAR  PASCAL   xform_metrics (hdc, lpMetrics)
;
;   Transforms the font metrics
;
;   Entry:
;       hdc
;       lpMetrics  target text metrics to be scaled
;
;   Note:
;       All sizes are returned in world coordinates
;
;   Returns:
;         CF = 0,
;
;   Error Returns:
;         CF = 1
;-----------------------------------------------------------------------;

        assumes     ds,nothing
        assumes     es,nothing

POINT_COUNT1        equ (foca_sCharSlope-foca_yEmHeight)/2
POINT_COUNT2        equ (foca_usKerningPairs-foca_ySubscriptXSize)/2
POINTS_IN_METRICS   equ (POINT_COUNT1+POINT_COUNT2)


        align   4
cProc   xform_metrics,<PUBLIC>,<esi,edi>                                ;**
        parmD   hdc
        parmD   lpMetrics
        localV  ptMetrics,%(size POINTL * (POINTS_IN_METRICS+1))
cBegin
        cld

; zero out the array of metrics, the first point will be 0,0

        lea     edi,ptMetrics                                           ;**

        mov     ecx,(size POINTL * (POINTS_IN_METRICS+1)) / 4
        xor     eax,eax
        rep     stosd

; move back to the second point in the array

        lea     edi,ptMetrics.size POINTL                               ;**

; move the heights and widths into the point array so that we can xform
; them. The information in the metrics consists of
;   eight   ys
;   three   xs
;   one     y
;   four    points
;   two     ys

        mov     esi,lpMetrics                                           ;**
        add     esi,fm_lEmHeight                                        ;**

; move the first eight ys

        .errnz  ((foca_yExternalLeading-foca_yEmHeight)/2)-7
        mov     ecx,((foca_yExternalLeading-foca_yEmHeight) / 2) + 1
move_eight_ys:
        lodsw
        mov     [edi].ptl_y.lo,ax                                       ;**
        inc     esi                                                     ;**
        inc     esi                                                     ;**
        add     edi,size POINTL                                         ;**
        loop    move_eight_ys

; now move the three xs

        mov     ecx,((foca_xEmInc-foca_xAveCharWidth) / 2) + 1
move_three_xs:
        lodsw
        mov     [edi].ptl_x.lo,ax                                       ;**
        inc     esi                                                     ;**
        inc     esi                                                     ;**
        add     edi,size POINTL                                         ;**
        loop    move_three_xs

; move one more y

        lodsw
        mov     [edi].ptl_y.lo,ax                                       ;**
        add     edi,size POINTL                                         ;**

        add     esi,2+foca_ySubscriptXSize-foca_sCharSlope              ;**

        .errnz  ((foca_xEmInc-foca_xAveCharWidth)/2)-2

; now we have to move four points

        mov     ecx,((foca_ySuperscriptXOffset-foca_ySubscriptXSize)/(size POINTS))+1
move_four_points:
        lodsw
        inc     esi                                                     ;**
        inc     esi                                                     ;**
        mov     [edi].ptl_x.lo,ax                                       ;**
        add     edi,size POINTL                                         ;**
        lodsw
        inc     esi                                                     ;**
        inc     esi                                                     ;**
        mov     [edi].ptl_y.lo,ax                                       ;**
        add     edi,size POINTL                                         ;**
        loop    move_four_points     ; zeros ecx

        mov     ecx,((foca_yStrikeoutPosition-foca_yUnderscoreSize)/2)+1

move_four_more_ys:

        lodsw
        inc     esi                                                     ;**
        inc     esi                                                     ;**
        mov     [edi].ptl_y.lo,ax                                       ;**
        add     edi,size POINTL                                         ;**
        loop    move_four_more_ys

; back to the begining

        lea     edi,ptMetrics                                           ;**
        assert  cx,E,0

;       check   Convert,<hdc,Source,Target,lpPolyXY,PolyN,hddc,FunN>

        push    NGreConvert
        push    0                       ; hddc
        push    POINTS_IN_METRICS+1
        push    edi
        push    1                       ; myTarget
        push    5                       ; mySource
        push    hdc                     ; myhdc
        call    InnerGre32Entry7
        sub     esp, 28

        or      eax,eax
        jnz     short not_error_xform_metrics

        stc
        jmp     exit_xform_metrics

; zero base the points before moving them back

not_error_xform_metrics:

        mov     ecx,POINTS_IN_METRICS
        mov     ax,word ptr [edi].ptl_x.lo        ; pick up the original x,y ;**
        mov     dx,word ptr [edi].ptl_x.hi                              ;**
        mov     bx,word ptr [edi].ptl_y.lo                              ;**
        mov     si,word ptr [edi].ptl_y.hi                              ;**

zero_base_points:
        add     edi,SIZE POINTL                                         ;**
        sub     [edi].ptl_x.lo,ax                                       ;**
        sbb     [edi].ptl_x.hi,dx                                       ;**
        sub     [edi].ptl_y.lo,bx                                       ;**
        sbb     [edi].ptl_y.hi,si                                       ;**
        loop    zero_base_points

; ok they are converted so now move them back to the metrics structure


        mov     edi,lpMetrics                                           ;**
        add     edi,fm_lEmHeight                                        ;**

        lea     esi,ptMetrics.size POINTL                               ;**

        .errnz  fm_lXHeight-fm_lEmHeight-4
        .errnz  fm_lMaxAscender-fm_lXHeight-4
        .errnz  fm_lMaxDescender-fm_lMaxAscender-4
        .errnz  fm_lLowerCaseAscent-fm_lMaxDescender-4
        .errnz  fm_lLowerCaseDescent-fm_lLowerCaseAscent-4
        .errnz  fm_lInternalLeading-fm_lLowerCaseDescent-4
        .errnz  fm_lExternalLeading-fm_lInternalLeading-4
        .errnz  fm_lAveCharWidth-fm_lExternalLeading-4
        .errnz  fm_lMaxCharInc-fm_lAveCharWidth-4
        .errnz  fm_lEmInc-fm_lMaxCharInc-4
        .errnz  fm_lMaxBaselineExt-fm_lEmInc-4

        mov     ecx,((fm_lMaxBaselineExt-fm_lEmHeight)/4)+1
        assert  ecx,E,12

store_up_to_fm_lMaxBaselineExt:
        cCall   get_vector_length
        stosw
        xchg    ax,dx
        stosw
        add     esi,size POINTL                                         ;**
        loop    store_up_to_fm_lMaxBaselineExt

        .errnz  fm_sCharSlope-fm_lMaxBaselineExt-4

        add     edi,fm_lSubscriptXSize-fm_sCharSlope                    ;**

        .errnz  fm_lSubscriptYSize-fm_lSubscriptXSize-4
        .errnz  fm_lSubscriptXOffset-fm_lSubscriptYSize-4
        .errnz  fm_lSubscriptYOffset-fm_lSubscriptXOffset-4
        .errnz  fm_lSuperscriptXSize-fm_lSubscriptYOffset-4
        .errnz  fm_lSuperscriptYSize-fm_lSuperscriptXSize-4
        .errnz  fm_lSuperscriptXOffset-fm_lSuperscriptYSize-4
        .errnz  fm_lSuperscriptYOffset-fm_lSuperscriptXOffset-4
        .errnz  fm_lUnderscoreSize-fm_lSuperscriptYOffset-4
        .errnz  fm_lUnderscorePosition-fm_lUnderscoreSize-4
        .errnz  fm_lStrikeoutSize-fm_lUnderscorePosition-4
        .errnz  fm_lStrikeoutPosition-fm_lStrikeoutSize-4

        mov     ecx,((fm_lStrikeoutPosition-fm_lSubscriptXSize)/4)+1
        assert  ecx,E,12

store_up_to_fm_lStrikeoutPosition:
        cCall   get_vector_length
        stosw
        xchg    ax,dx
        stosw
        add     esi,size POINTL                                         ;**
        loop    store_up_to_fm_lStrikeoutPosition
        clc

exit_xform_metrics:

cEnd

;---------------------------Public-Routine------------------------------;
; DWORD get_vector_length()
;
; finds the length of a vector in an array of vectors
;
; Entry:
;   esi --> point                                                       ;**
;
; Returns:
;   dx:ax --> the length of the point
; Error:
;   OF set
; History:
;  Mon 28-Mar-1988 17:17:13  -by-  Paul Klingler [paulk]
;-----------------------------------------------------------------------;

UQUAD   struc
uq0     dw      ?
uq1     dw      ?
uq2     dw      ?
uq3     dw      ?
UQUAD   ends

        assumes ds,nothing
        assumes es,nothing

        align   4
cProc   get_vector_length,<PUBLIC>,<ecx>                                ;**
        localQ  qSum                    ; UNSIGNED since squares are +
cBegin
        mov     ax,[esi].ptl_x.lo                                       ;**
        mov     dx,[esi].ptl_x.hi                                       ;**
        mov     bx,ax
        mov     cx,dx
        call    idmul                   ; dx:cx:bx:ax = x^2
        mov     qSum.uq0,ax
        mov     qSum.uq1,bx
        mov     qSum.uq2,cx
        mov     qSum.uq3,dx
        mov     ax,[esi].ptl_y.lo                                       ;**
        mov     dx,[esi].ptl_y.hi                                       ;**
        mov     bx,ax
        mov     cx,dx
        call    idmul                   ; dx:cx:bx:ax = y^2
        add     ax,qSum.uq0
        adc     bx,qSum.uq1
        adc     cx,qSum.uq2
        adc     dx,qSum.uq3             ; we will have no overflow (+)
        call    big_fix_square_root     ; DX:AX = sqrt(DX:CX:BX:AX)
cEnd


;---------------------------Public-Routine------------------------------;
; idmul
;
; This is an extended precision multiply routine, intended to emulate
; 80386 imul instruction.
;
; Entry:
;       DX:AX = LONG
;       CX:BX = LONG
; Returns:
;       DX:CX:BX:AX = QUAD product
; Registers Destroyed:
;       none
; History:
;  Tue 26-Jan-1988 23:47:02  -by-  Charles Whitmer [chuckwh]
; Wrote it.
;-----------------------------------------------------------------------;

        assumes ds,nothing
        assumes es,nothing

        align   4
cProc   idmul,<PUBLIC>,<si,di>                                          ;**
        localQ  qTemp
cBegin

; put one argument in safe registers

        mov     si,dx
        mov     di,ax

; do the low order unsigned product

        mul     bx
        mov     qTemp.uq0,ax
        mov     qTemp.uq1,dx

; do the high order signed product

        mov     ax,si
        imul    cx
        mov     qTemp.uq2,ax
        mov     qTemp.uq3,dx

; do a mixed product

        mov     ax,si
        cwd
        and     dx,bx
        sub     qTemp.uq2,dx            ; adjust for sign bit
        sbb     qTemp.uq3,0
        mul     bx
        add     qTemp.uq1,ax
        adc     qTemp.uq2,dx
        adc     qTemp.uq3,0

; do the other mixed product

        mov     ax,cx
        cwd
        and     dx,di
        sub     qTemp.uq2,dx
        sbb     qTemp.uq3,0
        mul     di

; pick up the answer

        mov     bx,ax
        mov     cx,dx
        xor     dx,dx
        mov     ax,qTemp.uq0
        add     bx,qTemp.uq1
        adc     cx,qTemp.uq2
        adc     dx,qTemp.uq3
cEnd

;-------------------------------Public-Routine---------------------------;
; big_fix_square_root
;
;   Approximates the square root of an unsigned BIGFIXED
;
; Entry:
;     DX:CX:BX:AX = unsigned BIGFIXED
; Returns:
;     DX:AX = square root
; Error Returns:
;     none
; Registers Destroyed:
;     BX,CX
; Calls:
;     none
; History:
;  Fri 29-Jul-1988 09:18:55 -by-  Lee A. Newberg [leen]
; Rounds answer to the nearest 0.0001h.  Fractions of 0.00008h or greater
; are rounded up.
;
;  Sun 06-Mar-1988            -by-   Martin Picha        [martinpi]
; Wrote it.  (pulled out of solve_quadratic)
;------------------------------------------------------------------------;
      assumes ds,nothing
      assumes es,nothing

        align   4
cProc big_fix_square_root,<PUBLIC>                                      ;**
cBegin

; normalize the BIGFIXED for a sqrt
        cCall   uqNormalize

        shr     cx,1
        jc      short it_will_be_even
        shr     dx,1
        rcr     ax,1
        dec     cx
it_will_be_even:
        shr     dx,1
        rcr     ax,1

; call the square root with almost 32 bits of precision

        save    <CX>
        cCall   square_root
        assert  NO

; remove the normalization
;                                                                16
        mov     dh,dl                           ; we lied about 2
        mov     dl,ah
        mov     ah,al
        xor     al,al

        jcxz    have_sqrt
        dec     cl                              ; leave answer doubled
        cmp     cl,16
        jb      short remove_final_16
        mov     ax,dx                           ; shift right by 16
        xor     dx,dx
        sub     cl,16
        jz      short have_double
remove_final_16:
        mov     bx,-1
        shr     bx,cl
        not     bx
        shr     ax,cl
        ror     dx,cl
        and     bx,dx
        or      ax,bx
        xor     dx,bx
have_double:

; Half this number but round it off.

        add     ax,1
        adc     dx,0
        shr     dx,1
        rcr     ax,1
        or      ax,ax                   ; clears overflow
have_sqrt:
        assert  NO,msg_Overflow
cEnd

;---------------------------Public-Routine------------------------------;
; uqNormalize
;
; Normalizes a UQUAD (unsigned quadword) so that the highest order bit
; is 1.  Returns only the top 32 bits of the normalized UQUAD.  Returns
; the number of shifts done.  Retuns ZF=1 if the UQUAD was zero.
;
; Entry:
;       DX:CX:BX:AX = UQUAD
; Returns:
;       DX:AX = normalized top 32 bits
;       CX    = shift count
;       ZF    = 1 if the UQUAD is zero, 0 otherwise
; Registers Destroyed:
;       BX
; History:
;  Mon 25-Jan-1988 22:07:03  -by-  Charles Whitmer [chuckwh]
; Wrote it.
;-----------------------------------------------------------------------;

        assumes ds,nothing
        assumes es,nothing

        align   4
cProc   uqNormalize,<PUBLIC>,<si,di>                                    ;**
cBegin

; use SI to count the shifts

        xor     si,si

; get the data into the top three words to free CX

        xchg    ax,cx                   ; CX = lowest order
        or      dx,dx
        js      short uqNormalize_exit
        jnz     short top_word_good
        mov     dx,ax
        mov     ax,bx
        mov     bx,cx
        mov     si,16
        or      dx,dx
        js      short uqNormalize_exit
        jnz     short top_word_good

; in this case, all the data fits in DX:AX, let ulNormalize do it

        mov     dx,ax
        mov     ax,bx
        call    ulNormalize
        jz      short uqNormalize_final_exit
        add     cx,32                   ; how much we'd shifted ourselves
        jmp     short uqNormalize_final_exit

; all data is in DX:AX:BX, let ulNormalize help

top_word_good:
        call    ulNormalize
        add     si,cx

; DX:AX is normalized, now we should move the top bits of BX into AX

        mov     di,-1
        rol     bx,cl           ; abcdexxxxxxxxxxx => xxxxxxxxxxxabcde
        shl     di,cl           ; 1111111111111111 => 1111111111100000
        not     di              ; 1111111111100000 => 0000000000011111
        and     bx,di           ; xxxxxxxxxxxabcde => 00000000000abcde
        or      ax,bx
        or      dx,dx           ; ZF = 0
uqNormalize_exit:
        mov     cx,si
uqNormalize_final_exit:
cEnd


;---------------------------Public-Routine------------------------------;
; square_root
;
; Computes the Fixed square root of a Fixed using Newton's method.
;
; This routine will occasionally give a different result from
; the old_square_root routine.  This is caused by a very small lack of
; precission.  For instance, the square root of 1.0001h is approximately
; 1.00007FFh.  This routine calculates it to be 1.0000800.  Hence when
; we round it off and put it in a fixed point we get 1.0001 instead
; of 1.0000.
;
; Entry:
;       DX:AX = number to square root
; Returns:
;       DX:AX = square root of input
;       Zero flag set if the output (and the input) are zero.
; Error Returns:
;       OF = 1, DX:AX = 0       if input is negative
; Registers Destroyed:
;       BX,CX
; Registers Preserved:
;       DS,ES,SI,DI
; History:
;  Tue 02-Aug-1988 12:50:30 -by-  Lee A. Newberg [leen]
; Wrote it.
;-----------------------------------------------------------------------;

        assumes ds,nothing
        assumes es,nothing

        align   4
cProc   square_root,<PUBLIC>,<SI,DI>                                    ;**
cBegin
        cCall   ulNormalize
        jnz     short input_non_zero          ; Input wasn't zero
        jmp     lee_root_exit
negative_input:
        xor     ax,ax
        shr     dx,1                    ; Sets Overflow
        cwd
        assert  O
        jmp     lee_root_exit

input_non_zero:
        jcxz    negative_input          ; Input was negative
        shr     cx,1
        jc      short even_shifts
        dec     cx
        shr     dx,1
        rcr     ax,1
even_shifts:
        shr     dx,1
        rcr     ax,1

; Let SI:DI = the normalized square

        push    ecx             ; Save the number of shifts
        mov     di,ax
        mov     si,dx           ; SI:DI = the input

; First find the square root of the *integer* DX:AX.
; We will use Newton's method.

; Zeroth guess is 8000

        mov     bx,dx
        add     bh,40h

; SI:DI = the normalized square
; BX = first guess = ((square / zeroth_guess) + zeroth_guess) / 2

; second_guess = ((square / first_guess) + first_guess) / 2

        div     bx
        add     bx,ax
        rcr     bx,1

; SI:DI = the normalized square
; BX = second guess
; third_guess = ((square / second_guess) + second_guess) / 2

        mov     ax,di
        mov     dx,si
        div     bx
        add     bx,ax
        rcr     bx,1

; SI:DI = the normalized square
; BX = third guess.  Now we'll go for some more precision.
; That is, we'll remember that we're working with fixed point
; numbers rather than integers.

        mov     ax,di
        mov     dx,si
        div     bx
        mov     cx,ax           ; CX = high part of quotient
        xor     ax,ax
        div     bx              ; AX = low part of quotient
        mov     dx,cx           ; DX = high part of quotient

; Now average BX:0000 and DX:AX

        add     dx,bx
        rcr     dx,1
        rcr     ax,1

unnormalize:
        pop     ecx             ; Restore the number of shifts.

; 7 = half the number of bits in the fraction of a fixed - 1

        add     cl,7
        test    cl,10h
        jz      short @F
        mov     ax,dx
        xor     dx,dx
@@:
        test    cl,8
        jz      short @F
        mov     al,ah
        mov     ah,dl
        mov     dl,dh
        xor     dh,dh
@@:
        and     cl,7
        jz      short @F
        mov     bx,-1
        shr     bx,cl
        not     bx
        shr     ax,cl
        ror     dx,cl
        and     bx,dx
        xor     dx,bx
        or      ax,bx
@@:

; DX:AX = double our answer.  Half it with round off

        rcr     dx,1
        rcr     ax,1
        adc     ax,0
        adc     dx,0
        assert  NO,msg_Overflow
lee_root_exit:
cEnd

;---------------------------Public-Routine------------------------------;
; ulNormalize
;
; Normalizes a ULONG so that the highest order bit is 1.  Returns the
; number of shifts done.  Also returns ZF=1 if the ULONG was zero.
;
; Entry:
;       DX:AX = ULONG
; Returns:
;       DX:AX = normalized ULONG
;       CX    = shift count
;       ZF    = 1 if the ULONG is zero, 0 otherwise
; Registers Destroyed:
;       none
; History:
;  Mon 25-Jan-1988 22:07:03  -by-  Charles Whitmer [chuckwh]
; Wrote it.
;-----------------------------------------------------------------------;

        assumes ds,nothing
        assumes es,nothing

        align   4
cProc   ulNormalize,<PUBLIC>                                            ;**
cBegin

; shift by words

        xor     cx,cx
        or      dx,dx
        js      short ulNormalize_exit
        jnz     short top_word_ok
        xchg    ax,dx
        or      dx,dx
        jz      short ulNormalize_exit        ; the zero exit
        mov     cl,16
        js      short ulNormalize_exit
top_word_ok:

; shift by bytes

        or      dh,dh
        jnz     short top_byte_ok
        mov     dh,dl
        mov     dl,ah
        mov     ah,al
        xor     al,al
        add     cl,8
        or      dh,dh
        js      short ulNormalize_exit
top_byte_ok:

; do the rest by bits

        inc     cx
        add     ax,ax
        adc     dx,dx
        js      short ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      short ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      short ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      short ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      short ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      short ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
ulNormalize_exit:
cEnd

_TEXT   ends                                                            ;**

END
