;*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.;
;*****************************************************************************/
ifdef DCAF
        page    ,132
;-----------------------------Module-Header-----------------------------;
; Module Name:  EXPAND.ASM
;
; This file contains the expansion code used by SetScreenBits.
;
; Created: Mon  3-Aug-1992
; Author:  John Batty, Data Connection Ltd.
;
;
;-----------------------------------------------------------------------;

        .xlist
?DF     equ     1       ; don't define _TEXT segment
        include cmacros.inc

INCL_GPIBITMAPS         equ     1
        include os2.inc
        include eddinclt.inc
        include eddhtype.inc
        include dcaf.inc
        include dcafmac.inc


;***********************************************************************
; Cope with Microsoft's naming convention
;***********************************************************************
Spad                      equ <_Spad>
convert_row_4pkext_4pkint equ<_convert_row_4pkext_4pkint>
convert_row_4pl_4pk       equ<_convert_row_4pl_4pk>
convert_row_4pk_8pk       equ<_convert_row_4pk_8pk>
convert_row_4pk_16pk      equ<_convert_row_4pk_16pk>
IFDEF S3
convert_row_4pk_24pk      equ<_convert_row_4pk_24pk>
ENDIF

;-----------------------------------------------------------------------

; External access for this module
extrn Spad                      :dword
extrn convert_row_4pkext_4pkint :proc
extrn convert_row_4pl_4pk       :proc
extrn convert_row_4pk_8pk       :proc
extrn convert_row_4pk_16pk      :proc
IFDEF S3
extrn convert_row_4pk_24pk      :proc
ENDIF

        .list


        .386p

;***********************************************************************
; The ExpandRectangle macro
;***********************************************************************
ExpandRectangle macro field_size
        local   next_row
        local   repeated_scanline
        local   copy_another_row
        local   not_scanline_repeat
        local   no_conversion
        local   decode_cell
        local   non_repeating_data
        local   not_mid_zero
        local   next_cell
        local   no_conversion_required
        local   row_finished
        local   panic_exit
        local   done_all_rows_exit
        local   final_exit

; There now follows the body of the function !

        localD  cDataFieldsToDo     ; Data fields left on current row
        localD  ulSavedEDI          ; Local save variable for edi


;***********************************************************************
; Set up @FLD8 and @FLD16 macros according to the data field size
; passed to the macro.
;***********************************************************************
if (field_size eq 8)
        INITFIELDSIZE    8
else
  if (field_size eq 16)
        INITFIELDSIZE    16
  else
        .err Invalid field size passed to ExpandRectangle function!
  endif
endif

cBegin
        ;***************************************************************
        ; Some clarification of terminology may be in order here.
        ; 
        ; A data field  - A 8 bits for 4bpp formats, 16 bits for all others.
        ; A cell        - A number of fields representing a run of pels.
        ;                 This can be:
        ;                   - repeating data
        ;                   - non-repeating data
        ;                   - repeated scanlines
        ;                   - repeated scanline pairs
        ; A scanline    - A whole (full-width) line of screen/bitmap data
        ; A row         - A single row of the rectangle being decompressed.
        ; 
        ;***************************************************************

        ; We need our string instructions to increment
        cld

        ; Get the pointer to the compressed (source) data in esi.
        mov     esi,Spad.ssb_pNextSrcByte

        ; Get the pointer to the first destination (bitmap) byte in edi.
        mov     edi,Spad.ssb_pTopDstRow

        ;***************************************************************
        ; This is the top of the loop that processes each row of the
        ; rectangle.
        ; 
        ; esi -> the current unit of compressed data
        ; edi -> the leftmost unit of current row
        ;***************************************************************
next_row:
        cmp     Spad.ssb_cyRowsToDo, 0
        je      done_all_rows_exit

        mov     eax, Spad.ssb_cDataFieldsPerRow
        mov     cDataFieldsToDo, eax

        ;***************************************************************
        ; Get the next unit to expand from the source buffer.
        ;***************************************************************
        ; Zero eax so we don't have to worry about the value of the
        ; upper bits.
        xor     eax,eax

        ; Read the next data field from the compressed source data.
