Jump to content

New XGA Display Driver Entry Points

From EDM2
Revision as of 22:14, 30 May 2023 by Ak120 (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Owner: Antonio Zivoli

Introduction

This document describes the additional entry points implementing the OS/2 Presentation Manager XGA Display Driver new capabilities to efficiently track the screen region that is updated by Presentation Manager drawing functions, and to handle the related data.

The XGA Display Driver modifications can be split into two distinct areas:

1) Efficient accumulation maintenance and querying of clipped screen bounds of all screen drawing operations. This is provided by the following new functions:

  • OpenScreenChangeArea
  • GetScreenChangeArea
  • CloseScreenChangeArea.

2) Compression of data from a specified area of the screen into a memory buffer, and (usually after transmission over a network) decompression of that data into an internal memory bitmap. Data conversion must be performed between the different modes (bpp) and the different data format (planar or packed), in order to allow the data interchange between XGA and VGA, between XGAs working in different internal formats, and in general between all the Display Drivers providing these entry points.

This is provided by the following new functions:

  • GetScreenBits
  • SetScreenBits.

The majority of the new code is contained within new modules that are simply linked in with the base Driver.

The areas of the core Driver code that must be modified are:

  • the Dispatch Table, which contains the new Entry Points
  • all drawing functions, which contain extra tests to determine whether they should accumulate clipped screen bounds and, if necessary, code to calculate the clipped bounding rectangle
  • Seamless Windows cursor exclusion code, which is modified to call the new bounds accumulation routine.

Dispatch Table

In the EDDEFLDB.C, where the dispatch table gets filled in with the Display Driver entry points, the 5 new entry points get included into the Driver Dispatch Table with the following Function Numbers:

GreOpenScreenChangeArea   0x4012
GreGetScreenChangeArea    0x4013
GreCloseScreenChangeArea  0x4014
GreGetScreenBits          0x401D
GreSetScreenBits          0x401E

Screen bounds

"Traditional" Presentation Manager bounds are unclipped, and use a single rectangle to define their limits.

In order to better define the bounding area, the changed XGA Display Driver has the ability to maintain one or more clipped, multirectangle regions (or Screen Change Areas) that are updated to indicate areas on the screen that have been drawn to.

This ability is provided by the following changes to the base Driver:

  • Three new entry points:
    • OpenScreenChangeArea (Section 6.1)
    • GetScreenChangeArea (Section 6.2)
    • CloseScreenChangeArea (Section 6.3)
to create, query and delete Screen Change Areas.
  • Two bounds accumulation functions, which add a single rectangle to all of the currently active SCAs (Section 2.3).
  • An extra flag test in the path of every Display Driver drawing function (for COM_SCR_BOUND in the high-order word in the last parameter of the GreXX calls - FunN). If this flag is set and the drawing operation is going to the screen, then the drawing function passes a clipped bounding rectangle of the drawing primitive to the bounds accumulation functions described above. The code required to do this is very Driver specific (Section 2.3).
  • Interception of Windows cursor exclusion calls and passing the supplied exclusion rectangle to the new bounds accumulation function (Section 5.0)

Compression/decompression

The bounding functions described above efficiently track the regions on the screen that are updated by Presentation Manager drawing functions.

The XGA Display Driver also provides the ability to compress, decompress and (if necessary) convert the format of screen data.

This ability is provided by two new entry points:

  • GetScreenBits (Section 6.4)
  • SetScreenBits (Section 6.5).

Screen Change Areas

SCA definition

A key part of the modifications concerns the maintenance and tracking of SCAs. These Areas track regions of the screen that are altered by Display Driver drawing routines as efficiently as possible. This is done by using multiple rectangles to define the area, rather than just the usual single bounding rectangle provided by "standard" Presentation Manager bounding functions.

SCAs are maintained within the Driver in a SCA structure, defined in the new DCAF.H file:

typedef struct _SCA { /* sca */
struct _SCA *     pscaNext;                       /* Linked list pointer    */
ULONG             cRects;                         /* No. rects in area      */
RECTL             arcl[MAX_SCA_RECTS+1];          /* Rectangle dimensions   */
ULONG             aulRectSize[MAX_SCA_RECTS+1];   /* Cached rectangle sizes */
} SCA;

Instances of this structure are dynamically created/destroyed upon calls to OpenScreenChangeArea/CloseScreenChangeArea.

A global variable, pStartSCA, points to the latest created SCA instance. If pStartSCA is null then there are no active SCAs.

All SCA instances are linked together in a list using the pscaNext field of the SCA structure. A null value in this field indicates the end of the list (first created SCA). For example:

Memory loc.
              -------------------       pStartSCA = 250;
              | pscaNext = 200  |
              |                 |
              |      4th SCA    |
   0x250      -------------------

              -------------------
              | pscaNext = 150  |
              |                 |
              |      3th SCA    |
   0x200      -------------------

              -------------------
              | pscaNext = 100  |
              |                 |
              |      2nd SCA    |
   0x150      -------------------

              -------------------
              | pscaNext = 0    |
              |                 |
              |      1st SCA    |
   0x100      -------------------

Each SCA instance can store multiple rectangles, up to MAX_SCA_RECTS (14), which define the area on the screen that has changed since the SCA was created or last queried. These rectangles are stored in the array arcl[].

The number of rectangles stored within the array is kept in the cRects field (which will never exceed MAX_SCA_RECTS). If cRects is zero then the SCA is a null area - the initial state.

The remaining field in the structure, aulRectSize[], is an array containing the sizes of the rectangles in arcl[]. This is not strictly necessary, because the sizes can be calculated on the fly (using the dimensions in arcl[]). However, when accumulating a rectangle into a SCA, the size of each of the rectangles is frequently needed. Caching the rectangle sizes in this array saves us having to recalculate the sizes every time, resulting in better performance.

The SCA structure defines space for (MAX_SCA_RECTS+1) rectangles, but only MAX_SCA_RECTS are ever used to define the SCA. The extra rectangle is used to simplify the routine that accumulates rectangles into the SCA (see Section 2.3).

Creating a new SCA

This task is accomplished by OpenScreenChangeArea (SCRAREA.C).

To create a new SCA:

  • Allocate memory for the new SCA instance.
  • Set the cRects field to be zero.
  • Set the pStartSca global variable to point to the new SCA address.
  • Link the instance into the linked list of SCAs.

The new created SCA will be identified by a 32 bit handle, actually the address of the SCA location in the Display Driver.

Accumulating a rectangle into a SCA

All Display Driver functions that draw to the screen are modified to accumulate clipped bounding rectangles into all active SCAs when necessary. The drawing functions determine whether they should do this by examining the FunN (Function Number/COM_ flags) parameter. If the COM_SCR_BOUND flag is set, and the function is drawing to the screen, then bounding rectangles are accumulated into the active SCAs; otherwise no accumulation takes place. The setting of COM_SCR_BOUND is controlled by the Open/CloseScreenChangeArea functions (see Section 6 for details).

In every drawing call the following test is performed:

    if (DCAFBoundsRequired(FunN))
    {
      accumulate the bound

    }

where the DCAFBoundsRequired macro is defined in DCAFEXTF.H as follows:

