;*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 = DITHER.ASM
;*
;* DESCRIPTIVE NAME = Dither brush related functions.
;*
;*
;* VERSION      V2.0
;*
;* DATE         05/11/90
;*
;* DESCRIPTION  This module contains dither brush related functions for 16
;*              color dithering scheme used on the EGA/VGA (and 8514 when the
;*              palette is restricted.)
;*
;*
;* FUNCTIONS    ComputeSymmetry
;*              MakeDitherBitmap
;*              WriteBrush
;*              ComputeSubSpace
;*              TransformColors
;*              MakeColorCntTable
;*              SortColorCntTable
;*              dither_brush
;*              mono_dither
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   05/11/90                     Written by John Colleran [johnc]
;*
;*****************************************************************************/

        .386

        .xlist

DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1

        include pmgre.inc
        include driver.inc
        include display.inc
        include assert.mac
        include extern.inc
        include protos.inc

        .list

        .MODEL FLAT

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

        .DATA


IPCs_API_RGB_00 equ     00000000h                ; black    00h
IPCs_API_RGB_15 equ     00FFFFFFh                ; white   03Fh
BACKGND_DWORD   equ     0*4
FOREGND_DWORD   equ     7*4

ColorCount      struct

    ccCount     db  ?                                ; number of colors with ccIndex
    ccIndex     db  ?                                ; IPC of color

ColorCount      ends

PlaneEquation   struct

    peOrigin    dw  3 dup(0)                         ; An origin and normal to that origin
    peNormal    dw  3 dup(0)                         ; define a plane

PlaneEquation  ends

Transformation  struct

    xOrigin     db  3 dup(0)                         ; defines a tranform which will transform
    xMatrix     db  9 dup(0)                         ; an RGB into a specially weighted RGB
    xCOrigin    db  0                                ; which all components add up to 64 (?)
    xBase       db  3 dup(0)

Transformation  ends

;/*
;** PColorIntensities is a ordering of the EGA/VGA palette entries from
;** dark to light
;*/

INV_DITHER_IPC  equ  8

PColorIntensities   label   byte    ;IPC

        db      0                   ;0  black
        db      01h                 ;1  dark blue
        db      03h                 ;2  dark green
        db      05h                 ;3  dark turquoise  (BG)
        db      02h                 ;4  dark red
        db      04h                 ;5  purple          (RB)
        db      06h                 ;6  mustard
        db      07h                 ;7  gray
        db      0ffh                                 ;8 light gray ;Not used in dither
        db      09h                 ;9  blue
        db      0bh                 ;10 green
        db      0dh                 ;11 turquoise (cyan)(BG)
        db      0ah                 ;12 red
        db      0ch                 ;13 pink (magenta)  (RB)
        db      0eh                 ;14 yellow
        db      0fh                 ;15 white

;/*
;** abDitherPattern is a an array of bits dispersed so that if pixels
;** are placed in the brush bitmap in the order specified below they
;** will tend to minimize checker-boarding
;*/

abDitherPattern  label   byte

    db   0, 32,  8, 40,  2, 34, 10, 42
    db  48, 16, 56, 24, 50, 18, 58, 26
    db  12, 44,  4, 36, 14, 46,  6, 38
    db  60, 28, 52, 20, 62, 30, 54, 22
    db   3, 35, 11, 43,  1, 33,  9, 41
    db  51, 19, 59, 27, 49, 17, 57, 25
    db  15, 47,  7, 39, 13, 45,  5, 37
    db  63, 31, 55, 23, 61, 29, 53, 21

;/*
;** maps limited color tetrahedron vertices to full color cube
;*/

PColorMap       label   byte

        db 0, 1, 3, 7, 09h, 0Bh, 0Fh, 0FFh
        db 0, 2, 3, 7, 0Ah, 0Bh, 0Fh, 0FFh
        db 0, 1, 5, 7, 09h, 0Dh, 0Fh, 0FFh
        db 0, 4, 5, 7, 0Ch, 0Dh, 0Fh, 0FFh
        db 0, 4, 6, 7, 0Ch, 0Eh, 0Fh, 0FFh
        db 0, 2, 6, 7, 0Ah, 0Eh, 0Fh, 0FFh
        db 0, 4, 5, 7, 0Ch, 0Dh, 0Fh, 0FFh

;/*
;** Plane definitions used to divide the limited color tetrahedron
;*/

SubDivPlanes    label   word

        DWORD   128, 128, 0                       ; origin sub-space 1  x <= 128
        DWORD   -1,  0,   0                       ; normal
        DWORD   128, 128, 0                       ; origin sub-space 2  x+y <= 256
        DWORD   -1,  -1,  0                       ; normal
        DWORD   128, 128, 128                     ; origin sub-space 3  x+z <= 256
        DWORD   -1,  0,   -1                      ; normal
        DWORD   255, 255, 0                       ; origin sub-space 4  all
        DWORD   -1,  0,   0                       ; normal