@FLD8  <lodsb>
@FLD16 <lodsw>

        ; Test for zero (we cleared the upper bits earlier).
        or      eax,eax

        ; A zero field means "repeat scanline" or "repeat scanline pair".
        jnz     short not_scanline_repeat

        ;***************************************************************
        ; We have some form of scanline repeat code.
        ;***************************************************************
        ; Read the next data field, which will tell us whether this
        ; is a repeated scanline, or a repeated scanline pair.
@FLD8  <lodsb>
@FLD16 <lodsw>

        ; Set up ebx to point to the previous row.
        mov     ebx,edi                     ; Current row
        sub     ebx,Spad.ssb_cbBytesPerScanline

        ; See if we are dealing with a repeated pair.
        or      eax,eax
        jnz     short repeated_scanline

        ; We have a repeated scanline pair.
        ; Move ebx up by another row.
        sub     ebx,Spad.ssb_cbBytesPerScanline

        ; Read in the count.
@FLD8  <lodsb>
@FLD16 <lodsw>

        ; The count indicates the number of repeated scanline pairs.
        ; Multiply by two to get the number of repeated scanlines.
        add     eax,eax

repeated_scanline:
ifdef FIREWALLS
        ; Check that we have at least 1 scan!
        or      eax,eax
        jnz     @F
        int     3
@@:
        ; Check that we don't have more scans than expected!
        cmp     Spad.ssb_cyRowsToDo,eax
        jae     short @F
        int     3
        jmp     panic_exit
@@:
endif ; FIREWALLS
        sub     Spad.ssb_cyRowsToDo,eax

        ; We now have:
        ;   ebx -> the scanline to be copied
        ;   eax = the number of scanlines to copy
        ;   esi -> next data field in source (compressed) data buffer
        ;   edi -> current row in destination bitmap

        ; We want the source pointer(currently in ebx) in esi, but must
        ; not lose the current esi value!
        xchg    esi,ebx

copy_another_row:
        mov     ecx,Spad.ssb_cbBytesPerDstRow

        ; We will always be moving an even number of bytes, so
        ; we can hurry things up by moving words rather than bytes.
ifdef   FIREWALLS
        test    ecx,1
        jz      short @F
        int     3
@@:
endif  ;FIREWALLS
        ; Number of words = number of bytes/2
        shr     ecx,1

        ; Copy the row!
        rep     movsw

ifdef S3
        ; The 24bpp case may have an odd number of bytes to copy that
        ; the previous shift would have clobbered.
        jnc     @F
        movsb
@@:
endif

        ; Update the row pointers to point to the beginning of the next row.
        add     edi,Spad.ssb_cbEndOfRowInc
        add     esi,Spad.ssb_cbEndOfRowInc

        ; Update the row count, and loop back if there are more rows to copy.
        dec     eax
        jnz     short copy_another_row

        ; Recover our source pointer
        mov     esi,ebx

        ; Go do the next one!
        jmp     short next_row



not_scanline_repeat:
        ;***************************************************************
        ; If the source and destination data formats differ then we
        ; expand the data into an intermediate buffer, and then
        ; convert the data from there into the destination bitmap.
        ; Alter the destination pointer here if necessary.
        ;***************************************************************
        mov     edx,Spad.ssb_fConversionRequired
        or      edx,edx
        jz      short no_conversion

        ; Conversion is required.
        ; Save the current destination (bitmap) pointer.
        mov     ulSavedEDI,edi

        ; Load edi with the address of our intermediate buffer.
        mov     edi,Spad.ssb_pConvertBuffer1

no_conversion:
decode_cell:
        ; Test the top bit of the data field to see if it is a
        ; "repeating data" (bit=0) or "non-repeating" (bit=1) data cell.
@FLD8  <or      al,al>
@FLD16 <or      ax,ax>
        js      short non_repeating_data

        ;***************************************************************
        ; We have a repeating data cell.
        ;***************************************************************
        ; Move the repeat count into ecx.
        mov     ecx,eax