#define DCAFBoundsRequired(FunN)      \
    ( (FunN & COM_SCR_BOUND) &&       \    /*  COM_SCR_BOUND is set          */
      (FunN & COM_DRAW) &&            \    /*  COM_DRAW is set               */
      (pdc->DCIDCType == OD_DIRECT) )      /*  the destination is the screen */

When the changed area tracking is not needed, COM_SCR_BOUND will never be set and the only difference in operation/performance of the XGA base Driver will be one additional check of the COM_SCR_BOUND flag per drawing function i.e. negligible.

Two routines (in SCBOUNDS.C) perform the bounding accumulation:

  • VOID AccumulateScreenBounds(PRECTL prclArgBound);
  • VOID AccumulateScreenBoundsThroughClips( pDevRect pBoundCoords, ULONG ulCoordType );

AccumulateScreenBounds:

This routine is called by the drawing calls that are able to pass pre-clipped bounding rectangle (actually only SetPel and PolyShortLine). Its task is to take the passed rectangle, and accumulates it into all the current SCAs. The passed rectangle is in exclusive SCREEN coords.

AccumulateScreenBoundsThroughClips:

This function takes the supplied (unclipped) bounding rectangle, intersects it with each of the clip rectangles in the DC and accumulates each of the clipped bounds into the active SCAs.

The supplied bounding rectangle can be in:

16-bit AI coordinates (COORD_AI)
(origin is top-left of screen)
16-bit Device Coords (COORD_DEVICE_WORD)
(origin is current DC origin)
32-bit Screen Coords
(origin is bottom-left of screen)

The ulCoordType parameter specifies which of these coords are being supplied.

This routine can be called from the same point in drawing functions as the ordinary (unclipped) bounds are accumulated. This minimizes the complexity and changes required in the main drawing code.

Before accumulating the bounding rectangle, according to the following accumulation algorithm, the coordinate conversion (if required) to exclusive SCREEN coordinates and the clipping of the passed rectangle must be performed. The clipping can be done against the DC cache clip rectangles, or may require to call back the Graphics Engine to get the clip set.

When a rectangle is added into a SCA, it is done so in such a way as to minimize the increase in area of the SCA.

The following algorithm does this:

for (pscaCurrent = each SCA in the linked list)
:
: // First check whether the new rect is already contained within this SCA
: for (rclCurrent = each rectangle in current SCA)
: : if rclNew lies completely within rclCurrent
: : : no more work - skip straight to next SCA
: : endif
: endfor

: // We have to add the rectangle to the SCA.
: // First see if there is free space for the rectangle within the SCA.
: if pscaCurrent->cRects < MAX_SCA_RECTS
: : copy rect into SCA
: : calculate size and store in SCA
: : increment pscaCurrent->cRects
: else
: : // All of the SCA rects are used.
: : // Copy the new rect into the SCA at position (MAX_SCA_RECTS+1) and the
: : // problem then becomes:
: : // We have MAX_SCA_RECTS+1 rectangles, which we have to reduce
: : // to MAX_SCA_RECTS by merging two of the rectangles into a single
: : // rectangle.
: : // The pair of rects that we merge are the two that will cause the smallest
: : // increase in area.
: : initialise ulSmallestAreaIncrease to be maximum possible value
: : for (iRect1 = each rectangle in the SCA)
: : : for (iRect2 = iRect1+1 to MAX_SCA_RECTS+1)
: : : // This inner loop is the performance bottleneck.
: : : // Make it as fast as possible, if you can!!
: : : : if area increase of (iRect1,iRect2) merged < ulSmallestAreaIncrease
: : : : : set ulSmallestAreaIncrease to be area increase of (iRect1,iRect2)
          merged
: : : : : set best pair of rects to be (iRect1,iRect2)
: : : : endif
: : : endfor
: : endfor
: :
: : merge best pair of rects found into the slot originally occupied by Rect1
: : if rclNew was not one of those merged
: : : copy rclNew into vacant slot made by merging pair
: : endif

: endif
endfor

When the changed area tracking is active, this function is called by every function that draws to the screen. The routine must therefore be as efficient as possible (particularly in the inner loop) to minimize the hit on performance.

Deleting a SCA

Task performed by CloseScreenChangeArea (SCRAREA.C). The steps taken are:

  • Unlink the SCA instance from the linked list of SCAs.
  • Free the memory for the SCA instance.

In the usual example, if we close the 2nd SCA:

Memory loc.

             -------------------       pStartSCA = 250;
             | pscaNext = 200  |
             |                 |
             |      4th SCA    |
  0x250      -------------------
             -------------------
             | pscaNext = 150  |
             |                 |
             |      3th SCA    |
  0x200      -------------------
             -------------------
             | pscaNext = 100  |
             |                 |
             |      2nd SCA    |
  0x150      -------------------
             -------------------
             | pscaNext = 0    |
             |                 |
             |      1st SCA    |
  0x100      -------------------

we will get:

Memory loc.
              -------------------       pStartSCA = 250;
              | pscaNext = 200  |
              |                 |
              |      4th SCA    |
   0x250      -------------------

              -------------------
              | pscaNext = 100  |
              |                 |
              |      3th SCA    |
   0x200      -------------------

              -------------------
              | pscaNext = 0    |
              |                 |
              |      1st SCA    |
   0x100      -------------------

If the last remaining SCA is being freed, then pStartSCA is set to NULL.

If the latest created SCA is being freed, then pStartSCA is set to the address of the SCA created immediately before it.

Compression/Decompression

Compressed data format

Task performed by CompressRect in COMPRESS.ASM.

The compressed data that is passed between Display Drivers uses a private format (i.e. no external application/program has the right to examine, alter, or make any assumptions about the content of this data). This allows the compression method to be improved in later versions of the Driver.

Definitions of the data structures are:

1) PACKET HEADER

dd   total_data_packet_length (including header)
dw   data_format

2) RECTANGLE HEADER

dw   xLeft
dw   yBottom
dw   xRight
dw   yTop

3) RECTANGLE DATA

The rectangle data is split into individual rows.

Each row is split into run-length encoded blocks("cells"), each of which comprises a length field followed by one or more data fields.

The size of both the length and data fields varies according to the format of the data being transmitted (as specified by the data_format field in the packet header):

  • 4bpp: field size is one byte (8 bits)
  • 8bpp, 16bpp: field size is two bytes (16 bits)

The following encoding rules are used:

If the length field contains a "positive" value (most significant bit not set) then the following single data field is repeated (length) times.
If the data field size is 8 bits, this value will be limited to a maximum of 127.
If the length field contains a "negative" value (most significant bit set) then (length - m.s. bit) fields of non-repeating data follow.
If the data field size is 8 bits, this value will be limited to a maximum of 127.
If the length field is zero and the following field is non-zero, the non-zero field is a count of the number of times that the single previous row is repeated.
If the data field size is 8 bits, this value will be limited to a maximum of 127. This will only appear as the first cell in a row, and only after there has been at least one row of data.
If the length field is zero and the following field is zero, the next (i.e. third) field is a count of the number of times that the previous pair of rows are repeated.
If the data field size is 8 bits, this value will be limited to a maximum of 127. This will only appear as the first cell in a row, and only after there have been at least two rows of data.
Example

The following example shows the hexadecimal values of an 8bpp compressed bitmap:

0003 0004 00FA 0405 0706 0802 0104 0903 0001 .... 0000 0003 0000 0000 0004 ...
 lf   df   lf   df   df   df   df   df   df        lf   df   lf   df   df
  cell1               cell2                         celln      celln+1

This bitmap would expand as follows (two-digit values represent a color index for a single pixel):

 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row1

                                  do three more identical rows (celln):

 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row2
 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row3
 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row4

                             do four pairs of identical couples (celln+1)

 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row5
 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row6

 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row7
 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row8

 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row9
 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row10

 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row11
 00 04 00 04 00 04 04 05 07 06 08 02 01 04 09 03 00 01 ............ row12

The following example shows the hexadecimal values of an 4bpp compressed bitmap:

 03 04  FA 04 05 07 06 08 02 ...........  00 03  00 00 04  ...
 lf df  lf df df df df df df              lf df  lf df df
 cell1        cell2                       celln  celln+1

This bitmap would expand as follows (one-digit value represents a color index for a single pixel):

 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row1

                                      do three more identical rows (celln):

 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row2
 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row3
 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row4

                              do four pairs of identical couples (celln+1)

 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row5
 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row6

 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row7
 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row8

 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row9
 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row10

 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row11
 0 4 0 4 0 4 0 4 0 5 0 7 0 6 0 8 0 2 ............ row12

Standard Golomb Run Length Encoding compression is inefficient at compressing 2x2 dither patterns, which are commonly used by Presentation Manager Display Drivers. The modified compression algorithm handles these patterns efficiently because:

  • for 4bpp and 8bpp the data field size is such that when compressing a row, pairs of adjacent pels are put in each data field. Note that there is no dithering at 16bpp.
  • when searching for duplicate scanlines, the algorithm also searches for duplicate scanline pairs which will match and compress patterns that repeat on alternate scanlines.

The actual pixel data is stored in Motorola format i.e. the most significant bits of a byte contain the leftmost pels. So, if we have a pair of pixels (PEL1,PEL2):

for the 4bpp format:

- PEL1 goes in bits 7..4
- PEL2 goes in bits 3..0

for the 8bpp format:

- PEL1 goes in bits 15..8
- PEL2 goes in bits  7..0

All 4bpp data is defined as indices into the standard VGA palette. All 8bpp data is defined as indices into the standard XGA palette. All 16bpp data is defined as XGA format 16-bit color values. (See Appendix A for details of these formats).

All changed Drivers must convert their own internal format into one of these "standard" formats before transmission (see Section 4.0).

Data conversion

The changed Display Drivers use differing internal data formats:

VGA
4bpp planar
XGA
4bpp packed, 8bpp packed, 16bpp packed

When data is transmitted between Display Drivers it is done so at the lowest bpp of the two Drivers communicating (or lower e.g. a pair of 16bpp XGA Drivers could communicate at 4bpp to reduce the amount of data transmitted).

Therefore the conversion routines required by the Display Driver are:

XGA (4bpp, 8bpp, 16bpp packed)

internal format   required format

16bpp packed  ->  8bpp packed (compression)
16bpp packed  ->  4bpp packed (compression)
16bpp packed  ->  4bpp planar (compression)
 8bpp packed  ->  8bpp packed (compression)
 8bpp packed  ->  4bpp packed (compression)
 8bpp packed  ->  4bpp planar (compression)
 4bpp packed  ->  4bpp planar (compression)

         external data format  internal format

(decompression)  8bpp packed -> 16bpp packed
(decompression)  8bpp packed ->  8bpp packed
(decompression)  4bpp packed -> 16bpp packed
(decompression)  4bpp packed ->  8bpp packed
(decompression)  4bpp planar -> 16bpp packed
(decompression)  4bpp planar ->  8bpp packed
(decompression)  4bpp planar ->  4bpp packed

The conversions from packed to planar and vice versa are assisted by using a lookup table to "split" packed bytes into bits that can conveniently be reassembled into planar format (and vice versa).

All conversions to planar format are done by first converting the bits per pel down to 4 (still in packed format) and then performing an optimized 4bpp packed to planar conversion. With conversions from 4bpp planar, a similar (reverse) process is performed - converting from 4bpp planar to 4bpp packed and then to the required packed destination format.

Conversions from 4bpp and 8bpp use a lookup table to efficiently translate the colors. Conversions from 16bpp cannot use a direct lookup table, because its size is prohibitive. Therefore colors have to be searched for on a "nearest color" basis in the destination color table. This is much slower than a simple table lookup. To improve performance, a cache of the most recently calculated colors is kept, which saves having to repeatedly recalculate commonly used colors.

Seamless Windows support

In OS/2 2.1, Seamless Windows is supported by allowing the Windows Display Driver to draw directly on the Presentation Manager screen. This means that Seamless Window updates do not go through the Presentation Manager drawing functions, and therefore will not update the active SCA in the usual way. Seamless Windows therefore requires special treatment.

Prior to drawing on the PM screen, the Seamless Windows Driver calls the PM Driver through an exported Entry Point (SeamlessExcludeCursor in SEAMTHNK.ASM) to exclude the PM cursor from the area that it is about to draw in.

Modified XGA Display Driver intercepts this call, and passes the rectangle coordinates to the bounds accumulation routine.

During PM Display Driver initialization, Seamless Windows must be granted addressability to all data and code that it will access during the call to the SeamlessExcludeCursor routine. Since we want to add the supplied rectangle to all active SCAs, which could reside anywhere in the Display Driver heap, what we do is having a single, static SSA, called scaSeamless.

All Seamless bounding rectangles will be accumulated into this SCA, and then merged with the contents of the normal SCAs when one is queried (using GetScreenChangeArea).

So, at init time, in InitializeSeamless (in SEAMLESS.C) the Seamless Driver is given access to the AccumulateScreenBounds routine, to the scaSeamless structure and to the DDT Display Driver control block, needed to retrieve the screen dimension at seamless time. The addresses of this data are stored in the SeamlessAddresses control block, owned by the Window Driver.

Before writing to the screen, the Seamless Driver will call SeamlessExcludeCursor, in SEAMTHNK.ASM. The exclusion rectangle will be passed in the following registers in AI coordinates (i.e. 16 bit inclusive, 0,0 is top left of screen):

left    top     right   bottom
cx      dx      si      di

The code will check whether the new bounds accumulation is needed by checking the value of the pStartSCA pointer, and - in the SeamlessExcludeCursor32 routine (in SEAMTHNK.ASM) will call _AccumulateSeamlessBounds (in SCBOUNDS.C) to cause the bound accumulation.

AccumulateSeamlessBound converts the passed rectangle to EXCLUSIVE SCREEN coordinates, clips it to the screen dimensions (sometimes Windows exceeds it!) and calls AccumulateScreenBounds, thus causing the rectangle supplied to SeamlessExcludeCursor to be added only to scaSeamless.

When a SCA is queried (using GetScreenChangeArea) the Seamless SCA is merged with all of the active SCAs, and then reset to be null.

New Entry Points

OpenScreenChangeArea (in SCRAREA.C)

DDIENTRY OpenScreenChangeArea( HDC       hdc,
                               PDC       pdcArg,
                               ULONG     FunN )

USER PARAMETERS:

             HDC hdc:
                  any DC handle

RETURN:
             HSCA hsca:
                        handle of the new SCA
             GPI_ERROR:
                        There was no memory available, so return an error.
                        The memory allocation failure will have been
                        logged by the memory allocation routine