;/*
;** Transforms applied to an RGB to map it into the given tetrahedron space
;*/

TransformTable  label   word

        db      32, 32, 0                         ; origin
        db      -2, 0,  0                         ; 3x3 matrix
        db      2,  -2, 0
        db      0,  0,  2
        db      2,  0,  1, 3                      ;pts 3, 1, 2, 4 - IPC of vertices

        db      32, 32, 0                         ; origin
        db      -2, -2, 0                         ; 3x3 matrix
        db      2,  0,  0
        db      0,  0,  2
        db      2,  1,  4, 3                      ;pts 3, 2, 5, 4 - IPC of vertices

        db      32, 32, 0                         ; origin
        db      1,  -1, 0                         ; 3x3 matrix
        db      1,  1,  0
        db      0,  0,  2
        db      2,  4,  5, 3                      ;pts 3, 5, 6, 4 - IPC of vertices

        db      64, 0,  0                         ; origin
        db      -2, 0,  0                         ; 3x3 matrix
        db      0,  1,  -1
        db      1,  0,  1
        db      4,  3,  5, 6                      ;pts 5, 4, 6, 7 - IPC of vertices

        .CODE

;/***************************************************************************
;*
;* FUNCTION NAME = ComputeSymmetry
;*
;* DESCRIPTION   = Given an RGB determine the symmetrical ordering
;*                 This maps any RGB into 1/6 of the color cube.
;*
;*                 Registers Preserved:
;*                       SI,DI,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX
;*
;* INPUT         = DX:AX  =  RGB color (AL=blue, AH=green, DL=red)
;* OUTPUT        = BL: red < CL: < green < CH: blue
;*                 AX = Ordering (symmetry index)
;*
;*                 AX   Original Order
;*                 -------------------
;*                 000  BL < CL < CH
;*                 001  CL < BL < CH
;*                 010  BL < CH < CL
;*                 011  xxxxxxxxxxxx
;*                 100  CH < CL < BL
;*                 101  CL < CH < BL
;*                 110  CH < BL < CL
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ComputeSymmetry PROC SYSCALL

        mov     bl,al
        mov     cl,ah
        mov     ch,dl
        xor     eax,eax                           ;zero counter

        cmp     bl,ch                             ;is BL greater or equal CH?
        ja      CheckPlane2                       ;if so, don't transpose (ja==jc)
        xchg    bl,ch                             ;transpose

CheckPlane2:

        rcl     ax,1                              ;put carry flag into LSB of AX
        cmp     cl,ch                             ;is CL greater or equal CH?
        ja      CheckPlane3
        xchg    cl,ch

CheckPlane3:

        rcl     ax,1
        cmp     bl,cl                             ;is BL greater or equal CL?
        ja      ComputeSymmetryDone
        xchg    bl,cl

ComputeSymmetryDone:

        rcl     ax,1

                RET

ComputeSymmetry ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = MakeDitherBitmap
;*
;* DESCRIPTION   = Creates a pattern with the specified colors
;*
;*                 Registers Preserved:
;*                       DI,BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,SI
;*
;* INPUT         = SS:SI = color-count structures
;*                 SS:DI = destination
;*                 SS:DX = pattern bitmap mask
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

MakeDitherBitmap        PROC SYSCALL USES edi,   nNumberOfColors:DWORD
                        LOCAL   nOldColorCount:DWORD,
                                iLoop:DWORD


        lea     ebx,abDitherPattern
        xor     eax,eax
        mov     nOldColorCount,eax

MDBMainLoop:

        mov     iLoop,SIZE_PATTERN
        push    edi
        push    edx
        push    ebx
        mov     eax,nOldColorCount

        ASSUME  esi:PTR ColorCount

        add     al,[esi].ccCount
        mov     nOldColorCount,eax
        mov     ch,al                             ;CH: value to compare with
        inc     esi

        .errnz  ColorCount.ccIndex-ColorCount.ccCount-1

        .errnz  ColorCount.ccCount

MDBOuterLoop:

        xchg    edx,ebp                           ;SS:BP-->pattern bit mask
        mov     ah,BYTE PTR [ebp]                ;get the previous pattern into AH
        mov     cl,8                              ;a byte has 8 bits

MDBInnerPatternLoop_10:                 ;compute next bit mask in AL

        cmp     [ebx],ch                          ;if CY make that bit a 1
        rcl     al,1
        inc     ebx
        dec     cl
        jnz     MDBInnerPatternLoop_10

        mov     BYTE PTR [ebp],al                ;save new bitmask
        inc     ebp
        xor     al,ah                             ;exclude bits from previous mask
        mov     ah,al
        mov     al,BYTE PTR [esi]                ;get color index in AL
        mov     cl,8

MDBInnerPatternLoop_20:

        shl     ah,1
        jnc     MDBInnerPatternLoop_30


        mov     BYTE PTR [edi],al

MDBInnerPatternLoop_30:

        inc     edi
        dec     cl
        jnz     MDBInnerPatternLoop_20

        xchg    edx,ebp                           ;can access local variables again
        dec     iLoop
        jnz     MDBOuterLoop

        inc     esi                     ;SS:SI-->next color index count
        pop     ebx
        pop     edx
        pop     edi
        dec     nNumberOfColors
        jnz     MDBMainLoop

                        RET

MakeDitherBitmap        ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = WriteBrush
;*
;* DESCRIPTION   = Write the bits to the DDC brush; basically a bit to planar
;*                 conversion
;*
;*                 Registers Preserved:
;*                      DS,ES,BP
;*                 Registers Destroyed:
;*                      AX,BX,CX,DX,DI,SI
;*
;* INPUT         = ES:DI = -> abColor Brush in DDC
;*                 SS:SI = -> array of IPCs (the new pattern)
;* OUTPUT        = ES:DI = written brush
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


WriteBrush      PROC SYSCALL

        push    ebp
        mov     ebp,SIZE_PATTERN-1
        mov     ebx,edi
        mov     ecx,SIZE_PATTERN                 ;outer loop counter

WPBRowLoop:

        push    ecx
        mov     ah,SIZE_PATTERN                  ;inner loop counter

WPBColumnLoop:

        lods    BYTE PTR [esi]
        shr     al,1
        rcl     cl,1
        shr     al,1
        rcl     ch,1
        shr     al,1
        rcl     dl,1
        shr     al,1
        rcl     dh,1
        dec     ah
        jnz     WPBColumnLoop

        mov     edi,ebx
        inc     ebx
        mov     al,cl
        stosb
        mov     al,ch
        add     edi,ebp
        stosb
        mov     al,dl
        add     edi,ebp
        stosb
        mov     al,dh
        add     edi,ebp
        stosb
        pop     ecx
        loop    WPBRowLoop

        pop     ebp

                RET

WriteBrush      ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = ComputeSubSpace
;*
;* DESCRIPTION   = Determines SubSpace in the limited color space of the
;*                 specified colors
;*
;*                 Registers Preserved:
;*                       BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI
;*
;* INPUT         = BL > CH > CL = (well ordered) RGB
;* OUTPUT        = AX = subspace {0,1,2,3}
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


ComputeSubSpace PROC SYSCALL

        movzx   ebx,bl                            ; zero counter
        lea     esi,SubDivPlanes
        push    ebp

CSSMainLoop:

        xor     edi,edi                           ;BL: red, CH: blue, CL: green
        mov     ebp,3

CSSMulLoop:

        xor     eax,eax
        mov     al,bl
        sub     eax,DWORD PTR [esi]              ;adjust by origin
        imul    DWORD PTR [esi][12]              ;scale
        add     esi,4                   ;     FIX SL% 12-19-91
        xchg    bl,cl                             ;rotate RGB bytes
        xchg    cl,ch
        add     edi,eax
        dec     ebp
        jnz     CSSMulLoop

        inc     bh
        add     esi,12          ;BUG FIX SL% 12-19-91
        or      edi,edi                           ;we are in this subspace iff positive
        js      CSSMainLoop                       ;  (once we go negative we're outside)
        mov     al,bh
        dec     eax
        pop     ebp

                RET

ComputeSubSpace ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = TransformColors
;*
;* DESCRIPTION   = Transforms the specified colors SS:BX by transform CS:SI
;*
;*                 Registers Preserved:
;*                       BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,DI,SI
;*
;* INPUT         = SS:BX = mapped RGB
;*                 CS:SI = transform
;* OUTPUT        = SS:BX = transformed colors
;*                 CS:SI = color Origin xform
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

TransformColors PROC SYSCALL

        assert  ah,E,0

        push    ebp
        xor     edi,edi                           ; DI = index.
        mov     ecx,3

CTMoveOriginLoop:

        ASSUME  esi:PTR Transformation

        lodsb   [esi].xOrigin           ; adjust by origin
        sub     [ebx][edi],ax
        inc     edi
        inc     edi
        loop    CTMoveOriginLoop

        mov     edx,3                             ; DX = outer loop counter

CTMainLoop:

        xor     edi,edi                           ; coord index
        xor     ebp,ebp                           ; mult accumulator
        mov     ecx,3                             ; CX = inner loop counter

CTXformLoop:

        lodsb                                    ; matrix multiply
        imul    byte ptr [ebx][edi]
        inc     edi
        inc     edi
        add     ebp,eax
        loop    CTXformLoop

        shr     edi,1
        sub     edi,edx
        shl     edi,1
        mov     [ebx][edi][6],ebp                ; store result
        dec     edx
        jnz     CTMainLoop

        add     ebx,6
        pop     ebp

                RET

TransformColors ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = MakeColorCntTable
;*
;* DESCRIPTION   = Determines the number of colors for this dither
;*
;*                 Registers Preserved:
;*                       BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI
;*
;* INPUT         = SS:DI = transformed color
;*                 SS:BX = dest color-count structs
;*                 CS:SI = transform (.xCOrigin)
;*
;* OUTPUT        = DX = nColors
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

MakeColorCntTable       PROC SYSCALL

        xor     edx,edx
        mov     eax,SIZE_PATTERN*SIZE_PATTERN
        sub     ax,[edi][0]
        sub     ax,[edi][2]
        sub     ax,[edi][4]
        mov     ah,al                             ;AH = overall brightness factor
        lodsb
        jz      MCTNoBaseCount
        xchg    al,ah
        mov     WORD PTR [ebx],ax
        inc     edx
        inc     ebx
        inc     ebx

MCTNoBaseCount:

        mov     ecx,3

MCTCopyLoop:

        mov     ah,BYTE PTR [edi]
        inc     edi
        inc     edi
        lodsb
        or      ah,ah
        jz      MCTBottomOfLoop
        xchg    al,ah
        mov     WORD PTR [ebx],ax
        inc     ebx
        inc     ebx
        inc     edx

MCTBottomOfLoop:

        loop    MCTCopyLoop

                        RET

MakeColorCntTable       ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = SortColorCntTable
;*
;* DESCRIPTION   = Sorts the colors for this dither based on brightnest
;*                 (dim to bright)
;*
;*                 Registers Preserved:
;*                       BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,DI,SI
;*
;* INPUT         = CX = count of color-count structs
;*                 SS:SI = array of color-count structs
;*
;* OUTPUT        = SS:SI = sorted color-count structs
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

SortColorCntTable       PROC SYSCALL

        DebugMsg <SortColorCntTable DITHER CLIFFL>

        push    ebp
        lea     edi,PColorIntensities
        mov     eax,ecx                              ; AX = color cound
        shl     eax,1
        sub     esp,eax                              ; create local space
        mov     ebp,esp
        push    eax
        mov     dh,cl                                ; DH = count

SCTOuterLoop:

        push    ecx
        push    esi
        xor     ebx,ebx
        mov     cl,dh
        mov     dl,-1                                ; Initialize to very bright color

SCTInnerLoop:

        lodsw
        mov     al,ah                                ; AL = IPC

        movzx   edi,al
        mov     al,BYTE PTR [edi+OFFSET PColorIntensities]
                                                     ; AL = brightness of color

        cmp     al,dl                                ; if this color is dimmer save it
        ja      SCTNextIteration
        mov     dl,al
        mov     bh,bl

SCTNextIteration:

        inc     ebx
        loop    SCTInnerLoop

        pop     esi
        mov     bl,bh
        movzx   ebx,bl
        shl     ebx,1
        mov     ax,WORD PTR [ebx][esi]
        mov     WORD PTR [ebp],ax
        inc     ebx
        inc     ebp
        inc     ebp
        mov     byte ptr [esi][ebx],INV_DITHER_IPC ;mark this color as used
        pop     ecx
        loop    SCTOuterLoop

        mov     cl,dh
        pop     edx
        sub     ebp,edx
        xchg    esi,ebp
        mov     edi,ebp

SCTCopyLoop:

        lodsw
        mov     WORD PTR [ebp],ax
        inc     ebp
        inc     ebp
        loop    SCTCopyLoop

        add     esp,edx
        pop     ebp
        mov     esi,edi

                        RET

SortColorCntTable       ENDP

;/*
;** We want to force these greys to be a solid gray, so we have a special
;** test for them
;*/

