GreGetScreenBits

From EDM2
Jump to: navigation, search

GreGetScreenBits queries a region of screen pixel data and saves it in the memory provided by the caller. The data is compressed, and can be converted into a format suitable for another supported display device. The process will stop when either of the following situations occurs:

  • The supplied memory area is full
  • The requested region has been returned

The area to be queried can be identified in one of two ways:

  • As a pointer to a single rectangle (RECTL, long values)
  • As a region handle

The GSB_OPT_HRGN flag in the flCmd field identifies the region: if the flag is set, it indicates a region handle; if the flag is not set, it indicates a pointer to a rectangle. If a RECTL is specified, the RECTL is assumed to be inclusive/inclusive.

The GreGetScreenBits function modifies the supplied rectangle or region to indicate the area that was NOT returned in the call. If the entire requested region was returned, the rectangle or region will be a null area.

The supplied device context must be direct; it is the source of the pixel data. Since this is not a drawing primitive, no correlation, bounds accumulation or drawing will take place.

Simulation support
This function is mandatory for display drivers that want to be DCAF-enabled.

Syntax

GreGetScreenBits(hdc, hrgnApp, pDest, pulLength, flCmd, pdcArg, FunN)

Parameters

hdc (HDC) - input
Any valid, direct (screen) device context handle.
hrgnApp (HRGN) - input
Area of the screen to be acquired.
Area can be either a valid region handle or a pointer to an inclusive RECTL, specified by a flag in the flCmd field.
pDest (PBYTE) - input
Pointer to the memory buffer where the compressed data will be written.
pulLength (PULONG) - input
Pointer to a ULONG.
The ULONG must contain the length of the memory buffer pointed to by pDest. Valid values range from 2071 bytes to 64K bytes. Upon exit, the ULONG contains the number of bytes stored in the memory buffer.
flCmd (ULONG) - input
Option flags.
These flags specify the format (bits per pel, linear or planar) of the data to be put in the memory buffer. flCmd also indicates whether hrgn contains a region handle or a pointer to a RECTL. Valid options include the following:
GSB_OPT_4BPP 0000H
GSB_OPT_8BPP 0001H
GSB_OPT_16BPP 0002H
GSB_OPT_LINEAR 0000H
GSB_OPT_PLANAR 0008H
GSB_OPT_HRGN 0010H
These option flags are ORed together to create the flCmd field.
pdcArg (PDC) - input
Pointer to instance data.
FunN (ULONG) - input
High-order word=Flags; Low-order word=NGreGetScreenBits.

Returns

rc (LONG) - returns
This function returns one of the following:
  • 1 The entire area was successfully compressed in the memory buffer. Upon exit, the supplied region or rectangle will be NULL.
  • 2 A subset of the area was saved in the memory buffer because the buffer was not big enough for the whole area. Upon exit, the supplied region or rectangle will be updated to contain the area that was NOT compressed. This implies that GreGetScreenBits will have to be called again to complete the area compression.
  • 0 An error occurred.
Possible Errors Detected: When an error is detected, the handling routine must call WinSetErrorInfo to post the condition. Error codes for conditions that the handling routine is expected to check include:
  • PMERR_INV_DC_TYPE
  • PMERR_INV_FORMAT_CONTROL
  • PMERR_INV_IMAGE_DIMENSION
  • PMERR_INV_IN_AREA
  • PMERR_INV_IN_PATH
  • PMERR_INV_LENGTH_OR_COUNT
  • PMERR_PEL_NOT_AVAILABLE
Refer to the "Error Explanations" section in the Presentation Manager Programming Reference for further explanation.

Sample

#define INCL_GRE_SCREEN
#include <os2.h>
#include <pmddi.h>

HDC       hdc;        /* Any valid, direct (screen) device context handle. */
HRGN      hrgnApp;    /* Area of the screen to be acquired. */
PBYTE     pDest;
PULONG    pulLength;  /* Pointer to a ULONG. */
ULONG     flCmd;      /* Option flags. */
PDC       pdcArg;     /* Pointer to instance data. */
ULONG     FunN;       /* High-order word=Flags; Low-order word=NGreGetScreenBits. */
LONG      rc;

rc = GreGetScreenBits(hdc, hrgnApp, pDest, pulLength, flCmd, pdcArg, FunN);

Remarks

It is permissible to implement this function by returning 0 to indicate that the bits were not saved and, therefore, must be saved by the calling routine.

GreGetScreenBits calls EnterDriver and performs some initial error tests, checking the FunN field and the DC type. If the COM_PATH or the COM_AREA flags are set in FunN, the function logs PMERR_INV_IN_PATH or PMERR_INV_IN_AREA, respectively, and returns 0 (zero).

Next, the parameters are checked for validity.

  • The buffer size must be in the following range:
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 nonrepeating data
65536 bytes
Limit due to PHUNK memory
Otherwise, the PMERR_INV_LENGTH_OR_COUNT error is logged and the call returns 0 (zero).
  • The flCmd field must be set with one or more valid flags.