ERRORS:
             PMERR_INV_HDC
             PMERR_MEMORY_ALLOCATION_ERR

MAIN TASKS:
             This routine will allocate a data area internal to the display
             driver in which the driver will accumulate screen changes. It
             returns a 32 bit handle which is required to identify the area in
             GetScreenChangeArea and CloseScreenChangeArea calls

This entry point first enters the Driver by calling EnterDriver (EDDGPROC.C) with the EDF_STANDARD | EDF_DONT_CLEAN flags set. This routine is called whenever the driver is entered, and gets the driver semaphore and performs entry checks.

Then simply attempts to create a new SCA instance (see Section 2.2), allocating a memory area internal to the display driver. This is done by calling AllocateMemory, a common routine in MEMMAN.C.

If the creation is OK, and this is the first SCA to be opened then we must start accumulating screen bounds, by telling the Graphic Engine to turn on the COM_SCR_BOUND flag.

The GreSetProcessControl is used to do so. The setting of this bit will cause subsequent screen drawing functions to accumulate the clipped bounds into the active SCAs.

Then the new created SCA gets initialised to null, and added to the SCA linked list. A pointer to the SCA is returned as the 32 bit handle to the SCA. This handle is required to identify the area in GetScreenChangeArea and CloseScreenChangeArea calls. If there is not enough memory to create the SCA then the PMERR_MEMORY_ALLOCATION_ERR error is logged and GPI_ERROR is returned.

Before returning, ExitDriver is called.

GetScreenChangeArea (in SCRAREA.C)

DDIENTRY GetScreenChangeArea( HDC        hdc,
                              HSCA       hsca,
                              PHRGN      phrgn,
                              PDC        pdcArg,
                              ULONG      FunN )

USER PARAMETERS:

             HDC hdc:
                 any valid DC

             HSCA hsca:
                 handle of the SCA to be queried; to be valid, it
                 nust be obtained from a previous call to OpenScreenChangeArea

             PHRGN phrgn:
                 pointer to a region handle

ERRORS:
             PMERR_INV_HDC
             PMERR_NO_HANDLE

RETURN:
             TRUE:
                 if passed a handle we recognize and all was OK;
             FALSE:
                 in all other cases.

MAIN TASKS:
             This routine takes a Screen Change Area handle, and for the SCA
             identified adds its rectangles to the region pointed to by the
             phrgn parameter.
             The SCA is reset to NULL as a result of this call.

This entry point first enters the Driver by calling EnterDriver (EDDGPROC.C) with the EDF_STANDARD | EDF_DONT_CLEAN flags set. This routine is called whenever the driver is entered, and gets the driver semaphore and performs entry checks.

Then, if there are any rects in the Seamless SCA scaSeamless, it merges them with all of the currently active SCAs. This is done by calling AccumulateScreenBounds for every rectangle in scaSeamless. At the end, the cRects field of scaSeamless is set to zero. Next step is to track along the linked list until we either find the SCA to be queried, or reach the end of the list. In the first case, GreCombineRectRegion is called for all the rectangles in the SCA, so adding (CRGN_OR) them to the region passed by the user. If the call is always successful, the cRects field of the SCA gets set to 0, so resetting the SCA, otherwise a FALSE rc is returned.

If the end of the list was reached without finding the needed SCA, a PMERR_NO_HANDLE error is logged by LogError, and a FALSE return code is returned.

If everything went OK, the routine calls ExitDriver and returns TRUE.

CloseScreenChangeArea (in SCRAREA.C)

DDIENTRY CloseScreenChangeArea( HDC      hdc,
                                HSCA     hsca,
                                PDC      pdcArg,
                                ULONG    FunN )

USER PARAMETERS:

             HDC hdc:
                  any valid DC

             HSCA hsca:
                  handle of the SCA to be closed; to be valid, it
                  must be obtained from a previous call to
                  OpenScreenChangeArea

ERRORS:
             PMERR_INV_HDC
             PMERR_NO_HANDLE

RETURN:
             Error conditions are not obvious here, but we will return TRUE
             if passed a handle we recognize, and FALSE in all other cases.

MAIN TASKS:
             This routine frees the data area internal to the display driver,
             identified by the SCA handle, which was accumulating screen
             changes. It returns a Boolean value indicating success or failure.

This entry point first enters the Driver by calling EnterDriver (EDDGPROC.C) with the EDF_STANDARD | EDF_DONT_CLEAN flags set. This routine is called whenever the driver is entered, and gets the driver semaphore and performs entry checks.

Then the entry point checks that the hsca parameter matches one of the already created SCAs, and if so removes the SCA from the linked list and frees the SCA memory (see Section 2.4), by using FreeMemory (in MEMMAN.C), and updates the linked list. If this is the latest created SCA to be closed (i.e. there are no more in the linked list) then the Graphics Engine function GreSetProcessControl is called to turn off the COM_SCR_BOUND bit on subsequent calls to the Display Driver. The resetting of this bit means that no further screen bounds accumulation will occur until another call is made to OpenScreenChangeArea.

If the hsca parameter does not match any of the existing SCAs, a rc equal to FALSE is returned and an error (PMERR_NO_HANDLE) is logged by LogError.

Before returning, ExitDriver is called.

GetScreenBits (in GETSCR.C)

DDIENTRY GetScreenBits( HDC     hdc,
                        HRGN    hrgnApp,
                        PBYTE   pDest,
                        PULONG  pulLength,
                        ULONG   flCmd,
                        PDC     pdcArg,
                        ULONG   FunN )

USER PARAMETERS:

HDC hdc
any valid direct (Screen) DC handle
HRGN hrgn
the area of the screen to be fetched. It can be either a valid region handle or a pointer to a (inclusive) RECTL, specified by a flag in the flCmd parameter.
PBYTE pDest
a pointer to the memory buffer where the compressed data will be written.
PULONG pulLength
a pointer to a ULONG, which must contain the length of the memory buffer pointed to by pBuffer. Valid values range from 2071 bytes to 64K bytes. Upon exit, the ULONG contains the number of bytes stored in the memory buffer.
ULONG flCmd
option flags. These specify the format (bits per pel, linear or planar) of the data to be put in the memory buffer, and whether the hrgn parameter contains a region handle or a pointer to a RECTL. Options available are:
                GSB_OPT_4BPP           0000H
                GSB_OPT_8BPP           0001H
                GSB_OPT_16BPP          0002H
                GSB_OPT_LINEAR         0000H
                GSB_OPT_PLANAR         0008H
                GSB_OPT_HRGN           0010H
The options are ORed together to make the flCmd parameter.
A couple of generally applicable rules are:
  • data can only be queried at bits per pel less than or equal to the bits per pel of the screen.
  • 4 bits per pel is the only valid planar format.

RETURN:

1: the entire area was successfully compressed in buffer. Upon exit, the supplied region/rectangle will be NULL;
2: a subset of the area was saved in buffer, because the buffer was not big enough for the whole area. Upon exit, the supplied region/rectangle will be updated to contain the area that was NOT compressed. This implies that GetScreenBits will have to be called again to complete the area compression.
0: an error occurred

ERRORS:

PMERR_INV_IN_PATH
PMERR_INV_IN_AREA
PMERR_INV_LENGTH_OR_COUNT
PMERR_INV_FORMAT_CONTROL
PMERR_INV_IMAGE_DIMENSION
PMERR_INV_DC_TYPE
PMERR_PEL_NOT_AVAILABLE

MAIN TASKS:

This routine queries a region of screen pixel data and saves it into the memory provided by the caller. It is compressed, and can be converted into a format suitable for another supported display device and will stop either when:
  • the supplied memory area is full
  • the requested region has been returned.
The region can be specified as either:
  • a pointer to a single rectangle (RECTL - long values)
  • a region handle
setting the GSB_OPT_HRGN flag in the flCmd parameter accordingly (if it is set, take a region; if it is not set, take a rect). If a RECTL is specified then it is assumed to be inclusive.
The function modifies the supplied rectangle/region to indicate the area that was NOT returned in the call. If the whole requested region was returned the rectangle/region will be a null area.
The supplied DC must be direct - it is the source of the pixel data.
This is not a drawing primitive, therefore no correlation, bounds accumulation or drawing will take place.

The routine first enters the driver by calling EnterDriver with the EDF_STANDARD | EDF_DONT_CLEAN flags set (this routine is called whenever the driver is entered, and gets the driver semaphore and performs entry checks).

Then it performs some initial error tests, checking the FunN parameter and the DC type. If the COM_PATH or the COM_AREA flags are set in FunN, the routine logs PM_INV_IN_PATH or PMERR_INV_IN_AREA by calling LogError, and returns 0; moreover, if the DC is not a direct one, the PMERR_INV_DC_TYPE is logged and the routine returns 0.

The next step is a check on the parameters for validity: the buffer size must range between MIN_BUFFER_SIZE (2071 bytes, the length of a buffer containing only a compressed row, at the maximum resolution and when the input row only contains non-repeating data) and 65536 bytes (limit due to the PHUNK memory), otherwise the PMERR_INV_LENGTH_OR_COUNT is logged and the routine returns 0; the flCmd must be set with one of more of the flags we know, otherwise the PMERR_INV_FORMAT_CONTROL error will be logged and the return code will be 0.

Moreover, we must check that the PM is in foreground (fXGAdead TRUE), otherwise the PMERR_PEL_NOT_AVAILABLE error will be logged and the routine will return 0. If all of these checks are OK, the CompressScreenBits routine (in GETSCR.C) is called. CompressScreenBits checks if the format is valid; this is done by creating a word containing the internal (screen) format and the external (requested) format.

The aGSBValidDataFormats table is then scanned for a match; this table is defined as as an array of valid source and destination format pairing, containing - for each valid couple the following info:

typedef struct _VALID_DATA_FORMATS
{
    ULONG   ulSrcDstFormat;             // Combined src + dst codes
    PFN     pfnRowConversionRoutine;    // address of the conversion routine
                                        // for this pair
    PBYTE  *ppConvertTable;             // address to the conversion table, if
                                        // required
    PFN     pfnCreateConvertTable;      // address to a routine aimed to create
                                        // a conversion table
} VALID_DATA_FORMATS;

If matched, the format is valid; otherwise it is invalid and the PMERR_INV_FORMAT_CONTROL gets logged and the call returns 0. Then, from the aGSBValidDataFormats structure, the pointer to the compression function for the required format combination is retrieved, and copied in the Spad global structure.

The following step is to initialize the conversion cache by calling the InitializeCache routine in DCAFCNV.ASM (this is currently only required if source is 16bpp and destination is 8bpp or 4bpp).

Then we check whether we need a conversion table for this combination of formats: actually, it is required for all the couples, except for the ones with the 16 bpp as a source. These latest couples have a null pointer in the last two fields of their aGSBValidDataFormats structures.

If the internal format is 16bpp, then we don't need a conversion table, whatever the target format may be.

If the requested format is 8bpp:

  • if the hardware palette is the default, then no conversion is required. This is checked by comparing two global variables, hForeGroundPal and DirectDeviceDefaultPalette. If there is still an old conversion table,it will be freed by FreeConvertTable (in DCAFCNVT.C).
  • else a palette has been realized and we need to convert the internal indices into "standard" external format i.e. indices into the default 256 color XGA (fudged) palette.

If a conversion table has been previously created then we need to ensure that the mapping refers to the current palette (the internal palette may have changed since we calculated the conversion table). We can do this because there is a handy global variable, Seamlessdata.ulLastPalUpdate, that is incremented when the hardware palette is changed. We take a copy of this when the conversion table is created, and on subsequent calls check the value has not changed before using the table. If the value has changed then we throw away the old table and create a new one.

If the requested format is 4bpp:

  • we will always need a conversion table, but must ensure that it correctly maps the current hardware palette. We use the same scheme as for 8bpp in this case.

If the conversion table creation is needed (according to the scheme above), the proper creation function (stored in the pfnCreateConvertTable field of the specific pair) is called and the pointer to the just created table is stored in the ppConvertTable field.

The creation calls for the conversion table of valid format couple, all of them are defined in DCAFCNVT.C:

CreateConvertTable_8int_4ext
creates a 256-entry table of bytes to convert 8bpp to 4bpp. To do this, after the memory allocation, the NearestRestrictedColourIndex (in CONVINT.C) is called, in order to map the RGB values in the HWPalette table to the nearest entry in the VGAstandard default palette (see Appendix A).
CreateConvertTable_8int_8ext
creates a 256-entry table of bytes to convert 8bpp internal indices to 8bpp external indices (XGA default palette).

To do this, after the memory allocation, the NearestRestrictedColourIndex (in CONVINT.C) is called, in order to map the RGB values in the HWPalette table to the nearest entry in the FullSizeDeviceDefaultPalette palette (the 256-entry XGA default palette defined in EDDDATA.C).

CreateConvertTable_4int_4ext
creates a 256-entry table of bytes to convert 4bpp internal (fudged) format to 4bpp external (standard VGA) format.

We convert pairs of pels at a time, which is why the conversion table is 256 entries, rather than the expected 16. To do this, after the memory allocation, the NearestRestrictedColourIndex (in CONVINT.C) is called, in order to map the RGB values in the Reduced16DeviceDefaultPalette table (the 16-entry XGA default palette defined in EDDDATA.C) to the nearest entry in the VGA standard default palette (see the Appendix A). The remaining values in the external table are obtained by combining pairs of conversion values, in the following way:

for (i = 0; i < 256; i++)
{
   pConvertTable[i] = (abLocalConvertTable[i >> 4] << 4) |
                      (abLocalConvertTable[i & 0x0F]) ;
}

Next step is to calculate the bounding rectangle of the supplied area to be queried: we'll use GreGetRegionBox.

Then, if the query area is a region, we ask the Graphics Engine for the first 10 rectangles from the query area, putting their address in the Spad.prclCurrent pointer (our local buffer). If the query area is a rectangle, we make it exclusive and put its address in Spad.prclCurrent.

A check is done to see if the bounding box (for the region) or the rectangle is a valid ordered rectangle: otherwise, a PMERR_INV_IMAGE_DIMENSION is logged and the call returns 0. Moreover, the call will return 0 if the rectangle is empty; and, if the bounding box or the rectangle exceeds the screen dimension, PMERR_INV_IMAGE_DIMENSION is logged and the call return 0. Now we move the destination pointer past the packet header.