SPECIAL_GRAY            equ                      0CCh   ; VGA 0xCCCCCC

;/***************************************************************************
;*
;* FUNCTION NAME = dither_brush
;*
;* DESCRIPTION   = Dithers the RGB in DX:AX into the DDC's abColor and
;*                 abMono bitmaps The 8514 uses this routine for the
;*                 restricted palette case (as dither_brush16)
;*
;*                 Registers Preserved:
;*                       SI,BP,DS
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,DI,ES,GS
;*
;* INPUT         = DS:SI --> ddc
;*                 DX:AX = RGB (AL=B, AH=G, DL=R)
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

dither_brush    PROC SYSCALL
        LOCAL   ipc1    :BYTE
        LOCAL   ipc2    :BYTE
        LOCAL   ipc3    :BYTE
        LOCAL   ipc4    :BYTE

        mov     bx,dx

;/*
;** AL = Blue, AH = Green, BL = Red       ; compute RRRGGGBB
;**
;** Test for true grey.
;*/

        cmp     al,0
        je      dont_dither_grey
        cmp     al,255
        je      dont_dither_grey
        cmp     al,ah
        jne     dont_dither_grey
        cmp     al,bl
        jne     dont_dither_grey
        jmp     dither_grey
dont_dither_grey:


;/*
;**---------- BLUE --------------
;*/

        mov     cx,ax
        xor     ah,ah
        imul    ax,12                   ; Gives AH = blue bits: 0000BBBB
        add     ax,86h                  ; add half an interval (80) + fudge factor (06)
        mov     al,ah
        shr     al,2                    ; AL = intensity per pel
        and     ah,03h                  ; AH = remainder
        mov     bh,ah                   ; accumulate remainders to set flags
        mov     ipc2,al
        sub     ah,3                    ; set CF is remainder != 3
        sbb     al,-1                   ; add one to intensity if remainder = 3
        mov     ipc3,al
        add     ah,1                    ; this will set CF if remainder was 2
        adc     al,0
        mov     ipc1,al
        add     ah,1                    ; this will set CF if remainder was 1
        adc     al,0
        mov     ipc4,al

