GreGetScreenBits
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.
Contents
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
- MIN_BUFFER_SIZE
- 65536 bytes
- Limit due to PHUNK memory
- 65536 bytes
- 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.