ifdef   FIREWALLS
        cmp     ecx,cDataFieldsToDo
        jle     short @F
        int     3
@@:
endif   ;FIREWALLS
        ; Update the number of fields left to process on this row.
        sub     cDataFieldsToDo, ecx

        ; Fetch the repeated data field...
@FLD8  <lodsb>
@FLD16 <lodsw>
        ; ... and write it to the destination the specified number of times.
@FLD8  <rep     stosb>
@FLD16 <rep     stosw>

        jmp     short next_cell


non_repeating_data:
        ;***************************************************************
        ; We have a non-repeating data cell.
        ;***************************************************************
        ; Move the non-repeating field count into ecx (clearing the top bit)
        mov     ecx,eax
@FLD8  <and     ecx, 7Fh>
@FLD16 <and     ecx, 7FFFh>

        ; Check that the number of fields does not exceed the number
        ; remaining on this row.
ifdef FIREWALLS
        cmp     ecx,cDataFieldsToDo
        jle     short @F
        int     3
@@:
endif ; FIREWALLS

        ; Update the number of data fields on this row.
        sub     cDataFieldsToDo, ecx

        ; Copy the specified number of data fields.
@FLD8  <rep     movsb>
@FLD16 <rep     movsw>


next_cell:
        ;***************************************************************
        ; If we have processed all of the data fields within the
        ; current scan then skip out now.
        ;***************************************************************
        cmp     cDataFieldsToDo, 0
        jz      short row_finished

        ; Zero eax so we don't have to worry about the value of the
        ; upper bits.
        xor     eax,eax

        ; If data fields are 8 bits then read a byte.
@FLD8  <lodsb>

        ; If data fields are 16 bits then read a word.
@FLD16 <lodsw>

ifdef FIREWALLS
        ; We are part way through a scan, so we do not expect to find
        ; a repeated scanline code.
        ; Check here to make sure!
        or      eax,eax
        jnz     short not_mid_zero
        int     3
        jmp     short panic_exit
not_mid_zero:
endif ; FIREWALLS

        ; Loop back to process the cell
        jmp     short decode_cell



row_finished:
        ; Update the number of rows left to process.
        dec     Spad.ssb_cyRowsToDo

        ; Now see if we need to convert the row that we have expanded.
        mov     eax,Spad.ssb_fConversionRequired
        or      eax,eax
        jz      short no_conversion_required

        ; We need to convert the data.
        ; The data has been expanded into a buffer.
        ; Now call an appropriate function to convert the data
        ; and write it to the destination bitmap.
        push    esi             ; Save current source pointer
        mov     esi,Spad.ssb_pConvertBuffer1
        mov     edi,ulSavedEDI  ; Recover destination pointer
        mov     ecx,Spad.ssb_cPelsPerRow
        mov     ebx,ecx
        shr     ebx,3           ; Convert pel count into plane offset
        call    Spad.ssb_pfnConvertRow
        pop     esi             ; Restore current source pointer



no_conversion_required:
        ;***************************************************************
        ; The row is now fully decompressed into the destination
        ; bitmap.
        ; Move to the next scan line in the destination, and loop back
        ; to process the next row.
        ;***************************************************************
        add     edi,Spad.ssb_cbEndOfRowInc
        jmp     next_row


ifdef   FIREWALLS
panic_exit:
        ; Zero eax to indicate that we had problems.
        xor     eax,eax
        jmp     short final_exit
endif   ;FIREWALLS

done_all_rows_exit:
        ; Return the source pointer value.
        mov     eax,esi

final_exit:
endm






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


;--------------------------Internal-Routine-----------------------------;
; ExpandRect8 and ExpandRect16 are the two rectangle expansion functions.
; The function of both routines is the same, except that they deal
; with 8 and 16 bit data fields respectively.
; They are split into separate routines to optimise performance.
;-----------------------------------------------------------------------;

cProc   ExpandRect8,<PUBLIC>,<esi,edi>

        ExpandRectangle 8
cEnd

cProc   ExpandRect16,<PUBLIC>,<esi,edi>

        ExpandRectangle 16
cEnd