;/*
;**---------- GREEN --------------
;*/

        mov     al,ch
        xor     ah,ah
        imul    ax,28                   ; Gives AH = green bits: 000GGGGG
        add     ax,8Eh                  ; add half an interval (80) + fudge factor (0E)
        mov     al,ah
        and     al,00011100b            ; AL = intensity per pel 000GGG00
        and     ah,03h                  ; AH = remainder
        add     bh,ah                   ; accumulate remainders to set flags
        or      ipc1,al

        sub     ah,3                    ; set CF is remainder != 3
        jb      @F
        add     al,100b                 ; add one to intensity if remainder = 3
@@:
        or      ipc4,al
        inc     ah
        jnz     @F
        add     al,100b                 ; add one to intensity if remainder = 2
@@:
        or      ipc2,al
        inc     ah
        jnz     @F
        add     al,100b                 ; add one to intensity if remainder = 1
@@:
        or      ipc3,al

;/*
;**---------- RED --------------
;*/

        mov     al,bl
        xor     ah,ah
        imul    ax,24                   ; Gives AH = red bits: 000RRRRR
        add     ax,8Ch                  ; add half an interval (80) + fudge factor (0E)
        mov     al,ah
        and     al,00011100b            ; AL = intensity per pel 000RRR00
        shl     al,3                    ; AL = intensity per pel RRR00000
        and     ah,03h                  ; AH = remainder
        add     bh,ah                   ; accumulate remainders to set flags
        or      ipc3,al

        sub     ah,3                    ; set CF is remainder != 3
        jb      @F
        add     al,100000b              ; increment intensity if remainder = 3