Then we exclude the cursor from the bounding rectangle using eddm_excludeCursor in EDDMCCRS.C, after rounding the bounding rectangle up to 8 pel boundaries, because the rectangles may be rounded up to 2 or 8 pel boundaries below (depending upon bpp).

Then we set up the drawing mode to always use the real XGA hardware, calling the SetDrawModeHard macro (in EDDMACRO.H).

After setting some ShadowXGARegs fields, we call WaitForRealHWFunction in HWACCESS.ASM to ensure that the hardware is ready, then we transfer some of the registers that we have just set up calling TransferShadowRegisters(TSR_MAP_A | TSR_COLOUR_MIX) in HWACCESS.ASM: at the moment, we can only transfer MAP_A and COLOR_MIX registers. MAP_B, COORDINATES and PIXELOP will be transferred later (when we know all of the values), since they depend on the rectangle in processing.

The a main loop starts:

For each rectangle in the Spad.prclCurrent local buffer: adjust the current rectangle coordinates if necessary. We alter the coords according to format and bits/pel so that we do not have to worry about the masking associated with compressing/decompressing partial bytes.

  • 4bpp formats are rounded to 8 pel boundaries because the destination could be planar VGA.
  • 8bpp formats are rounded to even pel boundaries because we transmit data in 16-bit fields i.e. two pels per data field.
: call CompressRect in COMPRESS.ASM, getting as return whether or not
         the output buffer is full
: if the output buffer is full
: : break
: endif
: if no more rectangles in the local buffer and a region was supplied and
         there are more rects in engine
: : subtract the already compressed rectangles from the supplied region using
    GreCombineRectRegion (CRGN_DIFF)
: : reload local buffer with more rects from engine
: endif
endfor

We can get out from the cycle above for two reasons:

  • no more rectangles to process: reset the provided region or rectangle using GreSetRectRegion;
  • no more room in the output buffer: subtract the already processed rectangles from the region or the processed area from the provided rectangle.

At the end, we fill in the total number of bytes written into packet header, reenable the cursor by reenable_cursor in EDDMCCRS.C, we exit the driver by ExitDriver and we return with a code indicating full (return code 1) or partial data returned (return code 2).

CompressRect (in COMPRESS.ASM)

if free bytes in output buffer < size of (RECTANGLE HEADER + WORST_CASE_ROW_LENGTH)

: set a variable saying that there is no room in the output buffer
: return
else
: write out rect header
: for iRow = each row of rect
: : // Check for duplicate scanlines
: : if iRow > first row
: : : if row[iRow] matches row[iRow-1]
: : : : count subsequent matching rows
: : : : write duplicate scanline code + count
: : : endif
: : endif
: : // Check for duplicate scanline pairs
: : if iRow > second row and iRow < last row
: : : if row[iRow] matches row[iRow-2] and row[iRow+1] matches row[iRow-1]
: : : : count subsequent matching row pairs
: : : : write duplicate scanline pair code + count
: : : endif
: : endif
: : // Compress the row
: : call appropriate row compression function
: : if free bytes in output buffer < WORST_CASE_ROW_LENGTH
: : : set a variable saying that there is no room in the output buffer
: : : update the passed rectangle to contain the area that was not compressed
: : : return
: : endif
: endfor
return
endif

There is a separate compression function for each of the valid src/dst format combinations (in COMPRESS.ASM): - compress_row_16_16 (src 16bpp packed, dst 16bpp packed)

                                calling only compression routine

- compress_row_16_8 (src 16bpp packed, dst 8bpp packed)

                                calling: convert_row_16pk_8pk;
                                         compression routine

- compress_row_16_4 (src 16bpp packed, dst 4bpp packed)

                                calling: convert_row_16pk_4pk;
                                         compression routine

- compress_row_16_4pl (src 16bpp packed, dst 4bpp planar)

                                calling: convert_row_16pk_4pk;
                                         convert_row_4pk_4pl;
                                         compression routine

- compress_row_8_8 (src 8bpp packed, dst 8bpp packed)

                                calling: convert_row_8pkint_8pkext;
                                         compression routine

- compress_row_8_4 (src 8bpp packed, dst 4bpp packed)

                                calling: convert_row_8pk_4pk;
                                         compression routine

- compress_row_8_4pl (src 8bpp packed, dst 4bpp planar)

                                calling: convert_row_8pk_4pk;
                                         convert_row_4pk_4pl;
                                         compression routine

- compress_row_4_4 (src 4bpp packed, dst 4bpp packed)

                                calling: convert_row_4pkint_4pkext;
                                         compression routine

- compress_row_4_4pl (src 4bpp packed, dst 4bpp planar)

                                calling: convert_row_4pkint_4pkext;
                                         convert_row_4pk_4pl;
                                         compression routine

Conversion between different data formats (e.g. 8bpp and 4bpp) is done independently from the data compression. i.e. if the source and destination formats differ, then the source data is first converted to the destination format (in an intermediate buffer defined on the PHUNK), and then compressed into the destination buffer. If the source and destination formats match then the data is compressed directly from the source to the destination. The data conversion routines use tables wherever possible to improve performance (see Section 4 for further details). The routines defined in COMPRESS.ASM are:

- convert_row_16pk_8pk (src 16bpp packed, dst 8bpp packed)

                                using NearestRestrictedDeviceDefaultPalette
                                to find, for every pixel, the nearest
                                entry in the FullSizeDeviceDefaultPalette
                                (the 256 entry default palette defined in
                                EDDDATA.C).

- convert_row_16pk_4pk (src 16bpp packed, dst 4bpp packed)

                                using NearestRestrictedDeviceDefaultPalette
                                      to find, for every pixel, the nearest
                                      entry in the StandardVGADefaultPalette

- convert_row_8pkint_8pkext (scr 8bpp packed, dst 8bpp packed)

                                using  ConvertTable_8int_8ext

- convert_row_8pk_4pk (src 8bpp packed, dst 4bpp packed)

                                using  ConvertTable_8int_4ext

- convert_row_4pk_4pl (src 4bpp packed, dst 4bpp planar)

                                changes the packed to planar format

- convert_row_4pkint_4pkext (src 4bpp packed, dst 4bpp packed)

                                     using  ConvertTable_4int_4ext

Additional work is done to make the required rows available for compression. Checks are performed to ensure that a row is available before accessing it: in the XGA Driver, VRAM is not directly accessible so the required rows have to be loaded into a locked buffer in System Memory before they can be worked on. There already exists a 64K buffer in the Driver called the PHUNK (PHysical chUNK) which is ideal for this purpose. Rectangles can easily exceed 64K in size, so this buffer will often be reloaded multiple times during the processing of a single rectangle.

SetScreenBits (in SETSCR.C)

DDIENTRY SetScreenBits( HDC      hdc,
                        PBYTE    pBuffer,
                        ULONG    cBytes,
                        HRGN     hrgn,
                        PDC      pdcArg,
                        ULONG    FunN )

USER PARAMETERS:

HDC hdc
any valid direct (Screen) DC handle, that has selected into it a bitmap the same size as the screen from which the source data was read.
PBYTE pBuffer
a pointer to the memory buffer where the source (compressed) data is located.
ULONG cBytes
the length of the memory buffer pointed to by pBuffer. This must be the same value as returned by the corresponding GetScreenBits call.
HRGN hrgn
a valid region handle. The area that is updated in the memory bitmap by this call is added to the region identified by this handle.