Otherwise, the PMERR_INV_FORMAT_CONTROL error is logged and the call returns 0 (zero).
  • Presentation Manager must be in the foreground (fXGAdead is set to TRUE).
Otherwise, PMERR_PEL_NOT_AVAILABLE error is logged and the call returns 0 (zero).

If all these checks are successful, the CompressScreenBits routine (in GETSCR.C) is called. CompressScreenBits checks whether the format is valid by creating a word containing the internal (screen) format and the external (requested) format. Then the aGSBValidDataFormats table is scanned for a match. This table is an array of valid source and destination format pairs, as defined in the VALID_DATA_FORMATS data structure. See VALID_DATA_FORMATS in Data Types for details on this data structure.

If a match is found, the format is valid. Otherwise, the format is invalid, the PMERR_INV_FORMAT_CONTROL error is logged and the call returns 0. Then, the pointer to the compression function for the required format combination is retrieved from the aGSBValidDataFormats structure and copied into the Spad global structure.

The following steps 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 a check is done to determine whether a conversion table is needed for this combination of formats (actually, it is required for all the pairs, except for those with 16 bpp as a source. These latest pairs have a null pointer in the last two fields of their aGSBValidDataFormats structures.

  • If the internal format is 16bpp, a conversion table is not needed, 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 an old conversion table, it will be freed by FreeConvertTable (in DCAFCNVT.C).
-Otherwise, a palette has been realized and the internal indices need to be converted into standard external format; that is, indices are converted into the default 256-color XGA (fudged) palette.
If a conversion table has been previously created, ensure that the mapping refers to the current palette (the internal palette may have changed since the conversion table was calculated). This is done via a global variable, Seamlessdata.ulLastPalUpdate, which is incremented when the hardware palette is changed. GreGetScreenBits takes a copy of this variable when the conversion table is created. Subsequent calls check whether the value has changed before using the table. If the value has changed, the old table is deleted and a new one is created.
  • If the requested format is 4bpp:
-A conversion table is always needed. It must correctly map the current hardware palette.
-In this case, the same scheme as for 8bpp is used.

If a conversion table 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 pairs; 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 VGA standard default palette (see the appendix regarding default color palettes in the IBM OS/2 Display Device Driver Reference).

  • 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.
Pels are converted a pair 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 regarding default color palettes in the IBM OS/2 Display Device Driver Reference).
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]) ; }

The next step is to calculate the bounding rectangle of the supplied area to be queried using GreGetRegionBox. If the query area is a region, the Graphics Engine is asked for the first 10 rectangles from the query area and puts their addresses in the Spad.prclCurrent pointer (our local buffer). If the query area is a rectangle, it is made EXCLUSIVE and its address is put in Spad.prclCurrent. A check is done to see if the bounding box (for the region) or the rectangle is a valid ordered rectangle. If it is not, a PMERR_INV_IMAGE_DIMENSION is logged and the call returns 0 (zero). The call also will return 0 if the rectangle is empty. If the bounding box or the rectangle exceeds the screen dimension, PMERR_INV_IMAGE_DIMENSION is logged and the call returns 0.

Next, the destination pointer is moved past the packet header. Then, the cursor is excluded 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). The drawing mode is set to always use the real XGA hardware, calling the SetDrawModeHard macro (in EDDMACRO.H).

After setting some ShadowXGARegs fields, WaitForRealHWFunction in HWACCESS.ASM is called to ensure that the hardware is ready. Then, some of the registers that were just set up (calling TransferShadowRegisters(TSR_MAP_A | TSR_COLOUR_MIX) in HWACCESS.ASM) are transferred. Currently, only MAP_A and COLOR_MIX registers can be transferred. MAP_B, COORDINATES and PIXELOP registers will be transferred later (when all of the values are known), since they depend on the rectangle in processing.

At this point, the main loop starts:

  • For each rectangle in the Spad.prclCurrent local buffer
: adjust the current rectangle coordinates if necessary. We alter the
  coordinates according to format and bits per pel so that we do not
  have to worry about the masking associated with compressing and
  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 a 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.

The number of bytes written is reported in the packet header and the cursor is renabled via reenable_cursor in EDDMCCRS.C. Then, ExitDriver is called and a return code indicating full (return code 1) or partial data returned (return code 2) is issued.

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), as follows:

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 (for example, 8bpp and 4bpp) is done independently of the data compression. That is, if the source and destination formats differ, the source data is first converted to the destination format (in an intermediate buffer defined on the PHUNK), and then is compressed into the destination buffer. If the source and destination formats match, the data is compressed directly from the source to the destination. The data conversion routines use tables wherever possible to improve performance (see Converting Data in Code Modifications Required in Base Driver for DCAF for more information). The routines defined in COMPRESS.ASM are as follows:

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 DCAF-enabled 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.