@@:
        or      ipc2,al
        inc     ah
        jnz     @F
        add     al,100000b              ; increment intensity if remainder = 2
@@:
        or      ipc4,al
        inc     ah
        jnz     @F
        add     al,100000b              ; increment intensity if remainder = 1
@@:
        or      ipc1,al

        cmp     ipc1,112
        jb      ipc1_set
        add     ipc1,32
ipc1_set:
        cmp     ipc2,112
        jb      ipc2_set
        add     ipc2,32
ipc2_set:
        cmp     ipc3,112
        jb      ipc3_set
        add     ipc3,32
ipc3_set:
        cmp     ipc4,112
        jb      ipc4_set
        add     ipc4,32
ipc4_set:
        jmp     dithering_done

;/*
;**---------- GREY --------------
;*/

dither_grey:
        mov     ch,al                   ; for mono dithering
        xor     ah,ah
        imul    ax,124
        add     ax,0BEh                 ; add half an interval (80) + fudge factor (1E)
        mov     al,ah
        shr     al,2                    ; AL = intensity per pel
        and     ah,03h                  ; AH = remainder
        mov     bh,ah                   ; accumulate remainders to set flags
        mov     ipc2,al
        add     ipc2,112

        sub     ah,3                    ; set CF is remainder != 3
        sbb     al,-1                   ; add one to intensity if remainder = 3
        mov     ipc3,al
        add     ipc3,112

        add     ah,1                    ; this will set CF if remainder was 2
        adc     al,0
        mov     ipc1,al
        add     ipc1,112

        add     ah,1                    ; this will set CF if remainder was 1
        adc     al,0
        mov     ipc4,al
        add     ipc4,112

;/*
;** if BH is zero then it is a solid color, else not solid.
;*/

dithering_done:

        ASSUME  esi:PTR DDC

        and     [esi].ddc_pa.pa_fb,not PA_GRAY
        or      bh,bh
        jnz     dither_not_solid_color
        or      [esi].ddc_pa.pa_fb,PA_SINGLE_CLR
dither_not_solid_color:

;/*
;** write the resulting brush in the DDC
;*/