RETURN:

1: the supplied data is successfully decompressed into the memory bitmap;
0: an error occurred.

ERRORS:

PMERR_INV_IN_PATH
PMERR_INV_IN_AREA
PMERR_INV_LENGTH_OR_COUNT
PMERR_INV_DC_TYPE
PMERR_BITMAP_NOT_SELECTED
PMERR_INCOMPAT_COLOR_FORMAT
PMERR_INV_RECT
PMERR_INV_IMAGE_DIMENSION

MAIN TASKS:

SetScreenBits takes compressed data (generated by a previous call to GetScreenBits) from a buffer and decompresses it into the currently selected memory bitmap. The call is only valid for a memory DC that has a bitmap selected that is the same size as the screen on the machine where the GetScreenBits was performed.
There is no clipping. If a rectangle exceeds the bitmap dimensions then the function will terminate immediately with an error logged. The bitmap may be left in a partially drawn state as prior rectangles may have been copied into it.
This is a drawing primitive, therefore correlation, boundary accumulation and drawing could take place.
However, we will totally ignore the function bits, and will always just do the drawing and nothing else.
The routine may be passed a region handle, in which case the area defined by the set bits will be added to the region.
The XGA driver may be passed 4bpp planar, 4bpp packed, 8bpp packed and 16bpp packed data.

The routine first enters the driver by calling EnterDriver with the EDF_STANDARD | EDF_DONT_CLEAN flags set (this routine is called whenever the driver is entered, and gets the driver semaphore and performs entry checks).

Then it performs some initial error tests, checking the FunN parameter and the DC type. If the COM_PATH or the COM_AREA flags are set in FunN, the routine logs PM_INV_IN_PATH or PMERR_INV_IN_AREA by calling LogError, and returns 0; moreover, if the DC is not a direct one, the PMERR_INV_DC_TYPE is logged and the routine returns 0. One more check is done on the DC to see if a bitmap is selected in it: the PMERR_BITMAP_NOT_SELECTED error is logged and the routine returns 0 if there is no bitmap selected.

If the passed data length is zero then we can exit immediately, returning 1. Next check is if the data length is at least the size of the header, and that the passed data length agrees with the data length given in the packet header: otherwise log PMERR_INV_LENGTH_OR_COUNT and return 0.

Next step is to check that the coupling local format/received data format is valid: this is done by creating a word containing the internal (screen) format and the external (data received) format.

The aGSBValidDataFormats table is then scanned for a match; this table is defined as an array of valid source and destination format pairing, containing

  • for each valid couple the following info:
typedef struct _VALID_DATA_FORMATS
{
    ULONG   ulSrcDstFormat;           // Combined src + dst codes
    PFN     pfnRowConversionRoutine;  // address of the conversion routine for
                                      // this pair
    PBYTE  *ppConvertTable;           // address to the conversion table, if
                                      // required
    PFN     pfnCreateConvertTable;    // address to a routine aimed to create a
                                      // conversion table
} VALID_DATA_FORMATS;

If matched, the format is valid; otherwise it is invalid and the PMERR_INCOMPAT_COLOR_FORMAT gets logged and the call returns 0. Then, see if conversion is required for that couple, and if so then the pointer to the compression function for the required format combination is retrieved from the aGSBValidDataFormats structure, and called to create the conversion table.

The creation calls for the conversion table of valid format couple, all of them are defined in DCAFCNVT.C:

CreateConvertTable_4ext_4int
creates a 256-entry table of bytes to convert 4bpp external (default VGA) format to 4bpp internal (fudged).

We convert pairs of pels at a time, which is why the conversion table is 256 entries, rather than the expected 16.

To do this, after the memory allocation, the NearestRestrictedColourIndex (in CONVINT.C) is called, in order to map the entries in the VGA default table (see Appendix A) to the nearest

entry in the Reduced16DeviceDefaultPalette table (the 16-entry XGA default palette defined in EDDDATA.C).

The remaining values in the internal table are obtained by combining pairs of conversion values, in the following way:
               for (i = 0; i < 256; i++)
               {
                 pConvertTable[i] = (abLocalConvertTable[i >> 4] << 4) |
                                    (abLocalConvertTable[i & 0x0F]) ;
               }
CreateConvertTable_4ext_8int
creates a 16-entry table of bytes to convert 4bpp to 8bpp. After the memory allocation, the NearestRestrictedColourIndex (in CONVINT.C) is called, in order to map the entries in the standard VGA table (see Appendix A) to the nearest entry in the FullSizeDeviceDefaultPalette (the 256-entry XGA default palette defined in EDDDATA.C).
CreateConvertTable_4ext_16int
creates a 16-entry table of words to convert 4bpp to 16bpp. After the memory allocation, the RGB16FromPRGB2 (macro in CONVFUNS.H) is used to transform the VGA standard palette (see Appendix A) to 16 bpp values. Note that the VGA standard palette is in RGB2 format (see Appendix A). The entries are returned in Intel format:
              Bit7..Bit 0   Bit15..Bit8
                BYTE 0        BYTE 1

and then they must be converted to Motorola format:

              Bit15...Bit8   Bit7..Bit0
                 BYTE 0        BYTE 1
CreateConvertTable_8ext_16int
creates a 256-entry table of words to convert 8bpp external to 16bpp internal. After the memory allocation, the RGB16FromPRGB2 (macro in CONVFUNS.H) in order to transform the XGA device standard palette FullSizeDeviceDefaultPalette to 16 bpp values. The entries are returned in Intel format and must be then converted to Motorola format.
Then, check that the hardware is idle before we start using the PHUNK buffers by calling WaitForRealHWFunction in HWACCESS.ASM: then, the main cycle starts:
: for next byte in the input buffer < last buffer in the input buffer
: : check that the next rectangle in the buffer is valid, otherwise log
: : PMERR_INV_RECT and return 0.
: : calculate some data on the current rectangle (number of row, bytes per row,
: : etc)
: : if source data is with 8-bit data fields
: : // expand the rectangle and return the pointer to the next rectangle in the
: : // input buffer
: : : call ExpandRect8
: : otherwise // source data is with 16-bit data fields
: : // expand the rectangle and return the pointer to the next rectangle in the
: : // input buffer
: : : call ExpandRect16
: : endif
: : subtract the just decompressed rectangle from the passed region using
    GreCombineRectRegion (CRGN_DIFF)
: endfor
return 1

At the end, we exit the driver by ExitDriver, returning 1.

The ExpandRect8 and ExpandRect16 calls (in EXPAND.ASM) perform the decompression of the data and, if the internal Display Driver format is different from the data format, the conversion of the data takes place. The following routines (in DCAFCNV.ASM and EXPAND.ASM) are used:

  • convert_row_4pl_4pk (scr 4bpp planar, dst 4bpp packed) changes only the planes to linear
  • convert_row_4pl_4pkint (src 4bpp planar, dst 4bpp packed) calling: convert_row_4pl_4pk; convert_row_4pk_16pk
  • convert_row_4pl_8pk (scr 4bpp planar, dst 8bpp packed) calling: convert_row_4pl_4pk; convert_row_4pkt_8pk
  • convert_row_4pl_16pk (scr 4bpp planar, dst 16bpp packed) calling: convert_row_4pl_4pk; convert_row_4pkext_4pkint
  • convert_row_4pkext_4pkint (scr 4bpp packed, dst 4bpp packed) using ConvertTable_4ext_4int
  • convert_row_4pk_8pk (scr 4bpp packed, dst 8bpp packed) using ConvertTable_4ext_8int
  • convert_row_4pk_16pk (scr 4bpp packed, dst 16bpp packed) using ConvertTable_4ext_16int
  • convert_row_8pk_16pk (scr 8bpp packed, dst 16bpp packed) using ConvertTable_8ext_16int