cProc   convert_row_4pl_8pk,<PUBLIC>
localD  cbPelsPerSrcRow
localD  pDest
localD  cDataFieldsPerPlane

cBegin
        ; Save input values.
        mov     pDest,edi
        mov     cbPelsPerSrcRow,ecx

        ; Convert the data from 4bpp planar to 4bpp packed
        ; Set up the required parameters.
        mov     edi,Spad.ssb_pConvertBuffer2

        ; The distance between consecutive planes is:
        ;   = length of row in bytes / 4
        ;   = length of row in pels / 8
        mov     ebx,ecx         ; Length of row in pels
        shr     ebx,3

        ; Do the conversion
        cCall   convert_row_4pl_4pk

        ; Convert the data from 4bpp packed to 8bpp packed
        mov     esi,Spad.ssb_pConvertBuffer2
        mov     edi,pDest
        mov     ecx,cbPelsPerSrcRow

        ; Do the conversion
        cCall   convert_row_4pk_8pk

cEnd

cProc   convert_row_4pl_16pk,<PUBLIC>
localD  cbPelsPerSrcRow
localD  pDest
localD  cDataFieldsPerPlane

cBegin
        ; Save input values.
        mov     pDest,edi
        mov     cbPelsPerSrcRow,ecx

        ; Convert the data from 4bpp planar to 4bpp packed
        ; Set up the required parameters.
        mov     edi,Spad.ssb_pConvertBuffer2

        ; The distance between consecutive planes is:
        ;   = length of row in bytes / 4
        ;   = length of row in pels / 8
        mov     ebx,ecx         ; Length of row in pels
        shr     ebx,3

        ; Do the conversion
        cCall   convert_row_4pl_4pk

        ; Convert the data from 4bpp packed to 16bpp packed
        mov     esi,Spad.ssb_pConvertBuffer2
        mov     edi,pDest
        mov     ecx,cbPelsPerSrcRow

        ; Do the conversion
        cCall   convert_row_4pk_16pk

cEnd


IFDEF S3

cProc   convert_row_4pl_24pk,<PUBLIC>
localD  cbPelsPerSrcRow
localD  pDest
localD  cDataFieldsPerPlane

cBegin
        ; Save input values.
        mov     pDest,edi
        mov     cbPelsPerSrcRow,ecx

        ; Convert the data from 4bpp planar to 4bpp packed
        ; Set up the required parameters.
        mov     edi,Spad.ssb_pConvertBuffer2

        ; The distance between consecutive planes is:
        ;   = length of row in bytes / 4
        ;   = length of row in pels / 8
        mov     ebx,ecx         ; Length of row in pels
        shr     ebx,3

        ; Do the conversion
        cCall   convert_row_4pl_4pk

        ; Convert the data from 4bpp packed to 16bpp packed
        mov     esi,Spad.ssb_pConvertBuffer2
        mov     edi,pDest
        mov     ecx,cbPelsPerSrcRow

        ; Do the conversion
        cCall   convert_row_4pk_24pk

cEnd


ENDIF


cProc   convert_row_4pl_4pkint,<PUBLIC>
localD  cbPelsPerSrcRow
localD  pDest
localD  cDataFieldsPerPlane

cBegin
        ; Save input values.
        mov     pDest,edi
        mov     cbPelsPerSrcRow,ecx

        ; Convert the data from 4bpp planar to 4bpp packed (external)
        ; Set up the required parameters.
        mov     edi,Spad.ssb_pConvertBuffer2

        ; The distance between consecutive planes is:
        ;   = length of row in bytes / 4
        ;   = length of row in pels / 8
        mov     ebx,ecx         ; Length of row in pels
        shr     ebx,3

        ; Do the conversion
        cCall   convert_row_4pl_4pk

        ; Convert the data from 4bpp packed external to internal
        mov     esi,Spad.ssb_pConvertBuffer2
        mov     edi,pDest
        mov     ecx,cbPelsPerSrcRow

        ; Do the conversion
        cCall   convert_row_4pkext_4pkint


cEnd


_TEXT   ends

endif ; DCAF

end