stuff_the_brush:
        mov     edi,[esi].ddc_pa.pa_abColor
        cld
        mov     al,ipc1
        mov     ah,ipc2     ; these could be a single word mov
        shl     eax,16
        mov     al,ipc1
        mov     ah,ipc2     ; these could be a single word mov
        stosd
        stosd
        add     edi,SIZE_PATTERN
        stosd
        stosd
        add     edi,SIZE_PATTERN
        stosd
        stosd
        add     edi,SIZE_PATTERN
        stosd
        stosd

        sub     edi,(SIZE_PATTERN * 6)
        mov     al,ipc3
        mov     ah,ipc4     ; these could be a single word mov
        shl     eax,16
        mov     al,ipc3
        mov     ah,ipc4     ; these could be a single word mov
        stosd
        stosd
        add     edi,SIZE_PATTERN
        stosd
        stosd
        add     edi,SIZE_PATTERN
        stosd
        stosd
        add     edi,SIZE_PATTERN
        stosd
        stosd

;/*
;** Create the monochrome dither pattern.  Right now we have our
;** original (input) RGBs as BL:CH:CL = R:G:B.  Graphics masters
;** would be happiest if we mixed about 11% Blue with 30% Red and
;** 59% Green to get the Mono Brightness.  For simplicity here we
;** will approximate this as B/8 + 2R/8 + 5G/8,
;** i.e. 12.5%, 25%, 62.5%
;*/


        xor     bh,bh
        xor     ax,ax
        xchg    al,ch

;/*
;** now BX:AX:CX = R:G:B
;** We want to produce
;** 2 parts R
;** 5 parts G
;** 1 parts B
;*/

        mov     dx,cx           ; 1 part Blue
        add     dx,bx           ; 1 part  Red
        add     dx,bx           ; 2 parts Red
        add     dx,ax           ; 1 part Green
        add     ax,ax
        add     ax,ax
        add     dx,ax           ; 4 more parts Green
        add     dx,4
        shr     dx,3            ; Gives (2/8)R + (5/8)G + (1/8)B

        lea     edi,[esi].ddc_pa.pa_abMono
        push    esi                      ; save DDC
        xor     esi,esi

        INVOKE  mono_dither                       ;ES:DI = destination

;/*
;** The mono_dither algorithm may generate an undesirable gray
;** (for 404040 and C0C0C0), so these are
;** special cased to a known grey that will be uniform across the
;** devices, and is known to have a satisfactory appearance.
;*/

        or      esi,esi                   ;Was this one of the greys?
        jz      mono_plane_dithered     ;  No
        test    esi,100b                 ;If black or white, ignore it
        jnz     mono_plane_dithered     ;  Was black or white
        add     esi,esi                   ;Special case the grey
        mov     ax,greys[esi][-2]
        sub     edi,SIZE_PATTERN
        stosw
        stosw
        stosw
        stosw
        .errnz  SIZE_PATTERN - 8
mono_plane_dithered:
        pop     esi
        mov     edi,[esi].ddc_pa.pa_abColor
        mov     ecx,BITS_PEL * SIZE_PATTERN
EXTERN FudgeDitherPattern:NEAR
        call    FudgeDitherPattern
        ret
dither_brush    ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = mono_dither
;*
;* DESCRIPTION   =
;*
;*        This function takes value between 0 - 255 and uses it to create
;*        an 8x8 pile of bits that can be used as a brush.  It does this
;*        by reducing the input value to a number between 0 - 64 and then
;*        turns on that many bits in the 8x8 field.  Thus the value 0 leads
;*        to brush of all zeros, a 255 value leads to a brush of all 1's,
;*        and a value of 128 leads to grey.
;*
;*        The algorithm is equivalent to turning on bits based on an array:
;*
;*             0 32  8 40  2 34 10 42
;*            48 16 56 24 50 18 58 26
;*            12 44  4 36 14 46  6 38         This table favors devices like
;*            60 28 52 20 62 30 54 22         the Hercules card where the
;*             3 35 11 43  1 33  9 41         aspect ratio is close to 1:1
;*            51 19 59 27 49 17 57 25
;*            15 47  7 39 13 45  5 37
;*            63 31 55 23 61 29 53 21
;*
;*             0 32 16 48  2 34 18 50
;*            24 56  8 40 26 58 10 42         This table favors devices like
;*             4 36 20 52  6 38 22 54         the CGA card where the aspect
;*            28 60 12 44 30 62 14 46         ratio is close to 2:1
;*             3 35 19 51  1 33 17 49
;*            27 59 11 43 25 57  9 41
;*             7 39 23 55  5 37 21 53
;*            31 63 15 47 29 61 13 45
;*
;*
;*        Where given a value between 0 - 64, turn on the bit if the array
;*        value is less then the desired value.
;*
;*        When dithering for color drivers, where each plane is dither
;*        independantly, this algorithm has a nice side effect in that
;*        for any value n < m, n is a proper subset of m (any bit turned
;*        on in n must have been turned on in m!).
;*
;*        For the 1:1 table, undesirable patterns are generated for light
;*        and dark grey.  These will be flagged when encountered.  For the
;*        2:1 table,  vertical lines are generated for grey, so they will
;*        be flagged when encountered.  The brush realization routines
;*        can then substitute the correct grey (we cannot do it because
;*        we do not know if the other planes of a color brush were the
;*        same pattern).
;*
;*        Reference:  A Survey of Techniques for the Display of Continuous
;*                    Tone Pictures on Bilevel Displays,;
;*                    Jarvis, Judice, & Ninke;
;*                    COMPUTER GRAPHICS AND IMAGE PROCESSING 5, pp 13-40, (1976)
;*
;*         Registers Destroyed:
;*               AX,BX,CX,DX,FLAGS
;*         Registers Preserved:
;*               none
;*
;* INPUT         = DL     =  value to dither
;*                 ES:DI --> destination
;*                 SI     =  previous grey indicator
;*
;* OUTPUT        = ES:DI --> next destination (ES:DI + SIZE_PATTERN)
;*                 DH     =  dither value (0-64)
;*                 SI     =  New grey indicator (SI << 3 || grey_indicator)
;*                             D2:D0 = 001b for dark  grey
;*                             D2:D0 = 010b for grey
;*                             D2:D0 = 011b for light grey
;*                             D2:D0 = 100b for black
;*                             D2:D0 = 101b for white
;*                             D2:D0 = 000b otherwise
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