Conversion between different data formats (e.g. 4bpp and 8bpp) is done independently from the data expansion. i.e. if the source and destination formats differ, then the source data is first expanded (in a intermediate buffer), and then converted to the destination format. If the source and destination formats match then the data is decompressed directly from the source buffer to the destination internal bitmap. As in GetScreenBits, the data conversion routines use tables wherever possible to improve performance (see Section 4 for further details).

Appendix A - Default color palette values

Default VGA (4bpp) palette
Index    RRGGBB
0:       000000
1:       000080
2:       008000
3:       008080
4:       800000
5:       800080
6:       808000
7:       808080
8:       CCCCCC
9:       0000FF
10:      00FF00
11:      00FFFF
12:      FF0000
13:      FF00FF
14:      FFFF00
15:      FFFFFF
Default XGA (8bpp) palette
Index   RRGGBB

0:      000000
1:      800000
2:      009200
3:      808000
4:      0000AA
5:      800080
6:      0092AA
7:      C1C1C1
8:      AAFFAA
9:      AAB6FF
10:     0049AA
11:     0049FF
12:     006D00
13:     006D55
14:     006DAA
15:     006DFF
16:     002400
17:     009255
18:     0024AA
19:     0092FF
20:     00B600
21:     00B655
22:     00B6AA
23:     00B6FF
24:     00DB00
25:     00DB55
26:     00DBAA
27:     00DBFF
28:     FFDBAA
29:     00FF55
30:     00FFAA
31:     FFFFAA
32:     2B0000
33:     2B0055
34:     2B00AA
35:     2B00FF
36:     2B2400
37:     2B2455
38:     2B24AA
39:     2B24FF
40:     2B4900
41:     2B4955
42:     2B49AA
43:     2B49FF
44:     2B6D00
45:     2B6D55
46:     2B6DAA
47:     2B6DFF
48:     2B9200
49:     2B9255
50:     2B92AA
51:     2B92FF
52:     2BB600
53:     2BB655
54:     2BB6AA
55:     2BB6FF
56:     2BDB00
57:     2BDB55
58:     2BDBAA
59:     2BDBFF
60:     2BFF00
61:     2BFF55
62:     2BFFAA
63:     2BFFFF
64:     550000
65:     550055
66:     5500AA
67:     5500FF
68:     552400
69:     552455
70:     5524AA
71:     5524FF
72:     554900
73:     554955
74:     5549AA
75:     5549FF
76:     556D00
77:     556D55
78:     556DAA
79:     556DFF
80:     559200
81:     559255
82:     5592AA
83:     5592FF
84:     55B600
85:     55B655
86:     55B6AA
87:     55B6FF
88:     55DB00
89:     55DB55
90:     55DBAA
91:     55DBFF
92:     55FF00
93:     55FF55
94:     55FFAA
95:     55FFFF
96:     000055
97:     800055
98:     002455
99:     8000FF
100:    802400
101:    802455
102:    8024AA
103:    8024FF
104:    804900
105:    804955
106:    8049AA
107:    8049FF
108:    806D00
109:    806D55
110:    806DAA
111:    806DFF
112:    080808
113:    0F0F0F
114:    171717
115:    1F1F1F
116:    272727
117:    2E2E2E
118:    363636
119:    3E3E3E
120:    464646
121:    4D4D4D
122:    555555
123:    5D5D5D
124:    646464
125:    6C6C6C
126:    747474
127:    7C7C7C
128:    FFDB00
129:    8B8B8B
130:    939393
131:    9B9B9B
132:    FFB6FF
133:    AAAAAA
134:    B2B2B2
135:    B9B9B9
136:    0024FF
137:    CCCCCC
138:    D1D1D1
139:    D8D8D8
140:    FFB6AA
141:    E8E8E8
142:    F0F0F0
143:    F7F7F7
144:    FFDBFF
145:    809255
146:    8092AA
147:    8092FF
148:    80B600
149:    80B655
150:    80B6AA
151:    80B6FF
152:    80DB00
153:    80DB55
154:    80DBAA
155:    80DBFF
156:    80FF00
157:    80FF55
158:    80FFAA
159:    80FFFF
160:    AA0000
161:    AA0055
162:    AA00AA
163:    AA00FF
164:    AA2400
165:    AA2455
166:    AA24AA
167:    AA24FF
168:    AA4900
169:    AA4955
170:    AA49AA
171:    AA49FF
172:    AA6D00
173:    AA6D55
174:    AA6DAA
175:    AA6DFF
176:    AA9200
177:    AA9255
178:    AA92AA
179:    AA92FF
180:    AAB600
181:    AAB655
182:    AAB6AA
183:    004955
184:    AADB00
185:    AADB55
186:    AADBAA
187:    AADBFF
188:    AAFF00
189:    AAFF55
190:    004900
191:    AAFFFF
192:    D50000
193:    D50055
194:    D500AA
195:    D500FF
196:    D52400
197:    D52455
198:    D524AA
199:    D524FF
200:    D54900
201:    D54955
202:    D549AA
203:    D549FF
204:    D56D00
205:    D56D55
206:    D56DAA
207:    D56DFF
208:    D59200
209:    D59255
210:    D592AA
211:    D592FF
212:    D5B600
213:    D5B655
214:    D5B6AA
215:    D5B6FF
216:    D5DB00
217:    D5DB55
218:    D5DBAA
219:    D5DBFF
220:    D5FF00
221:    D5FF55
222:    D5FFAA
223:    D5FFFF
224:    FFDB55
225:    FF0055
226:    FF00AA
227:    FFFF55
228:    FF2400
229:    FF2455
230:    FF24AA
231:    FF24FF
232:    FF4900
233:    FF4955
234:    FF49AA
235:    FF49FF
236:    FF6D00
237:    FF6D55
238:    FF6DAA
239:    FF6DFF
240:    FF9200
241:    FF9255
242:    FF92AA
243:    FF92FF
244:    FFB600
245:    FFB655
246:    E0E0E0
247:    A2A2A2
248:    838383
249:    FF0000
250:    00FF00
251:    FFFF00
252:    0000FF
253:    FF00FF
254:    00FFFF
255:    FFFFFF
XGA 16bpp format

This is a 5-6-5 format, i.e.

  • rrrrrggggggbbbbb
r = red (5 bits)
g = green (6 bits)
b = blue (5 bits)
   typedef struct _RGB2      /* rgb2 */
   {
      BYTE bBlue;            /* Blue component of the color definition */
      BYTE bGreen;           /* Green component of the color definition*/
      BYTE bRed;             /* Red component of the color definition  */
      BYTE fcOptions;        /* Reserved, must be zero                 */
   } RGB2;
   typedef RGB2 *PRGB2;