mono_dither     PROC SYSCALL

        mov     ecx,3                             ;Will be usefull
        xor     ebx,ebx                           ;Need a zero a few times
        xchg    eax,esi                           ;Need SI later
        shl     eax,cl                            ;Update old grey flags
        inc     ecx                               ;4 will be useful later

        shr     dl,1                              ;Convert value 0-255 into
        adc     dl,bl                             ;  value 0 - 64
        shr     dl,1
        mov     bl,dl                             ;base_pattern index is D6:D2
        mov     dh,dl                             ;Return mapped value to caller in DL
        and     bl,11111100b

        lea     esi,abMonoBasePatterns[ebx]    ;Copy the base pattern
        cld
        movsw
        movsw
        sub     esi,ecx                           ;Duplicate for second half of brush
        movsw
        movsw
        xchg    eax,esi                           ;Restore grey flags

        test    dl,00000011b                      ;A base pattern?
        jnz     not_base_pattern
        test    dl,00001111b                      ;A grey to special case?
        jnz     mono_dither_exit                 ;  No, it is not
        shr     ebx,cl                            ;D6:D4 is index into grey_flags
        mov     bl,grey_flags[ebx]               ;Or in grey flag
        or      esi,ebx

;/*
;** The mono_dither algorithm may generate an undesirable gray
;** (for 404040 and C0C0C0), so these are
;** special cased to a known grey that will be uniform across the
;** devices, and is known to have a satisfactory appearance.
;*/

        or      esi,esi                           ;Was this one of the greys?
        jz      mono_plane_dithered              ;  No
        test    esi,100b                          ;If black or white, ignore it
        jnz     mono_dither_exit                 ;  Was black or white
        add     esi,esi                           ;Special case the grey
        mov     ax,greys[esi][-2]
        sub     edi,SIZE_PATTERN
        stosw
        stosw
        stosw
        stosw

        .errnz  SIZE_PATTERN - 8

mono_plane_dithered:

        jmp     mono_dither_exit

not_base_pattern:

        shr     ebx,1                             ;D5:D2 will index into abRowsMasks
        and     bl,0FEh                           ;Indexing into words, must be 0
        mov     ax,auRowsMasks[ebx]              ;AL = base row, AH = base mask
        mov     bl,al                             ;Set base row
        sub     ebx,8                             ;Adjust for di pointing past brush
        mov     al,ah                             ;Shift pattern for proper use
        shr     al,cl                             ;AH is base pattern, AL is base >> 4

        and     dl,11b                            ;Dispatch as needed
        cmp     dl,2                              ;CL = 2
        jb      first_bit
        je      second_bit

third_bit:

        or      BYTE PTR [edi][ebx][0],al        ;Set third bit

second_bit:

        or      BYTE PTR [edi][ebx][4],al        ;Set second bit

first_bit:

        or      BYTE PTR [edi][ebx][0],ah        ;Set first bit

mono_dither_exit:

                RET

mono_dither     ENDP

        .DATA

page
;/*
;**       The grey_flags table contains the values to OR into the
;**       accumulated grey flag for colors which mapped to the
;**       greys which must be special cased.
;*/


greys       label   word

            db      088h,022h                        ;Dark grey dither
            db      0AAh,055h                        ;Grey dither
            db      0DDh,077h                        ;Light grey dither

grey_flags  label   byte

            db      100b                             ;Black (00)
            db      001b                             ;Dark Grey (16)
            db      010b                             ;Grey (32)
            db      011b                             ;Light Grey (48)
            db      101b                             ;White (64)

;/*
;**       The order in which the rows of the dither are accessed
;**       are as follows:
;**
;**           0,4,0,4,2,6,2,6                       ;For low resolution
;**           1,5,1,5,3,7,3,7
;**           0,4,0,4,2,6,2,6
;**           1,5,1,5,3,7,3,7
;**           0,4,0,4,2,6,2,6
;**           1,5,1,5,3,7,3,7
;**           0,4,0,4,2,6,2,6
;**           1,5,1,5,3,7,3,7
;**
;**           0,4,0,4,2,6,2,6                       ;For high resolution
;**           0,4,0,4,2,6,2,6
;**           1,5,1,5,3,7,3,7
;**           1,5,1,5,3,7,3,7
;**           0,4,0,4,2,6,2,6
;**           0,4,0,4,2,6,2,6
;**           1,5,1,5,3,7,3,7
;**           1,5,1,5,3,7,3,7
;**
;**
;**       The order in which bits are turned on is as follows:
;**
;**           80,08,08,80,80,08,08,80              ;For low resolutions
;**           20,02,02,20,20,02,02,20
;**           20,02,02,20,20,02,02,20
;**           80,08,08,80,80,08,08,80
;**           40,04,04,40,40,04,04,40
;**           10,01,01,10,10,01,01,10
;**           10,01,01,10,10,01,01,10
;**           40,04,04,40,40,04,04,40
;**
;**           80,08,08,80,20,02,02,20              ;For high resolution
;**           20,02,02,20,80,08,08,80
;**           40,04,04,40,10,01,01,10
;**           10,01,01,10,40,04,04,40
;**           40,04,04,40,10,01,01,10
;**           10,01,01,10,40,04,04,40
;**           80,08,08,80,20,02,02,20
;**           20,02,02,20,80,08,08,80
;**
;**
;**       If you work in groups of four, the following observations
;**       can be made about how the pixels are visited:
;**
;**       The rows are always accessed as follows:
;**
;**               row n
;**               row n+4
;**               row n
;**               row n+4
;**
;**
;**       The bits are always manipulated as follows:
;**
;**               bit n
;**               bit n >> 4
;**               bit n >> 4
;**               bit n
;**
;**
;**       Since base patterns are defined at intervals of four, the
;**       algorithm for turning on the remaining 1, 2, or 3 bits
;**       can be table driven.
;**
;**
;**       The following table will contain the row and the bitmask to
;**       use for turning on the remaining 1, 2, or 3 bits of the pattern.
;**       Bits D5:D2 of the mapped color are used to index into this table.
;**       The format is:
;**
;**               db      base row, base bit
;*/



auRowsMasks label   word

if SCAN_CNT NE 768

        db      0,80h
        db      2,80h
        db      1,20h
        db      3,20h
        db      0,20h
        db      2,20h
        db      1,80h
        db      3,80h
        db      0,40h
        db      2,40h
        db      1,10h
        db      3,10h
        db      0,10h
        db      2,10h
        db      1,40h
        db      3,40h

else

        db      0,80h
        db      2,20h
        db      0,20h
        db      2,80h
        db      1,40h
        db      3,10h
        db      1,10h
        db      3,40h
        db      0,40h
        db      2,10h
        db      0,10h
        db      2,40h
        db      1,80h
        db      3,20h
        db      1,20h
        db      3,80h

endif


abMonoBasePatterns      label                    byte

if SCAN_CNT NE 768

        db      000h,000h,000h,000h
        db      088h,000h,000h,000h
        db      088h,000h,088h,000h
        db      088h,022h,088h,000h
        db      088h,022h,088h,022h
        db      0AAh,022h,088h,022h
        db      0AAh,022h,0AAh,022h
        db      0AAh,0AAh,0AAh,022h
        db      0AAh,0AAh,0AAh,0AAh
        db      0EEh,0AAh,0AAh,0AAh
        db      0EEh,0AAh,0EEh,0AAh
        db      0EEh,0BBh,0EEh,0AAh
        db      0EEh,0BBh,0EEh,0BBh
        db      0FFh,0BBh,0EEh,0BBh
        db      0FFh,0BBh,0FFh,0BBh
        db      0FFh,0FFh,0FFh,0BBh
        db      0FFh,0FFh,0FFh,0FFh

else

        db      000h,000h,000h,000h
        db      088h,000h,000h,000h
        db      088h,000h,022h,000h
        db      0AAh,000h,022h,000h
        db      0AAh,000h,0AAh,000h
        db      0AAh,044h,0AAh,000h
        db      0AAh,044h,0AAh,011h
        db      0AAh,055h,0AAh,011h
        db      0AAh,055h,0AAh,055h
        db      0EEh,055h,0AAh,055h
        db      0EEh,055h,0BBh,055h
        db      0FFh,055h,0BBh,055h
        db      0FFh,055h,0FFh,055h
        db      0FFh,0DDh,0FFh,055h
        db      0FFh,0DDh,0FFh,077h
        db      0FFh,0FFh,0FFh,077h
        db      0FFh,0FFh,0FFh,0FFh

endif

end
