Jump to content

PDDR/2 - Generic Printer Library: Difference between revisions

From EDM2
Ak120 (talk | contribs)
mNo edit summary
 
(29 intermediate revisions by 3 users not shown)
Line 1: Line 1:
=Generic Printer Library=
{{IBM-Reprint}}
 
{{PrintDDRef}}
The Generic Printer Library (GenPLib) is a set of subroutine components that can be used in the development of 32-bit hardcopy presentation drivers.
The Generic Printer Library (GenPLib) is a set of subroutine components that can be used in the development of 32-bit hardcopy presentation drivers.


Line 6: Line 8:
The GenPLib consists of function packages that implement some of the more difficult code that is needed to create a printer driver. The library format is a way to realize software reuse so that other developers can benefit from code that can be made completely independent of any driver.
The GenPLib consists of function packages that implement some of the more difficult code that is needed to create a printer driver. The library format is a way to realize software reuse so that other developers can benefit from code that can be made completely independent of any driver.


GenPLib appears in the IBM Developer Connection Device Driver Kit for OS/2 (DDK) as a static library that is linked to OS/2 driver code. The GenPLib code is still in its early versions; updates to the functions and header files can occur with each release of the DDK. Eventually, the GenPLib may become a system DLL; but for now, you can use any static version of GenPLib . By convention, all functions exported from GenPLib begin with the ''Gpl'' prefix.
GenPLib appears in the IBM Developer Connection Device Driver Kit for OS/2 (DDK) as a static library that is linked to OS/2 driver code. The GenPLib code is still in its early versions; updates to the functions and header files can occur with each release of the DDK. Eventually, the GenPLib may become a system DLL; but for now, you can use any static version of GenPLib. By convention, all functions exported from GenPLib begin with the ''Gpl'' prefix.


Because of the design of the modules of the GenPLib, some functions can also be used for development in areas other than OS/2 printer drivers.
Because of the design of the modules of the GenPLib, some functions can also be used for development in areas other than OS/2 printer drivers.
Line 14: Line 16:
*Written in portable C source code that can be recompiled for other processors.
*Written in portable C source code that can be recompiled for other processors.
*Provides 32-bit functions for new 32-bit operating-system environments, such as OS/2 2.0+SP and up-including OS/2 Warp.
*Provides 32-bit functions for new 32-bit operating-system environments, such as OS/2 2.0+SP and up-including OS/2 Warp.
*Available in Intel, PowerPC, debug, and non-debug versions. The current 16 -bit drivers cannot run in these environments.
*Available in Intel, PowerPC, debug, and non-debug versions. The current 16-bit drivers cannot run in these environments.
*Provides a single, consistent source code for common functions.
*Provides a single, consistent source code for common functions.
*Packages are designed using data abstraction.
*Packages are designed using data abstraction.
Line 20: Line 22:
*Produces extensive debugging and logging information for developers and service personnel.
*Produces extensive debugging and logging information for developers and service personnel.
*Implemented with the newest, most appropriate OS/2 system APIs, tailored to the underlying hardware and operating system version on which it is running (that is, system memory, paging behavior, CPU detection, etc.).
*Implemented with the newest, most appropriate OS/2 system APIs, tailored to the underlying hardware and operating system version on which it is running (that is, system memory, paging behavior, CPU detection, etc.).
 
;Note:The GenPLib has no 16-bit routines.
'''Note:''' The GenPLib has no 16-bit routines.


==GenPLib Functional Packages==
==GenPLib Functional Packages==
Line 48: Line 49:
|[[#String Management]]||Provides a way to sort text strings that are being drawn to a printer output page so that they can be processed in many page orders when needed.
|[[#String Management]]||Provides a way to sort text strings that are being drawn to a printer output page so that they can be processed in many page orders when needed.
|}
|}
==GenPLib Architecture==
==GenPLib Architecture==
The GenPLib is a 32-bit static linking library, GENPLIB.LIB. Developers use LINK386.EXE to link the GPL with their 32-bit printer drivers.
The GenPLib is a 32-bit static linking library, GENPLIB.LIB. Developers use LINK386.EXE to link the GPL with their 32-bit printer drivers.
Line 53: Line 55:
Several [[C]] language include files (*.H) are associated with GENPLIB.LIB and define the function names and prototypes in the library. The main include file is GENPLIB.H.
Several [[C]] language include files (*.H) are associated with GENPLIB.LIB and define the function names and prototypes in the library. The main include file is GENPLIB.H.


GENPLIB.H includes other relevant library H files based on symbolics (INCLs ) defined to the compiler. For example, the following symbolics have a special meaning to the operation of GENPLIB.H when included by a C program:
GENPLIB.H includes other relevant library H files based on symbolics (INCLs) defined to the compiler. For example, the following symbolics have a special meaning to the operation of GENPLIB.H when included by a C program:
<pre>
<pre>
INCL_GENPLIB_ASSERT
INCL_GENPLIB_ASSERT
Line 67: Line 69:
</pre>
</pre>
To include all functions, use:
To include all functions, use:
  INCL_GENPLIB_ALL
  INCL_GENPLIB_ALL
When a developer uses GenPLib memory-management routines, the C source file that requires GenPLib memory management would include a set of code resembling the following:
When a developer uses GenPLib memory-management routines, the C source file that requires GenPLib memory management would include a set of code resembling the following:
  #define INCL_GENPLIB_MEMORY
  #define INCL_GENPLIB_MEMORY
  #include <genplib.h>
  #include <genplib.h>
This code is included before any GenPLib memory-management functions or structures are referenced. The compiler automatically includes GPLMEM.H within GENPLIB.H.
This code is included before any GenPLib memory-management functions or structures are referenced. The compiler automatically includes GPLMEM.H within GENPLIB.H.


=Components of the Generic Print Library=
==Components of the Generic Print Library==
The following sections describe the components and functions of the GenPLib .
The following sections describe the components and functions of the GenPLib.


==Error Assertion==
===Error Assertion===
To use the GenPLib Error-Assertion package, you must include:
To use the GenPLib Error-Assertion package, you must include:
  INCL_GENPLIB_ASSERT
  INCL_GENPLIB_ASSERT
The error-assertion package of the GenPLib consists of six APIs. Four are macros:
The error-assertion package of the GenPLib consists of six APIs. Four are macros:
*[[#ASSERTT () and ASSERTF ()]]
*[[#ASSERTT () and ASSERTF ()]]
*[[#DBPRINTF (()) and DBPRINTIF (())]]
*[[#DBPRINTF (()) and DBPRINTIF (())]]
Line 102: Line 97:
|}
|}


===ASSERTT () and ASSERTF ()===
====ASSERTT () and ASSERTF ()====
The ASSERTT and ASSERTF macros are very useful to precisely locate errors in code. Each is simply a test. If the test fails, the system generates a sound, stops the program, and displays a message. The more often an ASSERTT or ASSERTF macro is used, the better the chance that the first point of failure will be captured for analysis.
The ASSERTT and ASSERTF macros are very useful to precisely locate errors in code. Each is simply a test. If the test fails, the system generates a sound, stops the program, and displays a message. The more often an ASSERTT or ASSERTF macro is used, the better the chance that the first point of failure will be captured for analysis.


Line 147: Line 142:
</pre>
</pre>


===DBPRINTF (()) and DBPRINTIF (())===
====DBPRINTF (()) and DBPRINTIF (())====
DBPRINTF uses the printf functionally of ANSI C to write messages to the kernel debugger screen. DBPRINTF is a macro; but because it can have a variable number of parameters, it must have an extra set of parentheses.
DBPRINTF uses the printf functionally of ANSI C to write messages to the kernel debugger screen. DBPRINTF is a macro; but because it can have a variable number of parameters, it must have an extra set of parentheses.


The following is the structure of DBPRINTF:
The following is the structure of DBPRINTF:
<pre>
<pre>
DBPRINTF(               // Outputs a text string to debug terminal (like C printf command)
DBPRINTF(               // Outputs a text string to debug terminal (like C printf command)
   PSZ pszFormatString, // String with same format options as printf command
   PSZ pszFormatString, // String with same format options as printf command
   (...) );             // Parameter list of values to be placed in format string
   (...) );             // Parameter list of values to be placed in format string
</pre>
</pre>
The following is the structure of DBPRINTIF:
The following is the structure of DBPRINTIF:
<pre>
<pre>
DBPRINTIF(               // A conditional DBPRINTF command
DBPRINTIF(               // A conditional DBPRINTF command
   BOOL bDoit,           // Outputs format string if this parameter evaluates to TRUE
   BOOL bDoit,           // Outputs format string if this parameter evaluates to TRUE
   PSZ pszFormatString,   // String with same format options as printf command
   PSZ pszFormatString, // String with same format options as printf command
   (...) );               // Parameter list of values to be placed in format string
   (...) );             // Parameter list of values to be placed in format string
</pre>
</pre>
====ASSERTT, ASSERTF, DBPRINTF, and DBPRINTIF Notes:====
====ASSERTT, ASSERTF, DBPRINTF, and DBPRINTIF Notes:====
ASSERTT and ASSERTF as well as DBPRINTF and DBPRINTIF are macros. They are built for the debug-level build and disappear for the retail-level build.
ASSERTT and ASSERTF as well as DBPRINTF and DBPRINTIF are macros. They are built for the debug-level build and disappear for the retail-level build.


These macros rely on the PMDD device driver to be activated. To activate the PMDD device driver, add a /Cx parameter to the following statement (where x is the COM port number):
These macros rely on the PMDD device driver to be activated. To activate the PMDD device driver, add a /Cx parameter to the following statement (where x is the COM port number):
  DEVICE=C:\OS2\PMDD.SYS /Cx
  DEVICE=C:\OS2\PMDD.SYS /Cx


===PMAssert===
====PMAssert====
'''Description'''
'''Description'''


This function is similar to ASSERTT and ASSERTF, but it displays a message box if an error condition occurs. In addition to displaying a user-provided error message, the message box also displays the results of WinGetLastError (), the Presentation Manager error code. Use this function to assert OS/2 Presentation Manager APIs.
This function is similar to ASSERTT and ASSERTF, but it displays a message box if an error condition occurs. In addition to displaying a user-provided error message, the message box also displays the results of WinGetLastError(), the Presentation Manager error code. Use this function to assert OS/2 Presentation Manager APIs.


'''Format'''
'''Format'''
Line 195: Line 190:
           "GpiDestroyPS failed!" );
           "GpiDestroyPS failed!" );
</pre>
</pre>
===BaseAssert===
===BaseAssert===
'''Description'''
'''Description'''
Line 202: Line 198:
'''Format'''
'''Format'''
<pre>
<pre>
BaseAssert(               // Display message box if a base error (DOS) is passed in (ulError)
BaseAssert(               // Display message box if a base error (DOS) is passed in (ulError)
       ULONG  ulError,     // Value of base error as defined in BSEERR.H header file
       ULONG  ulError,     // Value of base error as defined in BSEERR.H header file
       PSZ    pszString ); // String to print out with base error code
       PSZ    pszString ); // String to print out with base error code
</pre>
</pre>
;Parameters:'''ulError''' (''ULONG'') Return code from OS/2 BaseAPI, either NO_ERROR (0) or one of the ERROR_xxx DOS Error codes. If ulError is not equal to NO_ERROR, a message box is displayed.
;Parameters:'''ulError''' (''ULONG'') Return code from OS/2 BaseAPI, either NO_ERROR (0) or one of the ERROR_xxx DOS Error codes. If ulError is not equal to NO_ERROR, a message box is displayed.
Line 216: Line 212:
   BaseAssert( "DosAllocMem failed", apirc );
   BaseAssert( "DosAllocMem failed", apirc );
</pre>
</pre>
==Banding==
==Banding==
This component controls all aspects of managing a journal file and banding such as:
This component controls all aspects of managing a journal file and banding such as:
Line 226: Line 223:
*Playing bands from top-to-bottom or bottom-to-top.
*Playing bands from top-to-bottom or bottom-to-top.
To use the GenPLib Banding package, you must include:
To use the GenPLib Banding package, you must include:
  INCL_GENPLIB_JOURNAL
  INCL_GENPLIB_JOURNAL


Line 262: Line 258:
The function ulPhysMem can be used in determining the amount of memory that should be reserved for banding. For the Omni driver, for example:
The function ulPhysMem can be used in determining the amount of memory that should be reserved for banding. For the Omni driver, for example:
<pre>
<pre>
    if( ulPhysMem <= 4 )
if( ulPhysMem <= 4 )
      ulMaxBandMem = 256 * 1024;      // 256K bands
    ulMaxBandMem = 256 * 1024;      // 256K bands
    elseif( ulPhysMem < 12 )
elseif( ulPhysMem < 12 )
      ulMaxBandMem = 512 * 1024;      // 512K bands
    ulMaxBandMem = 512 * 1024;      // 512K bands
    elseif( ulPhysMem < 20 )
elseif( ulPhysMem < 20 )
      ulMaxBandMem = 1000 + 1024;    // 1M bands
    ulMaxBandMem = 1000 + 1024;    // 1M bands
    elseif( ulPhysMem >=20 )
elseif( ulPhysMem >=20 )
      ulMaxBandMem = 2000 + 1024;    // 2M bands
    ulMaxBandMem = 2000 + 1024;    // 2M bands
</pre>
</pre>
;Returns:ulPhysMem - Amount of physical RAM installed on system.
;Returns:ulPhysMem - Amount of physical RAM installed on system.
Line 278: Line 274:
This function helps you decide if you need to break the page up into bands. If you do need to journal, then it will set up the band size to be constrained by the memory limit and to be a multiple of the modulus.
This function helps you decide if you need to break the page up into bands. If you do need to journal, then it will set up the band size to be constrained by the memory limit and to be a multiple of the modulus.


Based on the page size, number of bits per pel, number of color planes, etc ., the function calculates the number of bytes needed for the page bitmap. If the amount exceeds the memory limit, it calculates the number of bands into which to break the page.
Based on the page size, number of bits per pel, number of color planes, etc., the function calculates the number of bytes needed for the page bitmap. If the amount exceeds the memory limit, it calculates the number of bands into which to break the page.


'''Format'''
'''Format'''
<pre>
<pre>
APIRET APIENTRY GplJournalCalcBandSize(
APIRET APIENTRY GplJournalCalcBandSize(
                   PIJOURNAL  pIJournal,           // Journal data structure
                   PIJOURNAL  pIJournal,         // Journal data structure
                   USHORT    usBitsPerPel,       // 1, 4, 8, or 24 bits per pel
                   USHORT    usBitsPerPel,     // 1, 4, 8, or 24 bits per pel
                   USHORT    usNumPlanes,         // Number of color planes
                   USHORT    usNumPlanes,       // Number of color planes
                   USHORT    usModulus,           // Band size should be a multiple of this
                   USHORT    usModulus,         // Band size should be a multiple of this
                   USHORT    usDotsPerColumn,     // Resolution
                   USHORT    usDotsPerColumn,   // Resolution
                   ULONG      ulMaxBandMem );     // Maximum memory available for banding
                   ULONG      ulMaxBandMem );   // Maximum memory available for banding
</pre>
</pre>
;Parameters:'''pIJournal''' (''PIJOURNAL'') Journal data structure. The following input fields are used:
;Parameters:'''pIJournal''' (''PIJOURNAL'') Journal data structure. The following input fields are used:
Line 326: Line 322:
'''Format'''
'''Format'''
<pre>
<pre>
APIRET APIENTRY GplJournalCreateInstance( HJOURNAL  hJournal,   // Journal handle
APIRET APIENTRY GplJournalCreateInstance( HJOURNAL  hJournal,   // Journal handle
                                           PIJOURNAL  pIJournal,   // Journal data structure
                                           PIJOURNAL  pIJournal, // Journal data structure
                                           ULONG      ulFlags );   // Creation flags (defaults)
                                           ULONG      ulFlags ); // Creation flags (defaults)
</pre>
</pre>
;Parameters:'''hJournal''' (''HJOURNAL'') Handle to the journal file instance.
;Parameters: '''hJournal''' (''HJOURNAL'') Handle to the journal file instance.
:'''pIJournal''' (''PIJOURNAL'') A pointer to the input structure.
:'''pIJournal''' (''PIJOURNAL'') A pointer to the input structure.
:'''ulFlags''' (''ULONG'') This function performs different actions based on the ulFlags. The ulFlags is a bit vector (individual bits have different meanings). The options are separated into mutually exclusive groups that must have one member set.
:'''ulFlags''' (''ULONG'') This function performs different actions based on the ulFlags. The ulFlags is a bit vector (individual bits have different meanings). The options are separated into mutually exclusive groups that must have one member set.
Line 408: Line 404:
|'''pIJournal'''||NULL.
|'''pIJournal'''||NULL.
|}
|}
===GplJournalPlayInstance===
===GplJournalPlayInstance===
'''Description'''
'''Description'''
Line 627: Line 624:


Following are the dithering functions:
Following are the dithering functions:
*[[#GplDitherCreateInstance]]
*[[#GplDitherRGBtoCMYK]]
*[[#GplDitherRGBtoRGB]]
*[[#GplDitherDeleteInstance]]
Gamma-correction functions are a part of the color-dithering package and are used internally by the dithering code, but they are also available separately. See [[#Gamma Correction]].


*[[00026.htm|GplDitherCreateInstance]]<br />*[[00027.htm|GplDitherRGBtoCMYK]]<br />*[[00028.htm|GplDitherRGBtoRGB]]<br />*[[00029.htm|GplDitherDeleteInstance]]
===GplDitherCreateInstance===
 
Gamma-correction functions are a part of the color-dithering package and are used internally by the dithering code, but they are also available separately. See [[00030.htm|Gamma Correction]].
 
=== GplDitherCreateInstance ===
 
'''Description'''
'''Description'''


Line 639: Line 636:


'''Format'''
'''Format'''
<pre>
APIRET APIENTRY GplDitherCreateInstance(
    PPVOID        ppdh,    // Returned pointer to dither handle pointer
    HMCB          hmcb,    // User-specified memory handle
    PDITHEREQUEST  pReq,    // Pointer to dither-request structure
    PDITHERESULT  pReply ); // Pointer to dither-reply structure
</pre>
;Parameters:'''ppdh''' (''PPVOID'')
::Pointer to the dither handle pointer that was created and returned by [[#GplDitherCreateInstance]].
:'''hmcb''' (''HMCB'')
::If it is not zero, this parameter must be a handle (pointer to the API heap).
:'''pReq''' (''PDITHEREQUEST'')
::bReq (''BYTE'') Set to CMY_ALL or CMYK_ALL.
::lDitherType (''LONG'')
:::From JobProperties.
:::HT_LEVEL
::::When selected, the following may be ORed with HT_LEVEL:
::::HT_PARAMETER
::::HT_MIN_COLOR
::::HT_PERCENT
::::HT_FUNCTION
:::HT_DITHER_4x4
::::Use a 4x4 halftone pattern for 16 levels of perceived intensity. Also "snap.pHalftone" must be a pointer to a 16x4x4 byte array of dither patterns.
:::HT_DITHER_8x8
::::Use a 8x8 halftone pattern for 64 levels of perceived intensity. Also "snap.pHalftone" must be a pointer to a 16x8x8 byte array of dither patterns.
:::HT_MAGIC_SQUARES
::::A tessalated (tiled) dither that populates a dither matrix to guarantee a semi-random color mix yet controls average occurrence of colors (randomized pattern).
:::HT_ORDERED_SQUARES
::::Use recursive tesselated tiles of rectangular threshold arrays. Similar to HT_MAGIC_SQUARES, but uses a different method of populating dither matrix.
:::HT_ERR_DIFFUSION
::::Stucki Error Diffusion-a nice quality filter for photograph-quality images.
:::HT_FAST_DIFFUSION
::::Error diffusion with lower sampling rate, so error propagated less-essentially, a high-pass filter.
:::HT_STEINBERG_DIFFUSION
::::Steinberg Error Diffusion-slightly higher pass than Stucki.
::SrcOptions (''SRCMODS'')
:::Flags that indicate the state of the buffer information.
:::fStartOfPage (''BOOL'')
::::Set in users NewFrame function; initialized by SYSTEM.
:::fNewColors (''BOOL'')
::::Set if pSrcInfo Color Table changes; initialized by SYSTEM.
:::fModify (''BOOL'')
::::Set if pSrc may be modified for K (Black) when CMY set.
:::fResetErrBuf (''BOOL'')
::::Set between pages or pictures when Err Buffers must be reset.
:::ulResetErrBufs (''ULONG'')
::::0,1,2,or 3 for Diffusion ONLY.
:::ulValue (''ULONG'')
::::xxx << 20 value to be set as initialization value.
::snap (''SNAPIT'')
:::SYSTEM initialized midpoint and function control parameters.
:::pHalftone (''PRGB2'')
::::Pointer to 4x4 8x8 or user-supplied table.
:::ulParm (''ULONG'')
::::User-supplied midpoint or function address.
:::pArray (''PBYTE'')
::::Pointer to 16x16 tables such as paintMixer or OrderSquares.
::ulResolution (''ULONG'')
:::The larger of x or y resolution.
::iNumColors ( ''INT'')
:::Set by user (2 ** number bits per pel).
::iSrcRowPels (''INT'')
:::Number of pels in a source row.
::iNumDitherRows (''INT'')
:::Number of rows in a band.
::pSrcInfo (''PBITMAPINFO2'')
:::Pointer to user supplied BITMAPINFO2 structure; requires that cx and cy be set.
::pbSrc (''PBYTE'')
:::Printer to RGB2 input bitmap (rows are 32bit aligned).
::iNumSrcRowBytes32 (''INT'')
:::iDWordBytesInCMYKBandRow.
::iNumDestRowBytes (''INT'')
:::iBytesInSinglePlaneBandRow.
::delta (''DELTACOLOR'')
:::From JobProperties for tessalated-squares dithering only .
:::{|
|lHue (''LONG'')||0..360
|-
|lSaturation (''LONG'')||0..100
|-
|lValue (''LONG'')||0..100
|}
::fRemoveBlackColor (''BOOL'')||Eliminate coincident black and color after dithering.
::ulRGamma (''ULONG'')
:::3 .. 255 true gamma value; actual value times 10.
::ulRBias (''ULONG'')
:::0 .. 255 gamma table minimum value.
::ulGGamma (''ULONG'')
:::3 .. 255 true gamma value.
::ulGBias (''ULONG'')
:::0 .. 255 gamma table minimum value.
::ulBGamma (''ULONG'')
:::3 .. 255 true gamma value.
::ulBBias (''ULONG'')
:::0 .. 255 gamma table minimum value.
:;pReply (''PDITHERESULT''):Reply structure is allocated and initialized on return
;Returns:Success NO_ERROR 0
:Failure INVALID_PARAMETER
'''Example Code'''


<pre class="western">APIRET APIENTRY GplDitherCreateInstance(
Following is an example of filling the dither-request structure and using GplDitherCreateInstance from the Omni driver during start-document processing:
                        PPVOID        ppdh,    // Returned pointer to dither handle
<pre>
                                                  // pointer
pHandle->Req.iSrcRowPels = iBitsInArray;
                        HMCB          hmcb,    // User-specified memory handle
                        PDITHEREQUEST  pReq,    // Pointer to dither-request structure
                        PDITHERESULT  pReply ); // Pointer to dither-reply structure</pre>
'''Parameters'''
 
'''ppdh'''(''PPVOID'') Pointer to the dither handle pointer that was created and returned by [[00026.htm|GplDitherCreateInstance]].
 
'''hmcb'''(''HMCB'') If it is not zero, this parameter must be a handle (pointer to the API heap).


'''pReq'''(''PDITHEREQUEST'')
// Number of bytes in source, dword aligned
pHandle->Req.iNumSrcRowBytes32 = iDWordBitsInArray/8;


bReq (''BYTE'') Set to CMY_ALL or CMYK_ALL.
// Number of bytes in destination, byte aligned
pHandle->Req.iNumDestRowBytes = iBitsInArray/8;


lDitherType (''LONG'') From JobProperties.
// Fill in number of colors (derived from bitcount)
pHandle->Req.iNumColors = Power ( 2, pddc->bmp.cBitCount );
pHandle->Req.iNumDitherRows = 1;


HT_LEVEL When selected, the following may be ORed with HT_LEVEL:
if( pddc->pdb->pResInfo->ulXRes >= pddc->pdb->pResInfo->ulYRes )
  pHandle->Req.ulResolution = pddc->pdb->pResInfo->ulXRes;
else
  pHandle->Req.ulResolution = pddc->pdb->pResInfo->ulYRes;


<pre class="western">HT_PARAMETER
/*-------------------*/
HT_MIN_COLOR
/* Color Technology  */
HT_PERCENT
/*-------------------*/
HT_FUNCTION</pre>
HT_DITHER_4x4 Use a 4x4 halftone pattern for 16 levels of perceived intensity. Also &quot;snap.pHalftone&quot; must be a pointer to a 16x4x4 byte array of dither patterns.


HT_DITHER_8x8 Use a 8x8 halftone pattern for 64 levels of perceived intensity. Also &quot;snap.pHalftone&quot; must be a pointer to a 16x8x8 byte array of dither patterns.
pHandle->Req.ulRGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bRGamma;
pHandle->Req.ulGGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bGGamma;
pHandle->Req.ulBGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBGamma;
pHandle->Req.ulRBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;
pHandle->Req.ulGBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;
pHandle->Req.ulBBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;


HT_MAGIC_SQUARES A tessalated (tiled) dither that populates a dither matrix to guarantee a semi-random color mix yet controls average occurrence of colors (randomized pattern).
pHandle->Req.ulRGamma = (ULONG)( (LONG)pHandle->Req.ulRGamma + pddc->pdb->pJobProperties->lRedGamma );
pHandle->Req.ulGGamma = (ULONG)( (LONG)pHandle->Req.ulGGamma + pddc->pdb->pJobProperties->lGreenGamma );
pHandle->Req.ulBGamma = (ULONG)( (LONG)pHandle->Req.ulBGamma + pddc->pdb->pJobProperties->lBlueGamma );
pHandle->Req.ulRBias  = (ULONG)( (LONG)pHandle->Req.ulRBias  + pddc->pdb->pJobProperties->lRedBias );
pHandle->Req.ulGBias  = (ULONG)( (LONG)pHandle->Req.ulGBias  + pddc->pdb->pJobProperties->lGreenBias );
pHandle->Req.ulBBias  = (ULONG)( (LONG)pHandle->Req.ulBBias  + pddc->pdb->pJobProperties->lBlueBias );


HT_ORDERED_SQUARES Use recursive tesselated tiles of rectangular threshold arrays. Similar to HT_MAGIC_SQUARES, but uses a different method of populating dither matrix.
/*-------------------*/
/* Color Algorithm  */
/*-------------------*/


HT_ERR_DIFFUSION Stucki Error Diffusion-a nice quality filter for photograph-quality images.
switch ( pddc->pdb->pPrintMode->ulColorTech )
{
  case COLOR_TECH_CMYK:
    pHandle->Req.bReq = CMYK_ALL;
    break;


HT_FAST_DIFFUSION Error diffusion with lower sampling rate, so error propagated less-essentially, a high-pass filter.
  case COLOR_TECH_CMY:
    pHandle->Req.bReq = CMY_ALL;
    break;
}


HT_STEINBERG_DIFFUSION Steinberg Error Diffusion-slightly higher pass than Stucki.
/*-------------------*/
/* Color Algorithm  */
/*-------------------*/


SrcOptions (''SRCMODS'') Flags that indicate the state of the buffer information.
// Need gamma buffers only if printing in color (CMY/CMYK)


fStartOfPage (''BOOL'') Set in users NewFrame function; initialized by SYSTEM.
if( pddc->pdb->pPrintMode->ulColorTech != COLOR_TECH_K )
{
  // Init modify flag telling Gamma/HSV that we may replace/change
  // values in the source bitmap.  1,2,4,8 bit pel bitmaps; then
  // if C,M,Y are to be printed, the RGB index will be set to white.
  // If 16, 24 bit bitmaps, then the R,G,B value is replaced with white.
  pHandle->Req.SrcOptions.fModify  = TRUE;


fNewColors (''BOOL'') Set if pSrcInfo Color Table changes; initialized by SYSTEM.
  /*-------------------*/
  /* HSV              */
  /*-------------------*/


fModify (''BOOL'') Set if pSrc may be modified for K (Black) when CMY set.
  pHandle->Req.delta.lHue        = pddc->pdb->pJobProperties->lHue;
  pHandle->Req.delta.lSaturation = pddc->pdb->pJobProperties->lSaturation;
  pHandle->Req.delta.lValue      = pddc->pdb->pJobProperties->lValue;


fResetErrBuf (''BOOL'') Set between pages or pictures when Err Buffers must be reset.
  /*-------------------*/
  /* Dither            */
  /*-------------------*/


ulResetErrBufs (''ULONG'') 0,1,2,or 3 for Diffusion ONLY.
  pHandle->Req.lDitherType = pddc->pdb->pJobProperties->ulAlgorithm;


ulValue (''ULONG'') xxx &lt;&lt; 20 value to be set as initialization value.
  GPL_DITHER_SETUP_DEFAULTS( pHandle->Req,  pddc->pdb->pJobProperties->ulLevel )
}


snap (''SNAPIT'') SYSTEM initialized midpoint and function control parameters.
// Set printer's physical head position in our ddc
pddc -> ptlPrintHead.x = 0;


pHalftone (''PRGB2'') Pointer to 4x4 8x8 or user-supplied table.
if ( ORIENTATION_PORTRAIT == pddc -> pdb -> pJobProperties -> ulOrientation )  
    pddc -> ptlPrintHead.y = pddc -> pdb -> pFormInfo -> hcInfo.yPels - 1;
else
    pddc -> ptlPrintHead.y = pddc -> pdb -> pFormInfo -> hcInfo.xPels - 1;


ulParm (''ULONG'') User-supplied midpoint or function address.
GplDitherCreateInstance ( (PPVOID) & pHandle -> pdh,
                            pddc -> pdb -> hmcbHeap,
                          (PDITHEREQUEST) & pHandle -> Req,
                          (PDITHERESULT ) & pHandle -> Reply);


pArray (''PBYTE'') Pointer to 16x16 tables such as paintMixer or OrderSquares.
/*--------------------------------*/
/* Init Dither Req struct        */
/*--------------------------------*/


ulResolution (''ULONG'') The larger of x or y resolution.
pCMYReq->pSrcInfo = pbmi;  // Passed to choose rasterize


iNumColors ( ''INT'') Set by user (2 ** number bits per pel).
LOOP {                          // Each band


iSrcRowPels (''INT'') Number of pels in a source row.
  // Copy raster bits into our buffer
  pCMYReq->pbSrc = pbBitMap + iCurrentRow * pHandle->Req.iNumSrcRowBytes32;


iNumDitherRows (''INT'') Number of rows in a band.
  GplDitherRGBtoCMYK ( (PPVOID)&pHandle->pdh,
                        pCMYReq,
                        (PDITHERESULT)&pHandle->Reply );
</pre>


pSrcInfo (''PBITMAPINFO2'') Pointer to user supplied BITMAPINFO2 structure; requires that cx and cy be set.
===GplDitherRGBtoCMYK===
'''Description'''


pbSrc (''PBYTE'') Printer to RGB2 input bitmap (rows are 32bit aligned).
This function performs the dithering.


iNumSrcRowBytes32 (''INT'') iDWordBytesInCMYKBandRow.
'''Format'''
<pre>
APIRET APIENTRY GplDitherRGBtoCMYK(
  PHDITHER      pdh,
  PDITHEREQUEST  pReq,
  PDITHERESULT  pReply
);
</pre>
;Parameters:'''pdh''' (''PHDITHER'')
:: Pointer to the dither handler that was created by [[#GplDitherCreateInstance]].
:'''pReq''' (''PDITHEREQUEST'')
::If a diffusion dither algorithm is selected, use the macro GPL_SETUP_DITHER( req, ulLevel ) to set the SrcOptions structure.
::'''or'''
::If Magic Squares or Ordered Squares is selected, the following fields must be set:
:::'''lHue'''
:::'''lSaturation'''
:::'''lValue''' Address of the API's pre-stuffed request structure. The following fields must be set by the API:
:::{|
|fNewColors||Must be set to TRUE at the beginning of the page. This element is set to FALSE on return.
|-
| ||If fNewColors equals TRUE, the following two fields must be set:
:'''ulGamma'''
:'''lBias'''
|-
|pSrcInfo||Must have the following two fields set:
|-
| ||
:'''cx'''
:'''cy'''
|-
| ||If BitsPerPel is less than 16 and fNewColors equals TRUE, the pbmi array, argbColor[256] must be initialized.
|-
|pbSrc||Pointer to the surface bitmap.
|}
:'''pReply''' (''PDITHERESULT'')
::Pointer to the reply structure.
;Returns:Success NO_ERROR 0
:Failure INVALID_PARAMETER
'''Example Code'''


iNumDestRowBytes (''INT'') iBytesInSinglePlaneBandRow.
Following is an example of dithering RGB to CMYK data from the Omni driver' s "render" callback function:
<pre>
// Change logical height of band bitmap to 1 (this device chooses to dither each scanline)
ulSavedcy = pddc->pbmi->cy;
pddc->pbmi->cy = 1;


delta (''DELTACOLOR'') From JobProperties for tessalated-squares dithering only .
// For each row in the current band's bitmap (each scanline)
 
for( iCurrentRow=ulSavedcy; iCurrentRow > 0; iCurrentRow-- )
lHue (''LONG'') 0..360 <br />lSaturation (''LONG'') 0..100 <br />lValue (''LONG'') 0..100
{
  // Set pointer to current row in our band's bitmap
  cmykReq.pbSrc = pbBits + iCurrentRow * iDWordBytesInCMYKBandRow;


fRemoveBlackColor (''BOOL'') Eliminate coincident black and color after dithering.
  GplDitherRGBtoCMYK ( &pddc->pdh, &pddc->cmykReq, &pddc->pReply );


ulRGamma (''ULONG'') 3 .. 255 true gamma value; actual value times 10.
  // Now our consecutive KCMY buffers contain dithered data to send to printer


ulRBias (''ULONG'') 0 .. 255 gamma table minimum value.
} /* end for */


ulGGamma (''ULONG'') 3 .. 255 true gamma value.
// Restore bitmap height to TRUE value (no longer 1)
pddc->pbmi->cy = ulSavedcy;
</pre>
===GplDitherRGBtoRGB===
'''Description'''


ulGBias (''ULONG'') 0 .. 255 gamma table minimum value.
This function is a shell for [[#GplDitherRGBtoCMYK]]. It simply inverts the CMY ''pDest'' buffer to RGB for a printer that requires RGB.


ulBGamma (''ULONG'') 3 .. 255 true gamma value.
'''Format'''


ulBBias (''ULONG'') 0 .. 255 gamma table minimum value.
APIRET APIENTRY GplDitherRGBtoRGB( PHDITHER      pdh,
                                    PDITHEREQUEST  pReq
                                    PDITHERESULT  pReply );


'''pReply'''(''PDITHERESULT'') Reply structure is allocated and initialized on return
;Parameters:'''pdh''' (''PHDITHER'')
::Pointer to the dither handler that was created by [[#GplDitherCreateInstance]].
:'''pReq''' (''PDITHEREQUEST'')
::Address of the API's pre-filled request structure.
:'''pReply''' (''PDITHERESULT'')
::Address of the reply structure.
;Returns:Success NO_ERROR
===GplDitherDeleteInstance===


'''Returns'''
'''Description'''


Success NO_ERROR 0 <br />Failure INVALID_PARAMETER
This function must be called at the end of a document so that memory that was allocated in [[#GplDitherCreateInstance]] can be freed.


'''Format'''
APIRET APIENTRY GplDitherDeleteInstance( PHDITHER  pdh );
;Parameter:'''pdh'''(''PHDITHER'')
::Pointer to the dither handler that was created by [[#GplDitherCreateInstance]].
;Returns:Success NO_ERROR
'''Example Code'''
'''Example Code'''


Following is an example of filling the dither-request structure and using GplDitherCreateInstance from the Omni driver during start-document processing:
Following is an example of using GplDitherDeleteInstance:
<pre>
// Tell the dither function to deallocate all it's buffers
GplDitherDeleteInstance( (HDITHER)pHandle->pdh );


<pre class="western">   pHandle-&gt;Req.iSrcRowPels = iBitsInArray;
// Reminder:
// Free all buffers our driver allocated on behalf of GENPLIB's dithering code
// This includes error buffers for error-diffusion algorithms
// as well as the destination buffers used for rendering
</pre>


  // Number of bytes in source, dword aligned
==Gamma Correction==
  pHandle-&gt;Req.iNumSrcRowBytes32 = iDWordBitsInArray/8;
Printer paper, ink, resolution, and other characteristics can cause the actual printed color to be different from the desired color. This "skew" is non-linear and occurs over the entire color spectrum. A gamma-compensation curve is used for each color to approximate the non-linear skew. Because the reflected frequencies of colors can cause them to appear different than expected, you can modify the gamma of each color. Increasing the gamma will lighten an image that might otherwise look too dark.


  // Number of bytes in destination, byte aligned
Following are the gamma functions:
  pHandle-&gt;Req.iNumDestRowBytes = iBitsInArray/8;
*[[#GplGammaCreateTable]]
*[[#GplGammaColorTableToGamma2]]
===GplGammaCreateTable===
'''Description'''


  // Fill in number of colors (derived from bitcount)
This function creates an RGB (K) gamma table based on input gammas and bias .
  pHandle-&gt;Req.iNumColors = Power ( 2, pddc-&gt;bmp.cBitCount );
  pHandle-&gt;Req.iNumDitherRows = 1;


  if( pddc-&gt;pdb-&gt;pResInfo-&gt;ulXRes &gt;= pddc-&gt;pdb-&gt;pResInfo-&gt;ulYRes )
'''Format'''
    pHandle-&gt;Req.ulResolution = pddc-&gt;pdb-&gt;pResInfo-&gt;ulXRes;
<pre>
  else
APIRET APIENTRY GplGammaCreateTable( PBYTE  pbGamma,
    pHandle-&gt;Req.ulResolution = pddc-&gt;pdb-&gt;pResInfo-&gt;ulYRes;</pre>
                                    LONG  lRgamma,
                                    LONG  lGgamma,
                                    LONG  lBgamma,
                                    LONG  lbias );
</pre>
;Parameters:'''pbGamma''' (''PBYTE'')
::A byte pointer to a 768-byte buffer.
::pbGamma can be used as a PRGB2 instead of a PBYTE.
:'''lRgamma''' (''LONG'')
::Red. Gamma multiplied by 10, with a range of 1 to 99.
:'''lGgamma''' (''LONG'')
::Green. Gamma multiplied by 10, with a range of 1 to 99.
:'''lBgamma''' (''LONG'')
::Blue. Gamma multiplied by 10, with a range of 1 to 99.
:'''lbias''' (''LONG'')
::Offset, normally zero, generally for printers over 600 dpi.
;Returns:Success 0
:Failure INVALID_PARAMETER
===GplGammaColorTableToGamma2===
'''Description'''


For 8-bit pels or less, replaces an RGB color value in the PBITMAPINFO2 argbColor[0..256] structure with a gamma-corrected color value.


<pre class="western">  /*-------------------*/
'''Format'''
  /* Color Technology  */
  /*-------------------*/


  pHandle-&gt;Req.ulRGamma = (ULONG)( pddc-&gt;pdb-&gt;pDriver-&gt;pGammas-&gt;pSelect+i )-&gt;bRGamma;
APIRET APIENTRY GplGammaColorTableToGamma2( PBYTE          pbGamma,
  pHandle-&gt;Req.ulGGamma = (ULONG)( pddc-&gt;pdb-&gt;pDriver-&gt;pGammas-&gt;pSelect+i )-&gt;bGGamma;
                                            PBITMAPINFO2  pbmi );
  pHandle-&gt;Req.ulBGamma = (ULONG)( pddc-&gt;pdb-&gt;pDriver-&gt;pGammas-&gt;pSelect+i )-&gt;bBGamma;
;Parameters:'''pbGamma''' (''PBYTE'')
  pHandle-&gt;Req.ulRBias  = (ULONG)( pddc-&gt;pdb-&gt;pDriver-&gt;pGammas-&gt;pSelect+i )-&gt;bBias;
::A byte pointer to a 768-byte buffer.
  pHandle-&gt;Req.ulGBias  = (ULONG)( pddc-&gt;pdb-&gt;pDriver-&gt;pGammas-&gt;pSelect+i )-&gt;bBias;
::pbGamma can be used as a PRGB2 instead of a PBYTE.
  pHandle-&gt;Req.ulBBias  = (ULONG)( pddc-&gt;pdb-&gt;pDriver-&gt;pGammas-&gt;pSelect+i )-&gt;bBias;
:'''pbmi''' (''PBITMAPINFO2'')
::PBITMAPINFO2 is assumed to have a valid RGB2 color table as input. The following PBITMAPINFO2 fields are used:
::cBitCount - Bits per pel.
::argbColor - When cBitCount is 8 or less, this parameter must include values for 2 to the power of cBitCount (2**cBitCount).
;Returns:Success 0
:Failure INVALID_PARAMETER


  pHandle-&gt;Req.ulRGamma = (ULONG)( (LONG)pHandle-&gt;Req.ulRGamma +  pddc-&gt;pdb-&gt;
==Compression==
                                                      pJobProperties-&gt;lRedGamma );
The compression package contains routines that compress raster image data using common printer hardware-supported compression routines.
  pHandle-&gt;Req.ulGGamma = (ULONG)( (LONG)pHandle-&gt;Req.ulGGamma +  pddc-&gt;pdb-&gt;
                                                      pJobProperties-&gt;lGreenGamma );
  pHandle-&gt;Req.ulBGamma = (ULONG)( (LONG)pHandle-&gt;Req.ulBGamma +  pddc-&gt;pdb-&gt;
                                                      pJobProperties-&gt;lBlueGamma );
  pHandle-&gt;Req.ulRBias  = (ULONG)( (LONG)pHandle-&gt;Req.ulRBias  +  pddc-&gt;pdb-&gt;
                                                      pJobProperties-&gt;lRedBias );
  pHandle-&gt;Req.ulGBias  = (ULONG)( (LONG)pHandle-&gt;Req.ulGBias  +  pddc-&gt;pdb-&gt;
                                                      pJobProperties-&gt;lGreenBias );
  pHandle-&gt;Req.ulBBias  = (ULONG)( (LONG)pHandle-&gt;Req.ulBBias  +  pddc-&gt;pdb-&gt;
                                                      pJobProperties-&gt;lBlueBias );</pre>


Raster printer drivers render on a per-page basis into a raster representation of an output page. As soon as rendering is completed for a given page, that raster data can be compressed into a format that the printer hardware supports. Compressed data is typically smaller than the original uncompressed data and will therefore transmit faster across communication lines to the printer.


<pre class="western">  /*-------------------*/
'''Note:''' The caller of a compression routine should allocate a buffer to hold the compressed data whose size (in bytes) is as large as the original uncompressed data.
  /* Color Algorithm  */
  /*-------------------*/


  switch ( pddc-&gt;pdb-&gt;pPrintMode-&gt;ulColorTech )
To use the GenPLib Compression package, you must include:
  {
INCL_GENPLIB_COMPRESS
    case COLOR_TECH_CMYK:
Following are the GenPLib compression routines:
      pHandle-&gt;Req.bReq = CMYK_ALL;
*[[#GplCompressRLL]]
      break;
*[[#GplCompressTIFF]]
*[[#GplCompressDeltaRow]]
*[[#GplCompressRLLDeltaRow]]
*[[#GplCompressChooseMode]]
*[[#GplCompressAscii85]]
*[[#GplFaxG3EncodeBlock]]
**GplFaxG3StartBlock
**GplFaxG3EndBlock
**GplFaxG3EndDoc
**GplFaxG3NewFrame
*[[#GplFaxG4EncodeBlock]]
**GplFaxG4EndBlock
*[[#GplFaxTIFF2EncodeBlock]]
**GplFaxTIFF2StartBlock
**GplFaxTIFF2EndBlock
===GplCompressRLL===
'''Description'''


    case COLOR_TECH_CMY:
This function performs run-length limited (RLL) encoding.
      pHandle-&gt;Req.bReq = CMY_ALL;
      break;
  }</pre>


'''Format'''
<pre>
LONG APIENTRY GplCompressRLL(
                    PBYTE  pbDataIn,    // Input raster data to compress (page/band)
                    LONG  cbDataIn,    // Length of input data buffer (pbDataIn)
                    PBYTE  pbDataOut,  // Output buffer to which compressed data is written
                    LONG  cbDataOut ); // Length count of compressed data written
</pre>
;Parameters:'''pbDataIn''' (''PBYTE'')
::Pointer to input data.
:'''cbDataIn''' (''LONG'')
::Count of bytes in, pointed to by pbData.
:'''pbDataOut''' (''PBYTE'')
::Pointer to output data.
:'''cbDataOut''' (''LONG'')
::Size of buffer in bytes pointed to by pbDataOut.
;Returns:Signed LONG, which is the length in bytes of the compressed data pointed to by cbDataOut.
:Success
::Number of bytes in compressed return string.
:Failure
:: GPLCOMPRESS_ERROR (-1)
::The output buffer length has been exceeded.
'''Notes'''


<pre class="western">  /*-------------------*/
The input data is encoded with pairs of bytes. The first byte is the replacement count and the second byte is the data. A replacement count of 0 means that the data is not repeated.
  /* Color Algorithm  */
  /*-------------------*/


  // Need gamma buffers only if printing in color (CMY/CMYK)
===GplCompressTIFF===
'''Description'''


  if( pddc-&gt;pdb-&gt;pPrintMode-&gt;ulColorTech != COLOR_TECH_K )
This function performs tagged image file format (TIFF) encoding, PackBits encoding algorithm. This function compresses a scanline.
  {
    // Init modify flag telling Gamma/HSV that we may replace/change
    // values in the source bitmap.  1,2,4,8 bit pel bitmaps; then
    // if C,M,Y are to be printed, the RGB index will be set to white.
    // If 16, 24 bit bitmaps, then the R,G,B value is replaced with white.
    pHandle-&gt;Req.SrcOptions.fModify  = TRUE;</pre>


Once in a repeated run, this function stops and emits a block if a different byte is encountered.


<pre class="western">    /*-------------------*/
Once in a literal run, this function stops and emits a block if at least four repeated bytes follow. The next block will be a repeat block. Repeats of two or more instances of a byte b are represented by -(n-1) b. Literal runs of different bytes b1 b2 b3 ... bn are represented by (n-1) b1 b2 b3 . .. bn.
    /* HSV              */
    /*-------------------*/


    pHandle-&gt;Req.delta.lHue        = pddc-&gt;pdb-&gt;pJobProperties-&gt;lHue;
'''Format'''
    pHandle-&gt;Req.delta.lSaturation = pddc-&gt;pdb-&gt;pJobProperties-&gt;lSaturation;
<pre>
    pHandle-&gt;Req.delta.lValue      = pddc-&gt;pdb-&gt;pJobProperties-&gt;lValue;</pre>
LONG APIENTRY GplCompressTIFF(
                    PBYTE  pbDataIn,    // Input raster data to compress (page/band)
                    LONG  cbDataIn,    // Length of input data buffer (pbDataIn)
                    PBYTE  pbDataOut,  // Output buffer to which compressed data is written
                    LONG  cbDataOut ); // Length count of compressed data written
</pre>
;Parameters:'''pbDataIn''' (''PBYTE'')
::Pointer to input data.
:'''cbDataIn''' (''LONG'')
::Count of bytes in.
:'''pbDataOut''' (''PBYTE'')
::Pointer to output data.
:'''cbDataOut''' (''LONG'')
::Size of buffer in bytes pointed to by pbDataOut.
;Returns:Success Count of bytes put in destination. <br />Failure GPLCOMPRESS_ERROR (-1)


===GplCompressDeltaRow===
'''Description'''


<pre class="western">    /*-------------------*/
This function performs delta-row encoding. This method replaces only the bytes in a new row that are different from the preceding (seed) row. Unreplaced bytes are replicated from the seed row. The new row then becomes the seed row after it is printed.
    /* Dither            */
    /*-------------------*/


     pHandle-&gt;Req.lDitherType = pddc-&gt;pdb-&gt;pJobProperties-&gt;ulAlgorithm;
'''Format'''
 
<pre>
     GPL_DITHER_SETUP_DEFAULTS( pHandle-&gt;Reqpddc-&gt;pdb-&gt;pJobProperties-&gt;ulLevel )
INT APIENTRY GplCompressDeltaRow(
  }
                      INT     usTotalBytes,  // Bytes in the scan line
 
                      PBYTE    pbData,        // Input raster data to compress
  // Set printer ' s   physical  head  position  in  our  ddc
                      PBYTE    pbLastLine,    // Previous scanline's raster data
    pddc - &gt; ptlPrintHead . =   0 ;
                      INT     usMaxReturn,    // Length of output buffer
                      PBYTE    pbReturn,       // Output buffer with compressed data
                      PUSHORT pDeltas );     // Differences indexes-start/end pairs
</pre>
;Parameters:'''usTotalBytes''' (''INT'')
::Count of bytes in, pointed to by pbData.
:'''pbData''' (''PBYTE'')
::Pointer to bytes of data to compress.
:'''pbLastLine''' (''PBYTE'')
::Pointer to previous scanline's raster data.
:'''usMaxReturn''' (''INT'')
::Size of output buffer.
:'''pbReturn''' (''PBYTE'')
::Pointer to compressed data will be written.
:'''pDeltas''' (''PUSHORT'')
::Differences indexes = array of start and end pairs.
::'''Note:''' This array is returned as the result of calling [[#GplCompressChooseMode]]; therefore, you must call GplCompressChooseMode() before calling this function.
;Returns
:{|
|0||Current and previous line are identical.
|-
|>0||Length of data pointed to by pbReturn.
|-
| -1||GPLCOMPRESS_ERROR
|-
| ||usMaxReturn is not large enough for the compressed output.
|}
===GplCompressRLLDeltaRow===
'''Description'''


    if  (  ORIENTATION _ PORTRAIT  = =  pddc - &gt; pdb - &gt; pJobProperties - &gt; ulOrientation  )
This function performs RLL delta-row encoding. It compresses a string of bytes and returns the compressed string at pbReturn and the new string usLength as its return value. This method replaces only bytes in the current row that are different from the proceeding seed row. Unlike [[#GplCompressDeltaRow]], however, the replacement or delta bytes themselves are encoded.
      pddc - &gt; ptlPrintHead . y  =  pddc - &gt; pdb - &gt; pFormInfo - &gt; hcInfo . yPels - 1 ;
    else
      pddc - &gt; ptlPrintHead . y  =  pddc - &gt; pdb - &gt; pFormInfo - &gt; hcInfo . xPels - 1 ;


'''Format'''
<pre>
INT APIENTRY GplCompressRLLDeltaRow(
                      INT      usTotalBytes,  // Bytes in the scan line
                      PBYTE    pbData,        // Input raster data to compress
                      PBYTE    pbLastLine,    // Previous scanline's raster data
                      INT      usMaxReturn,    // Length of output buffer
                      PBYTE    pbReturn,      // Output buffer with compressed data
                      PUSHORT  pDeltas );      // Differences indexes-start/end pairs
</pre>
;Parameters:'''usTotalBytes''' (''INT'')
::Count of bytes pointed to by pbData and pbLastLine.
:'''pbData''' (''PBYTE'')
::Pointer to bytes of data to compress.
:'''pbLastLine''' (''PBYTE'')
::Pointer to previous uncompressed data.
:'''usMaxReturn''' (''INT'')
::Size of output buffer.
:'''pbReturn''' (''PBYTE'')
::Pointer to compressed data will be written.
:'''pDeltas''' (''PUSHORT'')
::Differences indexes = array of start and end pairs.
;Returns:
:{|
| 0||Current and previous line are identical.
|-
| >0||Length of data pointed to by pbReturn.
|-
| -1||GPLCOMPRESS_ERROR
|-
| ||usMaxOutput not large enough for the compressed output.
|}
===GplCompressChooseMode===
'''Description'''


    GplDitherCreateInstance  (  ( PPVOID ) &amp; pHandle - &gt; pdh ,
This function uses a semi-intelligent algorithm to select the compression mode that will most likely give the best compression for the row of data.
                                pddc - &gt; pdb - &gt; hmcbHeap ,
                                ( PDITHEREQUEST ) &amp; pHandle - &gt; Req ,
                                ( PDITHERESULT ) &amp; pHandle - &gt; Reply  ) ; </pre>


'''RLL Delta Row''' is currently chosen if this method best and if the printer supports this compression. '''Delta Row''' is chosen if most of the bytes are the same as the corresponding bytes in the previous row. '''TIFF''' is chosen if most of the bytes are repeated within this row. RLL is chosen if most of the bytes are repeated within this row and the printer does not support TIFF PackBits. '''No compression (value of 0)''' is chosen of most bytes are neither repeats within the row nor duplicates of the previous row.


<pre class="western">  /*--------------------------------*/
'''Note:''' The function GplCompressChooseMode() must be run before either of the delta-compression functions [[#GplCompressDeltaRow]]() or [[#GplCompressRLLDeltaRow]](). This function must be run first because pDelta is filled in by GplCompressChooseMode() while it is reading pbRow and pbLast_ Row. Performance is increased by combining GplCompressChooseMode() with either [[#GplCompressDeltaRow]]() or [[#GplCompressRLLDeltaRow]]().
  /* Init Dither Req struct        */
  /*--------------------------------*/


   pCMYReq-&gt;pSrcInfo = pbmi;   // Passed to choose rasterize
'''Format'''
 
<pre>INT APIENTRY GplCompressChooseMode( PBYTE    pbRow,
  LOOP {                          // Each band
                                    PBYTE    pbLast_Row,
                                    INT      usRow_Length,
                                    ULONG   CompressModes,
                                    PUSHORT  pDelta );
</pre>
;Parameters:'''pbRow''' (''PBYTE'')
::Pointer to the current row's data.
:'''pbLast_Row''' (''PBYTE'')
::Pointer to the last row's data.
:'''usRow_Length''' (''INT'')
::The number of bytes in the row. Note that both the previous row and the current row are equal in size.
:'''CompressModes''' (''ULONG'')
::Compression modes supported.
::This is a ULONG with the follow bit positions used to represent compress modes that this printer supports:
:::GPLCOMPRESS_NONE
:::GPLCOMPRESS_RLL
:::GPLCOMPRESS_TIFF
:::GPLCOMPRESS_DELTAROW
:::GPLCOMPRESS_RLLDELTAROW
::These values may be logically ORed together to create a bitfield of all compression methods to consider. For example,


    // Copy raster bits into our buffer
CompressModes = GPLCOMPRESS_RLL | GPLCOMPRESS_TIFF
    pCMYReq-&gt;pbSrc = pbBitMap + iCurrentRow * pHandle-&gt;Req.iNumSrcRowBytes32;
::This tells the routine to only choose between the RLL and TIFF compression routines and GPLCOMPRESS_NONE.
:'''pDelta''' (''PUSHORT'') Array of unsigned shorts to hold the offsets from the beginning of pbRow that differ from pbLast_Row. This data will be used by [[#GplCompressDeltaRow]] and [[#GplCompressRLLDeltaRow]]. Note that pDelta must have a length equal to two times usRow_Length plus one to hold the pDelta output . There is no check on the size of pDelta. You must supply a size of a minimum of two times usRow_Length plus one. This is because the maximum number of differences occur when every other byte differs from the preceding row.
;Returns:The mode that will probably compress the best:
::0 No compression
::1 RLL (run-length limited) compression
::2 TIFF (tagged image file format) compression
::3 Delta-row compression
::9 RLL delta-row compression
'''Notes'''


    GplDitherRGBtoCMYK ( (PPVOID)&amp;pHandle-&gt;pdh,
For [[#GplCompressDeltaRow]] and [[#GplCompressRLLDeltaRow]], pDelta is filled with the start and end of the difference fragments. Note that a difference fragment is defined as a contiguous group of bytes in the current row that differ from those in the previous row. For example:
                          pCMYReq,
{| class="wikitable"
                          (PDITHERESULT)&amp;pHandle-&gt;Reply );</pre>
!Column Number||1||2||3||4||5||6||7||8||9||10||11||12||13||14
|-
|Current Row Byte Value||7||8||1||0||4||5||6||7||23||128||257||1||7||8
|-
|Previous Row Byte Value||7||1||3||1||4||5||9||7||23||128||7||9||7||8
|}
There are three difference fragments:
*Fragment one begins at column 2 and ends at column 4.
*Fragment two begins at column 7 and ends at column 7.
*Fragment three begins at column 11 and ends at column 12.


=== GplDitherRGBtoCMYK ===
The output in pDelta will be 2 4 7 7 11 12 0. Note that these are unsigned shorts and the difference array is null-terminated. If no difference fragments are found (the previous and current buffers are the same) the first unsigned short in pDelta will be 0.


'''Description'''
''pDelta'' above will point to a array of the indexes stored to used by GplCompressDeltaRow() and GplCompressRLLDeltaRow().


This function performs the dithering.
'''Example Code'''


'''Format'''
Following is an example of GplCompressChooseMode usage from the Omni driver for a typical inkjet device:
<pre>
iNewMode = GplCompressChooseMode(
                pbBuffer,            // Contains current row of data
                pbLastLine,          // Contains previous row of data
                iBytesInRow,          // Number of bytes in output row (pbBuffer)
                GPLCOMPRESS_RLL |    // All compression modes supported
                GPLCOMPRESS_TIFF,
                pDelta );            // Offsets into pbBuffer ((2 x pbBuffer) + 1)


<pre class="western">APIRET APIENTRY GplDitherRGBtoCMYK( PHDITHER      pdh,
  if( iNewMode != iCurrentMode )
                                    PDITHEREQUEST  pReq
  {
                                    PDITHERESULT  pReply );</pre>
    // Send command to printer to set new mode
'''Parameters'''
    iCurrentMode = iNewMode;


'''pdh'''(''PHDITHER'') Pointer to the dither handler that was created by [[00026.htm|GplDitherCreateInstance]].
  } /* end if */


'''pReq'''(''PDITHEREQUEST'') If a diffusion dither algorithm is selected, use the macro GPL_SETUP_DITHER( req, ulLevel ) to set the SrcOptions structure.
  switch( iNewMode )
 
  {
'''or'''
    case 0:                          // No Compression
    break;
    case 1: iCompressed = GplCompressRLL();
    break;
    case 2: iCompressed = GplCompressTIFF();
    break;
    case 3: iCompressed = GplCompressDeltaRow();
    break;
    case 9: iCompressed = GplCompressRLLDeltaRow();
    break;


If Magic Squares or Ordered Squares is selected, the following fields must be set:
  } /* end switch */


'''lHue'''<br />'''lSaturation'''<br />'''lValue'''Address of the API's pre-stuffed request structure. The following fields must be set by the API:
  // Write raster data transfer command and compressed data
</pre>
===GplCompressAscii85===
'''Description'''


fNewColors Must be set to TRUE at the beginning of the page. This element is set to FALSE on return.
This function performs ASCII encoding. It encodes 4 bytes in to 5 bytes out , and it converts data from a base 256 on input to a base-85 representation on output. The output is then output in ASCII with base 85.


If fNewColors equals TRUE, the following two fields must be set:
Buffer allocation is the responsibility of the driver.


'''ulGamma'''<br />'''lBias'''
'''Format'''
<pre>
INT APIENTRY GplCompressAscii85(
                    PBYTE  pSource,        // Source input data buffer (normal Hex 256)
                    PBYTE  pDest,          // Destination output data buffer (ASCII 85)
                    INT    usSourceLen );  // Source buffer input length
</pre>
;Parameters:'''pSource''' (''PBYTE'')
::Pointer to input data buffer.
:'''pDest''' (''PBYTE'')
::Pointer to output data buffer. It must be at least 5/4 size of pSource.
:'''usSourceLen''' (''LONG'')
::Length of source buffer in bytes.
;Returns:Success: Count of bytes put in destination.
:Failure: GPLCOMPRESS_ERROR (-1)


pSrcInfo Must have the following two fields set:
===GplFaxG3EncodeBlock===
'''Description'''


'''cx'''<br />'''cy'''
This function performs G3 "Fax"-type compression.


If BitsPerPel is less than 16 and fNewColors equals TRUE, the pbmi array, argbColor[256] must be initialized.
It provides a run-length type compression with table-lookup replacement encodings for certain bit sequences. This type of compression is also know as "Modified Huffman."
 
'''Format'''
pbSrc Pointer to the surface bitmap.
<code>
 
:{|
'''pReply'''(''PDITHERESULT'') Pointer to the reply structure.
|GplFaxG3EncodeBlock(||HTHREAD||hThread,
 
|-
'''Returns'''
|||PBYTE||pbOutput,
 
|-
Success NO_ERROR 0 <br />Failure INVALID_PARAMETER
|||ULONG||ulOutputSize,
|-
|||PULONG||pulBitposition,
|-
|||PBYTE||pbInput,
|-
|||ULONG||ulBitwide,
|-
|||ULONG||ulBitheight,
|-
|||PFN||pfnFlushBuffer );
|}
</code>
;Parameters:'''hThread''' (''HTHREAD'')
::Handle.
:'''pbOutput''' (''PBYTE'')
::Pointer to output buffer to fill.
:'''ulOutputSize''' (''ULONG'')
::Number of output bytes; must be divisible by 4.
:'''pulBitposition''' (''PULONG'')
::Pointer to bit position in output buffer; gets updated and must be stored off the pddc.
:'''pbInput''' (''PBYTE'')
::Input pointer to upper left corner of raster image to compress.
:'''ulBitwide''' (''ULONG'')
::Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
:'''ulBitheight''' (''ULONG'')
::Height of raster image to compress in bits.
:'''pfnFlushBuffer''' (''PFN'')
::The prototype for this function must be:
<code>
::{|
|void pfnFlushBuffer (||PVOID,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::{|
|PVOID||Use is the same as G4.
|-
|PBYTE||Unsigned char * to output buffer; same as pbOutput.
|-
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|}
;Returns:Success: Count of data written out (compressed data count).
:Failure: GPLCOMPRESS_ERROR (-1)
;Related Functions:The following G3 "Fax"-compression related functions provide help in sending down special G3 commands.
;GplFaxG3StartBlock():Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of the first page. This function outputs the EOL to pbOutput and initializes pulBitposition to 12. It simply sends out the EOL bit sequence to signal the start of raster data (for example, '000000000001' binary, eleven 0's and a single 1).
::{|
|Inputs
|-
|||pbOutput||Output buffer to fill
|-
|||pulBitposition||Bit-position count in the output buffer
|-
|Returns||Void
|}


'''Example Code'''
;GplFaxG3EndBlock():End of block flushed.
 
<code>
Following is an example of dithering RGB to CMYK data from the Omni driver' s &quot;render&quot; callback function:
:{|
 
|GplFaxG3EndBlock(||PVOID||hThread,
<pre class="western">// Change logical height of band bitmap to 1 (this device chooses to dither each scanline)
|-
ulSavedcy = pddc-&gt;pbmi-&gt;cy;
|||PBYTE||pbOutput,
pddc-&gt;pbmi-&gt;cy = 1;
|-
 
|||ULONG||ulOutputSize,
// For each row in the current band's bitmap (each scanline)
|-
for( iCurrentRow=ulSavedcy; iCurrentRow &gt; 0; iCurrentRow-- )
|||PULONG||pulBitposition
{
|-
  // Set pointer to current row in our band's bitmap
|||PFN||pfnFlushBuffer );
  cmykReq.pbSrc = pbBits + iCurrentRow * iDWordBytesInCMYKBandRow;
|}
 
</code>
  GplDitherRGBtoCMYK ( &amp;pddc-&gt;pdh, &amp;pddc-&gt;cmykReq, &amp;pddc-&gt;pReply );
::{|
 
|Inputs
  // Now our consecutive KCMY buffers contain dithered data to send to printer
|-
 
|||hThread||PVOID.
} /* end for */
|-
 
|||pbOutput||Output buffer to fill.
// Restore bitmap height to TRUE value (no longer 1)
|-
pddc-&gt;pbmi-&gt;cy = ulSavedcy;</pre>
|||ulOutputSize||Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
 
|-
=== GplDitherRGBtoRGB ===
|||pulBitposition||Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc.
 
|-  
'''Description'''
|||pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 
|}
This function is a shell for [[00027.htm|GplDitherRGBtoCMYK]]. It simply inverts the CMY ''pDest''buffer to RGB for a printer that requires RGB.
<code>
 
::::{|
'''Format'''
|void pfnFlushBuffer(||PVOID,
 
|-
<pre class="western">APIRET APIENTRY GplDitherRGBtoRGB( PHDITHER      pdh,
|||PBYTE,
                                  PDITHEREQUEST  pReq
|-
                                  PDITHERESULT  pReply );</pre>
|||ULONG );
'''Parameters'''
|}
 
</code>
'''pdh'''(''PHDITHER'') Pointer to the dither handler that was created by [[00026.htm|GplDitherCreateInstance]].
::::{|
 
|PVOID||Use is the same as above.
'''pReq'''(''PDITHEREQUEST'') Address of the API's pre-filled request structure.
|-
 
|PBYTE||Unsigned char to output buffer; same as pbOutput.
'''pReply'''(''PDITHERESULT'') Address of the reply structure.
|-
 
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
'''Returns'''
|}
 
::{|
Success NO_ERROR
|Returns
 
|-
=== GplDitherDeleteInstance ===
|||Success||Count of data written out (compressed data count).
 
|-
'''Description'''
|||Failure||GPLCOMPRESS_ERROR (-1).
 
|}
This function must be called at the end of a document so that memory that was allocated in [[00026.htm|GplDitherCreateInstance]]can be freed.
;GplFaxG3EndDoc():Modified Huffman (MH) Group 3 return to control (RTC) sent at the end of document.
 
<code>
'''Format'''
:{|
 
|GplFaxG3EndDoc(||PVOID||hThread,
<pre class="western">APIRET APIENTRY GplDitherDeleteInstance( PHDITHER  pdh );</pre>
|-
'''Parameter'''
|||PBYTE||pbOutput,
 
|-
'''pdh'''(''PHDITHER'') Pointer to the dither handler that was created by [[00026.htm|GplDitherCreateInstance]].
|||ULONG||ulOutputSize,
 
|-
'''Returns'''
|||PULONG||pulBitposition
 
|-
Success NO_ERROR
|||PFN||pfnFlushBuffer );
|}
</code>
::{|
|Inputs
|-
|||hThread||PVOID.
|-
|||pbOutput||Output buffer to fill.
|-
|||ulOutputSize||Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
|-
|||pulBitposition||Bit-position count in the output buffer; gets updated and must be stored off the pddc.
|-
|||pfnFlushBuffer||Flush buffer function; prototype for this function must be:
|}
<code>
::::{|
|void  pfnFlushBuffer(||PVOID,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::::{|
|PVOID||Use is the same as above.
|-
|PBYTE||Unsigned char to output buffer; same as pbOutput.
|-
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|}
::{|
|Returns
|-
|||Success||Count of data written out (compressed data count).
|-
|||Failure||GPLCOMPRESS_ERROR (-1).
|}
;GplFaxG3NewFrame():Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of each new page. This function is the same as GplFaxG3EncodeBlock() except that it flushes data at this point to cause the current page to be completely sent. This call sends compressed data followed by the EOL bit sequence (see GplFaxG3StartBlock).
:Instead of GplFaxG3NewFrame() you can use pfnFlushBuffer() = NULL to see whether or not the buffer for GplFaxG3EncodeBlock() is large enough. You output the bytes after GplFaxG3EncodeBlock() returns OK. You can then call GplFaxG3StartBlock(). This function places an EOL in the output buffer and sets the pulBitposition to 12.
<code>
:{|
|GplFaxG3NewFrame(||HTHREAD||hThread,
|-
|||PBYTE||pbOutput,
|-
|||ULONG||ulOutputSize,
|-
|||PULONG||pulBitposition
|-
|||PFN||pfnFlushBuffer );
|}
</code>
::{|
|Inputs
|-
|||hThread||HTHREAD
|-
|||pbOutput||Output buffer to fill.
|-
|||ulOutputSize||Size in bytes of output buffer; must be a multiple of 32 bits.
|-
|||pulBitposition||Bit-position count in the output buffer.
|-
|||pfnFlushBuffer||Flush buffer function; prototype for this function must be:
|}
<code>
::::{|
|void  pfnFlushBuffer(||PVOID,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::::{|
|PVOID||Use is the same as above.
|-
|PBYTE||Unsigned char to output buffer; same as pbOutput.
|-
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|}
::{|
|Returns
|-
|||Success|| Count of data written out (compressed data count).
|-
|||Failure||GPLCOMPRESS_ERROR (-1)
|}


'''Example Code'''
===GplFaxG4EncodeBlock===
'''Description'''


Following is an example of using GplDitherDeleteInstance:
This function performs G4 "Fax"-type compression.


<pre class="western">// Tell the dither function to deallocate all it's buffers
It is similar to [[#GplFaxG3EncodeBlock]](), but it has five extra parameters. These extra parameters are used to keep track of on/off bit runs between calls.
GplDitherDeleteInstance( (HDITHER)pHandle-&gt;pdh );


// Reminder:
The driver is again responsible for all memory allocations.
// Free all buffers our driver allocated on behalf of GENPLIB's dithering code
// This includes error buffers for error-diffusion algorithms
// as well as the destination buffers used for rendering</pre>


=== Gamma Correction ===
Make sure all buffers for on/off bit tracking are equal to scanline DWORD aligned.
Printer paper, ink, resolution, and other characteristics can cause the actual printed color to be different from the desired color. This &quot;skew&quot; is non-linear and occurs over the entire color spectrum. A gamma-compensation curve is used for each color to approximate the non-linear skew. Because the reflected frequencies of colors can cause them to appear different than expected, you can modify the gamma of each color. Increasing the gamma will lighten an image that might otherwise look too dark.


Following are the gamma functions:
;Format
*[[00031.htm|GplGammaCreateTable]]
<code>
*[[00032.htm|GplGammaColorTableToGamma2]]
:{|
 
|GplFaxG4EncodeBlock(||PVOID||pddc,
=== GplGammaCreateTable ===
|-
 
|||PBYTE||pbOutput,
'''Description'''
|-
 
|||ULONG||ulOutputSize,
This function creates an RGB (K) gamma table based on input gammas and bias .
|-
 
|||PULONG||pulBitposition,
'''Format'''
|-
 
|||PBYTE||pbInput,
<pre class="western">APIRET APIENTRY GplGammaCreateTable( PBYTE  pbGamma,
|-
                                    LONG  lRgamma,
|||ULONG||ulBitwide,
                                    LONG  lGgamma,
|-
                                    LONG  lBgamma,
|||ULONG||ulBitheight,
                                    LONG  lbias );</pre>
|-
'''Parameters'''
|||PFN||pfnFlushBuffer,
 
|-
'''pbGamma'''(''PBYTE'') A byte pointer to a 768-byte buffer.
|||PULONG||pulwstartc,
 
|-
pbGamma can be used as a PRGB2 instead of a PBYTE.
|||PULONG||pulbstartc,
 
|-
'''lRgamma'''(''LONG'') Red. Gamma multiplied by 10, with a range of 1 to 99.
|||PULONG||pulwstartp,
 
|-
'''lGgamma'''(''LONG'') Green. Gamma multiplied by 10, with a range of 1 to 99.
|||PULONG||pulbstartp,
 
|-
'''lBgamma'''(''LONG'') Blue. Gamma multiplied by 10, with a range of 1 to 99.
|||ULONG||ulSkipCount );
 
|}
'''lbias'''(''LONG'') Offset, normally zero, generally for printers over 600 dpi.
</code>
 
;Parameters:'''pddc''' (''PVOID'')
'''Returns'''
::First parameter to pfnFlushBuffer; generally pddc or hThread.
 
:'''pbOutput''' (''PBYTE'')
Success 0 <br />Failure INVALID_PARAMETER
::Pointer to output buffer to fill.
 
:'''ulOutputSize''' (''ULONG'')
=== GplGammaColorTableToGamma2 ===
::Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
 
:'''pulBitposition''' (''PULONG'')
'''Description'''
::Pointer to position in the output buffer; gets updated and must be unique to each print job; *pulBitposition must be zero on the first invocation of this function for a block.
 
:'''pbInput''' (''PBYTE'')
For 8-bit pels or less, replaces an RGB color value in the PBITMAPINFO2 argbColor[0..256] structure with a gamma-corrected color value.
::Input pointer to upper left corner of raster image to compress.
 
:'''ulBitwide''' (''ULONG'')
'''Format'''
::Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
 
:'''ulBitheight''' (''ULONG'')
<pre class="western">APIRET APIENTRY GplGammaColorTableToGamma2( PBYTE          pbGamma,
::Height of raster image to compress in bits.
                                            PBITMAPINFO2  pbmi );</pre>
:'''pfnFlushBuffer''' (''PFN'')
'''Parameters'''
::The prototype for this function must be:
 
<code>
'''pbGamma'''(''PBYTE'') A byte pointer to a 768-byte buffer.
::{|
|void pfnFlushBuffer (||PVOID,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::{|
|PVOID||Use is the same as G3; necessary parameter for separating printout; generally pddc or hThread.
|-
|PBYTE||Unsigned char * to output buffer; same as pbOutput.
|-
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|}
::pfnFlushBuffer can be either NULL or point to a function that you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
::If pfnFlushBuffer() = NULL this function will compress the data and place it into pbOutput. If the output buffer size is not large enough for all the compressed data, the function will return with -1 = error. If pfnFlushBuffer() is not NULL, every time pbOutput is full, pfnFlushBuffer( ) is called to write the compressed bytes out. Note that the return value will the total number of bytes written even though the buffer was filled multiple times.
:'''pulwstartc''' (''PULONG'')
::Pointer to array of ULONGs. Its length must be ( ulBitwide + 31 ) / 32 + 2. The 2 extra ULONGs are needed. So for an image of 371 bits width, the length of this array is ( 371 + 31 ) / 32 = 12 plus 2 more-which is 14 unsigned longs. This array is used to store the start bits of the beginning of consecutive 0 bits for the current scan line.
:'''pulbstartc''' (''PULONG'')
::Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the current scan line.
:'''pulwstartp''' (''PULONG'')
::Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 0 bits for the previous scan line. This must be zero filled on the first invocation of this function.
:'''pulbstartp''' (''PULONG'')
::Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the previous scan line. This must be zero filled on the first invocation of this function.
:'''ulSkipCount''' (''ULONG'')
::Number of bytes to skip to the next row of input data to compress.
;Returns:Success: Count of data written out (compressed data count).
:Failure: GPLCOMPRESS_ERROR (-1)
;Related Function:The following is a G4-compression related function automatically sends the "End Block" data sequence (24 bits represented by Hex 001001).
;GplFaxG4EndBlock():Modified Read (MRR) Group 4 compression with K = infinity for sending end-of-facsimile block (EOFB). This function has the same first eight parameters as GplFaxG4EncodeBlock().
<code>
:{|
|GplFaxG4EndBlock(||PVOID||pHandle,
|-
|||PBYTE||pbOutput,
|-
|||ULONG||ulOutputSize,
|-
|||PULONG||pulBitposition,
|-
|||PBYTE||pbInput,
|-
|||ULONG||ulBitwide,
|-
|||ULONG||ulBitheight,
|-
|||PFN||pfnFlushBuffer );
|}
</code>
::{|
|Inputs
|-
|||pddc||PVOID; used as first parameter to the pfnFlushBuffer function; generally the pddc or HTHREAD.
|-
|||pbOutput||Output buffer to fill.
|-
|||ulOutputSize||Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
|-
|||pulBitposition||Bit-position count in the output buffer; gets updated and must be unique to each print job.
|-
|||ulBitwide||Width of the raster image in bits.
|-
|||ulBitheight||Height of the raster image in bits.
|-
|||pfnFlushBuffer||Flush buffer function; prototype for this function must be:
|}
<code>
::::{|
|void pfnFlushBuffer(||PVOID,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::::{|
|PVOID||Necessary parameter for separating printout; generally pddc or hThread.
|-
|PBYTE||Unsigned char * to output buffer; same as pbOutput.
|-
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|}
::::pfnFlushBuffer can be either NULL or point to the function you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when the output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
::{|
|Returns
|-
|||Success||Count of data written out (compressed data count).
|-
|||Failure|| GPLCOMPRESS_ERROR (-1)
|}


pbGamma can be used as a PRGB2 instead of a PBYTE.
===GplFaxTIFF2EncodeBlock===
 
;Description
'''pbmi'''(''PBITMAPINFO2'') PBITMAPINFO2 is assumed to have a valid RGB2 color table as input. The following PBITMAPINFO2 fields are used:
TIFF algorithm 2 compression. This function essentially provides a " Modified Huffman" G3 compression with no size limits. It is limited only by the byte representation of ULONG (4 Gigabytes).
 
;Format
cBitCount Bits per pel.
<code>
 
:{|
argbColor When cBitCount is 8 or less, this parameter must include values for 2 to the power of cBitCount (2**cBitCount).
|GplFaxTIFF2EncodeBlock(||PVOID||pHandle,
 
|-
'''Returns'''
|||PBYTE||pbOutput,
 
|-
Success 0 <br />Failure INVALID_PARAMETER
|||ULONG||ulOutputSize,
 
|-
=== Compression ===
|||PULONG||pulBitposition,
The compression package contains routines that compress raster image data using common printer hardware-supported compression routines.
|-
 
|||PBYTE||pbInput,
Raster printer drivers render on a per-page basis into a raster representation of an output page. As soon as rendering is completed for a given page, that raster data can be compressed into a format that the printer hardware supports. Compressed data is typically smaller than the original uncompressed data and will therefore transmit faster across communication lines to the printer.
|-
 
|||ULONG||ulBitwide,
'''Note:'''The caller of a compression routine should allocate a buffer to hold the compressed data whose size (in bytes) is as large as the the original uncompressed data.
|-
 
|||ULONG||ulBitheight,
To use the GenPLib Compression package, you must include:
|-
 
|||PFN||pfnFlushBuffer );
<pre class="western">INCL_GENPLIB_COMPRESS</pre>
|}
 
</code>
 
;Parameters:'''pHandle''' (''PVOID'')
Following are the GenPLib compression routines:
::Pointer to a handle.
 
:'''pbOutput''' (''PBYTE'')
*[[00034.htm|GplCompressRLL]]<br />*[[00035.htm|GplCompressTIFF]]<br />*[[00036.htm|GplCompressDeltaRow]]<br />*[[00037.htm|GplCompressRLLDeltaRow]]<br />*[[00038.htm|GplCompressChooseMode]]<br />*[[00039.htm|GplCompressAscii85]]<br />*[[00040.htm|GplFaxG3EncodeBlock]]<br />*&quot;GplFaxG3StartBlock&quot; <br />*&quot;GplFaxG3EndBlock&quot; <br />*&quot;GplFaxG3EndDoc&quot; <br />*&quot;GplFaxG3NewFrame&quot; <br />*[[00041.htm|GplFaxG4EncodeBlock]]<br />*&quot;GplFaxG4EndBlock&quot; <br />*[[00042.htm|GplFaxTIFF2EncodeBlock]]<br />*&quot;GplFaxTIFF2StartBlock&quot; <br />*&quot;GplFaxTIFF2EndBlock&quot;
::Pointer to output buffer to fill.
 
:'''ulOutputSize''' (''ULONG'')
=== GplCompressRLL ===
::Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
 
:'''pulBitposition''' (''PULONG'')
'''Description'''
::Pointer to position in output buffer.
 
:'''pbInput''' (''PBYTE'')
This function performs run-length limited (RLL) encoding.
::Input pointer to upper left corner of raster image to compress.
:'''ulBitwide''' (''ULONG'')
::Width of raster image to compress in bits.
:'''ulBitheight''' (''ULONG'')
::Height of raster image to compress in bits.
:'''pfnFlushBuffer''' (''PFN'')
::Flush buffer function; prototype for this function must be:
<code>
::{|
|void  pfnFlushBuffer(||PVOID,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::{|
|PVOID||Necessary parameter for separating printout; generally pddc or hThread.
|-
|PBYTE||Unsigned char * to output buffer; same as pbOutput.
|-
|ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|}
;Returns:Success:Count of data written out (compressed data count).
:Failure GPLCOMPRESS_ERROR (-1)
;Related Functions:The following are TIFF2-compression related functions.
;GplFaxTIFF2StartBlock():TIFF algorithm 2 compression start-of-document function.
:::{|
|Inputs
|-
|||pulBitposition||Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc; function just sets pulBitposition to 0 (you can do this yourself).
|-
|Returns||Void
|}
;GplFaxTIFF2EndBlock():TIFF algorithm 2 compression to flush output buffer.
<code>
:{|
|GplFaxTIFF2EndBlock(||PVOID||pHandle,
|-
|||PBYTE||pbOutput,
|-
|||ULONG||ulOutputSize,
|-
|||PULONG||pulBitposition,
|-
|||PBYTE||pbInput,
|-
|||ULONG||ulBitwide,
|-
|||ULONG||ulBitheight,
|-
|||PFN||pfnFlushBuffer );
|}
</code>
::{|
|Inputs
|-
|||hThread||HTHREAD.
|-
|||pbOutput||Output buffer to fill.
|-
|||ulOutputSize||Size in bytes of output buffer; must be divisible by 4.
|-
|||pulBitposition||Bit-position count in the output buffer; must be a multiple of 32 bits; gets updated and must be stored off the pddc.
|-
|||pbInput||Input pointer to upper left corner of raster image to compress.
|-
|||ulBitwide||Width of the raster image in bits.
|-
|||ulBitheight||Height of the raster image in bits.
|-
|||pfnFlushBuffer||Flush buffer function; prototype for this function must be:
|}
<code>
::::{|
|int  pfnFlushBuffer(||HTHREAD,
|-
|||PBYTE,
|-
|||ULONG );
|}
</code>
::{|
|||HTHREAD||Gotten from GplThreadCreateInstance().
|-
|||PBYTE||Unsigned char to output buffer; same as pbOutput.
|-
|||ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
|-
|Returns
|-
|||TRUE||If OK
|-
|||FALSE||If error
|}


'''Format'''
==Exception Management==
This package contains functions for setting errors with WinSetErrorInfo and logging appropriate warnings, exceptions, and error codes.


<pre class="western">LONG APIENTRY GplCompressRLL(
Four ascending severity levels are defined for error messages:
                    PBYTE pbDataIn,      // Input raster data to compress (page/band)
{|
                    LONG  cbDataIn,      // Length of input data buffer (pbDataIn)
|Warning||The function detected a problem, took corrective action, and was able to complete processing successfully.
                    PBYTE pbDataOut,    // Output buffer to which compressed data is written
|-
                    LONG  cbDataOut );   // Length count of compressed data written</pre>
|Error||The function detected a problem for which no sensible corrective action is possible. The function is not executed and the system remains at the same state as when the function was requested.
'''Parameters'''
|-
 
|Severe Error||The function detected a problem from which the system cannot reestablish its state. The function has partially executed and the application must now make some corrective action to restore the system to a known state.
'''pbDataIn'''(''PBYTE'') Pointer to input data.
|-
 
|Unrecoverable Error||The function detected an error from which it is impossible for the system to reestablish the state that it held at the time that the function was called. It is also impossible for the application to restore the system to a known state.
'''cbDataIn'''(''LONG'') Count of bytes in, pointed to by pbData.
|}
To use the GenPLib Exception-Management package, you must include:
INCL_GENPLIB_ERROR
The following functions are included in the package:
*[[#GplErrSetDosError]]
*[[#GplErrSetError]]
*[[#GplErrSetSevereError]]
*[[#GplErrSetWarning]]
*[[#GplErrSetUnrecoverableError]]
===GplErrSetDosError===
;Description
This function saves DOS error.
;Format
ERRORID APIENTRY GplErrSetDosError( USHORT usDosError );
;Parameter:'''usDosError''' (''USHORT'')
::Any error code defined in BSEERR.H that best identifies the failing event.
;Returns:Success: ERRORID
:Failure: None
===GplErrSetError===
;Description
This function saves an error.
;Format
  ERRORID APIENTRY GplErrSetError( USHORT usError );
;Parameter:'''usError''' (''USHORT'')
::Any error code defined in PMERR.H that best identifies the failing event.
;Returns:Success: ERRORID
:Failure None
===GplErrSetSevereError===
;Description
This function saves a severe error.
;Format
  ERRORID APIENTRY GplErrSetSevereError( USHORT usError );
;Parameter:'''usError''' (''USHORT'')
::Any error code defined in PMERR.H that best identifies the failing event.
;Returns:Success: ERRORID
:Failure: None
===GplErrSetWarning===
;Description
This function saves the error warning.
;Format
ERRORID APIENTRY GplErrSetWarning( USHORT usError );
;Parameter:'''usError''' (''USHORT'')
::Any error code defined in PMERR.H that best identifies the failing event.
;Returns:Success:ERRORID
:Failure: None
===GplErrSetUnrecoverableError===
;Description
This function saves an unrecoverable error.
;Format
ERRORID APIENTRY GplErrSetUnrecoverableError( USHORT usError );
;Parameter:'''usError''' (''USHORT'')
::Any error code defined in PMERR.H that best identifies the failing event.
;Returns:Success: ERRORID
:Failure: None


'''pbDataOut'''(''PBYTE'') Pointer to output data.
==Memory Management==
This package manages global and process (shared and non-shared) memory areas (heaps) using a single handle to a memory control block (HMCB) for all allocations. Therefore, you do not need to manage different heap or memory handles within your code.


'''cbDataOut'''(''LONG'') Size of buffer in bytes pointed to by pbDataOut.
This package calls appropriate OS/2 system APIs for memory creation and allocation. Dosxxx, SSxxx, and other API decisions are optimized for current OS/2 version performance. Examples include the underlying file system paging architecture and the number of processes in system.


'''Returns'''
Memory areas (heaps) are automatically grown when the initial memory size runs out. Committing more memory than needed is not required. Your code can use a single HMCB and underlying code will manage multiple memory areas ( heaps) if necessary.


Signed LONG, which is the length in bytes of the compressed data pointed to by cbDataOut.
Sets of debug trace information are displayed whenever memory is overwritten by other processes or writes to memory exceed the allocation size. Memory that has been allocated and not freed (memory leaks) is also shown as debug information.


Success Number of bytes in compressed return string.
To use the GenPLib Memory-Management package, you must include:


Failure GPLCOMPRESS_ERROR (-1) <br />The output buffer length has been exceeded.
INCL_GENPLIB_MEMORY


'''Notes'''
The following APIs comprise the GenPLib Memory-Management package:
*[[#GplMemoryCreateInstance]]
*[[#GplMemoryDeleteInstance]]
*[[#GplMemoryAlloc]]
*[[#GplMemoryFree]]
*[[#GplMemoryGetObjectSize]]
*[[#GplMemorySetUserID]]
===Call Flow===
The [[#GplMemoryCreateInstance|GplMemoryCreateInstance]]() function should be the first memory API called in order to get a handle to a memory control block (HMCB). The HMCB handles all memory allocations. Using an HMCB, any number of memory allocations can be made using [[#GplMemoryAlloc|GplMemoryAlloc]](). These allocations should be freed using [[#GplMemoryFree|GplMemoryFree]].


The input data is encoded with pairs of bytes. The first byte is the replacement count and the second byte is the data. A replacement count of 0 means that the data is not repeated.
[[#GplMemoryDeleteInstance|GplMemoryDeleteInstance]]() is used when the HMCB is no longer needed for memory allocations.


=== GplCompressTIFF ===
'''Note:''' Any memory that was allocated using GplMemoryAlloc() using an HMCB being deleted by [[#GplMemoryDeleteInstance|GplMemoryDeleteInstance]] and not freed by a corresponding GplMemoryFree() call will be shown as debug output (with location of function where allocation was done) for developers to correct memory leaks within their code.
===GplMemoryCreateInstance===
;Description
This function creates an MCB and a user heap.
;Format
<code>
{|
|HMCB APIENTRY GplMemoryCreateInstance(||ULONG||ulPrimarySize,
|-
|||ULONG||ulExtSize,
|-
|||ULONG||ulThreshold,
|-
|||ULONG||ulType );
|}
</code>
;Parameters:'''ulPrimarySize''' (''ULONG'')
::Initial Heap size in bytes.
:'''ulExtSize''' (''ULONG'')
::Heap growth size fills in bytes.
:'''ulThreshold''' (''ULONG'')
::Threshold for heap allocation. Requests larger than this will be converted to DosAllocMem.
:'''ulType''' (''ULONG'')
::Heap type (process, shared). See the following notes for a description of process and shared heaps.
;Returns:Success: Valid handle to memory control block.
:Failure: NULL.
::WinGetLastError() will return one of the following:
:::PMERR_MEMORY_ALLOCATION_ERROR
:::PMERR_INV_OR_INCOMPAT_OPTIONS
;Notes
Treat creating a memory instance like creating a memory heap. The ulSize parameter should be a reasonable amount of memory for most of your processing. It is not necessary to set ulSize to a high number so that you do not run out of memory, because the memory area will automatically grow if your processing allocates more than the amount originally set by ulSize.


'''Description'''
When the memory area is automatically grown, the ulExtSize value is used to determine how large an additional memory area should be created. A default value of 0 (zero) may be used, and the growth size will be tailored to a reasonable amount for the OS/2 version.


This function performs tagged image file format (TIFF) encoding, PackBits encoding algorithm. This function compresses a scanline.
The [[#GplMemoryAlloc]]() API will allocate memory blocks differently based on the ulThreshold value. Typically, allocations of memory of sizes less than the threshold are allocated in a fast access memory area, and sizes larger than the threshold are allocated from a different memory pool. A default value of 0 (zero) may be used and the threshold will be automatically set to perform most efficiently on the current OS/2 version.


Once in a repeated run, this function stops and emits a block if a different byte is encountered.
The ulType parameter allows you to specify the type of memory that you want to allocate. This defines the scope of accessibility to the memory within your driver or program and is one of the following:
:;SHARED_MEMORY:Used to create a global memory area (heap). For example, it is used for allocating global data like string tables in a printer driver.
:;PROCESS_MEMORY:Used to create a per process memory area (heap). For example , it is used for allocating a per device context (DC) memory in a printer driver.
===GplMemoryDeleteInstance===
;Description
This function frees memory heap(s) for the user.
;Format
APIRET APIENTRY GplMemoryDeleteInstance( HMCB  hMCB );
:Parameter:'''hMCB''' (''HMCB'')
::Pointer to memory allocated.
;Returns:Success: NO_ERROR. Returns valid handle to memory control block.
:Failure: ERROR_INVALID_PARA
::or return values from:
:::SSFreeMem
:::DosFreeMem
:::DosSubUnsetMem
:::WinGetLastError() returns one of the following:
::::PM_INV_OR_INCOMPAT_OPTIONS
::::PMERR_MEMORY_DEALLOCATION_ERR
===GplMemoryAlloc===
;Description
This function allocates memory for the user.
;Format
PVOID APIENTRY GplMemoryAlloc( HMCB  hMCB,
                                ULONG  ulSize );
;Parameters:'''hMCB''' (''HMCB'')
::Handle to memory control block.
:'''ulSize''' (''ULONG'')
::Amount to allocate.
;Returns:Success: Valid pointer to memory allocated.
:Failure: NULL.
::WinGetLastError() returns one of the following:
:::PMERR_INV_OR_INCOMPAT_OPTIONS
:::PMERR_INSUFFICIENT_MEMORY
===GplMemoryFree===
;Description
This function frees memory for a user.
;Format
APIRET APIENTRY GplMemoryFree( PVOID  pData );
;Parameter:'''pData''' (''PVOID'')
::Pointer to memory allocated.
;Returns:Success: NO_ERROR.
:Failure: ERROR_INVALID_PARA
::or return values from:
:::SSFreeMem
:::DosFreeMem
:::DosSubFreeMem
:::WinGetLastError() returns one of the following:
::::PM_INV_OR_INCOMPAT_OPTIONS
::::PMERR_MEMORY_DEALLOCATION_ERR
===GplMemoryGetObjectSize===
;Description
This function returns the size of a memory object allocated by [[#GplMemoryAlloc]].
;Format
LONG APIENTRY GplMemoryGetObjectSize( PVOID  pData );
;Parameter:'''pData''' (''PVOID'')
::Pointer to object.
;Returns:Success :Size of memory object.
::(LONG) lsize
:Failure: 0, no size found for memory object. WinGetLastError will return PMERR_INVALID_PARAMETERS (meaning pData was invalid or NULL).
===GplMemorySetUserID===
;Description
This function is optional, and it is helpful in debugging. You attach a pointer to the heap instance (like your pddc or pdb)-this extra pointer can then be viewed under the kernel debugger.
;Format
VOID APIENTRY GplMemorySetUserID( HMCB  hMCB,
                                  ULONG  ulUserID );
;Parameters:'''hMCB''' (''HMCB'')
::Handle to memory control block.
:'''ulUserID''' (''ULONG'')
::Pointer to something helpful.
===Implementation===
The GenPLib Memory-Management package was created to provide consistency and to simplify the growth and management of heaps.


Once in a literal run, this function stops and emits a block if at least four repeated bytes follow. The next block will be a repeat block. Repeats of two or more instances of a byte b are represented by -(n-1) b. Literal runs of different bytes b1 b2 b3 ... bn are represented by (n-1) b1 b2 b3 . .. bn.
This package was one of the first developed for driver use. When writing a driver "from scratch" or modifying a driver to use GenPLib, begin with this package because many of the other GenPLib packages require memory allocated in the unique format of the memory-management package.


'''Format'''
Previously, all drivers had to manage their own memory heaps, often using memory allocation packages exported from other parts of the OS/2 system-for example, the INCL_WINHEAP functions from PM WIN or the Subsystem Allocation routines in the PM Graphics Rendering Engine (GRE). It was never clear what routines should be used in a presentation driver or where they should be used (especially for portability). Many of these packages did not allow the caller to grow their heaps, provided no controls for memory leakage, and had no validation or recovery when a driver terminated unexpectedly.


<pre class="western">LONG APIENTRY GplCompressTIFF(
A typical raster printer driver creates two types of heaps: a shared heap during Dynamic Link Library (DLL) initialization processing, and a per-DC heap when the PM GRE enables the driver during the FILL_PHYSICAL (02 Hex) subfunction. These heaps are freed during corresponding DLL termination and DISABLE_PHYSICAL calls to the driver.
                    PBYTE  pbDataIn,      // Input raster data to compress (page/band)
                    LONG  cbDataIn,     // Length of input data buffer (pbDataIn)
                    PBYTE  pbDataOut,    // Output buffer to which compressed data is written
                    LONG  cbDataOut );  // Length count of compressed data written</pre>
'''Parameters'''


'''pbDataIn'''(''PBYTE'') Pointer to input data.
In addition, a per-process heap is also created for the OS2_PM_DRV_DEVMODE entry point in a printer presentation driver. This entry point causes the driver to display its printer and job properties dialogs to the user or to retrieve printer and job property information for a specific output device/ destination.


'''cbDataIn'''(''LONG'') Count of bytes in.
A typical driver initially allocates a 256KB shared global heap and a 100KB per-DC heap and elects to use the memory package's default heap growth size and threshold for larger allocations (achieved by passing in 0L for parameters 2 and 3 for [[#GplMemoryDeleteInstance]]).


'''pbDataOut'''(''PBYTE'') Pointer to output data.
In case of unexpected driver termination, a driver should register a per- process exception handler for each of the major presentation driver entry points. This exception handler can contain cleanup code that frees any per- process heaps created using [[#GplMemoryCreateInstance]] in case any code problems cause an exception to occur.


'''cbDataOut'''(''LONG'') Size of buffer in bytes pointed to by pbDataOut.
One major benefit of the GenPLib memory code is the ability to detect memory leaks (memory a driver allocates but never frees). Whenever a call to [[#GplMemoryDeleteInstance]]is made, all memory allocated from the heap is freed so that no leaks can occur. If the debug version of the GenPLib library is used, while [[#GplMemoryDeleteInstance]] is cleaning up memory areas that a developer forgot to free, it will inform the developer on a debugging terminal which line of code allocated the memory that was not freed. This is possible because the memory-management package records the function address (and other information) from where [[#GplMemoryAlloc]] is called.


'''Returns'''
An OS/2 presentation driver must consider the memory constraints of the user and other programs in the system. In the past, drivers allocated large heaps, hoping the memory an application needed would never overrun the amount initially allocated and cause an exception. Under the paging architecture of OS/2 Warp, however, all the heap memory is often committed during heap creation.


Success Count of bytes put in destination. <br />Failure GPLCOMPRESS_ERROR (-1)
All this memory could be forced in and never used by an application (for example, an application queries the driver for the devices it supports and performs no output). In this case, the driver's memory pages would not be touched, so OS/2 would slowly page it out and give it back to the applications that had it in the first place. As a result, a good deal of swapping could occur for active addressable memory, especially on low-RAM systems. The alternative was to force the driver to manage its own heap growth and consolidate multiple heaps.


=== GplCompressDeltaRow ===
Using the GenPLib Memory-Management package, the heap is identified to the driver as a generic handle to a memory control block (HMCB), not as an address into memory. This handle is supplied by [[#GplMemoryCreateInstance]] and is used to identify the heap for all subsequent memory allocations and frees.


'''Description'''
Therefore, a reasonable initial heap can be created that is just large enough to allow the driver to enable and respond to basic queries. As applications use the driver to render graphics (or other memory-intensive operations), the memory package detects when a given [[#GplMemoryAlloc]] call will overflow the current heap and grows it by a default or specified size. The caller does not ever need to know that the heap has grown because it continues to use the same HMCB, while the memory package chains these heaps together.


This function performs delta-row encoding. This method replaces only the bytes in a new row that are different from the preceding (seed) row. Unreplaced bytes are replicated from the seed row. The new row then becomes the seed row after it is printed.
===Example Code===
;Creating and Allocating From a Per-DC Heap
Following is an example of creating and allocating from a per-DC heap during a printer-driver enable via the OS2_PM_DRV_ENABLE entry point:
<pre>
ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )            // Determine which enable/disable subfunction
  {
    case FILL_PHYSICAL:          // Enable subfunction
    {
      // Create a per-DC memory area (heap)
      hmcbHeap = GplMemoryCreateInstance(
                          LEN_DCHEAP,            // Size of DC Heap (80K)
                          0L,                    // Default extent
                          0L,                    // Default threshold
                          PROCESS_MEMORY );      // Type of heap (define in GPLMEM.H)


'''Format'''
      // If GplMemoryCreateInstance succeeded
      if(hmcbHeap != NULL);
      {
        // Allocate PDEVICEBLOCK
        pddc->pdb = ( PDEVICEBLOCK ) GplMemoryAlloc( hmcbHeap, sizeof( DEVICEBLOCK ) );


<pre class="western">INT APIENTRY GplCompressDeltaRow(
      } /* end if */
                      INT      usTotalBytes,  // Bytes in the scan line
    } /* end case */
                      PBYTE    pbData,        // Input raster data to compress
    break;
                      PBYTE    pbLastLine,    // Previous scanline's raster data
  } /* end switch */
                      INT      usMaxReturn,    // Length of output buffer
} /* end OS2_PM_DRV_ENABLE */
                      PBYTE    pbReturn,      // Output buffer with compressed data
</pre>
                      PUSHORT  pDeltas );      // Differences indexes-start/end pairs</pre>
'''Note:''' System memory must be used when passing data buffers to PrtWrite() calls.
'''Parameters'''


'''usTotalBytes'''(''INT'') Count of bytes in, pointed to by pbData.
;Creating a Global Heap
 
Following is an example of creating a global heap:
'''pbData'''(''PBYTE'') Pointer to bytes of data to compress.
<pre>
 
ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
'''pbLastLine'''(''PBYTE'') Pointer to previous scanline's raster data.
{
 
  switch( ulFlag )       // Flag to determine which load/unload module subfunction
'''usMaxReturn'''(''INT'') Size of output buffer.
  {
 
    case 0:              // Load module
'''pbReturn'''(''PBYTE'') Pointer to compressed data will be written.
    {
 
      // Create a global heap
'''pDeltas'''(''PUSHORT'') Differences indexes = array of start and end pairs.
      globals.pvHeap = GplMemoryCreateInstance(
 
                                LEN_SHAREDHEAP,  // Initial size (256K)
'''Note:'''This array is returned as the result of calling [[00038.htm|GplCompressChooseMode]]; therefore, you must call GplCompressChooseMode() before calling this function.
                                0,                // Default extent
 
                                0,                // Default threshold
'''Returns'''
                                SHARED_MEMORY);  // Type of heap (defined in GPLMEM.H)
 
    } /* end case */
0 Current and previous line are identical.
    break;
  } /* end switch */
} /* end _DLL_InitTerm */
</pre>
;Allocating From a Global Heap
Following is an example of allocating from a global heap:
<pre>
switch( ulSubFunc )                       // Determine which enable/disable subfunction
{
  case FILL_LOGICAL:                      // Enable subfunction
  {
    // For every string in our driver's string resource table
    for( i=0; i < STRING_TABLE_SIZE; i++ )
    {
      // Load each string from resource table (returns length)
      sLen = WinLoadString (szTemp);


&gt;0 Length of data pointed to by pbReturn.
      // Allocate memory to hold string just loaded from global heap
      globals.pbStringTable[i] = GplMemoryAlloc( globals.pvSharedHeap, sLen+1 );


-1 GPLCOMPRESS_ERROR <br />usMaxReturn is not large enough for the compressed output.
      // Copy string from temporary buffer to our driver's global string table
      strcpy( globals.pbStringTable[i], szTemp );


=== GplCompressRLLDeltaRow ===
    } /* end for */
 
    break;
'''Description'''
  } /* end case */
 
} /* end switch */
This function performs RLL delta-row encoding. It compresses a string of bytes and returns the compressed string at pbReturn and the new string usLength as its return value. This method replaces only bytes in the current row that are different from the proceeding seed row. Unlike [[00036.htm|GplCompressDeltaRow]], however, the replacement or delta bytes themselves are encoded.
</pre>
 
;Deleting a Per-DC Heap
'''Format'''
Following is an example of deleting a per-DC heap:
<pre>
ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )
  {
    case DISABLE_PHYSICAL:
    {
      // Free per-DC heap after freeing per-DC memory objects
      GplMemoryDeleteInstance( hmcbHeap );


<pre class="western">INT APIENTRY GplCompressRLLDeltaRow(
    } /* end case */
                      INT      usTotalBytes,  // Bytes in the scan line
    break;
                      PBYTE   pbData,        // Input raster data to compress
   } /* end switch */
                      PBYTE    pbLastLine,    // Previous scanline's raster data
} /* end OS2_PM_DRV_ENABLE */
                      INT      usMaxReturn,    // Length of output buffer
</pre>
                      PBYTE    pbReturn,      // Output buffer with compressed data
;Deleting a Global Heap
                      PUSHORT  pDeltas );     // Differences indexes-start/end pairs</pre>
Following is an example of deleting a global heap:
'''Parameters'''
<pre>
ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )
  {
    case 1:
    {
       // Free global heap after freeing global memory objects
      GplMemoryDeleteInstance( globals.pvSharedHeap );


'''usTotalBytes'''(''INT'') Count of bytes pointed to by pbData and pbLastLine.
    } /* end case */
 
    break;
'''pbData'''(''PBYTE'') Pointer to bytes of data to compress.
  } /* end switch */
} /* end _DLL_InitTerm */
</pre>
==Output-Thread Management==
The output-thread management module provides a way to create multiple output threads that will manage the sending of data buffers to the printer. This functionality has also been called ''second-thread code''; however, the implementation allows a separate thread per physical output address (for example, LPT, COM, and file).


'''pbLastLine'''(''PBYTE'') Pointer to previous uncompressed data.
This package seamlessly manages multiple buffer allocations and chains them together. It adjusts the internal data buffering to always try to keep the printer busy processing data. The functions assure that buffer sizes and their reuse align for the best performance with the system-memory memory model (for example, the OS/2 paging architecture).


'''usMaxReturn'''(''INT'') Size of output buffer.
'''Note:''' You may override the default buffer size and the maximum number of buffers to create.


'''pbReturn'''(''PBYTE'') Pointer to compressed data will be written.
Other benefits of using the separate output-thread code in your driver include:
*Ability to pass off data buffers as fast as you can fill them without waiting for a device with slower transfer speeds
*Unknowingly, the OS/2 PM graphics engine (and other system components) might have locked down system resources, such as fonts, while calling your driver. Therefore, implementing the thread code allows you to return to the graphics engine and free up the resource for other parts of the system, such as the display. This makes the system and applications more responsive to the user.
*The package uses the GenPLib Exception-Management package for simple debug tracing.
*The package uses the GenPLib Memory-Management package for allocation of instance data.
To use the GenPLib Output-Thread Management package, you must include:
INCL_GENPLIB_THREAD
Following are the Output-Thread Management APIs:
*[[#GplThreadCreateInstance]]
*[[#GplThreadStart]]
*[[#GplThreadOutput]]
*[[#GplThreadEnd]]
*[[#GplThreadDeleteInstance]]
*[[#GplThreadAbortDoc]]
*[[#GplThreadResetAbortDoc]]
*[[#GplThreadFlushBuffer]]
*[[#GplThreadHookDeviceWrites]]
===Call Flow===
The thread instance (data and semaphores) is initialized; but a real thread is not spun off until you are certain that the application is really sending data to be printed. Therefore, you can systematically use [[#GplThreadCreateInstance|GplThreadCreateInstance]]() at enable and [[#GplThreadStart|GplThreadStart]]() at the first data write. This keeps the code straightforward.


'''pDeltas'''(''PUSHORT'') Differences indexes = array of start and end pairs.
===GplThreadCreateInstance===
;Description
This function is used to allocate and initialize thread instance data. This function must be called before making any other thread calls in order to get a thread instance handle (HTHREAD). Semaphores and buffers are not created or allocated by this call.
;Format
<code>
BOOL APIENTRY GplThreadCreateInstance(
  HMCB    hMcb,
  HMODULE  hMod,
  HTHREAD *phThread,
  PVOID    pvUserData );
</code>
;Parameters:'''hMcb''' (''HMCB'')
::Handle to the memory control block (See [[#Memory Management]]).
:'''hMod''' (''HMODULE'')
::Handle of calling module. Used for exception management and error logging.
:'''*phThread''' (''HTHREAD'')
::-output Thread instance handle.
:'''pvUserData''' (''PVOID'')
::-output This is used in debugging and is optional. It is recommended that you place a helpful pointer, such as your pddc or pdb pointer.
;Returns:Success: TRUE
:Failure: FALSE


'''Returns'''
===GplThreadStart===
;Description
This function starts or updates thread parameters. It creates semaphores and buffers, committing system resources.
;Format
<code>
BOOL APIENTRY GplThreadStart( HTHREAD hThread,
LHANDLE hSpooler,
HFILE  hDev,
ULONG  ulBufferSize,
USHORT  usMaxBuffers,
UCHAR  ucAbortChar,
ULONG  ulAbortCharCount,
ULONG  ulAbortBufferSize,
PBYTE  pbAbortBuffer,
PSZ    pszAbortString,
PSZ    pszLogAddress,
PSZ    pszWriteError,
PSZ    pszDeviceDescription,
LONG    lDCType );
</code>
;Parameters:'''hThread''' (''HTHREAD'')
::Instance handle of thread to start. Returned by [[#GplThreadCreateInstance]].
:'''hSpooler''' (''LHANDLE'')
::Handle to Spooler (OD_QUEUED). Returned from SplQmOpen. May be zero (0) if hDev is supplied.
:'''hDev''' (''HFILE'')
::Handle to Device (OD_DIRECT). Returned from PrtOpen. May be zero (0) if hSpooler is supplied.
:'''ulBufferSize''' (''ULONG'')
::Size of buffer allocations. May be 0 to use default size.
:'''usMaxBuffers''' (''USHORT'')
::Maximum number of buffers to create. May be 0 to use default # of buffers.
:'''ucAbortChar''' (''UCHAR'')
::Character to send before the abort string. ucAbortChar can be used for devices that need to be sent a number of bytes before the abort string can be sent. ulAbortChar is sent first during the abort sequence.
:'''ulAbortCharCount''' (''ULONG'')
::Number of ucAbortChars to send to the device.
:'''ulAbortBufferSize''' (''ULONG'')
::Abort buffer size in bytes.
:'''pbAbortBuffer''' (''PBYTE'')
::Pointer to buffer to send when aborting a job. Buffer must not be deleted until after [[#GplThreadEnd]]. ulAbortBuffer is sent after ulAbortChar.
:'''pszAbortString''' (''PSZ'')
::Abort job reset command for your specific printer language. May be NULL and no string will be sent. pszAbortString is sent last in the abort sequence.
:'''pszLogAddress''' (''PSZ'')
::File or port name (for example, LPT1). Used for error reporting. May be NULL.
:'''pszWriteError''' (''PSZ'')
::Error prompt string to display in a SplMsgBox (for example, "Unable to write to printer"). May be NULL.
:'''pszDeviceDescription''' (''PSZ'')
::Device description to display in a SplMsgBox ( for example, "Laserprinter"). May be NULL.
:'''lDCType''' (''LONG'')
::DC type (OD_QUEUED, OD_DIRECT).
;Returns:Success: TRUE
:Failure: FALSE
;Notes
:ulAbortChar is sent first during the abort sequence.
:ulAbortBuffer is sent after ulAbortChar.
:pszAbortString is sent last in the abort sequence.


0 Current and previous line are identical.
===GplThreadOutput===
;Description
This function is used to pass data to a thread instance (referenced by the thread instance handle passed in) for output. [[#GplThreadStart]] must be called prior to calling GplThreadOutput.
;Format
<code>
BOOL APIENTRY GplThreadOutput(
  HTHREAD hThread,
  PBYTE pBytes,
  ULONG ulCount,
  ULONG fulDataType);
</code>
;Parameters:'''hThread''' (''HTHREAD'')
::Thread instance handle.
:'''pBytes''' (''PBYTE'')
::Pointer to data to buffer.
:'''ulCount''' (''ULONG'')
::Size of data (byte size) pointed to by pBytes parameter
:'''fulDataType''' (''ULONG'')
::Data type flag.
::THREAD_DT_PRINTERLANGUAGE
:::Data is printer language commands and only requires an abort string to be sent when aborting during this buffer. See pszAbortString in [[#GplThreadStart]].
::THREAD_DT_BINARY
:::Data is binary (raster) data and requires a series of bytes to be sent to ensure the printer is not in binary transfer mode before the abort string can be sent. See ucAbortChar in GplThreadStart.
;Returns:Success: TRUE
:Failure: FALSE
===GplThreadEnd===
;Description
This function frees up any resources allocated by the thread instance (semaphores and data buffers). A call to this function implies that any remaining data buffers are flushed before the return of this call. This call is the logical pair to GplThreadCreate, which allocates system resources.
;Format
BOOL APIENTRY GplThreadEnd( HTHREAD  hThread );
;Parameter:'''hThread''' (''HTHREAD'')
::Handle to thread instance.
;Returns:Success: TRUE
:Failure: FALSE


&gt;0 Length of data pointed to by pbReturn.
===GplThreadDeleteInstance===
;Description
This function is used to free thread instance data allocated by a corresponding call to [[#GplThreadCreateInstance]]().
;Format
BOOL APIENTRY GplThreadDeleteInstance( HTHREAD *phThread );
;Parameter:'''phThread''' (''HTHREAD'')
::Pointer to the thread instance handle to be freed.
;Returns:Success: TRUE
:Failure: FALSE


-1 GPLCOMPRESS_ERROR <br />usMaxOutput not large enough for the compressed output.
===GplThreadAbortDoc===
;Description
This function is called to abort output from the thread that is referenced by the thread instance handle that was passed in. After output is stopped, the function will attempt to restore the printer to a known state where it can process data from another print job.


=== GplCompressChooseMode ===
The first issue to consider when using this function is that the device may be executing a command that has a large amount of data that it is expecting to receive as a block. Therefore the parameter ulAbortChar passed in on [[#GplThreadStart]] is sent ulAbortCharCount times.


'''Description'''
The second issue to consider is that the abort string 'pszAbortString' provided in [[#GplThreadStart]]() is sent last.
;Format
BOOL APIENTRY GplThreadAbortDoc( HTHREAD  hThread );
;Parameter:'''hThread''' (''HTHREAD'')
::Handle to thread instance.
;Returns:Success: TRUE
:Failure: FALSE


This function uses a semi-intelligent algorithm to select the compression mode that will most likely give the best compression for the row of data.
===GplThreadResetAbortDoc===
;Description
This function is used to resolve any previous calls to [[#GplThreadAbortDoc]]() so that another job can start printing using the same thread instance handle.


'''RLL Delta Row'''is currently chosen if this method best and if the printer supports this compression. '''Delta Row'''is chosen if most of the bytes are the same as the corresponding bytes in the previous row. '''TIFF'''is chosen if most of the bytes are repeated within this row. RLL is chosen if most of the bytes are repeated within this row and the printer does not support TIFF PackBits. '''No compression (value of 0)'''is chosen of most bytes are neither repeats within the row nor duplicates of the previous row.
This function will wait, if necessary, for the abort processing started by [[#GplThreadAbortDoc]] to be completed before returning so that the thread instance can be put into a state to receive data from a new print job.
;Format
BOOL APIENTRY GplThreadResetAbortDoc( HTHREAD  hThread );
;Parameter:'''hThread''' (''HTHREAD'')
::Handle to thread instance.
;Returns:Success: TRUE
:Failure: FALSE
===GplThreadFlushBuffer===
;Description
This function causes all data buffers currently being managed by the thread instance to be flushed (written) to their final output destination (for example, parallel port and spool file).


'''Note:'''The function GplCompressChooseMode() must be run before either of the delta-compression functions [[00036.htm|GplCompressDeltaRow]]() or [[00037.htm|GplCompressRLLDeltaRow]](). This function must be run first because pDelta is filled in by GplCompressChooseMode() while it is reading pbRow and pbLast_ Row. Performance is increased by combining GplCompressChooseMode() with either [[00036.htm|GplCompressDeltaRow]]() or [[00037.htm|GplCompressRLLDeltaRow]]().
This call allows a flag to be set so that all the data buffers are sent (flushed) before this call returns (fwait == TRUE) or synchronously (fwait = = FALSE).
;Format
BOOL APIENTRY GplThreadFlushBuffer(
  HTHREAD  hThread,
  BOOL    fWait );
;Parameters:'''hThread''' (''HTHREAD'')
::Handle to thread instance.
:'''fWait''' (''BOOL'')
::Flag indicating flush asynchronously or synchronously.
;Returns:Success: TRUE
:Failure: FALSE


'''Format'''
===GplThreadHookDeviceWrites===
;Description
This is an optional function. This call was created because a class of devices added to the Omni driver did not set status lines according to known standard protocols. Calling this function allows the Omni driver to interpret the status lines because it knows about the devices' special protocols.
;Format
<code>
BOOL APIENTRY GplThreadHookDeviceWrites(
  HTHREAD hThread,
  PFN PfnPrtWrite,
  pddc);
</code>
;Parameters:'''hThread''' (''HTHREAD'')
::Handle to thread instance.
:'''pfnPrtWrite''' (''PFN'')
::Our function.
:'''pddc'''
::Parameter to our function.
;Returns:Success: TRUE
:Failure: FALSE


<pre class="western">INT APIENTRY GplCompressChooseMode( PBYTE    pbRow,
===Implementation===
                                    PBYTE    pbLast_Row,
After the memory-management package is implemented, the next logical package that can be added to a driver is the GenPLib Output-Thread Management package.
                                    INT      usRow_Length,
                                    ULONG    CompressModes,
                                    PUSHORT  pDelta );</pre>
'''Parameters'''


'''pbRow'''(''PBYTE'') Pointer to the current row's data.
The best place to implement threads in a driver is when sending and buffering data to what is more than likely a slower output device (or file) . This allows the driver to return back to the application and PM GRE for rendering the next page immediately to improve system response.


'''pbLast_Row'''(''PBYTE'') Pointer to the last row's data.
The benefits of adding multiple threads are proven in tests performed on OS/2 printer drivers that have added the GenPLib thread-management package. Results have shown a 7%-12% performance increase.


'''usRow_Length'''(''INT'') The number of bytes in the row. Note that both the previous row and the current row are equal in size.
A printer driver retrieves a thread handle (HTHREAD) from GenPLib by calling [[#GplThreadCreateInstance]] during the FILL_PHYSICAL enable subfunction . This call is made only if the DC type is OD_QUEUED or OD_DIRECT (the DC types that request output). This call does not start a thread at this time but causes the GenPLib thread code to initialize its internal structures with information you supply. Once again, a corresponding call to [[#GplThreadDeleteInstance]] is made during the driver's disable subfunction DISABLE_PHYSICAL to free the thread handle.


'''CompressModes'''(''ULONG'') Compression modes supported.
The actual thread is started in the driver when the start-document escape code is received from the application (DEVESC_STARTDOC from the DevEscape() device call) using [[#GplThreadStart]]. The [[#GplThreadEnd]] call that stops the thread is placed in disable subfunction BEGIN_CLOSE_DC by the driver. It is not placed in end-document (DEVESC_ENDDOC) code because some applications choose to call many DEVESC_STARTDOC/DEVESC_ENDDOC pairs for the same DC. Placing the call in a disable subfunction assures that a thread will not start and stop many times in a row. The side effect of this is that the GenPLib thread code will receive multiple [[#GplThreadStart]] calls for the same thread. In this case, subsequent GplThreadStart calls cause an implied flush of all data received at that time for the thread.


This is a ULONG with the follow bit positions used to represent compress modes that this printer supports:
If you call [[#GplThreadStart]] multiple times, the thread code accepts any changed information for input parameters to this call. This allows the driver to dynamically change the buffer size and the maximum number of buffers used by the thread code.


GPLCOMPRESS_NONE <br />GPLCOMPRESS_RLL <br />GPLCOMPRESS_TIFF <br />GPLCOMPRESS_DELTAROW <br />GPLCOMPRESS_RLLDELTAROW
The [[#GplThreadAbortDoc]] call is also added to the driver's escape code processing routine for the abort-document call (DEVESC_ABORTDOC). This call the thread code to clear data out of all its buffers and to ignore any other data received by the thread until a [[#GplThreadResetAbortDoc]] call is made to the thread. As soon as all data is flushed by [[#GplThreadAbortDoc]], the abort sequences supplied during [[#GplThreadStart]] will place the printer in a known state for the next print job.


These values may be logically ORed together to create a bitfield of all compression methods to consider. For example,
[[#GplThreadResetAbortDoc]] tells the thread code that the job has ended, that no more data will be sent for this print job, and to resume sending data. This call is added during the end-document escape processing (DEVESC_ENDDOC) in the driver. This allows applications that perform multiple DEVESC_ STARTDOC/DEVESC_ENDDOC calls for a single DC (that is, submitting multiple print jobs using one DC) to function normally.


<pre class="western">    CompressModes = GPLCOMPRESS_RLL | GPLCOMPRESS_TIFF</pre>
[[#GplThreadOutput]] is used to add any data to the thread's output buffers. The call has been added for the DEVESC_RAWDATA escape code. This escape code is sent to a driver to pass through raw data received from DOS or Windows applications along with a data buffer and data count. The call is also implemented wherever the driver needs to send out its own escape codes for graphical or text generating output. This data includes printer/job initialization commands, raster image data (compressed or uncompressed), font/text data, and job/printer termination commands.
This tells the routine to only choose between the RLL and TIFF compression routines and GPLCOMPRESS_NONE.


'''pDelta'''(''PUSHORT'') Array of unsigned shorts to hold the offsets from the beginning of pbRow that differ from pbLast_Row. This data will be used by [[00036.htm|GplCompressDeltaRow]]and [[00037.htm|GplCompressRLLDeltaRow]]. Note that pDelta must have a length equal to two times usRow_Length plus one to hold the pDelta output . There is no check on the size of pDelta. You must supply a size of a minimum of two times usRow_Length plus one. This is because the maximum number of differences occur when every other byte differs from the preceding row.
The last function in the thread-management package is [[#GplThreadFlushBuffer]], which tells the thread code to send all data currently held in the thread's buffers before the call returns. This call appears just after [[#GplThreadResetAbortDoc]] in a driver's DEVESC_ENDDOC processing code.


'''Returns'''
===Example Code===
 
;Starting a Thread With Output to a Printer
The mode that will probably compress the best:
Following is an example of starting a thread with output to a printer:
<pre>
  case DEVESC_STARTDOC:                          // Escape code signals start of document
  {
    // If going to a file or direct to a printer
    if( OD_DIRECT == ulDCType )
    {
      // Open output port (printer-driver version of DosOpen)
      rc  =  PrtOpen( hFile, pszLogAddress );


0 No compression <br />1 RLL (run-length limited) compression <br />2 TIFF (tagged image file format) compression <br />3 Delta-row compression <br />9 RLL delta-row compression
      // Create/Update the second print thread
 
      rc = GplThreadStart( hThread,                // Instance handle
'''Notes'''
                          0,                      // Handle to spooler
 
                          hFile,                  // Handle to device
For [[00036.htm|GplCompressDeltaRow]]and [[00037.htm|GplCompressRLLDeltaRow]], pDelta is filled with the start and end of the difference fragments. Note that a difference fragment is defined as a contiguous group of bytes in the current row that differ from those in the previous row. For example:
                          0,                      // Default buffer size
 
                          0,                       // Default number of buffers
<pre class="western">/-----------------------------------------------------------------------\
                          '\0',                    // Abort character
|Column Number  |1  |2  |3  |4  |5  |6  |7  |8  |9  |10 |11 |12 |13 |14 |
                          1024,                    // Abort character repeat count
|---------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---|
                          sizeof( pszCmdAbort );  // Length of abort command
|Current Row    |7  |8  |1  |0 |4  |5  |6  |7  |23 |128|257|1  |7  |8  |
                          pszCmdAbort,            // Our abort command
|Byte Value    |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
                          pszLogAddress,          // For example, LPT1
|---------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---|
                          "Error",                 // Error string if write fails
|Previous Row  |7  |1  |3  |1  |4  |5  |9  |7  |23 |128|7  |9  |7  |8  |
                          pszDeviceName,          // For example, "Epson LQ2550"
|Byte Value    |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
                          ulDCType );              // DC type
\-----------------------------------------------------------------------/</pre>
     } /* end if */
There are three difference fragments:
  } /* end case */
 
</pre>
*Fragment one begins at column 2 and ends at column 4. <br />*Fragment two begins at column 7 and ends at column 7. <br />*Fragment three begins at column 11 and ends at column 12.
;Starting a Thread With Output Queuing "Raw" Data
 
Following is an example of starting a thread with output queuing "raw" printer escapes:
The output in pDelta will be 2 4 7 7 11 12 0. Note that these are unsigned shorts and the difference array is null-terminated. If no difference fragments are found (the previous and current buffers are the same) the first unsigned short in pDelta will be 0.
<pre>
 
   case DEVESC_STARTDOC:                          // Escape code signals start of document
''pDelta''above will point to a array of the indexes stored to used by GplCompressDeltaRow() and GplCompressRLLDeltaRow().
 
'''Example Code'''
 
Following is an example of GplCompressChooseMode usage from the Omni driver for a typical inkjet device:
 
<pre class="western">iNewMode = GplCompressChooseMode(
                pbBuffer,             // Contains current row of data
                pbLastLine,          // Contains previous row of data
                iBytesInRow,          // Number of bytes in output row (pbBuffer)
                GPLCOMPRESS_RLL |     // All compression modes supported
                GPLCOMPRESS_TIFF,
                pDelta );             // Offsets into pbBuffer ((2 x pbBuffer) + 1)
 
   if( iNewMode != iCurrentMode )
   {
   {
     // Send command to printer to set new mode
     // Else DC type is queued, not direct to a printer
     iCurrentMode = iNewMode;
     elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_RAW )
      {
        if( !hspl )
        {
          // Open appropriate queue (from DevOpenData) for output
          hspl  =  SplQmOpen( "*", 9, (PQMOPENDATA)&DevOpenData );
          SplQmStartDoc( hspl, szDocumentName );


  } /* end if */
        } /* end if */


   switch( iNewMode )
        // Create/Update the second print thread
        rc = GplThreadStart( hThread,                // Instance handle
                            hspl,                    // Handle to spooler
                            0,                      // Handle to device
                            0,                      // Default buffer size
                            0,                      // Default number of buffers
                            '\0',                    // Abort character
                            1024,                    // Abort character repeat count
                            sizeof( pszCmdAbort );   // Length of abort command
                            pszCmdAbort,            // Our abort command
                            pszLogAddress,          // For example, LPT1
                            "Error",                // Error string if write fails
                            pszDeviceName,          // For example, "Epson LQ2550"
                            ulDCType );              // DC Type
      } /* end if */
    } /* end if */
  } /* end case */
</pre>
;Starting a Thread With Output Queuing "Standard" Data
Following is an example of starting a thread with output queuing "standard" data:
<pre>
  case DEVESC_STARTDOC:                          // Escape code signals start of document
   {
   {
    case 0:                          // No Compression
    // Else DC type is queued, not direct to a printer
    break;
    elseif( OD_QUEUED == ulDCType )
    case 1: iCompressed = GplCompressRLL();
    {
    break;
      if( ulDataType == PM_Q_STD )
    case 2: iCompressed = GplCompressTIFF();
      {
    break;
        SplStdStart( pddc->pdb->hdc );
    case 3: iCompressed = GplCompressDeltaRow();
    break;
    case 9: iCompressed = GplCompressRLLDeltaRow();
    break;


  } /* end switch */
        // Open appropriate queue (from DevOpenData) for output
        hspl = SplQmOpen( "*", 9, (PQMOPENDATA)&DevOpenData );


  // Write raster data transfer command and compressed data</pre>
        // Signal start of document (no thread is started by Omni driver)
        SplQmStartDoc( hspl, szDocumentName );


=== GplCompressAscii85 ===
      } /* end if */
    } /* end if */
  } /* end case */
</pre>
;GplThreadCreateInstance and GplThreadDeleteInstance Usage
The enable and disable subfunctions FILL_PHYSICAL and DISABLE_PHYSICAL are ideal points at which to create and delete a thread instance. Following is an example of [[#GplThreadCreateInstance]]() and [[#GplThreadDeleteInstance]]() usage :
<pre>
ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                                // Determine enable subfunction
  {
      case FILL_PHYSICAL:                            // An enable subfunction
      {
        // Only create a thread if we expect to output data
        if( (OD_QUEUED == pdb->ulType )  ||  (OD_DIRECT == pdb->ulType ) )
        {
          // Create an output thread instance (do not start thread yet)
          bRC = GplThreadCreateInstance( hmcbHeap, globals.hmod, &hThread, pddc );


'''Description'''
        } /* end if */
      } /* end case */
      break;


This function performs ASCII encoding. It encodes 4 bytes in to 5 bytes out , and it converts data from a base 256 on input to a base-85 representation on output. The output is then output in ASCII with base 85.
      case DISABLE_PHYSICAL:                          // A disable subfunction
      {
        // Delete the output thread instance if allocated
        if( hThread )
        {
          bRC = GplThreadDeleteInstance( &hThread );


Buffer allocation is the responsibility of the driver.
        } /* end if */
      } /* end case */
      break;


'''Format'''
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */
</pre>
;GplThreadOutput Usage
Following is an example of [[#GplThreadOutput]]() usage:
<pre>
  case DEVESC_NEWFRAME:                            // Page eject requested
  {
    CHAR achRasterCmd[] = "*Esc*r1Q";


<pre class="western">INT APIENTRY GplCompressAscii85(
    // When getting a DEVESC_NEWFRAME or DEVESC_ENDDOC,
                    PBYTE  pSource,        // Source input data buffer (normal Hex 256)
    // The current page's raster image can be retrieved from the
                    PBYTE  pDest,          // Destination output data buffer (ASCII 85)
    // device surface and transferred to the printer
                    INT    usSourceLen );  // Source buffer input length</pre>
'''Parameters'''


'''pSource'''(''PBYTE'') Pointer to input data buffer.
    // Enter raster mode by sending down escape code for current device
    bRC = GplThreadOutput( hThread,                // Thread instance handle
                            achRasterCmd,            // Pointer to data buffer
                            strlen(achRasterCmd),    // Size of data buffer
                            THREAD_DT_BINARY );      // Data type


'''pDest'''(''PBYTE'') Pointer to output data buffer. It must be at least 5/4 size of pSource.
    // Dither and/or compress raster data and send to output thread
    bRC = GplThreadOutput( hThread,
                            pbRasterData,
                            ulRasterDataLength,
                            THREAD_DT_BINARY );


'''usSourceLen'''(''LONG'') Length of source buffer in bytes.
  } /* end case */
</pre>
;GplThreadEnd Usage
Following is an example of [[#GplThreadEnd]] usage during OS2_PM_DRV_ENABLE:
<code>
ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                              // Determine enable subfunction
  {
    case BEGIN_CLOSE_DC:
    {
      // If we created an output thread
      if( hThread )
      {
        // End the second thread implied flush with wait
        bRC = GplThreadEnd( hThread );
      } /* end if */
    } /* end case */
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */
</code>
;GplThreadAbortDoc Usage
Following is an example of [[#GplThreadAbortDoc]] usage for handling an abort- document escape:
<pre>
  case DEVESC_ABORTDOC:
  {
    // Tell second thread that an abort doc occurred
    GplThreadAbortDoc( hThread );


'''Returns'''
    // Store a state flag in our pddc to indicate abort is in effect
    pddc->fAbortDocCalled = TRUE;


Success Count of bytes put in destination. <br />Failure GPLCOMPRESS_ERROR (-1)
  } /* end case */
</pre>
;GplThreadResetAbortDoc Usage
Following is an example of using [[#GplThreadResetAbortDoc]]:
<pre>
case DEVESC_ENDDOC:
{
  // If we received a DEVESC_ABORTDOC
  if( pddc->fAbortDocCalled )
  {
    // Notify the output thread that the abort condition is over
    GplThreadResetAbortDoc( hThread );


=== GplFaxG3EncodeBlock ===
    // Reset the abort state so thread will begin processing data again
    pddc->fAbortDocCalled = FALSE;


'''Description'''
  } /* end if */
} /* end case */
</pre>
;GplThreadFlushBuffer Usage
Following is an example of using [[#GplThreadFlushBuffer]]:
<pre>
case DEVESC_ENDDOC:
{
  // If going to a file or direct to a printer
  if( OD_DIRECT == ulDCType )
  {
    // Flush all data in second thread; we are done
    GplThreadFlushBuffer( hThread, TRUE );


This function performs G3 &quot;Fax&quot;-type compression.
  } /* end if */


It provides a run-length type compression with table-lookup replacement encodings for certain bit sequences. This type of compression is also know as &quot;Modified Huffman.&quot;
  elseif( OD_QUEUED == ulDCType  &&  PM_Q_RAW == ulDataType )
  {
    // Flush all data in second thread; we are done
    GplThreadFlushBuffer( hThread, TRUE );


'''Format'''
  } /* end if */
 
} /* end case */
<pre class="western">GplFaxG3EncodeBlock( HTHREAD  hThread,
</pre>
                    PBYTE    pbOutput,
;GplThreadHookDeviceWrites Usage
                    ULONG    ulOutputSize,
Following is an example of using [[#GplThreadHookDeviceWrites]]:
                    PULONG  pulBitposition,
<pre>
                    PBYTE    pbInput,
case DEVESC_STARTDOC:
                    ULONG    ulBitwide,
{
                    ULONG    ulBitheight,
  // We have just called thread Start
                    PFN      pfnFlushBuffer );</pre>
  GplThreadStart();
'''Parameters'''


'''hThread'''(''HTHREAD'') Handle.
  // We inform the thread that we want to subclass the write of data
  // to the output destination, replacing it with our callback function
  GplThreadHookDeviceWrites( hThread,        // Thread handle
                            pfnPrtWrite,    // Our "write" function
                            pddc );        // Parameter to our "write" function
} /* end case */
</pre>
==Pattern Creation==
This module contains functions to create bitmap patterns.


'''pbOutput'''(''PBYTE'') Pointer to output buffer to fill.
To use the GenPLib Pattern-Creation package, you must include:
INCL_GENPLIB_MEMORY
INCL_GENPLIB_PATTERNS
The following functions are used to create bitmap patterns.
*[[#GplPatternCreateBitmaps]]
*[[#GplPatternDeleteBitmaps]]
*[[#GplPatternCreate]]


'''ulOutputSize'''(''ULONG'') Number of output bytes; must be divisible by 4.
===GplPatternCreateBitmaps===
;Description
This function fills in an array of BMAPINFO structures with the PATSYM patterns. This function is designed to be used to fill in the DEVICESURFACE structure with patterns that match the resolution of the target device. GplPattern functions use the GenPLib Memory-Management package and require that a HMCB had been created with [[#GplMemoryCreateInstance]].
;Format
<code>
LONG APIENTRY GplPatternCreateBitmaps(
  HMCB        hMcb,          // Memory instance handle
  ULONG      ulXResolution,  // X device resolution
  ULONG      ulYResolution,  // Y device resolution
  ULONG      ulWidth,        // Desired pattern width
  ULONG      ulHeight,      // Desired pattern height
  PBMAPINFO  paBmapInfo,    // Array of pattern bitmaps (output)
  LONG        lCount,        // Number of bitmaps to create
  ULONG      ulFlip );      // Flip bitmaps horiz/vertically
</code>
;Parameters:'''hMcb''' (''HMCB'')
::Handle to initialized memory control block.
:'''ulXResolution''' (''ULONG'')
::Target device x resolution.
:'''ulYResolution''' (''ULONG'')
::Target device y resolution.
:'''ulWidth''' (''ULONG'')
::Size of bitmap X.
:'''ulHeigh''' (''ULONG'')
::Size of bitmap Y.
:'''paBmapInfo''' (''PBMAPINFO'')
::Pointer to an array of BMAPINFO.
:'''lCount''' (''LONG'')
::Number of bitmap information structures.
:'''ulFlip''' (''ULONG'')
::Bit vector. If GPLPAT_INVERT_BITS is set, the bitmaps are inverted.
;Returns:TRUE: Success
:FALSE Failure


'''pulBitposition'''(''PULONG'') Pointer to bit position in output buffer; gets updated and must be stored off the pddc.
===GplPatternDeleteBitmaps===
;Description
This function deletes the bits and clears the array of BMAPINFO.
;Format
<code>
LONG APIENTRY GplPatternDeleteBitmaps(
  HMCB      hMcb,        // Memory instance handle
  PBMAPINFO  paBmapInfo,  // Pointer to bitmaps to be freed
  LONG      lCount );    // Count of bitmaps to be freed
</code>
;Parameters:'''hMcb''' (''HMCB'')
::Handle to initialized memory control block.
:'''paBmapInfo''' (''PBMAPINFO'')
::Pointer to an array of BMAPINFO.
:'''lCount''' (''LONG'')
::Number of bitmap information structures.
;Returns:TRUE: Success
:FALSE: Failure


'''pbInput'''(''PBYTE'') Input pointer to upper left corner of raster image to compress.
===GplPatternCreate===
;Description
This function can be used by a presentation driver to create a bitmap representation of any one of the predefined OS/2 patterns. Typically, it is called to create all or a subset of patterns a device driver will support at the start of a print job.
;Format
<code>
LONG APIENTRY GplPatternCreate(
  HMCB      hMcb,          // Memory instance handle
  ULONG    ulXResolution, // X device resolution
  ULONG    ulYResolution, // Y device resolution
  ULONG    ulPatSym,      // OS/2 pattern define
  PBMAPINFO pBmapInfo );  // A pattern bitmap (output)
</code>
;Parameters:'''hMcb''' (''HMCB'')
::Handle to initialized Memory control block.
:'''ulXResolution''' (''ULONG'')
::Target device x resolution.
:'''ulYResolution''' (''ULONG'')
::Target device y resolution.
:'''ulPatSym''' (''ULONG'')
::Pattern number as defined for OS/2 in PMDDI.H:
::PATSYM_DEFAULT
::PATSYM_DENSE1
::PATSYM_DENSE2
::PATSYM_DENSE3
::PATSYM_DENSE4
::PATSYM_DENSE5
::PATSYM_DENSE6
::PATSYM_DENSE7
::PATSYM_DENSE8
::PATSYM_VERT
::PATSYM_HORIZ
::PATSYM_DIAG1
::PATSYM_DIAG2
::PATSYM_DIAG3
::PATSYM_DIAG4
::PATSYM_NOSHADE
::PATSYM_SOLID
::PATSYM_BLANK
::PATSYM_HALFTONE
::PATSYM_HATCH
::PATSYM_DIAGHATCH
:'''pBmapInfo''' (''PBMAPINFO'')
::Number of bitmap information structures.
;Returns:TRUE: Success
:FALSE Failure
===Example Code===
;GplPatternCreateBitmaps Usage
Following is an example of using [[#GplPatternCreateBitmaps]]:
<pre>
// PM Graphics Rendering Engine v2.2 Device Surface Function from the Omni driver


'''ulBitwide'''(''ULONG'') Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
LONG APIENTRY QueryDeviceSurface( PDDC pddc, PVOID pv )
{
  // Initialize all fields of the device surface structure, then...
  if( !pddc->pdb->bAllocatedPatterns )
  {
      if( ORIENTATION_PORTRAIT == pdb->pJobProperties->ulOrientation )
      {
        if( GplPatternCreateBitmaps ( pddc->hmcbHeap,
                                      pddc->pResInfo->ulXRes,
                                      pddc->pResInfo->ulYRes,
                                      64L,
                                      64L,
                                      (PBMAPINFO)&pds->abmapinfoDefPattern,
                                      DEFAULT_PATTERNS_NUMBER,
                                      0L ) )
        {
          pddc->pdb->bAllocatedPatterns = TRUE;


'''ulBitheight'''(''ULONG'') Height of raster image to compress in bits.
        } /* end if */
 
      } /* end if */
'''pfnFlushBuffer'''(''PFN'') The prototype for this function must be:
  } /* end if */
} /* end QueryDeviceSurface */
</pre>
;GplPatternDeleteBitmaps Usage
Following is an example of using [[#GplPatternDeleteBitmaps]]:
<pre>
case BEGIN_CLOSE_DC:


<pre class="western">void   pfnFlushBuffer ( PVOID,
   // Perform other disable processing, then near end of case...
                        PBYTE,
  if( GRE_22 <= globals.ulGreVersion ) // PM Graphics Rendering Engine v2.2 or higher
                        ULONG );</pre>
  {
PVOID Use is the same as G4.
    PDEVICESURFACE pds;


PBYTE Unsigned char * to output buffer; same as pbOutput.
    // Note:  We only allocate patterns in QueryDeviceSurface for GRE v2.2 and higher
    pds = (PDEVICESURFACE)pddc->pdb->pDeviceSurface;
    if( GplPatternDeleteBitmaps( pddc->pdb->hmcbHeap,
                                (PBMAPINFO)&pds->abmapinfoDefPattern,
                                DEFAULT_PATTERNS_NUMBER ) )
    {
      pddc->pdb->bAllocatedPatterns = FALSE;


ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
    } /* end if */
  } /* end if */
</pre>


'''Returns'''
==Semaphores==
Printer drivers are responsible for serializing threads' access to any of their device contexts (DCs). OS/2 mutual exclusion (MUTEX) semaphores are a possible choice to implement this serialization. When two threads attempt to use the same printer DC at the same time, the second thread should receive a failing result code and PMERR_HDC_BUSY from WinGetLastError.


Success Count of data written out (compressed data count). <br />Failure GPLCOMPRESS_ERROR (-1)
Given this behavior of OS/2 and its presentation drivers, most OS/2 applications use just one thread when printing. Whenever there is just one thread, there is little contention for the DC; but presentation drivers must safely manage this contention.


'''Related Functions'''The following G3 &quot;Fax&quot;-compression related functions provide help in sending down special G3 commands.
There is some performance overhead to consider whenever a thread calls into the OS/2 kernel, as is the case for calls to DosRequest/ReleaseMutexSem. The overhead occurs because the kernel reloads code and data selectors on kernel entry and exit. OS/2 MUTEX semaphores are always safe semaphores, however, because of this transition to the kernel.


GplFaxG3StartBlock() Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of the first page. This function outputs the EOL to pbOutput and initializes pulBitposition to 12. It simply sends out the EOL bit sequence to signal the start of raster data (for example, '000000000001 ' binary, eleven 0's and a single 1).
===Semaphores in the GenPLib===
The semaphore package in the GenPLib implements a Ring-3 RAM semaphore when running on the 486 processor. The 486 has the CMPXCHG instruction that is not present on the 386. This single instruction implements a fast, safe, RAM semaphore at Ring 3.


Inputs
On a 486, a thread requesting an unowned semaphore will remain at Ring 3 and not enter the kernel. If the semaphore is owned and the thread intends to wait, however, then it will enter the kernel to block on an event semaphore.


pbOutput Output buffer to fill <br />pulBitposition Bit-position count in the output buffer
On a 386, the GenPLib simply uses OS/2 MUTEX semaphores. Implementing a semaphore on the 386 requires multiple instructions; thus, a switch to the kernel is required to disable interrupts while these multiple instructions execute.


Returns Void
For either processor, a thread may repeatedly request the same semaphore without an intervening release (nested requests). Use [[#GplSemQueryMutexSem]] to query the nested use count.


GplFaxG3EndBlock() End of block flushed.
The GenPLib detects the processor type at runtime.


<pre class="western">GplFaxG3EndBlock( PVOID    hThread,
All GenPLib semaphore APIs return 0 on success. When not zero, they return the same result codes documented for their OS/2 MUTEX semaphore counterparts.
                  PBYTE    pbOutput,
                  ULONG    ulOutputSize,
                  PULONG  pulBitposition
                  PFN      pfnFlushBuffer );</pre>
Inputs


hThread PVOID.
The GenPLib semaphore APIs require a pointer to a MTXSEM data structure instead of a semaphore handle like OS/2 MUTEX semaphores. It is the responsibility of the client to allocate one of these structures per semaphore and supply its pointer to the GenPLib semaphore API.


pbOutput Output buffer to fill.
When the MTXSEM semaphore structure resides on shared memory, it may be created with the DC_SEM_SHARED flag. Additional processes may obtain access to this shared memory and open the semaphore.


ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
All GenPLib semaphores are created in the unowned state.


pulBitposition Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc.
To use the GenPLib Semaphores package, you must include:
INCL_GENPLIB_SEMAPHORES
The following APIs are included in the semaphore package of GenPLib:
*[[#GplSemCreateMutexSem]]
*[[#GplSemOpenMutexSem]]
*[[#GplSemRequestMutexSem]]
*[[#GplSemReleaseMutexSem]]
*[[#GplSemCloseMutexSem]]
*[[#GplSemQueryMutexSem]]
With the exception of the first parameter, PMTXSEM, the parameters to the GenPLib semaphore APIs are as documented for OS/2 MUTEX semaphores.


pfnFlushBuffer Flush buffer function; prototype for this function must be:
===GplSemCreateMutexSem===
;Description
This call creates a MUTEX semaphore.
;Format
APIRET APIENTRY GplSemCreateMutexSem( PMTXSEM  pmtxsem,
                                      ULONG    flAttr )
;Parameters:'''pmtxsem''' (''PMTXSEM'')
::Address of MTXSEM structure.
:'''flAttr''' (''ULONG'')
::Creation flags. DC_SEM_SHARED is the only flag.
;Returns:Success: NO_ERROR
:Failure: ERROR_INVALID_PARAMETER
:or return code from:
::DosQueryMem
::DosCreateEventSem
::DosCreateMutexSem
;Notes
The caller must first allocate memory for the MTXSEM structure. See GPLSEM. H, where this structure is defined. If the memory is allocated from shared memory, the caller may specify the DC_SEM_SHARED flag for flAttr parameter.


<pre class="western">void  pfnFlushBuffer( PVOID,
===GplSemOpenMutexSem===
                      PBYTE,
;Description
                      ULONG );</pre>
This call opens a shared GPL Mutex semaphore.
PVOID Use is the same as above.
;Format
APIRET APIENTRY GplSemOpenMutexSem( PMTXSEM  pmtxsem );
;Parameter:'''pmtxsem''' (''PMTXSEM'')
::Address of MTXSEM structure.
:;Notes:When multiple processes are using a shared semaphore, then subsequent processes must open the semaphore. It is not necessary for the creating process to perform an open.
;Returns:Success: NO_ERROR
:Failure: ERROR_INVALID_PARAMETER
:or return code from:
::DosOpenEventSem
::DosOpenMutexSem


PBYTE Unsigned char to output buffer; same as pbOutput.
===GplSemRequestMutexSem===
;Description
This call is used to request an OS/2 PM MUTEX semaphore.
;Format
APIRET APIENTRY GplSemRequestMutexSem( PMTXSEM  pmtxsem,
                                        LONG    lTime );
;Parameters:'''pmtxsem''' (''PMTXSEM'')
::Address of MTXSEM structure.
:'''lTime''' (''LONG'')
::Time out value in milliseconds.
;Returns:Success: NO_ERROR
:Failure: Typical error returns for this function include ERROR_INVALID_ PARAMETER or return codes from DosRequestMutexSem.
:ERROR_TIMEOUT (RC 640d) means that the semaphore is owned.
;Notes
The caller can specify the timeout value for the request. When timeout is 0 , the call returns immediately. When timeout is -1, the call is an indefinite wait. Otherwise the call will block up to the timeout specified.


ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
===GplSemReleaseMutexSem===
;Description
This call will release an owned MUTEX semaphore.
;Format
APIRET APIENTRY GplSemReleaseMutexSem( PMTXSEM  pmtxsem );
;Parameter:'''pmtxsem''' (''PMTXSEM'')
::Address of MTXSEM structure.
;Returns:Success: NO_ERROR
:Failure:
::ERROR_INVALID_PARAMETER
::ERROR_NOT_OWNER (not the owner of the MUTEX semaphore)
::DosReleaseMutexSem
;Notes
This call decrements the nested use count for the semaphore. When the nested use count goes to zero, the semaphore is truly released.


Returns
===GplSemCloseMutexSem===
;Description
This call closes a GPL MUTEX semaphore for the process.
;Format
APIRET APIENTRY GplSemCloseMutexSem( PMTXSEM  pmtxsem );
Parameter:'''pmtxsem''' (''PMTXSEM'')
::Address of MTXSEM structure.
;Returns:Success: NO_ERROR
:Failure:
::ERROR_INVALID_PARAMETER
::DosCloseEventSem
::DosCloseMutexSem
;Notes
Every process using GPL semaphores should close each one with this API.


Success Count of data written out (compressed data count).
===GplSemQueryMutexSem===
;Description
This call returns information about the semaphore.
;Format
<code>
APIRET APIENTRY GplSemQueryMutexSem( PMTXSEM pmtxsem,
PID *ppid,
TID *ptid,
PULONG pulCount);
</code>
;Parameters:'''pmtxsem''' (''PMTXSEM'')
::Address of MTXSEM structure.
:'''*ppid''' (''PPID'')
::Address where process ID is returned.
:'''*ptid''' (''PTID'')
::Address where thread ID is returned.
:'''pulCount''' (''PULONG'')
::Request count.
;Returns:Success: NO_ERROR
:Failure: ERROR_INVALID_PARAMETER
:or the return codes from:
::DosQueryMutexSem
;Notes
This API will return information about the semaphore, such as what process and thread currently owns it, and if owned, what is the nested use count.


Failure GPLCOMPRESS_ERROR (-1).
It is important for asynchronously killed threads to make this call to determine if they own the DC semaphore. If they own the semaphore, they should release it the appropriate number of times before exiting.


GplFaxG3EndDoc() Modified Huffman (MH) Group 3 return to control (RTC) sent at the end of document.
===Differences Between GenPLib Semaphores and Kernel Semaphores===
If a nonzero request time times out, it will timeout to within 30 milliseconds of the supplied time out.


<pre class="western">GplFaxG3EndDoc( PVOID    hThread,
DosQueryMutexSem returns a process and thread ID even if the MUTEX sem is unowned; that is, the count returned is zero. This semaphore package returns process and thread ID of zero when the semaphore is unowned regardless of 386 or 486.
                PBYTE    pbOutput,
                ULONG    ulOutputSize,
                PULONG  pulBitposition
                PFN      pfnFlushBuffer );</pre>
Inputs


hThread PVOID.
==String Management==
The string-management package provides string-sorting functionality for printer output devices.


pbOutput Output buffer to fill.
Certain printer output devices can output data only in small bands, yet they are expected to keep track of strings that might be positioned anywhere on a physical output page. These devices need an independent mechanism for keeping those strings in sorted "page" order until the device is actually processing the band of that string.


ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
This is true for raster printers that utilize a print head and must only send text strings that are drawn using device (printer) fonts within a certain range of the print head when printing graphics data. The following diagrams represent this concept:
<pre>
                        Output Page
                        +----------+
                        |          |
                        |          |
                        |xxxxxxxxxx|
                        |xxxxxxxxxx| <--- Current band of raster output
                        |          |      that can be processed by the
                        |          |      output device.
                        | Hello    |
                        |          |
                        |          | <--- Text is below current band so
                        |    ABCD  |      we can't output it yet
                        |          |
                        |          |
                        |          |
                        +----------+


pulBitposition Bit-position count in the output buffer; gets updated and must be stored off the pddc.


pfnFlushBuffer Flush buffer function; prototype for this function must be:
                        Output Page (later)
 
                        +----------+
<pre class="western">void  pfnFlushBuffer( PVOID,
                        |          |
                      PBYTE,
                        |          |
                      ULONG );</pre>
                        |          |
PVOID Use is the same as above.
                        |          |
 
                        |          |
PBYTE Unsigned char to output buffer; same as pbOutput.
                        |xHelloxxxx| <--- Current band of raster output
                        |xxxxxxxxxx|      that can be processed by the
                        |          |      output device.
Lower string still --> |    ABCD  |      This time the text string is in
not in range          |          |      the band region and it should be
                        |          |      processed at this time
                        |          |
                        |          |
                        +----------+


ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.


Returns
                        Output Page (later)
                        +----------+
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
  String already  --> | Hello    |
  processed            |          |
                        |          |
                        |          |
                        |xxxxABCDxx|  <--- Current band of raster output
                        |xxxxxxxxxx|        that can be processed by the
                        |          |        output device.
                        |          |        This time the second string is in
                        +----------+        the band region and it should be
                                            processed at this time.
</pre>
The package sorts text strings passed in page order. Top-to-bottom and left -to-right is the current default page order.


Success Count of data written out (compressed data count).
Once strings are inserted, you can extract strings in respect to a rectangular area (typically the current graphics band rectangle) that you pass in.


Failure GPLCOMPRESS_ERROR (-1).
The string-sorting package utilizes the GenPLib Memory-Management and Exception-Management packages, thereby providing very efficient system resource usage and consistent error reporting and logging.


GplFaxG3NewFrame() Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of each new page. This function is the same as GplFaxG3EncodeBlock() except that it flushes data at this point to cause the current page to be completely sent. This call sends compressed data followed by the EOL bit sequence (see GplFaxG3StartBlock).
To use the GenPLib String-Management package, you must include:
INCL_GENPLIB_STRING
The following functions comprise the string-management package:
*[[#GplStringSorterCreateInstance]]
*[[#GplStringSorterInsertString]]
*[[#GplStringSorterQueryNumStrings]]
*[[#GplStringSorterGetFirstString]]
*[[#GplStringSorterGetNextString]]
*[[#GplStringSorterRemoveStrings]]
*[[#GplStringSorterReset]]
*[[#GplStringSorterDeleteInstance]]


Instead of GplFaxG3NewFrame() you can use pfnFlushBuffer() = NULL to see whether or not the buffer for GplFaxG3EncodeBlock() is large enough. You output the bytes after GplFaxG3EncodeBlock() returns OK. You can then call GplFaxG3StartBlock(). This function places an EOL in the output buffer and sets the pulBitposition to 12.
===GplStringSorterCreateInstance===
;Description
This function creates a string sorter instance (memory resource allocated) and returns a handle that must be used by all other string sorter package functions.


<pre class="western">GplFaxG3NewFrame( HTHREAD  hThread,
The default heap size that is created is based on current printer driver media sizes and application text usage. A parameter is supplied so that the user can increase the initial memory area that is created to hold text strings. Otherwise a default value, based on test averages, will be used.
                  PBYTE   pbOutput,
;Format
                  ULONG    ulOutputSize,
<code>
                  PULONG  pulBitposition
APIRET APIENTRY GplStringSorterCreateInstance( HDC hdc,
                  PFN      pfnFlushBuffer );</pre>
   PHSORTER phStrSort,
Inputs
  ULONG    ulHeapSize,
  ULONG    ulOptions );
</code>
;Parameters:'''hdc''' (''HDC'')
::Handle to current DC.
:'''phStrSort''' (''PHSORTER'')
::Pointer to the location where string-sorter handle will be placed.
:'''ulHeapSize''' (''ULONG'')
::Initial size of heap to allocate for strings. This can be zero. A default memory area will be created.
:'''ulOptions''' (''ULONG'')
::Creation options (for example, DBCS and TTY). This parameter is reserved for future use. This parameter must be zero.
;Returns:Success: NO_ERROR
:Failure: ERROR_xxx


hThread HTHREAD
===GplStringSorterInsertString===
;Description
This function adds a text string to a string instance. Information, including the bounding rectangle passed in and a pointer to the data, is stored with the string. You can store with the string (for example, a pointer to a structure containing font name, font ID, and color).


pbOutput Output buffer to fill.
Currently, the sort order is determined by the top-left corner of the text box passed in. The strings are sorted in page order. Page order is the order the text would be printed by a banding device on a hardcopy page.
;Format
<code>
APIRET APIENTRY GplStringSorterInsertString( HSORTER hStrSort,
  PSZ  pszStr,
  RECTL rectlStr,
  PVOID pUserDef,
  PVOID pUnused );
</code>
;Parameters:'''hStrSort''' (''HSORTER'')
::Handle of the string sorter to be used for insert operation.
:'''pszStr''' (''PSZ'')
::Pointer to text string to insert.
:'''rectlStr''' (''RECTL'')
::Bounding rectangle of string being inserted.
:'''pUserDef''' (''PVOID'')
::Pointer to user-defined data to be stored with this string .
:'''pUnused''' (''PVOID'')
::NULL. Reserved for future use.
;Returns:Success: NO_ERROR
:Failure:
::ERROR_INVALID_PARAMETER
::ERROR_xxx


ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits.
===GplStringSorterQueryNumStrings===
;Description
This function returns the current number of strings in the string sorter instance (HSORTER) referenced on the call.
;Format
APIRET APIENTRY GplStringSorterQueryNumStrings( HSORTER  hStrSort,
                                                PULONG  pulNumStr );
;Parameters:'''hStrSort''' (''HSORTER'')
::Handle of string sorter instance to be queried.
:'''pulNumStr''' (''PULONG'')
::Pointer to the location where number is to be returned.
;Returns:Success: NO_ERROR
:Failure: ERROR_xxx


pulBitposition Bit-position count in the output buffer.
===GplStringSorterGetFirstString===
;Description
This function is used to get a linked list of strings from the string sorter in relation to a band rectangle passed in.


pfnFlushBuffer Flush buffer function; prototype for this function must be:
The strings are not deleted from the linked list on this call because we allocated the information and will de-allocate on a [[#GplStringSorterReset]]() call or a [[#GplStringSorterRemoveStrings]]() call.


<pre class="western">void   pfnFlushBuffer( PVOID,
Currently, the defined relationships that can be selected are:
                      PBYTE,
{|class="wikitable"
                      ULONG );</pre>
!Option||Description
PVOID Use is the same as above.
|-
|STR_INCL_PART_IN_BAND||Return any string whose text box intersects any part of the band passed in.
|-
|STR_INCL_ALL_IN_BAND||Return any string whose text box is entirely inside the band passed in.
|-
|STR_INCL_TOPLEFT_IN_BAND||Return any string whose top-left coordinate is inside the band passed in.
|}
;Notes:
#All retrievals are inclusive on all boundaries of the ''rectlBand'' passed in.
#GplStringSorterGetFirstString retrievals are non-destructive. (the string remains in the string-sorter instance and are only removed by [[#GplStringSorterRemoveStrings]] or [[#GplStringSorterReset]] calls).
;Format
<code>
APIRET APIENTRY GplStringSorterGetFirstString(
  HSORTER hStrSort,
  ULONG   ulOption,
  RECTL  rectlBand,
  PSZ    *ppszStr,
  PRECTL  prectlStr,
  PPVOID  ppUserDef );
</code>
;Parameters:'''hStrSort''' (''HSORTER'')
::Handle of string-sorter instance to be used for string retrieval.
:'''ulOption''' (''ULONG'')
::Option passed in to indicate the relationship to test for on the "Get" operation. See options above.
:'''rectlBand''' (''RECTL'')
::The band rectangle passed in that will be used to test all text-string rectangles against.
:'''*ppszStr''' (''PSZ'')
::-output Pointer to first text string meeting retrieve criteria (ulOption).
:'''prectlStr''' (''PRECTL'')
::-output Pointer to the bounding rectangle of first string retrieved (passed in during [[#GplStringSorterInsertString]]).
:'''ppUserDef''' (''PPVOID'')
::-output Pointer to the user-defined data that was associated with text string retrieved (passed in during [[#GplStringSorterInsertString]]).
;Returns:Success: NO_ERROR
:Failure: ERROR_xxx
===GplStringSorterGetNextString===
;Description
This function is used to retrieve the next string matching the retrieve criteria passed in on the last call to [[#GplStringSorterGetFirstString]](). The criteria is based on the ''ulOption'' and ''rectlBand'' parameters from that function). This function can be called multiple times until an ERROR_NO_ MORE_ITEMS return code is detected. Each successful call (NO_ERROR) returns another string from the string sorter instance meeting the criteria until no more strings meet the criteria.
;Format
<code>
APIRET APIENTRY GplStringSorterGetNextString(
  HSORTER hStrSort
  PSZ *ppszStr,
  PRECTL prectlStr
  PPVOID ppUserDef );
</code>
;Parameters:'''hStrSort''' (''HSORTER'')
::Handle of the string-sorter instance to be used for the retrieve operation.
:'''*ppszStr''' (''PSZ'')
::- output Pointer to the location that the text string will be retrieved to.
:'''prectlStr''' (''PRECTL'')
::- output Bounding rectangle of the string being retrieved.
:'''ppUserDef''' (''PPVOID'')
::- output Pointer to user-defined data stored with string during the insert operation.
;Returns:Success: NO_ERROR
:Failure: ERROR_xxx


PBYTE Unsigned char to output buffer; same as pbOutput.
===GplStringSorterRemoveStrings===
;Description
This function is used to delete all strings from the string sorter instance, based on a relationship (ulOption) to a rectangle (rectlBand) both passed.


ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
See StringGetFirstStringFromSorter() and StringGetNextStringFromSorter() calls for non-destructive string retrievals.


Returns
Remove options include:
{|class="wikitable"
!Option||Description
|-
|STR_INCL_PART_IN_BAND||Return any string whose text box intersects any part of the band passed in.
|-
|STR_INCL_ALL_IN_BAND||Return any string whose text box is entirely inside the band passed in.
|-
|STR_INCL_TOPLEFT_IN_BAND||Return any string whose top-left coordinate is inside the band passed in.
|}
Removes are inclusive on all sides of ''rectlBand'' passed in.
;Format
APIRET APIENTRY GplStringSorterRemoveStrings(
    HSORTER  hStrSort,
    ULONG    ulOption,
    RECTL    rectlBand );
;Parameters:'''hStrSort''' (''HSORTER'')
::Handle of the string sorter to be used for remove operation.
:'''ulOption''' (''ULONG'')
::Remove options.
:'''rectlBand''' (''RECTL'')
::Rectangle that is used for comparison by the remove option.
;Returns:Success: NO_ERROR
:Failure: ERROR_xxx


Success Count of data written out (compressed data count).
===GplStringSorterReset===
;Description
This function resets the string sorter instance by removing all text strings inserted into the sorter.
;Format
APIRET APIENTRY GplStringSorterReset( HSORTER  hStrSort );
;Parameter:'''hStrSort''' (''HSORTER'')
::Handle of the string sorter to be reset.
;Returns:Success: NO_ERROR
:Failure:ERROR_xxx


Failure GPLCOMPRESS_ERROR (-1)
===GplStringSorterDeleteInstance===
;Description
This function destroys a string sorter instance by freeing any system resources allocated on the corresponding call to [[#GplStringSorterCreateInstance]](). This function implies a [[#GplStringSorterReset]]() call.
;Format
APIRET APIENTRY GplStringSorterDeleteInstance( HSORTER  hStrSort );
;Parameter:'''hStrSort''' (''HSORTER'')
::Handle of string sorter to be destroyed. The handle is no longer valid after the call.
;Returns:Success: NO_ERROR
:Failure: ERROR_INVALID_PARAMETER


=== GplFaxG4EncodeBlock ===
===Example Code===
;Retrieving Strings From String Sorter During Band Processing
Following is an example of retrieving strings from string sorter during band processing in a raster printer driver:
<pre>
HSORTER hSorter;
CharString( PDDC pddc, ..., LONG cChars, PCH pchString, ... )
{
  if( bDeviceFont )                        // If we have a device font to manage
  {
    if( ! hSorter )                        // If we have not yet created a sorter
    {
      // Create a string sorter to handle our device fonts
      apiret = GplStringSorterCreateInstance( pddc,&hSorter, 0L, 0L );


'''Description'''
    } /* end if */


This function performs G4 &quot;Fax&quot;-type compression.
    // Calculate text box of string from our character-width tables
 
    // Insert character string into sorter
It is similar to [[00040.htm|GplFaxG3EncodeBlock]](), but it has five extra parameters. These extra parameters are used to keep track of on/off bit runs between calls.
    apiret = GplStringSorterInsertString( hSorter,
 
                                          (PSZ)pchString,
The driver is again responsible for all memory allocations.
                                          rectlTextBox,
                                          (PVOID)NULL,
                                          (PVOID)NULL );


Make sure all buffers for on/off bit tracking are equal to scanline DWORD aligned.
    // Example: check number of strings currently in sorter
    apiret = GplStringSorterQueryNumStrings( hSorter,&ulNumStr );


'''Format'''
  } /* end if */
} /* end CharString */
</pre>
;Usage Notes:Used default values for heap size and options during 'StringCreate' API (LTR, TTB).
:Used default value for during 'StringInsert' API.
:For 'StringInsert' API, we could have allocated and filled in our own structure that helps us process this string (for example, font name, identifier, color, etc.).


<pre class="western">GplFaxG4EncodeBlock( PVOID  pddc,
;Removing Strings From a String Sorter During Processing of a Graphics Band Following is an example of removing strings from a string sorter during processing of a graphics band (banding):
                    PBYTE  pbOutput,
<pre>
                    ULONG  ulOutputSize,
ProcessBand( PDDC pddc, ...)
                    PULONG pulBitposition,
{
                    PBYTE   pbInput,
  APIRET apiret;
                    ULONG  ulBitwide,
  RECTL   rectlString, rectlBand;
                    ULONG  ulBitheight,
  PSZ     pszString;
                    PFN     pfnFlushBuffer,
                    PULONG  pulwstartc,
                    PULONG  pulbstartc,
                    PULONG  pulwstartp,
                    PULONG  pulbstartp,
                    ULONG  ulSkipCount );</pre>
'''Parameters'''


'''pddc'''(''PVOID'') First parameter to pfnFlushBuffer; generally pddc or hThread.
  // Retrieve first sting in the raster band we are processing
  apiret = GplStringSorterGetFirstString(
                hSorter,
                STR_INCL_TOP_LEFT_IN_BAND,
                                // Retrieve strings whose Top-Left corner are in band
                rectlBand,
                                // Rectangle of the graphic band we are processing
                &pszString,
                                // Pointer that first string will be returned in
                &rectlString,
                                // Text box of the string
                (PVOID)NULL );
                                // We had no user-defined data to retrieve


'''pbOutput'''(''PBYTE'') Pointer to output buffer to fill.
  // if we have a string meeting retrieval criteria
 
  while( apiret != ERROR_NO_MORE_ITEMS )
'''ulOutputSize'''(''ULONG'') Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
  {
    // Get next string (uses same retrieve option as "GetFirst")
    apiret = GplStringSorterGetNextString( hSorter,
                                            &pszString,
                                            &rectlString,
                                            (PVOID)NULL );
  } /* end while */


'''pulBitposition'''(''PULONG'') Pointer to position in the output buffer; gets updated and must be unique to each print job; *pulBitposition must be zero on the first invocation of this function for a block.
} /* end ProcessBand */
</pre>
;Removing Strings From a String Sorter After Retrieving Them
Following is an example of removing strings from a string sorter after retrieving them (during graphics band processing):
<pre>
ProcessBand( PDDC pddc, ... )
{
  // After retrieving all strings from band and sending them to printer
  // See the previous example


'''pbInput'''(''PBYTE'') Input pointer to upper left corner of raster image to compress.
  // We can remove these strings from the sorter
  // since we do not need to process them again
  apiret = GplStringSorterRemoveStrings( hSorter,
                                          STR_INCL_TOP_LEFT_IN_BAND,
                                          rectlBand );
} /* end ProcessBand */
</pre>
;Resetting and Destroying the String Sorter During Escape Processing
Following is an example of resetting and destroying the string sorter during escape processing in a printer driver:
<pre>
Escape( PDDC pddc, ... )
{
  APIRET apiret;


'''ulBitwide'''(''ULONG'') Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
  case DEVESC_NEWFRAME:
  {
    // Process all graphics bands on page


'''ulBitheight'''(''ULONG'') Height of raster image to compress in bits.
    // Reset sorter for next page
    apiret = GplStringSorterReset( hSorter );


'''pfnFlushBuffer'''(''PFN'') The prototype for this function must be:
    } /* end case */
    break;


<pre class="western">void  pfnFlushBuffer ( PVOID,
    case DEVESC_ENDDOC:
                        PBYTE,
    {
                        ULONG );</pre>
      // Process the last page (implied NewFrame)
PVOID Use is the same as G3; necessary parameter for separating printout; generally pddc or hThread.


PBYTE Unsigned char * to output buffer; same as pbOutput.
      // Sorter no longer needed for this print job
      apiret = GplStringSorterDeleteInstance( hSorter );


ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
     } /* end case */
 
     break;
pfnFlushBuffer can be either NULL or point to a function that you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
  } /* end Escape */
 
</pre>
If pfnFlushBuffer() = NULL this function will compress the data and place it into pbOutput. If the output buffer size is not large enough for all the compressed data, the function will return with -1 = error. If pfnFlushBuffer() is not NULL, every time pbOutput is full, pfnFlushBuffer( ) is called to write the compressed bytes out. Note that the return value will the total number of bytes written even though the buffer was filled multiple times.
 
'''pulwstartc'''(''PULONG'') Pointer to array of ULONGs. Its length must be ( ulBitwide + 31 ) / 32 + 2. The 2 extra ULONGs are needed. So for an image of 371 bits width, the length of this array is ( 371 + 31 ) / 32 = 12 plus 2 more-which is 14 unsigned longs. This array is used to store the start bits of the beginning of consecutive 0 bits for the current scan line.
 
'''pulbstartc'''(''PULONG'') Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the current scan line.
 
'''pulwstartp'''(''PULONG'') Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 0 bits for the previous scan line. This must be zero filled on the first invocation of this function.
 
'''pulbstartp'''(''PULONG'') Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the previous scan line. This must be zero filled on the first invocation of this function.
 
'''ulSkipCount'''(''ULONG'') Number of bytes to skip to the next row of input data to compress.
 
'''Returns'''
 
Success Count of data written out (compressed data count). <br />Failure GPLCOMPRESS_ERROR (-1)
 
'''Related Function'''The following is a G4-compression related function automatically sends the &quot;End Block&quot; data sequence (24 bits represented by Hex 001001).
 
GplFaxG4EndBlock() Modified Read ( MRR) Group 4 compression with K = infinity for sending end-of-facsimile block (EOFB). This function has the same first eight parameters as GplFaxG4EncodeBlock().
 
<pre class="western">GplFaxG4EndBlock( PVOID  pHandle,
                  PBYTE  pbOutput,
                  ULONG  ulOutputSize,
                  PULONG  pulBitposition,
                  PBYTE  pbInput,
                  ULONG  ulBitwide,
                  ULONG  ulBitheight,
                  PFN    pfnFlushBuffer );</pre>
Inputs
 
pddc PVOID; used as first parameter to the pfnFlushBuffer function; generally the pddc or HTHREAD.
 
pbOutput Output buffer to fill.
 
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
 
pulBitposition Bit-position count in the output buffer; gets updated and must be unique to each print job.
 
ulBitwide Width of the raster image in bits.
 
ulBitheight Height of the raster image in bits.
 
pfnFlushBuffer Flush buffer function; prototype for this function must be:
 
<pre class="western">void  pfnFlushBuffer( PVOID,
                      PBYTE,
                      ULONG );</pre>
PVOID Necessary parameter for separating printout; generally pddc or hThread.
 
PBYTE Unsigned char * to output buffer; same as pbOutput.
 
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
 
pfnFlushBuffer can be either NULL or point to the function you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when the output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
 
Returns
 
Success Count of data written out (compressed data count). <br />Failure GPLCOMPRESS_ERROR (-1)
 
=== GplFaxTIFF2EncodeBlock ===
 
'''Description'''
 
TIFF algorithm 2 compression. This function essentially provides a &quot; Modified Huffman&quot; G3 compression with no size limits. It is limited only by the byte representation of ULONG (4 Gigabytes).
 
'''Format'''
 
<pre class="western">GplFaxTIFF2EncodeBlock( PVOID  pHandle,
                        PBYTE  pbOutput,
                        ULONG  ulOutputSize,
                        PULONG  pulBitposition,
                        PBYTE  pbInput,
                        ULONG  ulBitwide,
                        ULONG  ulBitheight,
                        PFN    pfnFlushBuffer );</pre>
'''Parameters'''
 
'''pHandle'''(''PVOID'') Pointer to a handle.
 
'''pbOutput'''(''PBYTE'') Pointer to output buffer to fill.
 
'''ulOutputSize'''(''ULONG'') Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
 
'''pulBitposition'''(''PULONG'') Pointer to position in output buffer.
 
'''pbInput'''(''PBYTE'') Input pointer to upper left corner of raster image to compress.
 
'''ulBitwide'''(''ULONG'') Width of raster image to compress in bits.
 
'''ulBitheight'''(''ULONG'') Height of raster image to compress in bits.
 
'''pfnFlushBuffer'''(''PFN'') Flush buffer function; prototype for this function must be:
 
<pre class="western">void  pfnFlushBuffer( PVOID,
                      PBYTE,
                      ULONG );</pre>
PVOID Necessary parameter for separating printout; generally pddc or hThread.
 
PBYTE Unsigned char * to output buffer; same as pbOutput.
 
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
 
'''Returns'''
 
Success Count of data written out (compressed data count). <br />Failure GPLCOMPRESS_ERROR (-1)
 
'''Related Functions'''The following are TIFF2-compression related functions.
 
GplFaxTIFF2StartBlock() TIFF algorithm 2 compression start-of-document function.
 
Inputs
 
pulBitposition Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc; function just sets pulBitposition to 0 (you can do this yourself).
 
Returns Void
 
GplFaxTIFF2EndBlock() TIFF algorithm 2 compression to flush output buffer.
 
<pre class="western">GplFaxTIFF2EndBlock( PVOID  pHandle,
                    PBYTE  pbOutput,
                    ULONG  ulOutputSize,
                    PULONG  pulBitposition,
                    PBYTE  pbInput,
                    ULONG  ulBitwide,
                    ULONG  ulBitheight,
                    PFN    pfnFlushBuffer );</pre>
Inputs
 
hThread HTHREAD.
 
pbOutput Output buffer to fill.
 
ulOutputSize Size in bytes of output buffer; must be divisible by 4.
 
pulBitposition Bit-position count in the output buffer; must be a multiple of 32 bits; gets updated and must be stored off the pddc.
 
pbInput Input pointer to upper left corner of raster image to compress.
 
ulBitwide Width of the raster image in bits.
 
ulBitheight Height of the raster image in bits.
 
pfnFlushBuffer Flush buffer function; prototype for this function must be:
 
<pre class="western">int  pfnFlushBuffer( HTHREAD,
                    PBYTE,
                    ULONG );</pre>
HTHREAD Gotten from GplThreadCreateInstance().
 
PBYTE Unsigned char to output buffer; same as pbOutput.
 
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
 
Returns
 
TRUE If OK <br />FALSE If error
 
=== Exception Management ===
This package contains functions for setting errors with WinSetErrorInfo and logging appropriate warnings, exceptions, and error codes.
 
Four ascending severity levels are defined for error messages:
 
Warning The function detected a problem, took corrective action, and was able to complete processing successfully.
 
Error The function detected a problem for which no sensible corrective action is possible. The function is not executed and the system remains at the same state as when the function was requested.
 
Severe Error The function detected a problem from which the system cannot reestablish its state. The function has partially executed and the application must now make some corrective action to restore the system to a known state.
 
Unrecoverable Error The function detected an error from which it is impossible for the system to reestablish the state that it held at the time that the function was called. It is also impossible for the application to restore the system to a known state.
 
To use the GenPLib Exception-Management package, you must include:
 
<pre class="western">INCL_GENPLIB_ERROR</pre>
 
The following functions are included in the package:
 
[[00044.htm|GplErrSetDosError]]<br />[[00045.htm|GplErrSetError]]<br />[[00046.htm|GplErrSetSevereError]]<br />[[00047.htm|GplErrSetWarning]]<br />[[00048.htm|GplErrSetUnrecoverableError]]
 
=== GplErrSetDosError ===
 
'''Description'''
 
This function saves DOS error.
 
'''Format'''
 
<pre class="western">ERRORID APIENTRY GplErrSetDosError( USHORT usDosError );</pre>
'''Parameter'''
 
'''usDosError'''(''USHORT'') Any error code defined in BSEERR.H that best identifies the failing event.
 
'''Returns'''
 
Success ERRORID <br />Failure None
 
=== GplErrSetError ===
 
'''Description'''
 
This function saves an error.
 
'''Format'''
 
<pre class="western">ERRORID APIENTRY GplErrSetError( USHORT usError );</pre>
'''Parameter'''
 
'''usError'''(''USHORT'') Any error code defined in PMERR.H that best identifies the failing event.
 
'''Returns'''
 
Success ERRORID <br />Failure None
 
=== GplErrSetSevereError ===
 
'''Description'''
 
This function saves a severe error.
 
'''Format'''
 
<pre class="western">ERRORID APIENTRY GplErrSetSevereError( USHORT usError );</pre>
'''Parameter'''
 
'''usError'''(''USHORT'') Any error code defined in PMERR.H that best identifies the failing event.
 
'''Returns'''
 
Success ERRORID <br />Failure None
 
=== GplErrSetWarning ===
 
'''Description'''
 
This function saves the error warning.
 
'''Format'''
 
<pre class="western">ERRORID APIENTRY GplErrSetWarning( USHORT usError );</pre>
'''Parameter'''
 
'''usError'''(''USHORT'') Any error code defined in PMERR.H that best identifies the failing event.
 
'''Returns'''
 
Success ERRORID <br />Failure None
 
=== GplErrSetUnrecoverableError ===
 
'''Description'''
 
This function saves an unrecoverable error.
 
'''Format'''
 
<pre class="western">ERRORID APIENTRY GplErrSetUnrecoverableError( USHORT usError );</pre>
'''Parameter'''
 
'''usError'''(''USHORT'') Any error code defined in PMERR.H that best identifies the failing event.
 
'''Returns'''
 
Success ERRORID <br />Failure None
 
=== Memory Management ===
This package manages global and process (shared and non-shared) memory areas (heaps) using a single handle to a memory control block (HMCB) for all allocations. Therefore, you do not need to manage different heap or memory handles within your code.
 
This package calls appropriate OS/2 system APIs for memory creation and allocation. Dosxxx, SSxxx, and other API decisions are optimized for current OS/2 version performance. Examples include the underlying file system paging architecture and the number of processes in system.
 
Memory areas (heaps) are automatically grown when the initial memory size runs out. Committing more memory than needed is not required. Your code can use a single HMCB and underlying code will manage multiple memory areas ( heaps) if necessary.
 
Sets of debug trace information are displayed whenever memory is overwritten by other processes or writes to memory exceed the allocation size. Memory that has been allocated and not freed (memory leaks) is also shown as debug information.
 
To use the GenPLib Memory-Management package, you must include:
 
<pre class="western">INCL_GENPLIB_MEMORY</pre>
 
 
The following APIs comprise the GenPLib Memory-Management package:
 
*[[00051.htm|GplMemoryCreateInstance]]<br />*[[00052.htm|GplMemoryDeleteInstance]]<br />*[[00053.htm|GplMemoryAlloc]]<br />*[[00054.htm|GplMemoryFree]]<br />*[[00055.htm|GplMemoryGetObjectSize]]<br />*[[00056.htm|GplMemorySetUserID]]
 
=== Call Flow ===
The [[00051.htm|GplMemoryCreateInstance]]() function should be the first memory API called in order to get a handle to a memory control block (HMCB). The HMCB handles all memory allocations. Using an HMCB, any number of memory allocations can be made using [[00053.htm|GplMemoryAlloc]](). These allocations should be freed using [[00054.htm|GplMemoryFree]].
 
[[00052.htm|GplMemoryDeleteInstance]]() is used when the HMCB is no longer needed for memory allocations.
 
'''Note:'''Any memory that was allocated using [[00053.htm|GplMemoryAlloc]]() using an HMCB being deleted by [[00052.htm|GplMemoryDeleteInstance]]and not freed by a corresponding [[00054.htm|GplMemoryFree]]() call will be shown as debug output (with location of function where allocation was done) for developers to correct memory leaks within their code.
 
=== GplMemoryCreateInstance ===
 
'''Description'''
 
This function creates an MCB and a user heap.
 
'''Format'''
 
<pre class="western">HMCB APIENTRY GplMemoryCreateInstance( ULONG  ulPrimarySize,
                                      ULONG  ulExtSize,
                                      ULONG  ulThreshold,
                                      ULONG  ulType );</pre>
'''Parameters'''
 
'''ulPrimarySize'''(''ULONG'') Initial Heap size in bytes.
 
'''ulExtSize'''(''ULONG'') Heap growth size fills in bytes.
 
'''ulThreshold'''(''ULONG'') Threshold for heap allocation. Requests larger than this will be converted to DosAllocMem.
 
'''ulType'''(''ULONG'') Heap type (process, shared). See the following notes for a description of process and shared heaps.
 
'''Returns'''
 
Success Valid handle to memory control block. Failure NULL. <br />WinGetLastError() will return one of the following: <br />PMERR_MEMORY_ALLOCATION_ERROR <br />PMERR_INV_OR_INCOMPAT_OPTIONS
 
'''Notes'''
 
Treat creating a memory instance like creating a memory heap. The ulSize parameter should be a reasonable amount of memory for most of your processing. It is not necessary to set ulSize to a high number so that you do not run out of memory, because the memory area will automatically grow if your processing allocates more than the amount originally set by ulSize.
 
When the memory area is automatically grown, the ulExtSize value is used to determine how large an additional memory area should be created. A default value of 0 (zero) may be used, and the growth size will be tailored to a reasonable amount for the OS/2 version.
 
The [[00053.htm|GplMemoryAlloc]]() API will allocate memory blocks differently based on the ulThreshold value. Typically, allocations of memory of sizes less than the threshold are allocated in a fast access memory area, and sizes larger than the threshold are allocated from a different memory pool. A default value of 0 (zero) may be used and the threshold will be automatically set to perform most efficiently on the current OS/2 version.
 
The ulType parameter allows you to specify the type of memory that you want to allocate. This defines the scope of accessibility to the memory within your driver or program and is one of the following:
 
'''SHARED_MEMORY'''Used to create a global memory area (heap). For example, it is used for allocating global data like string tables in a printer driver.
 
'''PROCESS_MEMORY'''Used to create a per process memory area (heap). For example , it is used for allocating a per device context (DC) memory in a printer driver.
 
=== GplMemoryDeleteInstance ===
 
'''Description'''
 
This function frees memory heap(s) for the user.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplMemoryDeleteInstance( HMCB  hMCB );</pre>
'''Parameter'''
 
'''hMCB'''(''HMCB'') Pointer to memory allocated.
 
'''Returns'''
 
Success NO_ERROR. <br />Returns valid handle to memory control block. Failure ERROR_INVALID_PARA <br />or return values from: <br />SSFreeMem <br />DosFreeMem <br />DosSubUnsetMem <br />WinGetLastError() returns one of the following: <br />PM_INV_OR_INCOMPAT_OPTIONS <br />PMERR_MEMORY_DEALLOCATION_ERR
 
=== GplMemoryAlloc ===
 
'''Description'''
 
This function allocates memory for the user.
 
'''Format'''
 
<pre class="western">PVOID APIENTRY GplMemoryAlloc( HMCB  hMCB,
                              ULONG  ulSize );</pre>
'''Parameters'''
 
'''hMCB'''(''HMCB'') Handle to memory control block.
 
'''ulSize'''(''ULONG'') Amount to allocate.
 
'''Returns'''
 
Success Valid pointer to memory allocated. Failure NULL. <br />WinGetLastError() returns one of the following: <br />PMERR_INV_OR_INCOMPAT_OPTIONS <br />PMERR_INSUFFICIENT_MEMORY
 
=== GplMemoryFree ===
 
'''Description'''
 
This function frees memory for a user.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplMemoryFree( PVOID  pData );</pre>
'''Parameter'''
 
'''pData'''(''PVOID'') Pointer to memory allocated.
 
'''Returns'''
 
Success NO_ERROR. Failure ERROR_INVALID_PARA <br />or return values from: <br />SSFreeMem <br />DosFreeMem <br />DosSubFreeMem <br />WinGetLastError() returns one of the following: <br />PM_INV_OR_INCOMPAT_OPTIONS <br />PMERR_MEMORY_DEALLOCATION_ERR
 
=== GplMemoryGetObjectSize ===
 
'''Description'''
 
This function returns the size of a memory object allocated by [[00053.htm|GplMemoryAlloc]].
 
'''Format'''
 
<pre class="western">LONG APIENTRY GplMemoryGetObjectSize( PVOID  pData );</pre>
'''Parameter'''
 
'''pData'''(''PVOID'') Pointer to object.
 
'''Returns'''
 
Success Size of memory object.
 
(LONG) lsize
 
Failure 0, no size found for memory object. WinGetLastError will return PMERR_INVALID_PARAMETERS (meaning pData was invalid or NULL).
 
 
=== GplMemorySetUserID ===
 
'''Description'''
 
This function is optional, and it is helpful in debugging. You attach a pointer to the heap instance (like your pddc or pdb)-this extra pointer can then be viewed under the kernel debugger.
 
'''Format'''
 
<pre class="western">VOID APIENTRY GplMemorySetUserID( HMCB  hMCB,
                                  ULONG  ulUserID );</pre>
'''Parameters'''
 
'''hMCB'''(''HMCB'') Handle to memory control block.
 
'''ulUserID'''(''ULONG'') Pointer to something helpful.
 
=== Implementation ===
The GenPLib Memory-Management package was created to provide consistency and to simplify the growth and management of heaps.
 
This package was one of the first developed for driver use. When writing a driver &quot;from scratch&quot; or modifying a driver to use GenPLib, begin with this package because many of the other GenPLib packages require memory allocated in the unique format of the memory-management package.
 
Previously, all drivers had to manage their own memory heaps, often using memory allocation packages exported from other parts of the OS/2 system-for example, the INCL_WINHEAP functions from PM WIN or the Subsystem Allocation routines in the PM Graphics Rendering Engine (GRE). It was never clear what routines should be used in a presentation driver or where they should be used (especially for portability). Many of these packages did not allow the caller to grow their heaps, provided no controls for memory leakage, and had no validation or recovery when a driver terminated unexpectedly.
 
A typical raster printer driver creates two types of heaps: a shared heap during Dynamic Link Library (DLL) initialization processing, and a per-DC heap when the PM GRE enables the driver during the FILL_PHYSICAL (02 Hex) subfunction. These heaps are freed during corresponding DLL termination and DISABLE_PHYSICAL calls to the driver.
 
In addition, a per-process heap is also created for the OS2_PM_DRV_DEVMODE entry point in a printer presentation driver. This entry point causes the driver to display its printer and job properties dialogs to the user or to retrieve printer and job property information for a specific output device/ destination.
 
A typical driver initially allocates a 256KB shared global heap and a 100KB per-DC heap and elects to use the memory package's default heap growth size and threshold for larger allocations (achieved by passing in 0L for parameters 2 and 3 for [[00052.htm|GplMemoryDeleteInstance]]).
 
In case of unexpected driver termination, a driver should register a per- process exception handler for each of the major presentation driver entry points. This exception handler can contain cleanup code that frees any per- process heaps created using [[00051.htm|GplMemoryCreateInstance]]in case any code problems cause an exception to occur.
 
One major benefit of the GenPLib memory code is the ability to detect memory leaks (memory a driver allocates but never frees). Whenever a call to [[00052.htm|GplMemoryDeleteInstance]]is made, all memory allocated from the heap is freed so that no leaks can occur. If the debug version of the GenPLib library is used, while [[00052.htm|GplMemoryDeleteInstance]]is cleaning up memory areas that a developer forgot to free, it will inform the developer on a debugging terminal which line of code allocated the memory that was not freed. This is possible because the memory-management package records the function address (and other information) from where [[00053.htm|GplMemoryAlloc]]is called.
 
An OS/2 presentation driver must consider the memory constraints of the user and other programs in the system. In the past, drivers allocated large heaps, hoping the memory an application needed would never overrun the amount initially allocated and cause an exception. Under the paging architecture of OS/2 Warp, however, all the heap memory is often committed during heap creation.
 
All this memory could be forced in and never used by an application (for example, an application queries the driver for the devices it supports and performs no output). In this case, the driver's memory pages would not be touched, so OS/2 would slowly page it out and give it back to the applications that had it in the first place. As a result, a good deal of swapping could occur for active addressable memory, especially on low-RAM systems. The alternative was to force the driver to manage its own heap growth and consolidate multiple heaps.
 
Using the GenPLib Memory-Management package, the heap is identified to the driver as a generic handle to a memory control block (HMCB), not as an address into memory. This handle is supplied by [[00051.htm|GplMemoryCreateInstance]]and is used to identify the heap for all subsequent memory allocations and frees.
 
Therefore, a reasonable initial heap can be created that is just large enough to allow the driver to enable and respond to basic queries. As applications use the driver to render graphics (or other memory-intensive operations), the memory package detects when a given [[00053.htm|GplMemoryAlloc]]call will overflow the current heap and grows it by a default or specified size. The caller does not ever need to know that the heap has grown because it continues to use the same HMCB, while the memory package chains these heaps together.
 
=== Example Code ===
 
'''Creating and Allocating From a Per-DC Heap'''
 
Following is an example of creating and allocating from a per-DC heap during a printer-driver enable via the OS2_PM_DRV_ENABLE entry point:
 
<pre class="western">ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )            // Determine which enable/disable subfunction
  {
    case FILL_PHYSICAL:          // Enable subfunction
    {
      // Create a per-DC memory area (heap)
      hmcbHeap = GplMemoryCreateInstance(
                          LEN_DCHEAP,            // Size of DC Heap (80K)
                          0L,                    // Default extent
                          0L,                    // Default threshold
                          PROCESS_MEMORY );      // Type of heap (define in GPLMEM.H)
 
      // If GplMemoryCreateInstance succeeded
      if(hmcbHeap != NULL);
      {
        // Allocate PDEVICEBLOCK
        pddc-&gt;pdb = ( PDEVICEBLOCK ) GplMemoryAlloc( hmcbHeap, sizeof( DEVICEBLOCK ) );
 
      } /* end if */
    } /* end case */
    break;
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */</pre>
'''Note:'''System memory must be used when passing data buffers to PrtWrite() calls.
 
'''Creating a Global Heap'''
 
Following is an example of creating a global heap:
 
<pre class="western">ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )        // Flag to determine which load/unload module subfunction
  {
    case 0:              // Load module
    {
      // Create a global heap
      globals.pvHeap = GplMemoryCreateInstance(
                                LEN_SHAREDHEAP,  // Initial size (256K)
                                0,                // Default extent
                                0,                // Default threshold
                                SHARED_MEMORY);  // Type of heap (defined in GPLMEM.H)
    } /* end case */
    break;
  } /* end switch */
} /* end _DLL_InitTerm */</pre>
'''Allocating From a Global Heap'''
 
Following is an example of allocating from a global heap:
 
<pre class="western">switch( ulSubFunc )                      // Determine which enable/disable subfunction
{
  case FILL_LOGICAL:                      // Enable subfunction
  {
    // For every string in our driver's string resource table
    for( i=0; i &lt; STRING_TABLE_SIZE; i++ )
    {
      // Load each string from resource table (returns length)
      sLen = WinLoadString (szTemp);
 
      // Allocate memory to hold string just loaded from global heap
      globals.pbStringTable[i] = GplMemoryAlloc( globals.pvSharedHeap, sLen+1 );
 
      // Copy string from temporary buffer to our driver's global string table
      strcpy( globals.pbStringTable[i], szTemp );
 
    } /* end for */
    break;
  } /* end case */
} /* end switch */</pre>
'''Deleting a Per-DC Heap'''
 
Following is an example of deleting a per-DC heap:
 
<pre class="western">ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )
  {
    case DISABLE_PHYSICAL:
    {
      // Free per-DC heap after freeing per-DC memory objects
      GplMemoryDeleteInstance( hmcbHeap );
 
    } /* end case */
    break;
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */</pre>
'''Deleting a Global Heap'''
 
Following is an example of deleting a global heap:
 
<pre class="western">ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )
  {
    case 1:
    {
      // Free global heap after freeing global memory objects
      GplMemoryDeleteInstance( globals.pvSharedHeap );
 
     } /* end case */
     break;
  } /* end switch */
} /* end _DLL_InitTerm */</pre>
 
=== Output-Thread Management ===
The output-thread management module provides a way to create multiple output threads that will manage the sending of data buffers to the printer. This functionality has also been called ''second-thread code''; however, the implementation allows a separate thread per physical output address (for example, LPT, COM, and file).
 
This package seamlessly manages multiple buffer allocations and chains them together. It adjusts the internal data buffering to always try to keep the printer busy processing data. The functions assure that buffer sizes and their reuse align for the best performance with the system-memory memory model (for example, the OS/2 paging architecture).
 
'''Note:''' You may override the default buffer size and the maximum number of buffers to create.
 
Other benefits of using the separate output-thread code in your driver include:
 
*Ability to pass off data buffers as fast as you can fill them without waiting for a device with slower transfer speeds
 
*Unknowingly, the OS/2 PM graphics engine (and other system components) might have locked down system resources, such as fonts, while calling your driver. Therefore, implementing the thread code allows you to return to the graphics engine and free up the resource for other parts of the system, such as the display. This makes the system and applications more responsive to the user.
 
*The package uses the GenPLib Exception-Management package for simple debug tracing.
 
*The package uses the GenPLib Memory-Management package for allocation of instance data.
 
To use the GenPLib Output-Thread Management package, you must include:
 
<pre class="western">INCL_GENPLIB_THREAD</pre>
 
 
Following are the Output-Thread Management APIs:
 
*[[00061.htm|GplThreadCreateInstance]]
*[[00062.htm|GplThreadStart]]
*[[00063.htm|GplThreadOutput]]
*[[00064.htm|GplThreadEnd]]
*[[00065.htm|GplThreadDeleteInstance]]
*[[00066.htm|GplThreadAbortDoc]]
*[[00067.htm|GplThreadResetAbortDoc]]
*[[00068.htm|GplThreadFlushBuffer]]
*[[00069.htm|GplThreadHookDeviceWrites]]
 
=== Call Flow ===
The thread instance (data and semaphores) is initialized; but a real thread is not spun off until you are certain that the application is really sending data to be printed. Therefore, you can systematically use [[00061.htm|GplThreadCreateInstance]]() at enable and [[00062.htm|GplThreadStart]]() at the first data write. This keeps the code straightforward.
 
=== GplThreadCreateInstance ===
 
'''Description'''
 
This function is used to allocate and initialize thread instance data. This function must be called before making any other thread calls in order to get a thread instance handle (HTHREAD). Semaphores and buffers are not created or allocated by this call.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadCreateInstance( HMCB      hMcb
                                      HMODULE  hMod,
                                      HTHREAD  *phThread,
                                      PVOID    pvUserData );</pre>
'''Parameters'''
 
'''hMcb'''(''HMCB'') Handle to the memory control block (See [[00049.htm|Memory Management]]).
 
'''hMod'''(''HMODULE'') Handle of calling module. Used for exception management and error logging.
 
'''*phThread'''(''HTHREAD'')-output Thread instance handle.
 
'''pvUserData'''(''PVOID'')-output This is used in debugging and is optional. It is recommended that you place a helpful pointer, such as your pddc or pdb pointer.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadStart ===
 
'''Description'''
 
This function starts or updates thread parameters. It creates semaphores and buffers, committing system resources.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadStart( HTHREAD  hThread,
                              LHANDLE  hSpooler,
                              HFILE    hDev,
                              ULONG    ulBufferSize,
                              USHORT  usMaxBuffers,
                              UCHAR    ucAbortChar,
                              ULONG    ulAbortCharCount,
                              ULONG    ulAbortBufferSize,
                              PBYTE    pbAbortBuffer,
                              PSZ      pszAbortString,
                              PSZ      pszLogAddress,
                              PSZ      pszWriteError,
                              PSZ      pszDeviceDescription,
                              LONG    lDCType );</pre>
'''Parameters'''
 
'''hThread'''(''HTHREAD'') Instance handle of thread to start. Returned by [[00061.htm|GplThreadCreateInstance]].
 
'''hSpooler'''(''LHANDLE'') Handle to Spooler (OD_QUEUED). Returned from SplQmOpen. May be zero (0) if hDev is supplied.
 
'''hDev'''(''HFILE'') Handle to Device (OD_DIRECT). Returned from PrtOpen. May be zero (0) if hSpooler is supplied.
 
'''ulBufferSize'''(''ULONG'') Size of buffer allocations. May be 0 to use default size.
 
'''usMaxBuffers'''(''USHORT'') Maximum number of buffers to create. May be 0 to use default # of buffers.
 
'''ucAbortChar'''(''UCHAR'') Character to send before the abort string. ucAbortChar can be used for devices that need to be sent a number of bytes before the abort string can be sent. ulAbortChar is sent first during the abort sequence.
 
'''ulAbortCharCount'''(''ULONG'') Number of ucAbortChars to send to the device.
 
'''ulAbortBufferSize'''(''ULONG'') Abort buffer size in bytes.
 
'''pbAbortBuffer'''(''PBYTE'') Pointer to buffer to send when aborting a job. Buffer must not be deleted until after [[00064.htm|GplThreadEnd]]. ulAbortBuffer is sent after ulAbortChar.
 
'''pszAbortString'''(''PSZ'') Abort job reset command for your specific printer language. May be NULL and no string will be sent. pszAbortString is sent last in the abort sequence.
 
'''pszLogAddress'''(''PSZ'') File or port name (for example, LPT1). Used for error reporting. May be NULL.
 
'''pszWriteError'''(''PSZ'') Error prompt string to display in a SplMsgBox (for example, &quot;Unable to write to printer&quot;). May be NULL.
 
'''pszDeviceDescription'''(''PSZ'') Device description to display in a SplMsgBox ( for example, &quot;Laserprinter&quot;). May be NULL.
 
'''lDCType'''(''LONG'') DC type (OD_QUEUED, OD_DIRECT).
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
'''Notes'''
 
ulAbortChar is sent first during the abort sequence. <br />ulAbortBuffer is sent after ulAbortChar. <br />pszAbortString is sent last in the abort sequence.
 
=== GplThreadOutput ===
 
'''Description'''
 
This function is used to pass data to a thread instance (referenced by the thread instance handle passed in) for output. [[00062.htm|GplThreadStart]]must be called prior to calling GplThreadOutput.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadOutput( HTHREAD  hThread,
                              PBYTE    pBytes,
                              ULONG    ulCount,
                              ULONG    fulDataType );</pre>
'''Parameters'''
 
'''hThread'''(''HTHREAD'') Thread instance handle.
 
'''pBytes'''(''PBYTE'') Pointer to data to buffer.
 
'''ulCount'''(''ULONG'') Size of data (byte size) pointed to by pBytes parameter
 
'''fulDataType'''(''ULONG'') Data type flag.
 
THREAD_DT_PRINTERLANGUAGE Data is printer language commands and only requires an abort string to be sent when aborting during this buffer. See pszAbortString in [[00062.htm|GplThreadStart]].
 
THREAD_DT_BINARY Data is binary (raster) data and requires a series of bytes to be sent to ensure the printer is not in binary transfer mode before the abort string can be sent. See ucAbortChar in [[00062.htm|GplThreadStart]].
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadEnd ===
 
'''Description'''
 
This function frees up any resources allocated by the thread instance ( semaphores and data buffers). A call to this function implies that any remaining data buffers are flushed before the return of this call. This call is the logical pair to GplThreadCreate, which allocates system resources.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadEnd( HTHREAD  hThread );</pre>
'''Parameter'''
 
'''hThread'''(''HTHREAD'') Handle to thread instance.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadDeleteInstance ===
 
'''Description'''
 
This function is used to free thread instance data allocated by a corresponding call to [[00061.htm|GplThreadCreateInstance]]().
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadDeleteInstance( HTHREAD *phThread );</pre>
'''Parameter'''
 
'''phThread'''(''HTHREAD'') Pointer to the thread instance handle to be freed.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadAbortDoc ===
 
'''Description'''
 
This function is called to abort output from the thread that is referenced by the thread instance handle that was passed in. After output is stopped, the function will attempt to restore the printer to a known state where it can process data from another print job.
 
The first issue to consider when using this function is that the device may be executing a command that has a large amount of data that it is expecting to receive as a block. Therefore the parameter ulAbortChar passed in on [[00062.htm|GplThreadStart]]is sent ulAbortCharCount times.
 
The second issue to consider is that the abort string 'pszAbortString' provided in [[00062.htm|GplThreadStart]]() is sent last.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadAbortDoc( HTHREAD  hThread );</pre>
'''Parameter'''
 
'''hThread'''(''HTHREAD'') Handle to thread instance.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadResetAbortDoc ===
 
'''Description'''
 
This function is used to resolve any previous calls to [[00066.htm|GplThreadAbortDoc]]() so that another job can start printing using the same thread instance handle.
 
This function will wait, if necessary, for the abort processing started by [[00066.htm|GplThreadAbortDoc]]to be completed before returning so that the thread instance can be put into a state to receive data from a new print job.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadResetAbortDoc( HTHREAD  hThread );</pre>
'''Parameter'''
 
'''hThread'''(''HTHREAD'') Handle to thread instance.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadFlushBuffer ===
 
'''Description'''
 
This function causes all data buffers currently being managed by the thread instance to be flushed (written) to their final output destination (for example, parallel port and spool file).
 
This call allows a flag to be set so that all the data buffers are sent ( flushed) before this call returns (fwait == TRUE) or synchronously (fwait = = FALSE).
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadFlushBuffer( HTHREAD  hThread,
                                    BOOL    fWait );</pre>
'''Parameters'''
 
'''hThread'''(''HTHREAD'') Handle to thread instance.
 
'''fWait'''(''BOOL'') Flag indicating flush asynchronously or synchronously.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== GplThreadHookDeviceWrites ===
 
'''Description'''
 
This is an optional function. This call was created because a class of devices added to the Omni driver did not set status lines according to known standard protocols. Calling this function allows the Omni driver to interpret the status lines because it knows about the devices' special protocols.
 
'''Format'''
 
<pre class="western">BOOL APIENTRY GplThreadHookDeviceWrites( HTHREAD  hThread,
                                        PFN      PfnPrtWrite,
                                                  pddc );</pre>
'''Parameters'''
 
'''hThread'''(''HTHREAD'') Handle to thread instance.
 
'''pfnPrtWrite'''(''PFN'') Our function.
 
'''pddc'''Parameter to our function.
 
'''Returns'''
 
Success TRUE <br />Failure FALSE
 
=== Implementation ===
After the memory-management package is implemented, the next logical package that can be added to a driver is the GenPLib Output-Thread Management package.
 
The best place to implement threads in a driver is when sending and buffering data to what is more than likely a slower output device (or file) . This allows the driver to return back to the application and PM GRE for rendering the next page immediately to improve system response.
 
The benefits of adding multiple threads are proven in tests performed on OS /2 printer drivers that have added the GenPLib thread-management package. Results have shown a 7%-12% performance increase.
 
A printer driver retrieves a thread handle (HTHREAD) from GenPLib by calling [[00061.htm|GplThreadCreateInstance]]during the FILL_PHYSICAL enable subfunction . This call is made only if the DC type is OD_QUEUED or OD_DIRECT (the DC types that request output). This call does not start a thread at this time but causes the GenPLib thread code to initialize its internal structures with information you supply. Once again, a corresponding call to [[00065.htm|GplThreadDeleteInstance]]is made during the driver's disable subfunction DISABLE_PHYSICAL to free the thread handle.
 
The actual thread is started in the driver when the start-document escape code is received from the application (DEVESC_STARTDOC from the DevEscape() device call) using [[00062.htm|GplThreadStart]]. The [[00064.htm|GplThreadEnd]]call that stops the thread is placed in disable subfunction BEGIN_CLOSE_DC by the driver. It is not placed in end-document (DEVESC_ENDDOC) code because some applications choose to call many DEVESC_STARTDOC/DEVESC_ENDDOC pairs for the same DC. Placing the call in a disable subfunction assures that a thread will not start and stop many times in a row. The side effect of this is that the GenPLib thread code will receive multiple [[00062.htm|GplThreadStart]]calls for the same thread. In this case, subsequent [[00062.htm|GplThreadStart]]calls cause an implied flush of all data received at that time for the thread.
 
If you call [[00062.htm|GplThreadStart]]multiple times, the thread code accepts any changed information for input parameters to this call. This allows the driver to dynamically change the buffer size and the maximum number of buffers used by the thread code.
 
The [[00066.htm|GplThreadAbortDoc]]call is also added to the driver's escape code processing routine for the abort-document call (DEVESC_ABORTDOC). This call the thread code to clear data out of all its buffers and to ignore any other data received by the thread until a [[00067.htm|GplThreadResetAbortDoc]]call is made to the thread. As soon as all data is flushed by [[00066.htm|GplThreadAbortDoc]], the abort sequences supplied during [[00062.htm|GplThreadStart]]will place the printer in a known state for the next print job.
 
[[00067.htm|GplThreadResetAbortDoc]]tells the thread code that the job has ended, that no more data will be sent for this print job, and to resume sending data. This call is added during the end-document escape processing (DEVESC_ENDDOC ) in the driver. This allows applications that perform multiple DEVESC_ STARTDOC/DEVESC_ENDDOC calls for a single DC (that is, submitting multiple print jobs using one DC) to function normally.
 
[[00063.htm|GplThreadOutput]]is used to add any data to the thread's output buffers. The call has been added for the DEVESC_RAWDATA escape code. This escape code is sent to a driver to pass through raw data received from DOS or Windows applications along with a data buffer and data count. The call is also implemented wherever the driver needs to send out its own escape codes for graphical or text generating output. This data includes printer/job initialization commands, raster image data (compressed or uncompressed), font/text data, and job/printer termination commands.
 
The last function in the thread-management package is [[00068.htm|GplThreadFlushBuffer]], which tells the thread code to send all data currently held in the thread's buffers before the call returns. This call appears just after [[00067.htm|GplThreadResetAbortDoc]]in a driver's DEVESC_ENDDOC processing code.
 
=== Example Code ===
 
'''Starting a Thread With Output to a Printer'''
 
Following is an example of starting a thread with output to a printer:
 
<pre class="western"> case DEVESC_STARTDOC:                          // Escape code signals start of document
  {
    // If going to a file or direct to a printer
    if( OD_DIRECT == ulDCType )
    {
      // Open output port (printer-driver version of DosOpen)
      rc  =  PrtOpen( hFile, pszLogAddress );
 
      // Create/Update the second print thread
      rc = GplThreadStart( hThread,                // Instance handle
                          0,                      // Handle to spooler
                          hFile,                  // Handle to device
                          0,                      // Default buffer size
                          0,                      // Default number of buffers
                          '\0',                    // Abort character
                          1024,                    // Abort character repeat count
                          sizeof( pszCmdAbort );  // Length of abort command
                          pszCmdAbort,            // Our abort command
                          pszLogAddress,          // For example, LPT1
                          &quot;Error&quot;,                // Error string if write fails
                          pszDeviceName,          // For example, &quot;Epson LQ2550&quot;
                          ulDCType );              // DC type
    } /* end if */
  } /* end case */</pre>
'''Starting a Thread With Output Queuing &quot;Raw&quot; Data'''
 
Following is an example of starting a thread with output queuing &quot;raw&quot; printer escapes:
 
<pre class="western">  case DEVESC_STARTDOC:                          // Escape code signals start of document
  {
    // Else DC type is queued, not direct to a printer
    elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_RAW )
      {
        if( !hspl )
        {
          // Open appropriate queue (from DevOpenData) for output
          hspl  =  SplQmOpen( &quot;*&quot;, 9, (PQMOPENDATA)&amp;DevOpenData );
          SplQmStartDoc( hspl, szDocumentName );
 
        } /* end if */
 
        // Create/Update the second print thread
        rc = GplThreadStart( hThread,                // Instance handle
                            hspl,                    // Handle to spooler
                            0,                      // Handle to device
                            0,                      // Default buffer size
                            0,                      // Default number of buffers
                            '\0',                    // Abort character
                            1024,                    // Abort character repeat count
                            sizeof( pszCmdAbort );  // Length of abort command
                            pszCmdAbort,            // Our abort command
                            pszLogAddress,          // For example, LPT1
                            &quot;Error&quot;,                // Error string if write fails
                            pszDeviceName,          // For example, &quot;Epson LQ2550&quot;
                            ulDCType );                // DC Type
      } /* end if */
    } /* end if */
  } /* end case */</pre>
'''Starting a Thread With Output Queuing &quot;Standard&quot; Data'''
 
Following is an example of starting a thread with output queuing &quot;standard&quot; data:
 
<pre class="western">  case DEVESC_STARTDOC:                          // Escape code signals start of document
  {
    // Else DC type is queued, not direct to a printer
    elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_STD )
      {
        SplStdStart( pddc-&gt;pdb-&gt;hdc );
 
        // Open appropriate queue (from DevOpenData) for output
        hspl = SplQmOpen( &quot;*&quot;, 9, (PQMOPENDATA)&amp;DevOpenData );
 
        // Signal start of document (no thread is started by Omni driver)
        SplQmStartDoc( hspl, szDocumentName );
 
      } /* end if */
    } /* end if */
  } /* end case */</pre>
'''GplThreadCreateInstance and GplThreadDeleteInstance Usage'''
 
The enable and disable subfunctions FILL_PHYSICAL and DISABLE_PHYSICAL are ideal points at which to create and delete a thread instance. Following is an example of [[00061.htm|GplThreadCreateInstance]]() and [[00065.htm|GplThreadDeleteInstance]]() usage :
 
<pre class="western">ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                                // Determine enable subfunction
  {
      case FILL_PHYSICAL:                            // An enable subfunction
      {
        // Only create a thread if we expect to output data
        if( (OD_QUEUED == pdb-&gt;ulType )  ||  (OD_DIRECT == pdb-&gt;ulType ) )
        {
          // Create an output thread instance (do not start thread yet)
          bRC = GplThreadCreateInstance( hmcbHeap, globals.hmod, &amp;hThread, pddc );
 
        } /* end if */
      } /* end case */
      break;
 
      case DISABLE_PHYSICAL:                          // A disable subfunction
      {
        // Delete the output thread instance if allocated
        if( hThread )
        {
          bRC = GplThreadDeleteInstance( &amp;hThread );
 
        } /* end if */
      } /* end case */
      break;
 
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */</pre>
'''GplThreadOutput Usage'''
 
Following is an example of [[00063.htm|GplThreadOutput]]() usage:
 
<pre class="western">  case DEVESC_NEWFRAME:                            // Page eject requested
  {
    CHAR achRasterCmd[] = &quot;*Esc*r1Q&quot;;
 
    // When getting a DEVESC_NEWFRAME or DEVESC_ENDDOC,
    // The current page's raster image can be retrieved from the
    // device surface and transferred to the printer
 
    // Enter raster mode by sending down escape code for current device
    bRC = GplThreadOutput( hThread,                // Thread instance handle
                            achRasterCmd,            // Pointer to data buffer
                            strlen(achRasterCmd),    // Size of data buffer
                            THREAD_DT_BINARY );      // Data type
 
    // Dither and/or compress raster data and send to output thread
    bRC = GplThreadOutput( hThread,
                            pbRasterData,
                            ulRasterDataLength,
                            THREAD_DT_BINARY );
 
  } /* end case */</pre>
'''[[00064.htm|GplThreadEnd]]Usage'''
 
Following is an example of [[00064.htm|GplThreadEnd]]usage during OS2_PM_DRV_ENABLE:
 
<pre class="western">ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                              // Determine enable subfunction
  {
    case BEGIN_CLOSE_DC:
    {
      // If we created an output thread
      if( hThread )
      {
        // End  the  second  thread  implied  flush  with  wait
        bRC  =  GplThreadEnd (  hThread  ) ;
 
      }  / *  end  if  * /
    }  / *  end  case  * /
  }  / *  end  switch  * /
}  / *  end  OS2 _ PM _ DRV _ ENABLE  * / </pre>
'''[[00066.htm|GplThreadAbortDoc]]Usage'''
 
Following is an example of [[00066.htm|GplThreadAbortDoc]]usage for handling an abort- document escape:
 
<pre class="western">  case DEVESC_ABORTDOC:
  {
    // Tell second thread that an abort doc occurred
    GplThreadAbortDoc( hThread );
 
    // Store a state flag in our pddc to indicate abort is in effect
    pddc-&gt;fAbortDocCalled = TRUE;
 
  } /* end case */</pre>
'''[[00067.htm|GplThreadResetAbortDoc]]Usage'''
 
Following is an example of using [[00067.htm|GplThreadResetAbortDoc]]:
 
<pre class="western">case DEVESC_ENDDOC:
{
  // If we received a DEVESC_ABORTDOC
  if( pddc-&gt;fAbortDocCalled )
  {
    // Notify the output thread that the abort condition is over
    GplThreadResetAbortDoc( hThread );
 
    // Reset the abort state so thread will begin processing data again
    pddc-&gt;fAbortDocCalled = FALSE;
 
  } /* end if */
} /* end case */</pre>
'''[[00068.htm|GplThreadFlushBuffer]]Usage'''
 
Following is an example of using [[00068.htm|GplThreadFlushBuffer]]:
 
<pre class="western">case DEVESC_ENDDOC:
{
  // If going to a file or direct to a printer
  if( OD_DIRECT == ulDCType )
  {
    // Flush all data in second thread; we are done
    GplThreadFlushBuffer( hThread, TRUE );
 
  } /* end if */
 
  elseif( OD_QUEUED == ulDCType  &amp;&amp;  PM_Q_RAW == ulDataType )
  {
    // Flush all data in second thread; we are done
    GplThreadFlushBuffer( hThread, TRUE );
 
  } /* end if */
} /* end case */</pre>
'''[[00069.htm|GplThreadHookDeviceWrites]]Usage'''
 
Following is an example of using [[00069.htm|GplThreadHookDeviceWrites]]:
 
<pre class="western">case DEVESC_STARTDOC:
{
  // We have just called thread Start
  GplThreadStart();
 
  // We inform the thread that we want to subclass the write of data
  // to the output destination, replacing it with our callback function
  GplThreadHookDeviceWrites( hThread,        // Thread handle
                            pfnPrtWrite,    // Our &quot;write&quot; function
                            pddc );        // Parameter to our &quot;write&quot; function
} /* end case */</pre>
 
=== Pattern Creation ===
 
This module contains functions to create bitmap patterns.
 
To use the GenPLib Pattern-Creation package, you must include:
 
<pre class="western">INCL_GENPLIB_MEMORY
INCL_GENPLIB_PATTERNS</pre>
 
 
The following functions are used to create bitmap patterns.
 
[[00073.htm|GplPatternCreateBitmaps]]<br />[[00074.htm|GplPatternDeleteBitmaps]]<br />[[00075.htm|GplPatternCreate]]
 
=== GplPatternCreateBitmaps ===
 
'''Description'''
 
This function fills in an array of BMAPINFO structures with the PATSYM patterns. This function is designed to be used to fill in the DEVICESURFACE structure with patterns that match the resolution of the target device. GplPattern functions use the GenPLib Memory-Management package and require that a HMCB had been created with [[00051.htm|GplMemoryCreateInstance]].
 
'''Format'''
 
<pre class="western">LONG APIENTRY GplPatternCreateBitmaps(
                            HMCB        hMcb,              // Memory instance handle
                            ULONG      ulXResolution,      // X device resolution
                            ULONG      ulYResolution,      // Y device resolution
                            ULONG      ulWidth,            // Desired pattern width
                            ULONG      ulHeight,          // Desired pattern height
                            PBMAPINFO  paBmapInfo,        // Array of pattern bitmaps (output)
                            LONG        lCount,            // Number of bitmaps to create
                            ULONG      ulFlip );          // Flip bitmaps horiz/vertically</pre>
'''Parameters'''
 
'''hMcb'''(''HMCB'') Handle to initialized memory control block.
 
'''ulXResolution'''(''ULONG'') Target device x resolution.
 
'''ulYResolution'''(''ULONG'') Target device y resolution.
 
'''ulWidth'''(''ULONG'') Size of bitmap X.
 
'''ulHeigh'''(''ULONG'') Size of bitmap Y.
 
'''paBmapInfo'''(''PBMAPINFO'') Pointer to an array of BMAPINFO.
 
'''lCount'''(''LONG'') Number of bitmap information structures.
 
'''ulFlip'''(''ULONG'') Bit vector. If GPLPAT_INVERT_BITS is set, the bitmaps are inverted.
 
'''Returns'''
 
TRUE Success <br />FALSE Failure
 
=== GplPatternDeleteBitmaps ===
 
'''Description'''
 
This function deletes the bits and clears the array of BMAPINFO.
 
'''Format'''
 
<pre class="western">LONG APIENTRY GplPatternDeleteBitmaps(
                            HMCB      hMcb,            // Memory instance handle
                            PBMAPINFO  paBmapInfo,      // Pointer to bitmaps to be freed
                            LONG      lCount );        // Count of bitmaps to be freed</pre>
'''Parameters'''
 
'''hMcb'''(''HMCB'') Handle to initialized memory control block.
 
'''paBmapInfo'''(''PBMAPINFO'') Pointer to an array of BMAPINFO.
 
'''lCount'''(''LONG'') Number of bitmap information structures.
 
'''Returns'''
 
TRUE Success <br />FALSE Failure
 
=== GplPatternCreate ===
 
'''Description'''
 
This function can be used by a presentation driver to create a bitmap representation of any one of the predefined OS/2 patterns. Typically, it is called to create all or a subset of patterns a device driver will support at the start of a print job.
 
'''Format'''
 
<pre class="western">LONG APIENTRY GplPatternCreate( HMCB      hMcb,                // Memory instance handle
                                ULONG      ulXResolution,        // X device resolution
                                ULONG      ulYResolution,        // Y device resolution
                                ULONG      ulPatSym,            // OS/2 pattern define
                                PBMAPINFO  pBmapInfo );          // A pattern bitmap (output)</pre>
'''Parameters'''
 
'''hMcb'''(''HMCB'') Handle to initialized Memory control block.
 
'''ulXResolution'''(''ULONG'') Target device x resolution.
 
'''ulYResolution'''(''ULONG'') Target device y resolution.
 
'''ulPatSym'''(''ULONG'') Pattern number as defined for OS/2 in PMDDI.H:
 
<pre class="western">  PATSYM_DEFAULT
  PATSYM_DENSE1
  PATSYM_DENSE2
  PATSYM_DENSE3
  PATSYM_DENSE4
  PATSYM_DENSE5
  PATSYM_DENSE6
  PATSYM_DENSE7
  PATSYM_DENSE8
  PATSYM_VERT
  PATSYM_HORIZ
  PATSYM_DIAG1
  PATSYM_DIAG2
  PATSYM_DIAG3
  PATSYM_DIAG4
  PATSYM_NOSHADE
  PATSYM_SOLID
  PATSYM_BLANK
  PATSYM_HALFTONE
  PATSYM_HATCH
  PATSYM_DIAGHATCH</pre>
'''pBmapInfo'''(''PBMAPINFO'') Number of bitmap information structures.
 
'''Returns'''
 
TRUE Success <br />FALSE Failure
 
=== Example Code ===
 
'''GplPatternCreateBitmaps Usage'''
 
Following is an example of using [[00073.htm|GplPatternCreateBitmaps]]:
 
<pre class="western">// PM Graphics Rendering Engine v2.2 Device Surface Function from the Omni driver
 
LONG APIENTRY QueryDeviceSurface( PDDC pddc, PVOID pv )
{
  // Initialize all fields of the device surface structure, then...
  if( !pddc-&gt;pdb-&gt;bAllocatedPatterns )
  {
      if( ORIENTATION_PORTRAIT == pdb-&gt;pJobProperties-&gt;ulOrientation )
      {
        if( GplPatternCreateBitmaps ( pddc-&gt;hmcbHeap,
                                      pddc-&gt;pResInfo-&gt;ulXRes,
                                      pddc-&gt;pResInfo-&gt;ulYRes,
                                      64L,
                                      64L,
                                      (PBMAPINFO)&amp;pds-&gt;abmapinfoDefPattern,
                                      DEFAULT_PATTERNS_NUMBER,
                                      0L ) )
        {
          pddc-&gt;pdb-&gt;bAllocatedPatterns = TRUE;
 
        } /* end if */
      } /* end if */
  } /* end if */
} /* end QueryDeviceSurface */</pre>
'''GplPatternDeleteBitmaps Usage'''
 
Following is an example of using [[00074.htm|GplPatternDeleteBitmaps]]:
 
<pre class="western">case BEGIN_CLOSE_DC:
 
  // Perform other disable processing, then near end of case...
  if( GRE_22 &lt;= globals.ulGreVersion )  // PM Graphics Rendering Engine v2.2 or higher
  {
    PDEVICESURFACE pds;
 
    // Note:  We only allocate patterns in QueryDeviceSurface for GRE v2.2 and higher
    pds = (PDEVICESURFACE)pddc-&gt;pdb-&gt;pDeviceSurface;
    if( GplPatternDeleteBitmaps( pddc-&gt;pdb-&gt;hmcbHeap,
                                (PBMAPINFO)&amp;pds-&gt;abmapinfoDefPattern,
                                DEFAULT_PATTERNS_NUMBER ) )
    {
      pddc-&gt;pdb-&gt;bAllocatedPatterns = FALSE;
 
    } /* end if */
  } /* end if */</pre>
 
=== Semaphores ===
Printer drivers are responsible for serializing threads' access to any of their device contexts (DCs). OS/2 mutual exclusion (MUTEX) semaphores are a possible choice to implement this serialization. When two threads attempt to use the same printer DC at the same time, the second thread should receive a failing result code and PMERR_HDC_BUSY from WinGetLastError.
 
Given this behavior of OS/2 and its presentation drivers, most OS/2 applications use just one thread when printing. Whenever there is just one thread, there is little contention for the DC; but presentation drivers must safely manage this contention.
 
There is some performance overhead to consider whenever a thread calls into the OS/2 kernel, as is the case for calls to DosRequest/ReleaseMutexSem. The overhead occurs because the kernel reloads code and data selectors on kernel entry and exit. OS/2 MUTEX semaphores are always safe semaphores, however, because of this transition to the kernel.
 
=== Semaphores in the GenPLib ===
The semaphore package in the GenPLib implements a Ring-3 RAM semaphore when running on the 486 processor. The 486 has the CMPXCHG instruction that is not present on the 386. This single instruction implements a fast, safe, RAM semaphore at Ring 3.
 
On a 486, a thread requesting an unowned semaphore will remain at Ring 3 and not enter the kernel. If the semaphore is owned and the thread intends to wait, however, then it will enter the kernel to block on an event semaphore.
 
On a 386, the GenPLib simply uses OS/2 MUTEX semaphores. Implementing a semaphore on the 386 requires multiple instructions; thus, a switch to the kernel is required to disable interrupts while these multiple instructions execute.
 
For either processor, a thread may repeatedly request the same semaphore without an intervening release (nested requests). Use [[00084.htm|GplSemQueryMutexSem]]to query the nested use count.
 
The GenPLib detects the processor type at runtime.
 
All GenPLib semaphore APIs return 0 on success. When not zero, they return the same result codes documented for their OS/2 MUTEX semaphore counterparts.
 
The GenPLib semaphore APIs require a pointer to a MTXSEM data structure instead of a semaphore handle like OS/2 MUTEX semaphores. It is the responsibility of the client to allocate one of these structures per semaphore and supply its pointer to the GenPLib semaphore API.
 
When the MTXSEM semaphore structure resides on shared memory, it may be created with the DC_SEM_SHARED flag. Additional processes may obtain access to this shared memory and open the semaphore.
 
All GenPLib semaphores are created in the unowned state.
 
To use the GenPLib Semaphores package, you must include:
 
<pre class="western">INCL_GENPLIB_SEMAPHORES</pre>
The following APIs are included in the semaphore package of GenPLib:
 
*[[00079.htm|GplSemCreateMutexSem]]
*[[00080.htm|GplSemOpenMutexSem]]
*[[00081.htm|GplSemRequestMutexSem]]
*[[00082.htm|GplSemReleaseMutexSem]]
*[[00083.htm|GplSemCloseMutexSem]]
*[[00084.htm|GplSemQueryMutexSem]]
 
With the exception of the first parameter, PMTXSEM, the parameters to the GenPLib semaphore APIs are as documented for OS/2 MUTEX semaphores.
 
=== GplSemCreateMutexSem ===
 
'''Description'''
 
This call creates a MUTEX semaphore.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplSemCreateMutexSem( PMTXSEM  pmtxsem,
                                      ULONG    flAttr );</pre>
'''Parameters'''
 
'''pmtxsem'''(''PMTXSEM'') Address of MTXSEM structure.
 
'''flAttr'''(''ULONG'') Creation flags. DC_SEM_SHARED is the only flag.
 
'''Returns'''
 
Success NO_ERROR Failure ERROR_INVALID_PARAMETER <br />or return code from: <br />DosQueryMem <br />DosCreateEventSem <br />DosCreateMutexSem
 
'''Notes'''
 
The caller must first allocate memory for the MTXSEM structure. See GPLSEM. H, where this structure is defined. If the memory is allocated from shared memory, the caller may specify the DC_SEM_SHARED flag for flAttr parameter.
 
=== GplSemOpenMutexSem ===
 
'''Description'''
 
This call opens a shared GPL Mutex semaphore.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplSemOpenMutexSem( PMTXSEM  pmtxsem );</pre>
'''Parameter'''
 
'''pmtxsem'''(''PMTXSEM'') Address of MTXSEM structure.
 
'''Notes'''
 
When multiple processes are using a shared semaphore, then subsequent processes must open the semaphore. It is not necessary for the creating process to perform an open.
 
'''Returns'''
 
Success NO_ERROR Failure ERROR_INVALID_PARAMETER <br />or return code from: <br />DosOpenEventSem <br />DosOpenMutexSem
 
=== GplSemRequestMutexSem ===
 
'''Description'''
 
This call is used to request an OS/2 PM MUTEX semaphore.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplSemRequestMutexSem( PMTXSEM  pmtxsem,
                                      LONG    lTime );</pre>
'''Parameters'''
 
'''pmtxsem'''(''PMTXSEM'') Address of MTXSEM structure.
 
'''lTime'''(''LONG'') Time out value in milliseconds.
 
'''Returns'''
 
Success NO_ERROR
 
Failure Typical error returns for this function include ERROR_INVALID_ PARAMETER or return codes from DosRequestMutexSem.
 
ERROR_TIMEOUT (RC 640d) means that the semaphore is owned.
 
'''Notes'''
 
The caller can specify the timeout value for the request. When timeout is 0 , the call returns immediately. When timeout is -1, the call is an indefinite wait. Otherwise the call will block up to the timeout specified.
 
=== GplSemReleaseMutexSem ===
 
'''Description'''
 
This call will release an owned MUTEX semaphore.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplSemReleaseMutexSem( PMTXSEM  pmtxsem );</pre>
'''Parameter'''
 
'''pmtxsem'''(''PMTXSEM'') Address of MTXSEM structure.
 
'''Returns'''
 
Success NO_ERROR Failure ERROR_INVALID_PARAMETER <br />ERROR_NOT_OWNER (not the owner of the MUTEX semaphore) <br />DosReleaseMutexSem
 
'''Notes'''
 
This call decrements the nested use count for the semaphore. When the nested use count goes to zero, the semaphore is truly released.
 
=== GplSemCloseMutexSem ===
 
'''Description'''
 
This call closes a GPL MUTEX semaphore for the process.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplSemCloseMutexSem( PMTXSEM  pmtxsem );</pre>
'''Parameter'''
 
'''pmtxsem'''(''PMTXSEM'') Address of MTXSEM structure.
 
'''Returns'''
 
Success NO_ERROR Failure ERROR_INVALID_PARAMETER <br />DosCloseEventSem <br />DosCloseMutexSem
 
'''Notes'''
 
Every process using GPL semaphores should close each one with this API.
 
=== GplSemQueryMutexSem ===
 
'''Description'''
 
This call returns information about the semaphore.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplSemQueryMutexSem( PMTXSEM  pmtxsem,
                                    PID      *ppid,
                                    TID      *ptid,
                                    PULONG  pulCount );</pre>
'''Parameters'''
 
'''pmtxsem'''(''PMTXSEM'') Address of MTXSEM structure.
 
'''*ppid'''(''PPID'') Address where process ID is returned.
 
'''*ptid'''(''PTID'') Address where thread ID is returned.
 
'''pulCount'''(''PULONG'') Request count.
 
'''Returns'''
 
Success NO_ERROR Failure ERROR_INVALID_PARAMETER <br />or the return codes from: <br />DosQueryMutexSem
 
'''Notes'''
 
This API will return information about the semaphore, such as what process and thread currently owns it, and if owned, what is the nested use count.
 
It is important for asynchronously killed threads to make this call to determine if they own the DC semaphore. If they own the semaphore, they should release it the appropriate number of times before exiting.
 
=== Differences Between GenPLib Semaphores and Kernel Semaphores ===
 
If a nonzero request time times out, it will timeout to within 30 milliseconds of the supplied time out.
 
DosQueryMutexSem returns a process and thread ID even if the MUTEX sem is unowned; that is, the count returned is zero. This semaphore package returns process and thread ID of zero when the semaphore is unowned regardless of 386 or 486.
 
=== String Management ===
The string-management package provides string-sorting functionality for printer output devices.
 
Certain printer output devices can output data only in small bands, yet they are expected to keep track of strings that might be positioned anywhere on a physical output page. These devices need an independent mechanism for keeping those strings in sorted &quot;page&quot; order until the device is actually processing the band of that string.
 
This is true for raster printers that utilize a print head and must only send text strings that are drawn using device (printer) fonts within a certain range of the print head when printing graphics data. The following diagrams represent this concept:
 
<pre class="western">                        Output Page
                        /----------\
                        |          |
                        |          |
                        |xxxxxxxxxx|
                        |xxxxxxxxxx| &lt;--- Current band of raster output
                        |          |      that can be processed by the
                        |          |      output device.
                        | Hello    |
                        |          |
                        |          | &lt;--- Text is below current band so
                        |    ABCD  |      we can't output it yet
                        |          |
                        |          |
                        |          |
                        \----------/</pre>
 
 
<pre class="western">                        Output Page (later)
                        /----------\
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
                        |xHelloxxxx| &lt;--- Current band of raster output
                        |xxxxxxxxxx|      that can be processed by the
                        |          |      output device.
Lower string still --&gt; |    ABCD  |      This time the text string is in
not in range          |          |      the band region and it should be
                        |          |      processed at this time
                        |          |
                        |          |
                        \----------/</pre>
 
 
<pre class="western">                        Output Page (later)
                        /----------\
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
  String already  --&gt; | Hello    |
  processed            |          |
                        |          |
                        |          |
                        |xxxxABCDxx|  &lt;--- Current band of raster output
                        |xxxxxxxxxx|        that can be processed by the
                        |          |        output device.
                        |          |        This time the second string is in
                        \----------/        the band region and it should be
                                            processed at this time.</pre>
The package sorts text strings passed in page order. Top-to-bottom and left -to-right is the current default page order.
 
Once strings are inserted, you can extract strings in respect to a rectangular area (typically the current graphics band rectangle) that you pass in.
 
The string-sorting package utilizes the GenPLib Memory-Management and Exception-Management packages, thereby providing very efficient system resource usage and consistent error reporting and logging.
 
To use the GenPLib String-Management package, you must include:
 
<pre class="western">INCL_GENPLIB_STRING</pre>
 
The following functions comprise the string-management package:
*[[00087.htm|GplStringSorterCreateInstance]]
*[[00088.htm|GplStringSorterInsertString]]
*[[00089.htm|GplStringSorterQueryNumStrings]]
*[[00090.htm|GplStringSorterGetFirstString]]
*[[00091.htm|GplStringSorterGetNextString]]
*[[00092.htm|GplStringSorterRemoveStrings]]
*[[00093.htm|GplStringSorterReset]]
*[[00094.htm|GplStringSorterDeleteInstance]]
 
=== GplStringSorterCreateInstance ===
 
'''Description'''
 
This function creates a string sorter instance (memory resource allocated) and returns a handle that must be used by all other string sorter package functions.
 
The default heap size that is created is based on current printer driver media sizes and application text usage. A parameter is supplied so that the user can increase the initial memory area that is created to hold text strings. Otherwise a default value, based on test averages, will be used.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterCreateInstance( HDC      hdc,
                                              PHSORTER  phStrSort,
                                              ULONG    ulHeapSize,
                                              ULONG    ulOptions );</pre>
'''Parameters'''
 
'''hdc'''(''HDC'') Handle to current DC.
 
'''phStrSort'''(''PHSORTER'') Pointer to the location where string-sorter handle will be placed.
 
'''ulHeapSize'''(''ULONG'') Initial size of heap to allocate for strings. This can be zero. A default memory area will be created.
 
'''ulOptions'''(''ULONG'') Creation options (for example, DBCS and TTY). This parameter is reserved for future use. This parameter must be zero.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_xxx
 
=== GplStringSorterInsertString ===
 
'''Description'''
 
This function adds a text string to a string instance. Information, including the bounding rectangle passed in and a pointer to the data, is stored with the string. You can store with the string (for example, a pointer to a structure containing font name, font ID, and color).
 
Currently, the sort order is determined by the top-left corner of the text box passed in. The strings are sorted in page order. Page order is the order the text would be printed by a banding device on a hardcopy page.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterInsertString( HSORTER  hStrSort,
                                            PSZ      pszStr,
                                            RECTL    rectlStr,
                                            PVOID    pUserDef,
                                            PVOID    pUnused );</pre>
'''Parameters'''
 
'''hStrSort'''(''HSORTER'') Handle of the string sorter to be used for insert operation.
 
'''pszStr'''(''PSZ'') Pointer to text string to insert.
 
'''rectlStr'''(''RECTL'') Bounding rectangle of string being inserted.
 
'''pUserDef'''(''PVOID'') Pointer to user-defined data to be stored with this string .
 
'''pUnused'''(''PVOID'') NULL. Reserved for future use.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_INVALID_PARAMETER <br />ERROR_xxx
 
 
 
 
 
=== GplStringSorterQueryNumStrings ===
 
'''Description'''
 
This function returns the current number of strings in the string sorter instance (HSORTER) referenced on the call.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterQueryNumStrings( HSORTER  hStrSort,
                                                PULONG  pulNumStr );</pre>
'''Parameters'''
 
'''hStrSort'''(''HSORTER'') Handle of string sorter instance to be queried.
 
'''pulNumStr'''(''PULONG'') Pointer to the location where number is to be returned.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_xxx
 
 
 
 
 
=== GplStringSorterGetFirstString ===
 
'''Description'''
 
This function is used to get a linked list of strings from the string sorter in relation to a band rectangle passed in.
 
The strings are not deleted from the linked list on this call because we allocated the information and will de-allocate on a [[00093.htm|GplStringSorterReset]]() call or a [[00092.htm|GplStringSorterRemoveStrings]]() call.
 
Currently, the defined relationships that can be selected are:
 
Option Description
 
STR_INCL_PART_IN_BAND Return any string whose text box intersects any part of the band passed in.
 
STR_INCL_ALL_IN_BAND Return any string whose text box is entirely inside the band passed in.
 
STR_INCL_TOPLEFT_IN_BAND Return any string whose top-left coordinate is inside the band passed in.
 
'''Notes:'''
 
1.All retrievals are inclusive on all boundaries of the ''rectlBand''passed in.
 
2.GplStringSorterGetFirstString retrievals are non-destructive. (the string remains in the string-sorter instance and are only removed by [[00092.htm|GplStringSorterRemoveStrings]]or [[00093.htm|GplStringSorterReset]]calls).
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterGetFirstString( HSORTER  hStrSort,
                                              ULONG    ulOption,
                                              RECTL    rectlBand,
                                              PSZ      *ppszStr,
                                              PRECTL  prectlStr,
                                              PPVOID  ppUserDef );</pre>
'''Parameters'''
 
'''hStrSort'''(''HSORTER'') Handle of string-sorter instance to be used for string retrieval.
 
'''ulOption'''(''ULONG'') Option passed in to indicate the relationship to test for on the &quot;Get&quot; operation. See options above.
 
'''rectlBand'''(''RECTL'') The band rectangle passed in that will be used to test all text-string rectangles against.
 
'''*ppszStr'''(''PSZ'')-output Pointer to first text string meeting retrieve criteria (ulOption).
 
'''prectlStr'''(''PRECTL'')-output Pointer to the bounding rectangle of first string retrieved (passed in during [[00088.htm|GplStringSorterInsertString]]).
 
'''ppUserDef'''(''PPVOID'')-output Pointer to the user-defined data that was associated with text string retrieved (passed in during [[00088.htm|GplStringSorterInsertString]]).
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_xxx
 
 
 
 
 
=== GplStringSorterGetNextString ===
 
'''Description'''
 
This function is used to retrieve the next string matching the retrieve criteria passed in on the last call to [[00090.htm|GplStringSorterGetFirstString]](). The criteria is based on the ''ulOption''and ''rectlBand''parameters from that function). This function can be called multiple times until an ERROR_NO_ MORE_ITEMS return code is detected. Each successful call (NO_ERROR) returns another string from the string sorter instance meeting the criteria until no more strings meet the criteria.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterGetNextString( HSORTER  hStrSort
                                              PSZ      *ppszStr,
                                              PRECTL  prectlStr
                                              PPVOID  ppUserDef );</pre>
'''Parameters'''
 
'''hStrSort'''(''HSORTER'') Handle of the string-sorter instance to be used for the retrieve operation.
 
'''*ppszStr'''(''PSZ'') - output Pointer to the location that the text string will be retrieved to.
 
'''prectlStr'''(''PRECTL'') - output Bounding rectangle of the string being retrieved.
 
'''ppUserDef'''(''PPVOID'') - output Pointer to user-defined data stored with string during the insert operation.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_xxx
 
 
 
 
 
=== GplStringSorterRemoveStrings ===
 
'''Description'''
 
This function is used to delete all strings from the string sorter instance , based on a relationship (ulOption) to a rectangle (rectlBand) both passed .
 
See StringGetFirstStringFromSorter() and StringGetNextStringFromSorter() calls for non-destructive string retrievals.
 
Remove options include:
 
Option Description
 
STR_INCL_PART_IN_BAND Return any string whose text box intersects any part of the band passed in.
 
STR_INCL_ALL_IN_BAND Return any string whose text box is entirely inside the band passed in.
 
STR_INCL_TOPLEFT_IN_BAND Return any string whose top-left coordinate is inside the band passed in.
 
Removes are inclusive on all sides of ''rectlBand''passed in.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterRemoveStrings( HSORTER  hStrSort,
                                              ULONG    ulOption,
                                              RECTL    rectlBand );</pre>
'''Parameters'''
 
'''hStrSort'''(''HSORTER'') Handle of the string sorter to be used for remove operation.
 
'''ulOption'''(''ULONG'') Remove options.
 
'''rectlBand'''(''RECTL'') Rectangle that is used for comparison by the remove option.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_xxx
 
 
 
 
 
=== GplStringSorterReset ===
 
'''Description'''
 
This function resets the string sorter instance by removing all text strings inserted into the sorter.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterReset( HSORTER  hStrSort );</pre>
'''Parameter'''
 
'''hStrSort'''(''HSORTER'') Handle of the string sorter to be reset.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_xxx
 
 
 
 
 
=== GplStringSorterDeleteInstance ===
 
'''Description'''
 
This function destroys a string sorter instance by freeing any system resources allocated on the corresponding call to [[00087.htm|GplStringSorterCreateInstance]](). This function implies a [[00093.htm|GplStringSorterReset]]() call.
 
'''Format'''
 
<pre class="western">APIRET APIENTRY GplStringSorterDeleteInstance( HSORTER  hStrSort );</pre>
'''Parameter'''
 
'''hStrSort'''(''HSORTER'') Handle of string sorter to be destroyed. The handle is no longer valid after the call.
 
'''Returns'''
 
Success NO_ERROR <br />Failure ERROR_INVALID_PARAMETER
 
 
 
 
 
=== Example Code ===
 
'''Retrieving Strings From String Sorter During Band Processing'''
 
Following is an example of retrieving strings from string sorter during band processing in a raster printer driver:
 
<pre class="western"> HSORTER hSorter;
CharString( PDDC pddc, ..., LONG cChars, PCH pchString, ... )
{
  if( bDeviceFont )                        // If we have a device font to manage
  {
    if( ! hSorter )                        // If we have not yet created a sorter
    {
      // Create a string sorter to handle our device fonts
      apiret = GplStringSorterCreateInstance( pddc,&amp;hSorter, 0L, 0L );
 
    } /* end if */
 
    // Calculate text box of string from our character-width tables
    // Insert character string into sorter
    apiret = GplStringSorterInsertString( hSorter,
                                          (PSZ)pchString,
                                          rectlTextBox,
                                          (PVOID)NULL,
                                          (PVOID)NULL );
 
    // Example: check number of strings currently in sorter
    apiret = GplStringSorterQueryNumStrings( hSorter,&amp;ulNumStr );
 
  } /* end if */
} /* end CharString */
 
Usage Notes:
 
  Used default values for heap size and options during 'StringCreate' API (LTR, TTB).
 
  Used default value for during 'StringInsert' API.
 
  For 'StringInsert' API, we could have allocated and filled in our own
  structure that helps us process this string (for example, font name, identifier,
  color, etc.).</pre>
'''Removing Strings From a String Sorter During Processing of a Graphics Band'''
 
Following is an example of removing strings from a string sorter during processing of a graphics band (banding):
 
<pre class="western"> ProcessBand( PDDC pddc, ...)
{
  APIRET  apiret;
  RECTL  rectlString, rectlBand;
  PSZ    pszString;
 
  // Retrieve first sting in the raster band we are processing
  apiret = GplStringSorterGetFirstString(
                hSorter,
                STR_INCL_TOP_LEFT_IN_BAND,
                                // Retrieve strings whose Top-Left corner are in band
                rectlBand,
                                // Rectangle of the graphic band we are processing
                &amp;pszString,
                                // Pointer that first string will be returned in
                &amp;rectlString,
                                // Text box of the string
                (PVOID)NULL );
                                // We had no user-defined data to retrieve
 
  // if we have a string meeting retrieval criteria
  while( apiret != ERROR_NO_MORE_ITEMS )
  {
    // Get next string (uses same retrieve option as &quot;GetFirst&quot;)
    apiret = GplStringSorterGetNextString( hSorter,
                                            &amp;pszString,
                                            &amp;rectlString,
                                            (PVOID)NULL );
  } /* end while */
 
} /* end ProcessBand */</pre>
'''Removing Strings From a String Sorter After Retrieving Them'''
 
Following is an example of removing strings from a string sorter after retrieving them (during graphics band processing):
 
<pre class="western"> ProcessBand( PDDC pddc, ... )
{
  // After retrieving all strings from band and sending them to printer
  // See the previous example
 
  // We can remove these strings from the sorter
  // since we do not need to process them again
  apiret = GplStringSorterRemoveStrings( hSorter,
                                          STR_INCL_TOP_LEFT_IN_BAND,
                                          rectlBand );
} /* end ProcessBand */</pre>
'''Resetting and Destroying the String Sorter During Escape Processing'''
 
Following is an example of resetting and destroying the string sorter during escape processing in a printer driver:
 
<pre class="western"> Escape( PDDC pddc, ... )
{
  APIRET apiret;
 
  case DEVESC_NEWFRAME:
  {
    // Process all graphics bands on page
 
    // Reset sorter for next page
    apiret = GplStringSorterReset( hSorter );
 
    } /* end case */
    break;


    case DEVESC_ENDDOC:
[[Category:Printer Device Driver Reference]]
    {
      // Process the last page (implied NewFrame)
 
      // Sorter no longer needed for this print job
      apiret = GplStringSorterDeleteInstance( hSorter );
 
    } /* end case */
    break;
} /* end Escape */</pre>

Latest revision as of 22:38, 11 February 2020

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

Printer Device Driver Reference

The Generic Printer Library (GenPLib) is a set of subroutine components that can be used in the development of 32-bit hardcopy presentation drivers.

Presentation-driver developers often write similar sets of code to implement functions commonly required by hardcopy devices. Heap creation, output buffering, and banding raster data are examples. The GenPLib simplifies development and decreases the development time of 32-bit OS/2 printer presentation drivers because it provides these necessary functional components.

The GenPLib consists of function packages that implement some of the more difficult code that is needed to create a printer driver. The library format is a way to realize software reuse so that other developers can benefit from code that can be made completely independent of any driver.

GenPLib appears in the IBM Developer Connection Device Driver Kit for OS/2 (DDK) as a static library that is linked to OS/2 driver code. The GenPLib code is still in its early versions; updates to the functions and header files can occur with each release of the DDK. Eventually, the GenPLib may become a system DLL; but for now, you can use any static version of GenPLib. By convention, all functions exported from GenPLib begin with the Gpl prefix.

Because of the design of the modules of the GenPLib, some functions can also be used for development in areas other than OS/2 printer drivers.

Following are some reasons to use the GenPLib:

  • Allows developers to quickly, easily, and incrementally add function packages to their printer drivers.
  • Written in portable C source code that can be recompiled for other processors.
  • Provides 32-bit functions for new 32-bit operating-system environments, such as OS/2 2.0+SP and up-including OS/2 Warp.
  • Available in Intel, PowerPC, debug, and non-debug versions. The current 16-bit drivers cannot run in these environments.
  • Provides a single, consistent source code for common functions.
  • Packages are designed using data abstraction.
  • Makes printer presentation-driver code more readable and maintainable.
  • Produces extensive debugging and logging information for developers and service personnel.
  • Implemented with the newest, most appropriate OS/2 system APIs, tailored to the underlying hardware and operating system version on which it is running (that is, system memory, paging behavior, CPU detection, etc.).
Note
The GenPLib has no 16-bit routines.

GenPLib Functional Packages

The GenPLib currently contains the following function packages:

#Error Assertion Provides a consistent way of checking for errors in driver code and reporting and logging useful information for developers and field support.
#Banding Simplifies the use of journaling that assists in banding pages of raster output data to printers.
#Color Dithering Provides color-dithering support.
#Gamma Correction Provides gamma-correction support.
#Compression Provides commonly used compression routines recognized by printers.
#Exception Management Provides ways of setting errors with WinSetErrorInfo and logging appropriate warnings, exceptions, and error codes.
#Memory Management Provides a simple way of managing global and process memory needed by presentation drivers.
#Pattern Creation Provides functionality to create bitmap patterns.
#Output-Thread Management Provides multi-threaded output and buffer management to printer devices for improved performance.
#Semaphores Helps drivers protect their data in multitasking environments. The implementation detects the current CPU level and implements semaphores based on the smallest possible set of assembly instructions.
#String Management Provides a way to sort text strings that are being drawn to a printer output page so that they can be processed in many page orders when needed.

GenPLib Architecture

The GenPLib is a 32-bit static linking library, GENPLIB.LIB. Developers use LINK386.EXE to link the GPL with their 32-bit printer drivers.

Several C language include files (*.H) are associated with GENPLIB.LIB and define the function names and prototypes in the library. The main include file is GENPLIB.H.

GENPLIB.H includes other relevant library H files based on symbolics (INCLs) defined to the compiler. For example, the following symbolics have a special meaning to the operation of GENPLIB.H when included by a C program:

INCL_GENPLIB_ASSERT
INCL_GENPLIB_ERROR
INCL_GENPLIB_HANDLER
INCL_GENPLIB_JOURNAL
INCL_GENPLIB_MEMORY
INCL_GENPLIB_SEMAPHORES
INCL_GENPLIB_STRINGSORT
INCL_GENPLIB_THREAD
INCL_GENPLIB_UTIL
INCL_GENPLIB_PATTERNS

To include all functions, use:

INCL_GENPLIB_ALL

When a developer uses GenPLib memory-management routines, the C source file that requires GenPLib memory management would include a set of code resembling the following:

#define INCL_GENPLIB_MEMORY
#include <genplib.h>

This code is included before any GenPLib memory-management functions or structures are referenced. The compiler automatically includes GPLMEM.H within GENPLIB.H.

Components of the Generic Print Library

The following sections describe the components and functions of the GenPLib.

Error Assertion

To use the GenPLib Error-Assertion package, you must include:

INCL_GENPLIB_ASSERT

The error-assertion package of the GenPLib consists of six APIs. Four are macros:

The remaining two are functions:

PMAssert Validates the return code from OS/2 Presentation Manager (PM) APIs that display the WinGetLastError() result and a supplied text string in a message box.
BaseAssert Validates OS/2 APIs that use the APIRET return code.
ASSERTT () and ASSERTF () ASSERTT causes an error assertion if the supplied test evaluates to TRUE. ASSERTF causes an error assertion if the supplied test evaluates to FALSE. An assertion is an INT3 with kernel debug information displaying the failed test, file name, and line number of the location from which the assertion was called.
DBPRINTF (()) and DBPRINTIF (()) DBPRINTF outputs the supplied text string to the kernel debugger. DBPRINTIF outputs the supplied text string to the kernel debugger if the supplied condition is TRUE.

ASSERTT () and ASSERTF ()

The ASSERTT and ASSERTF macros are very useful to precisely locate errors in code. Each is simply a test. If the test fails, the system generates a sound, stops the program, and displays a message. The more often an ASSERTT or ASSERTF macro is used, the better the chance that the first point of failure will be captured for analysis.

Use ASSERTT or ASSERTF prior to:

Pointers Assert that pointers are nonzero.
Divisors Assert that divisors are nonzero.
:Strings Assert that strings are valid.
:Copying Assert that enough space is present before copying.

Use ASSERTT or ASSERTF after:

OS/2 APIs and internal functions Assert result codes of OS/2 APIs and your internal functions.
Pointers Assert pointers after allocation.
Switch statements Use ASSERTT (TRUE) in the default case of switch statements

Do not use ASSERTT or ASSERTF in:

Essential Code - These macros are not included in the retail build of the product. For example, ASSERTT (0 != DosRequestMutexSem (hsem))

The following is sample output on the kernel debugger:

Assertion failed on line 41 of file code.c
'rc == 0'
eax=XXXXXXXX ebx=XXXXXXXX etc=XXXXXXXX edx=XXXXXXXX esi=XXXXXXXX edi=XXXXXXXX
eip=XXXXXXXX esp=XXXXXXXX ebp=XXXXXXXX iopl=2 -- -- -- xx xx xx xx xx xx xx xx
cs=XXXX ss=XXXX ds=XXXX es=XXXX fs=XXXX gs=XXXX  cr2=XXXXXXXX cr3=XXXXXXXX
program:CODE32:BREAKPOINT
XXXX:XXXXXXXX cc                 int    3
##

Use ASSERTT and ASSERTF under the following conditions:

ASSERTT (z);
where z is a Boolean expression that should evaluate to zero or FALSE on success.
ASSERTF (nz);
where nz is a Boolean expression that should evaluate to nonzero or TRUE on success.

The following example shows how to use ASSERTT and ASSERTF:

pointer = NULL;
rc = DosAllocMem( &pointer, size, PAG_EXECUTE | PAG_READ | PAG_WRITE );
ASSERTT (rc);       // Return code should be zero (success)
ASSERTF (pointer);  // Pointer should be nonzero

DBPRINTF (()) and DBPRINTIF (())

DBPRINTF uses the printf functionally of ANSI C to write messages to the kernel debugger screen. DBPRINTF is a macro; but because it can have a variable number of parameters, it must have an extra set of parentheses.

The following is the structure of DBPRINTF:

DBPRINTF(               // Outputs a text string to debug terminal (like C printf command)
   PSZ pszFormatString, // String with same format options as printf command
   (...) );             // Parameter list of values to be placed in format string

The following is the structure of DBPRINTIF:

DBPRINTIF(               // A conditional DBPRINTF command
   BOOL bDoit,           // Outputs format string if this parameter evaluates to TRUE
   PSZ pszFormatString,  // String with same format options as printf command
   (...) );              // Parameter list of values to be placed in format string

ASSERTT, ASSERTF, DBPRINTF, and DBPRINTIF Notes:

ASSERTT and ASSERTF as well as DBPRINTF and DBPRINTIF are macros. They are built for the debug-level build and disappear for the retail-level build.

These macros rely on the PMDD device driver to be activated. To activate the PMDD device driver, add a /Cx parameter to the following statement (where x is the COM port number):

DEVICE=C:\OS2\PMDD.SYS /Cx

PMAssert

Description

This function is similar to ASSERTT and ASSERTF, but it displays a message box if an error condition occurs. In addition to displaying a user-provided error message, the message box also displays the results of WinGetLastError(), the Presentation Manager error code. Use this function to assert OS/2 Presentation Manager APIs.

Format

PMAssert(                 // Display a message box with pszString, and show WinGetLastError()
     BOOL  bDoit,         // Display message box only if this expression evaluates to TRUE
     HAB   habErr,        // Anchor block handle of thread checking for a PM error
     PSZ   pszString );   // Message to display on message box with PM error code
Parameters
bDoit (BOOL) Boolean value. If TRUE, then a message box is displayed.
habErr (HAB) Anchor block of program.
pszString (PSZ) Message string.
Returns
TRUE Evaluated to TRUE
FALSE Evaluated to FALSE

Example Code

BOOL rc;
rc = GpiDestroyPS( hps );
PMAssert( rc == FALSE,
          hab,
          "GpiDestroyPS failed!" );

BaseAssert

Description

This function is similar to ASSERTT and ASSERTF, but it displays a message box if an error condition occurs. Use this function to assert return codes from those OS/2 APIs that use DOS Error-type return codes.

Format

BaseAssert(               // Display message box if a base error (DOS) is passed in (ulError)
      ULONG  ulError,     // Value of base error as defined in BSEERR.H header file
      PSZ    pszString ); // String to print out with base error code
Parameters
ulError (ULONG) Return code from OS/2 BaseAPI, either NO_ERROR (0) or one of the ERROR_xxx DOS Error codes. If ulError is not equal to NO_ERROR, a message box is displayed.
pszString (PSZ) Message string.
Returns
TRUE Evaluated to TRUE
FALSE Evaluated to FALSE

Example Code

APIRET apirc;
   apirc = DosAllocMem( pbuf, 1024h, PAG_READ | PAG_WRITE );
   BaseAssert( "DosAllocMem failed", apirc );

Banding

This component controls all aspects of managing a journal file and banding such as:

  • Calculating the band size-determines if banding is necessary based on the input parameters (resolution, bits/pel, page size, etc.).
  • Creating a journal file, and managing COM_DRAW flags.
  • Playing back either a full page or bands within a page, and managing the DC offset for each band.
  • Offering many different play options.
  • Handling destructive and non-destructive plays (if it is necessary to play more than once).
  • Handling portrait and landscape cases.
  • Playing bands from top-to-bottom or bottom-to-top.

To use the GenPLib Banding package, you must include:

INCL_GENPLIB_JOURNAL

Journaling Overview

A journal file is a record of all the GreXXX function calls that a driver receives between the time that it starts recording a journal file and the time it stops recording a journal file. Typically, recording starts at the beginning of a page and stops at the end of a page. After the recording has stopped, you can replay the journal file as often as necessary.

There are many reasons to record the GreXXX calls for a particular handle to a drawing context (HDC). Color sorting and banding are two examples.

For color sorting, printer commands are output for a particular color while the journal file is recorded. Then, the journal file is played back for the remaining colors. Printer commands are output only when the current color is the color with which you are working.
For banding, a portion of the page is defined as a bitmap and only that area is drawn in while the journal file is played back. Then, the DC origin is moved up the size of the band and the journal file is replayed. This must be done for every band on the page.

The following functions are used for journaling:

#GplQueryPhysicalMem
#GplJournalCalcBandSize
#GplJournalCreateInstance
#GplJournalPlayInstance
#GplJournalDeleteInstance
#GplJournalAbortDoc
#GplJournalResetAbortDoc

Call Flow

GplQueryPhysicalMem is the first function that you can call. It tells you how much physical RAM you have available.

GplJournalCalcBandSize is the second function that you can call. It is an optional helper function that tells you whether or not you need to band given your memory requirements and band size.

The next function that you call is GplJournalCreateInstance. This function initializes an instance or reuses an instance.

The next function that you call is GplJournalPlayInstanceGplJournalPlayInstance. This function replays the journal file either per-page or per-band; it can also preserve or change the attributes of the page.

Finally, you call GplJournalDeleteInstance to clean up the instance data.

GplQueryPhysicalMem

Description

This function tells you how much physical RAM is installed on your system.

The function ulPhysMem can be used in determining the amount of memory that should be reserved for banding. For the Omni driver, for example:

 if( ulPhysMem <= 4 )
    ulMaxBandMem = 256 * 1024;      // 256K bands
 elseif( ulPhysMem < 12 )
    ulMaxBandMem = 512 * 1024;      // 512K bands
 elseif( ulPhysMem < 20 )
    ulMaxBandMem = 1000 + 1024;     // 1M bands
 elseif( ulPhysMem >=20 )
    ulMaxBandMem = 2000 + 1024;     // 2M bands
Returns
ulPhysMem - Amount of physical RAM installed on system.

GplJournalCalcBandSize

Description

This function helps you decide if you need to break the page up into bands. If you do need to journal, then it will set up the band size to be constrained by the memory limit and to be a multiple of the modulus.

Based on the page size, number of bits per pel, number of color planes, etc., the function calculates the number of bytes needed for the page bitmap. If the amount exceeds the memory limit, it calculates the number of bands into which to break the page.

Format

APIRET APIENTRY GplJournalCalcBandSize(
                   PIJOURNAL  pIJournal,         // Journal data structure
                   USHORT     usBitsPerPel,      // 1, 4, 8, or 24 bits per pel
                   USHORT     usNumPlanes,       // Number of color planes
                   USHORT     usModulus,         // Band size should be a multiple of this
                   USHORT     usDotsPerColumn,   // Resolution
                   ULONG      ulMaxBandMem );    // Maximum memory available for banding
Parameters
pIJournal (PIJOURNAL) Journal data structure. The following input fields are used:
usXLength Length of page in pels along x axis.
usYLength Length of page in pels along y axis.
bPortrait Orientation of page-portrait or landscape.
usBitsPerPel (USHORT) Page bitmap, number of bits per pel.
usNumPlanes (USHORT) Page bitmap, number of planes.
usModulus (USHORT) The band size must be zero or a multiple of this number. This may be necessary to cause byte, word, or Dword alignment to align band size to match the width of a print head. This functionality may also be helpful if you need to send the bits in a certain column height.
usDotsPerColumn (USHORT) Resolution.
ulMaxBandMem (ULONG) Maximum memory available for banding.
This parameter is the limit to the number of bytes that your band bitmap can exceed.
Returns
FALSE Journaling is not necessary.
TRUE Journaling is necessary. The following is filled in and valid:
pIJournal (PIJOURNAL)
Journal data structure
usBandLength
Band length in pels
pIJournal is output with usBandLength updated as follows:
x-band length indicates landscape orientation.
y-band length indicates portrait orientation.

Notes

This function should be called at COMPLETE_OPEN_DC time; because at this time, job properties have been obtained that may affect how pIJournalis filled in (for example, form size, mono/color selections).

GplJournalCreateInstance

Description

This function will create a reusable instance for a particular DC. The opposite of this function is #GplJournalDeleteInstance. Following are the four cases in which this function may be called:

Case 1 Ask for the number of bytes to allocate for the handle.
Case 2 DEVESC_STARTDOC time is the first (real) time this function is called. hJournal has been allocated. Initialize hJournal's bytes to 0.
Case 3 At DEVESC_NEWFRAME time, another page, more processing. After the call to #GplJournalPlayInstance has completed, set it up for the next page.
Case 4 At DEVESC_NEWFRAME time, another page, but this time we want the information about the page to change. The call to GplJournalPlayInstance has completed.

Format

APIRET APIENTRY GplJournalCreateInstance( HJOURNAL   hJournal,   // Journal handle
                                          PIJOURNAL  pIJournal,  // Journal data structure
                                          ULONG      ulFlags );  // Creation flags (defaults)
Parameters
hJournal (HJOURNAL) Handle to the journal file instance.
pIJournal (PIJOURNAL) A pointer to the input structure.
ulFlags (ULONG) This function performs different actions based on the ulFlags. The ulFlags is a bit vector (individual bits have different meanings). The options are separated into mutually exclusive groups that must have one member set.
CREATE_DRAW_WHILE_RECORDING Turn on drawing while the journal file is being recorded.
Returns
Success TRUE
Failure FALSE

Implementation

This function will perform different actions based on the input parameters:

Case 1

The function will return the size (in the number of bytes) of a handle. It is the responsibility of the of caller to allocate and free the memory for a journal handle.

hJournal NULL.
pIJournal NULL.

Case 2

This function creates an engine journal file and starts recording to it. It also starts the accumulation of bounds. Call #GplJournalPlayInstance to stop recording and play the journal file.

hJournal Address of newly allocated memory.
pIJournal Address of the input structure. The structure has following format:
ulSize Size of this structure.
hModule Module handle of the calling program.
hdc Current HDC.
usXLength Page size in pels.
usYLength Page size in pels.
bPortrait Orientation of page.
TRUE -portrait
FALSE -landscape
usBandLength Band height in pels.
pfunBand Pointer to a callback function with the following prototype. This function is only called at the end of each band. (_System *)(PVOID, PRECTL)
pfunArg Any pointer. This will be passed to the first parameter of the previous callback function.
Flags Contains creation flags.

Case 3

The function performs the same functionality as Case 2. However, you can change any of the parameters in the input structure from the previous call to GplJournalCreateInstance.

hJournal Handle to a journal file instance.
pIJournal Address of the input structure. The structure has following format:
ulSize Size of this structure.
hModule Module handle of the calling program.
hdc Current HDC.
usXLength Page size in pels.
usYLength Page size in pels.
bPortrait Orientation of page.
TRUE -portrait
FALSE -landscape

Case 4

For this case, a new page has been sent and you will restart the entire process (Case 2). This function uses the same parameters that were given to the original call to GplJournalCreateInstance.

hJournal Handle to a journal file instance.
pIJournal NULL.

GplJournalPlayInstance

Description

This function signifies the end of the recording. If you want to call this function multiple times for a page, then for calls 1..n-1 the flag PLAY_ NONDESTRUCTIVE should be used. On the last call, n, the flag PLAY_ DESTRUCTIVE should be used.

Format

APIRET APIENTRY GplJournalPlayInstance( HJOURNAL  hJournal, ULONG     ulFlags );
Parameters
hJournal (HJOURNAL) Handle to the journal file instance.
ulFlags (ULONG) This function will perform different actions based on the ulFlags. The ulFlags is a bit vector (individual bits have different meanings). The options are separated into mutually exclusive groups that must have one member set.
PLAY_JOURNAL_BAND Play all bands at this time. The callback function that was defined at #GplJournalCreateInstance time will be called after each band has been played.
or
PLAY_JOURNAL_PAGE Plays the journal file once for this page.
-
PLAY_NONDESTRUCTIVE The DC state will not be altered after the call has been completed.
or
PLAY_DESTRUCTIVE The DC state will now have the final attributes of the page.
-
PLAY_TOP_TO_BOTTOM Bands are played from the maximum Y value to 0.
or
PLAY_BOTTOM_TO_TOP Bands are played from 0 to the maximum Y value.
Returns
Success TRUE
Failure FALSE

GplJournalDeleteInstance

Description

This function is the opposite of #GplJournalCreateInstance. It should be called at DEVESC_ENDDOC or enable subfunction 11 (begin close DC) time. It is responsible for cleaning up all of the resources that it used.

Format

APIRET APIENTRY GplJournalDeleteInstance( HJOURNAL hJournal );
Parameter
hJournal (HJOURNAL) Handle to the journal file instance.
Returns
Success TRUE
Failure FALSE

GplJournalAbortDoc

Description

This function is called when a DEVESC_ABORTDOC is received.

Format

APIRET APIENTRY GplJournalAbortDoc( HJOURNAL  hJournal );
Parameter
hJournal (HJOURNAL) Handle to the journal file instance.
Returns
Success TRUE
Failure FALSE

GplJournalResetAbortDoc

Description

This function is called when a DEVESC_ENDDOC is received and the job has been aborted.

Format

APIRET APIENTRY GplJournalResetAbortDoc( HJOURNAL  hJournal );
Parameter
hJournal (HJOURNAL) Handle to the journal file instance.
Returns
Success TRUE
Failure FALSE

Example Code

#GplQueryPhysicalMem Usage

Following is an example of GplQueryPhysicalMem usage:

case COMPLETE_OPEN_DC:                             // Final driver enable subfunction
{
  // If we are enabling for a DC where we may need to journal
  if( OD_DIRECT == ulDCType || PM_Q_RAW == ulDataType )
  {
    // Fill in journal information structure
    IJournal.ulSize      = sizeof( pddc->IJournal );
    IJournal.bPortrait   = ( pddc->pJobProps->ulOrientation == PORTRAIT );
    IJournal.hdc         = pddc->hdc;                         // Handle to DC
    IJournal.hModule     = globals.hModule;             // Driver's module handle
    IJournal.pfunBand    = (PJFUN)NewFrame;             // Callback function
    IJournal.pfunArg     = (PVOID)pddc;                 // Arguments to callback function
    IJournal.usXLength   = pddc->pCurrentForm->hcInfo.xPels;
    IJournal.usYLength   = pddc->pCurrentForm->hcInfo.yPels;

    // Find out how much physical RAM is installed on the system
    GplQueryPhysicalMem( &ulPhysMem );

    // Omni uses ulPhysMem to determine the maximum memory for banding
    If( ulPhysMem <= 4 )
        ulMaxBandMem = 256 * 1024;      // 256K bands
    elseif( ulPhysMem < 12 )
        ulMaxBandMem = 512 * 1024;      // 512K bands
    elseif( ulPhysMem < 20 )
        ulMaxBandMem = 1000 + 1024;     // 1M bands
    elseif( ulPhysMem >= 20 )
        ulMaxBandMem = 2000 + 1024;     // 2M bands

  } /* end if */
} /* end case */

#GplJournalCalcBandSize Usage

Following is an example of #GplJournalCalcBandSize usage during OS2_PM_DRV_ ENABLE for a dot-matrix printer:

case COMPLETE_OPEN_DC:                            // Final driver enable subfunction
{
    IJournal.ulSize      = sizeof( pddc->IJournal );
    IJournal.bPortrait   = TRUE;
    IJournal.hdc         = hdc;
    IJournal.hModule     = globals.hModule;
    IJournal.pfunBand    = (PJFUN)NewFrame;
    IJournal.pfunArg     = (PVOID)pddc;
    IJournal.usXLength   = hcInfo.xPels;
    IJournal.usYLength   = hcInfo.yPels;

    // NOTE: Modulus should be should be '1' on full-page printers or print-head width
    bBanding = GplJournalCalcBandSize(
                                     &IJournal,      // PIJournal
                                     1,              // Bits per pel
                                     1,              // Planes
                                     40,             // Modulus
                                     64000 );        // Maximum memory available

} /* end case */

#GplJournalCreateInstance Usage

Following is an example of GplJournalCreateInstance usage to allocate/create a journal instance handle during processing of the start-document escape. At this point, the port is opened and the output thread is already created.

case DEVESC_STARTDOC:
{
  if( ulDCType == OD_DIRECT || ulDataType == PM_Q_RAW )
  {
    // If we need to band (that implies journaling)
    if( pddc->fBanding )
    {
      //  First call to size of memory needed for journal handle
      ulSize = GplJournalCreateInstance( NULL, NULL, 0 );

      // Allocate memory needed for journal handle (driver's responsibility)
      hJournal = GplMemoryAlloc( hmcbHeap, ulSize );  // User per-DC heap

      // Allocate the memory for formal instance
      lrc = GplJournalCreateInstance( hJournal,        // Journal handle
                                      &IJournal ),     // Journal information structure
                                      0 );             // Creation flags (defaults)
    } /* end if */
  } /* end if */
/* end case */

#GplJournalPlayInstance and #GplJournalDeleteInstance Usage

Following are examples of #GplJournalPlayInstance and #GplJournalDeleteInstance during the processing of the end-document escape:

case DEVESC_ENDDOC:
{
  if( ulDCType == OD_DIRECT || ulDataType == PM_Q_RAW )
  {
    // If we need to band (that implies journaling)
    if( pddc->fBanding )
    {
      apiret = GplJournalPlayInstance( hJournal,
                                       PLAY_JOURNAL_BAND  |
                                       PLAY_DESTRUCTIVE   |
                                       PLAY_TOP_TO_BOTTOM );

      apiret = GplJournalDeleteInstance( hJournal );

      // Remember:  Free memory allocated for journal instance handle (hjournal)

     } /* end if */
  } /* end if */
} /* end case */

#GplJournalAbortDoc Usage

Following is an example of GplJournalAbortDoc usage:

case DEVESC_ABORTDOC:
{
  if( ulDCType == OD_DIRECT && ulDataType == PM_Q_RAW )
  {
    // If we have a journal file (that is, we are banding)
    if( pddc->hJournal )
    {
      // Inform journal code that abort condition is in effect
      GplJournalAbortDoc( pddc->hJournal );

    } /* end if */
  } /* end if */
} /* end case */

#GplJournalResetAbortDoc Usage

Following is an example of GplJournalResetAbortDoc usage:

case DEVESC_ENDDOC:
{
  if( ulDCType == OD_DIRECT && ulDataType == PM_Q_RAW )
  {
    // If we have been called with an abort document
    if( pddc->fAbortDocCalled )
    {
      // Inform journal code that abort condition is over
      GplJournalResetAbortDoc( pddc->hJournal );

    } /* end if */
  } /* end if */
} /* end case */

Color Dithering

Color dithering is technique of interleaving cyan, magenta, yellow, and optionally black so that the eye perceives another color-such as pink or orange. Color dithering converts picture pels in sizes 2, 4, 8, 16, 24, or 32 bits of RGB/K color to a 3 or 4 bitpel consisting of cyan, magenta, yellow, and optionally K(black). The best dither functions diffuse or spread out colors and also act as a low-pass filter to changes in color. The diffusion of color causes the printing of cyan, magenta, and yellow in the proper density so that from a distance, the eye perceives the correct color.

Following are the dithering functions:

Gamma-correction functions are a part of the color-dithering package and are used internally by the dithering code, but they are also available separately. See #Gamma Correction.

GplDitherCreateInstance

Description

To optimize throughput, a dither handle is initialized when this function is invoked. This includes allocation and initialization of transform tables and parameters.

Format

APIRET APIENTRY GplDitherCreateInstance(
    PPVOID         ppdh,     // Returned pointer to dither handle pointer
    HMCB           hmcb,     // User-specified memory handle
    PDITHEREQUEST  pReq,     // Pointer to dither-request structure
    PDITHERESULT   pReply ); // Pointer to dither-reply structure
Parameters
ppdh (PPVOID)
Pointer to the dither handle pointer that was created and returned by #GplDitherCreateInstance.
hmcb (HMCB)
If it is not zero, this parameter must be a handle (pointer to the API heap).
pReq (PDITHEREQUEST)
bReq (BYTE) Set to CMY_ALL or CMYK_ALL.
lDitherType (LONG)
From JobProperties.
HT_LEVEL
When selected, the following may be ORed with HT_LEVEL:
HT_PARAMETER
HT_MIN_COLOR
HT_PERCENT
HT_FUNCTION
HT_DITHER_4x4
Use a 4x4 halftone pattern for 16 levels of perceived intensity. Also "snap.pHalftone" must be a pointer to a 16x4x4 byte array of dither patterns.
HT_DITHER_8x8
Use a 8x8 halftone pattern for 64 levels of perceived intensity. Also "snap.pHalftone" must be a pointer to a 16x8x8 byte array of dither patterns.
HT_MAGIC_SQUARES
A tessalated (tiled) dither that populates a dither matrix to guarantee a semi-random color mix yet controls average occurrence of colors (randomized pattern).
HT_ORDERED_SQUARES
Use recursive tesselated tiles of rectangular threshold arrays. Similar to HT_MAGIC_SQUARES, but uses a different method of populating dither matrix.
HT_ERR_DIFFUSION
Stucki Error Diffusion-a nice quality filter for photograph-quality images.
HT_FAST_DIFFUSION
Error diffusion with lower sampling rate, so error propagated less-essentially, a high-pass filter.
HT_STEINBERG_DIFFUSION
Steinberg Error Diffusion-slightly higher pass than Stucki.
SrcOptions (SRCMODS)
Flags that indicate the state of the buffer information.
fStartOfPage (BOOL)
Set in users NewFrame function; initialized by SYSTEM.
fNewColors (BOOL)
Set if pSrcInfo Color Table changes; initialized by SYSTEM.
fModify (BOOL)
Set if pSrc may be modified for K (Black) when CMY set.
fResetErrBuf (BOOL)
Set between pages or pictures when Err Buffers must be reset.
ulResetErrBufs (ULONG)
0,1,2,or 3 for Diffusion ONLY.
ulValue (ULONG)
xxx << 20 value to be set as initialization value.
snap (SNAPIT)
SYSTEM initialized midpoint and function control parameters.
pHalftone (PRGB2)
Pointer to 4x4 8x8 or user-supplied table.
ulParm (ULONG)
User-supplied midpoint or function address.
pArray (PBYTE)
Pointer to 16x16 tables such as paintMixer or OrderSquares.
ulResolution (ULONG)
The larger of x or y resolution.
iNumColors ( INT)
Set by user (2 ** number bits per pel).
iSrcRowPels (INT)
Number of pels in a source row.
iNumDitherRows (INT)
Number of rows in a band.
pSrcInfo (PBITMAPINFO2)
Pointer to user supplied BITMAPINFO2 structure; requires that cx and cy be set.
pbSrc (PBYTE)
Printer to RGB2 input bitmap (rows are 32bit aligned).
iNumSrcRowBytes32 (INT)
iDWordBytesInCMYKBandRow.
iNumDestRowBytes (INT)
iBytesInSinglePlaneBandRow.
delta (DELTACOLOR)
From JobProperties for tessalated-squares dithering only .
lHue (LONG) 0..360
lSaturation (LONG) 0..100
lValue (LONG) 0..100
fRemoveBlackColor (BOOL)||Eliminate coincident black and color after dithering.
ulRGamma (ULONG)
3 .. 255 true gamma value; actual value times 10.
ulRBias (ULONG)
0 .. 255 gamma table minimum value.
ulGGamma (ULONG)
3 .. 255 true gamma value.
ulGBias (ULONG)
0 .. 255 gamma table minimum value.
ulBGamma (ULONG)
3 .. 255 true gamma value.
ulBBias (ULONG)
0 .. 255 gamma table minimum value.
pReply (PDITHERESULT)
Reply structure is allocated and initialized on return
Returns
Success NO_ERROR 0
Failure INVALID_PARAMETER

Example Code

Following is an example of filling the dither-request structure and using GplDitherCreateInstance from the Omni driver during start-document processing:

 pHandle->Req.iSrcRowPels = iBitsInArray;

 // Number of bytes in source, dword aligned
 pHandle->Req.iNumSrcRowBytes32 = iDWordBitsInArray/8;

 // Number of bytes in destination, byte aligned
 pHandle->Req.iNumDestRowBytes = iBitsInArray/8;

 // Fill in number of colors (derived from bitcount)
 pHandle->Req.iNumColors = Power ( 2, pddc->bmp.cBitCount );
 pHandle->Req.iNumDitherRows = 1;

 if( pddc->pdb->pResInfo->ulXRes >= pddc->pdb->pResInfo->ulYRes )
   pHandle->Req.ulResolution = pddc->pdb->pResInfo->ulXRes;
 else
   pHandle->Req.ulResolution = pddc->pdb->pResInfo->ulYRes;

 /*-------------------*/
 /* Color Technology  */
 /*-------------------*/

 pHandle->Req.ulRGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bRGamma;
 pHandle->Req.ulGGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bGGamma;
 pHandle->Req.ulBGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBGamma;
 pHandle->Req.ulRBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;
 pHandle->Req.ulGBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;
 pHandle->Req.ulBBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;

 pHandle->Req.ulRGamma = (ULONG)( (LONG)pHandle->Req.ulRGamma + pddc->pdb->pJobProperties->lRedGamma );
 pHandle->Req.ulGGamma = (ULONG)( (LONG)pHandle->Req.ulGGamma + pddc->pdb->pJobProperties->lGreenGamma );
 pHandle->Req.ulBGamma = (ULONG)( (LONG)pHandle->Req.ulBGamma + pddc->pdb->pJobProperties->lBlueGamma );
 pHandle->Req.ulRBias  = (ULONG)( (LONG)pHandle->Req.ulRBias  + pddc->pdb->pJobProperties->lRedBias );
 pHandle->Req.ulGBias  = (ULONG)( (LONG)pHandle->Req.ulGBias  + pddc->pdb->pJobProperties->lGreenBias );
 pHandle->Req.ulBBias  = (ULONG)( (LONG)pHandle->Req.ulBBias  + pddc->pdb->pJobProperties->lBlueBias );

 /*-------------------*/
 /* Color Algorithm   */
 /*-------------------*/

 switch ( pddc->pdb->pPrintMode->ulColorTech )
 {
   case COLOR_TECH_CMYK:
     pHandle->Req.bReq = CMYK_ALL;
     break;

   case COLOR_TECH_CMY:
     pHandle->Req.bReq = CMY_ALL;
     break;
 }

 /*-------------------*/
 /* Color Algorithm   */
 /*-------------------*/

 // Need gamma buffers only if printing in color (CMY/CMYK)

 if( pddc->pdb->pPrintMode->ulColorTech != COLOR_TECH_K )
 {
   // Init modify flag telling Gamma/HSV that we may replace/change
   // values in the source bitmap.  1,2,4,8 bit pel bitmaps; then
   // if C,M,Y are to be printed, the RGB index will be set to white.
   // If 16, 24 bit bitmaps, then the R,G,B value is replaced with white.
   pHandle->Req.SrcOptions.fModify  = TRUE;

   /*-------------------*/
   /* HSV               */
   /*-------------------*/

   pHandle->Req.delta.lHue        = pddc->pdb->pJobProperties->lHue;
   pHandle->Req.delta.lSaturation = pddc->pdb->pJobProperties->lSaturation;
   pHandle->Req.delta.lValue      = pddc->pdb->pJobProperties->lValue;

   /*-------------------*/
   /* Dither            */
   /*-------------------*/

   pHandle->Req.lDitherType = pddc->pdb->pJobProperties->ulAlgorithm;

   GPL_DITHER_SETUP_DEFAULTS( pHandle->Req,  pddc->pdb->pJobProperties->ulLevel )
 }

 // Set printer's physical head position in our ddc 
 pddc -> ptlPrintHead.x = 0;

 if ( ORIENTATION_PORTRAIT == pddc -> pdb -> pJobProperties -> ulOrientation ) 
     pddc -> ptlPrintHead.y = pddc -> pdb -> pFormInfo -> hcInfo.yPels - 1;
 else
     pddc -> ptlPrintHead.y = pddc -> pdb -> pFormInfo -> hcInfo.xPels - 1;

 GplDitherCreateInstance ( (PPVOID) & pHandle -> pdh,
                             pddc -> pdb -> hmcbHeap,
                           (PDITHEREQUEST) & pHandle -> Req, 
                           (PDITHERESULT ) & pHandle -> Reply);

 /*--------------------------------*/
 /* Init Dither Req struct         */
 /*--------------------------------*/

 pCMYReq->pSrcInfo = pbmi;   // Passed to choose rasterize

 LOOP {                           // Each band

   // Copy raster bits into our buffer
   pCMYReq->pbSrc = pbBitMap + iCurrentRow * pHandle->Req.iNumSrcRowBytes32;

   GplDitherRGBtoCMYK ( (PPVOID)&pHandle->pdh,
                        pCMYReq,
                        (PDITHERESULT)&pHandle->Reply );

GplDitherRGBtoCMYK

Description

This function performs the dithering.

Format

APIRET APIENTRY GplDitherRGBtoCMYK(
  PHDITHER       pdh,
  PDITHEREQUEST  pReq,
  PDITHERESULT   pReply
);
Parameters
pdh (PHDITHER)
Pointer to the dither handler that was created by #GplDitherCreateInstance.
pReq (PDITHEREQUEST)
If a diffusion dither algorithm is selected, use the macro GPL_SETUP_DITHER( req, ulLevel ) to set the SrcOptions structure.
or
If Magic Squares or Ordered Squares is selected, the following fields must be set:
lHue
lSaturation
lValue Address of the API's pre-stuffed request structure. The following fields must be set by the API:
fNewColors Must be set to TRUE at the beginning of the page. This element is set to FALSE on return.
If fNewColors equals TRUE, the following two fields must be set:
ulGamma
lBias
pSrcInfo Must have the following two fields set:
cx
cy
If BitsPerPel is less than 16 and fNewColors equals TRUE, the pbmi array, argbColor[256] must be initialized.
pbSrc Pointer to the surface bitmap.
pReply (PDITHERESULT)
Pointer to the reply structure.
Returns
Success NO_ERROR 0
Failure INVALID_PARAMETER

Example Code

Following is an example of dithering RGB to CMYK data from the Omni driver' s "render" callback function:

// Change logical height of band bitmap to 1 (this device chooses to dither each scanline)
ulSavedcy = pddc->pbmi->cy;
pddc->pbmi->cy = 1;

// For each row in the current band's bitmap (each scanline)
for( iCurrentRow=ulSavedcy; iCurrentRow > 0; iCurrentRow-- )
{
  // Set pointer to current row in our band's bitmap
  cmykReq.pbSrc = pbBits + iCurrentRow * iDWordBytesInCMYKBandRow;

  GplDitherRGBtoCMYK ( &pddc->pdh, &pddc->cmykReq, &pddc->pReply );

  // Now our consecutive KCMY buffers contain dithered data to send to printer

} /* end for */

// Restore bitmap height to TRUE value (no longer 1)
pddc->pbmi->cy = ulSavedcy;

GplDitherRGBtoRGB

Description

This function is a shell for #GplDitherRGBtoCMYK. It simply inverts the CMY pDest buffer to RGB for a printer that requires RGB.

Format

APIRET APIENTRY GplDitherRGBtoRGB( PHDITHER       pdh,
                                   PDITHEREQUEST  pReq
                                   PDITHERESULT   pReply );
Parameters
pdh (PHDITHER)
Pointer to the dither handler that was created by #GplDitherCreateInstance.
pReq (PDITHEREQUEST)
Address of the API's pre-filled request structure.
pReply (PDITHERESULT)
Address of the reply structure.
Returns
Success NO_ERROR

GplDitherDeleteInstance

Description

This function must be called at the end of a document so that memory that was allocated in #GplDitherCreateInstance can be freed.

Format

APIRET APIENTRY GplDitherDeleteInstance( PHDITHER  pdh );
Parameter
pdh(PHDITHER)
Pointer to the dither handler that was created by #GplDitherCreateInstance.
Returns
Success NO_ERROR

Example Code

Following is an example of using GplDitherDeleteInstance:

// Tell the dither function to deallocate all it's buffers
GplDitherDeleteInstance( (HDITHER)pHandle->pdh );

// Reminder:
// Free all buffers our driver allocated on behalf of GENPLIB's dithering code
// This includes error buffers for error-diffusion algorithms
// as well as the destination buffers used for rendering

Gamma Correction

Printer paper, ink, resolution, and other characteristics can cause the actual printed color to be different from the desired color. This "skew" is non-linear and occurs over the entire color spectrum. A gamma-compensation curve is used for each color to approximate the non-linear skew. Because the reflected frequencies of colors can cause them to appear different than expected, you can modify the gamma of each color. Increasing the gamma will lighten an image that might otherwise look too dark.

Following are the gamma functions:

GplGammaCreateTable

Description

This function creates an RGB (K) gamma table based on input gammas and bias .

Format

APIRET APIENTRY GplGammaCreateTable( PBYTE  pbGamma,
                                     LONG   lRgamma,
                                     LONG   lGgamma,
                                     LONG   lBgamma,
                                     LONG   lbias );
Parameters
pbGamma (PBYTE)
A byte pointer to a 768-byte buffer.
pbGamma can be used as a PRGB2 instead of a PBYTE.
lRgamma (LONG)
Red. Gamma multiplied by 10, with a range of 1 to 99.
lGgamma (LONG)
Green. Gamma multiplied by 10, with a range of 1 to 99.
lBgamma (LONG)
Blue. Gamma multiplied by 10, with a range of 1 to 99.
lbias (LONG)
Offset, normally zero, generally for printers over 600 dpi.
Returns
Success 0
Failure INVALID_PARAMETER

GplGammaColorTableToGamma2

Description

For 8-bit pels or less, replaces an RGB color value in the PBITMAPINFO2 argbColor[0..256] structure with a gamma-corrected color value.

Format

APIRET APIENTRY GplGammaColorTableToGamma2( PBYTE          pbGamma,
                                            PBITMAPINFO2   pbmi );
Parameters
pbGamma (PBYTE)
A byte pointer to a 768-byte buffer.
pbGamma can be used as a PRGB2 instead of a PBYTE.
pbmi (PBITMAPINFO2)
PBITMAPINFO2 is assumed to have a valid RGB2 color table as input. The following PBITMAPINFO2 fields are used:
cBitCount - Bits per pel.
argbColor - When cBitCount is 8 or less, this parameter must include values for 2 to the power of cBitCount (2**cBitCount).
Returns
Success 0
Failure INVALID_PARAMETER

Compression

The compression package contains routines that compress raster image data using common printer hardware-supported compression routines.

Raster printer drivers render on a per-page basis into a raster representation of an output page. As soon as rendering is completed for a given page, that raster data can be compressed into a format that the printer hardware supports. Compressed data is typically smaller than the original uncompressed data and will therefore transmit faster across communication lines to the printer.

Note: The caller of a compression routine should allocate a buffer to hold the compressed data whose size (in bytes) is as large as the original uncompressed data.

To use the GenPLib Compression package, you must include:

INCL_GENPLIB_COMPRESS

Following are the GenPLib compression routines:

GplCompressRLL

Description

This function performs run-length limited (RLL) encoding.

Format

LONG APIENTRY GplCompressRLL(
                     PBYTE  pbDataIn,    // Input raster data to compress (page/band)
                     LONG   cbDataIn,    // Length of input data buffer (pbDataIn)
                     PBYTE  pbDataOut,   // Output buffer to which compressed data is written
                     LONG   cbDataOut ); // Length count of compressed data written
Parameters
pbDataIn (PBYTE)
Pointer to input data.
cbDataIn (LONG)
Count of bytes in, pointed to by pbData.
pbDataOut (PBYTE)
Pointer to output data.
cbDataOut (LONG)
Size of buffer in bytes pointed to by pbDataOut.
Returns
Signed LONG, which is the length in bytes of the compressed data pointed to by cbDataOut.
Success
Number of bytes in compressed return string.
Failure
GPLCOMPRESS_ERROR (-1)
The output buffer length has been exceeded.

Notes

The input data is encoded with pairs of bytes. The first byte is the replacement count and the second byte is the data. A replacement count of 0 means that the data is not repeated.

GplCompressTIFF

Description

This function performs tagged image file format (TIFF) encoding, PackBits encoding algorithm. This function compresses a scanline.

Once in a repeated run, this function stops and emits a block if a different byte is encountered.

Once in a literal run, this function stops and emits a block if at least four repeated bytes follow. The next block will be a repeat block. Repeats of two or more instances of a byte b are represented by -(n-1) b. Literal runs of different bytes b1 b2 b3 ... bn are represented by (n-1) b1 b2 b3 . .. bn.

Format

LONG APIENTRY GplCompressTIFF(
                     PBYTE  pbDataIn,    // Input raster data to compress (page/band)
                     LONG   cbDataIn,    // Length of input data buffer (pbDataIn)
                     PBYTE  pbDataOut,   // Output buffer to which compressed data is written
                     LONG   cbDataOut ); // Length count of compressed data written
Parameters
pbDataIn (PBYTE)
Pointer to input data.
cbDataIn (LONG)
Count of bytes in.
pbDataOut (PBYTE)
Pointer to output data.
cbDataOut (LONG)
Size of buffer in bytes pointed to by pbDataOut.
Returns
Success Count of bytes put in destination.
Failure GPLCOMPRESS_ERROR (-1)

GplCompressDeltaRow

Description

This function performs delta-row encoding. This method replaces only the bytes in a new row that are different from the preceding (seed) row. Unreplaced bytes are replicated from the seed row. The new row then becomes the seed row after it is printed.

Format

INT APIENTRY GplCompressDeltaRow(
                      INT      usTotalBytes,   // Bytes in the scan line
                      PBYTE    pbData,         // Input raster data to compress
                      PBYTE    pbLastLine,     // Previous scanline's raster data
                      INT      usMaxReturn,    // Length of output buffer
                      PBYTE    pbReturn,       // Output buffer with compressed data
                      PUSHORT  pDeltas );      // Differences indexes-start/end pairs
Parameters
usTotalBytes (INT)
Count of bytes in, pointed to by pbData.
pbData (PBYTE)
Pointer to bytes of data to compress.
pbLastLine (PBYTE)
Pointer to previous scanline's raster data.
usMaxReturn (INT)
Size of output buffer.
pbReturn (PBYTE)
Pointer to compressed data will be written.
pDeltas (PUSHORT)
Differences indexes = array of start and end pairs.
Note: This array is returned as the result of calling #GplCompressChooseMode; therefore, you must call GplCompressChooseMode() before calling this function.
Returns
0 Current and previous line are identical.
>0 Length of data pointed to by pbReturn.
-1 GPLCOMPRESS_ERROR
usMaxReturn is not large enough for the compressed output.

GplCompressRLLDeltaRow

Description

This function performs RLL delta-row encoding. It compresses a string of bytes and returns the compressed string at pbReturn and the new string usLength as its return value. This method replaces only bytes in the current row that are different from the proceeding seed row. Unlike #GplCompressDeltaRow, however, the replacement or delta bytes themselves are encoded.

Format

INT APIENTRY GplCompressRLLDeltaRow(
                      INT      usTotalBytes,   // Bytes in the scan line
                      PBYTE    pbData,         // Input raster data to compress
                      PBYTE    pbLastLine,     // Previous scanline's raster data
                      INT      usMaxReturn,    // Length of output buffer
                      PBYTE    pbReturn,       // Output buffer with compressed data
                      PUSHORT  pDeltas );      // Differences indexes-start/end pairs
Parameters
usTotalBytes (INT)
Count of bytes pointed to by pbData and pbLastLine.
pbData (PBYTE)
Pointer to bytes of data to compress.
pbLastLine (PBYTE)
Pointer to previous uncompressed data.
usMaxReturn (INT)
Size of output buffer.
pbReturn (PBYTE)
Pointer to compressed data will be written.
pDeltas (PUSHORT)
Differences indexes = array of start and end pairs.
Returns
0 Current and previous line are identical.
>0 Length of data pointed to by pbReturn.
-1 GPLCOMPRESS_ERROR
usMaxOutput not large enough for the compressed output.

GplCompressChooseMode

Description

This function uses a semi-intelligent algorithm to select the compression mode that will most likely give the best compression for the row of data.

RLL Delta Row is currently chosen if this method best and if the printer supports this compression. Delta Row is chosen if most of the bytes are the same as the corresponding bytes in the previous row. TIFF is chosen if most of the bytes are repeated within this row. RLL is chosen if most of the bytes are repeated within this row and the printer does not support TIFF PackBits. No compression (value of 0) is chosen of most bytes are neither repeats within the row nor duplicates of the previous row.

Note: The function GplCompressChooseMode() must be run before either of the delta-compression functions #GplCompressDeltaRow() or #GplCompressRLLDeltaRow(). This function must be run first because pDelta is filled in by GplCompressChooseMode() while it is reading pbRow and pbLast_ Row. Performance is increased by combining GplCompressChooseMode() with either #GplCompressDeltaRow() or #GplCompressRLLDeltaRow().

Format

INT APIENTRY GplCompressChooseMode( PBYTE    pbRow,
                                    PBYTE    pbLast_Row,
                                    INT      usRow_Length,
                                    ULONG    CompressModes,
                                    PUSHORT  pDelta );
Parameters
pbRow (PBYTE)
Pointer to the current row's data.
pbLast_Row (PBYTE)
Pointer to the last row's data.
usRow_Length (INT)
The number of bytes in the row. Note that both the previous row and the current row are equal in size.
CompressModes (ULONG)
Compression modes supported.
This is a ULONG with the follow bit positions used to represent compress modes that this printer supports:
GPLCOMPRESS_NONE
GPLCOMPRESS_RLL
GPLCOMPRESS_TIFF
GPLCOMPRESS_DELTAROW
GPLCOMPRESS_RLLDELTAROW
These values may be logically ORed together to create a bitfield of all compression methods to consider. For example,
CompressModes = GPLCOMPRESS_RLL | GPLCOMPRESS_TIFF
This tells the routine to only choose between the RLL and TIFF compression routines and GPLCOMPRESS_NONE.
pDelta (PUSHORT) Array of unsigned shorts to hold the offsets from the beginning of pbRow that differ from pbLast_Row. This data will be used by #GplCompressDeltaRow and #GplCompressRLLDeltaRow. Note that pDelta must have a length equal to two times usRow_Length plus one to hold the pDelta output . There is no check on the size of pDelta. You must supply a size of a minimum of two times usRow_Length plus one. This is because the maximum number of differences occur when every other byte differs from the preceding row.
Returns
The mode that will probably compress the best:
0 No compression
1 RLL (run-length limited) compression
2 TIFF (tagged image file format) compression
3 Delta-row compression
9 RLL delta-row compression

Notes

For #GplCompressDeltaRow and #GplCompressRLLDeltaRow, pDelta is filled with the start and end of the difference fragments. Note that a difference fragment is defined as a contiguous group of bytes in the current row that differ from those in the previous row. For example:

Column Number 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Current Row Byte Value 7 8 1 0 4 5 6 7 23 128 257 1 7 8
Previous Row Byte Value 7 1 3 1 4 5 9 7 23 128 7 9 7 8

There are three difference fragments:

  • Fragment one begins at column 2 and ends at column 4.
  • Fragment two begins at column 7 and ends at column 7.
  • Fragment three begins at column 11 and ends at column 12.

The output in pDelta will be 2 4 7 7 11 12 0. Note that these are unsigned shorts and the difference array is null-terminated. If no difference fragments are found (the previous and current buffers are the same) the first unsigned short in pDelta will be 0.

pDelta above will point to a array of the indexes stored to used by GplCompressDeltaRow() and GplCompressRLLDeltaRow().

Example Code

Following is an example of GplCompressChooseMode usage from the Omni driver for a typical inkjet device:

iNewMode = GplCompressChooseMode(
                 pbBuffer,             // Contains current row of data
                 pbLastLine,           // Contains previous row of data
                 iBytesInRow,          // Number of bytes in output row (pbBuffer)
                 GPLCOMPRESS_RLL |     // All compression modes supported
                 GPLCOMPRESS_TIFF,
                 pDelta );             // Offsets into pbBuffer ((2 x pbBuffer) + 1)

  if( iNewMode != iCurrentMode )
  {
    // Send command to printer to set new mode
    iCurrentMode = iNewMode;

  } /* end if */

  switch( iNewMode )
  {
     case 0:                           // No Compression
     break;
     case 1: iCompressed = GplCompressRLL();
     break;
     case 2: iCompressed = GplCompressTIFF();
     break;
     case 3: iCompressed = GplCompressDeltaRow();
     break;
     case 9: iCompressed = GplCompressRLLDeltaRow();
     break;

  } /* end switch */

  // Write raster data transfer command and compressed data

GplCompressAscii85

Description

This function performs ASCII encoding. It encodes 4 bytes in to 5 bytes out , and it converts data from a base 256 on input to a base-85 representation on output. The output is then output in ASCII with base 85.

Buffer allocation is the responsibility of the driver.

Format

INT APIENTRY GplCompressAscii85(
                     PBYTE  pSource,         // Source input data buffer (normal Hex 256)
                     PBYTE  pDest,           // Destination output data buffer (ASCII 85)
                     INT    usSourceLen );   // Source buffer input length
Parameters
pSource (PBYTE)
Pointer to input data buffer.
pDest (PBYTE)
Pointer to output data buffer. It must be at least 5/4 size of pSource.
usSourceLen (LONG)
Length of source buffer in bytes.
Returns
Success: Count of bytes put in destination.
Failure: GPLCOMPRESS_ERROR (-1)

GplFaxG3EncodeBlock

Description

This function performs G3 "Fax"-type compression.

It provides a run-length type compression with table-lookup replacement encodings for certain bit sequences. This type of compression is also know as "Modified Huffman." Format

GplFaxG3EncodeBlock( HTHREAD hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Parameters
hThread (HTHREAD)
Handle.
pbOutput (PBYTE)
Pointer to output buffer to fill.
ulOutputSize (ULONG)
Number of output bytes; must be divisible by 4.
pulBitposition (PULONG)
Pointer to bit position in output buffer; gets updated and must be stored off the pddc.
pbInput (PBYTE)
Input pointer to upper left corner of raster image to compress.
ulBitwide (ULONG)
Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
ulBitheight (ULONG)
Height of raster image to compress in bits.
pfnFlushBuffer (PFN)
The prototype for this function must be:

void pfnFlushBuffer ( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as G4.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success: Count of data written out (compressed data count).
Failure: GPLCOMPRESS_ERROR (-1)
Related Functions
The following G3 "Fax"-compression related functions provide help in sending down special G3 commands.
GplFaxG3StartBlock()
Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of the first page. This function outputs the EOL to pbOutput and initializes pulBitposition to 12. It simply sends out the EOL bit sequence to signal the start of raster data (for example, '000000000001' binary, eleven 0's and a single 1).
Inputs
pbOutput Output buffer to fill
pulBitposition Bit-position count in the output buffer
Returns Void
GplFaxG3EndBlock()
End of block flushed.

GplFaxG3EndBlock( PVOID hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition
PFN pfnFlushBuffer );

Inputs
hThread PVOID.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as above.
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1).
GplFaxG3EndDoc()
Modified Huffman (MH) Group 3 return to control (RTC) sent at the end of document.

GplFaxG3EndDoc( PVOID hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition
PFN pfnFlushBuffer );

Inputs
hThread PVOID.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; gets updated and must be stored off the pddc.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as above.
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1).
GplFaxG3NewFrame()
Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of each new page. This function is the same as GplFaxG3EncodeBlock() except that it flushes data at this point to cause the current page to be completely sent. This call sends compressed data followed by the EOL bit sequence (see GplFaxG3StartBlock).
Instead of GplFaxG3NewFrame() you can use pfnFlushBuffer() = NULL to see whether or not the buffer for GplFaxG3EncodeBlock() is large enough. You output the bytes after GplFaxG3EncodeBlock() returns OK. You can then call GplFaxG3StartBlock(). This function places an EOL in the output buffer and sets the pulBitposition to 12.

GplFaxG3NewFrame( HTHREAD hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition
PFN pfnFlushBuffer );

Inputs
hThread HTHREAD
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits.
pulBitposition Bit-position count in the output buffer.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as above.
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1)

GplFaxG4EncodeBlock

Description

This function performs G4 "Fax"-type compression.

It is similar to #GplFaxG3EncodeBlock(), but it has five extra parameters. These extra parameters are used to keep track of on/off bit runs between calls.

The driver is again responsible for all memory allocations.

Make sure all buffers for on/off bit tracking are equal to scanline DWORD aligned.

Format

GplFaxG4EncodeBlock( PVOID pddc,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer,
PULONG pulwstartc,
PULONG pulbstartc,
PULONG pulwstartp,
PULONG pulbstartp,
ULONG ulSkipCount );

Parameters
pddc (PVOID)
First parameter to pfnFlushBuffer; generally pddc or hThread.
pbOutput (PBYTE)
Pointer to output buffer to fill.
ulOutputSize (ULONG)
Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition (PULONG)
Pointer to position in the output buffer; gets updated and must be unique to each print job; *pulBitposition must be zero on the first invocation of this function for a block.
pbInput (PBYTE)
Input pointer to upper left corner of raster image to compress.
ulBitwide (ULONG)
Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
ulBitheight (ULONG)
Height of raster image to compress in bits.
pfnFlushBuffer (PFN)
The prototype for this function must be:

void pfnFlushBuffer ( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as G3; necessary parameter for separating printout; generally pddc or hThread.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
pfnFlushBuffer can be either NULL or point to a function that you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
If pfnFlushBuffer() = NULL this function will compress the data and place it into pbOutput. If the output buffer size is not large enough for all the compressed data, the function will return with -1 = error. If pfnFlushBuffer() is not NULL, every time pbOutput is full, pfnFlushBuffer( ) is called to write the compressed bytes out. Note that the return value will the total number of bytes written even though the buffer was filled multiple times.
pulwstartc (PULONG)
Pointer to array of ULONGs. Its length must be ( ulBitwide + 31 ) / 32 + 2. The 2 extra ULONGs are needed. So for an image of 371 bits width, the length of this array is ( 371 + 31 ) / 32 = 12 plus 2 more-which is 14 unsigned longs. This array is used to store the start bits of the beginning of consecutive 0 bits for the current scan line.
pulbstartc (PULONG)
Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the current scan line.
pulwstartp (PULONG)
Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 0 bits for the previous scan line. This must be zero filled on the first invocation of this function.
pulbstartp (PULONG)
Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the previous scan line. This must be zero filled on the first invocation of this function.
ulSkipCount (ULONG)
Number of bytes to skip to the next row of input data to compress.
Returns
Success: Count of data written out (compressed data count).
Failure: GPLCOMPRESS_ERROR (-1)
Related Function
The following is a G4-compression related function automatically sends the "End Block" data sequence (24 bits represented by Hex 001001).
GplFaxG4EndBlock()
Modified Read (MRR) Group 4 compression with K = infinity for sending end-of-facsimile block (EOFB). This function has the same first eight parameters as GplFaxG4EncodeBlock().

GplFaxG4EndBlock( PVOID pHandle,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Inputs
pddc PVOID; used as first parameter to the pfnFlushBuffer function; generally the pddc or HTHREAD.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; gets updated and must be unique to each print job.
ulBitwide Width of the raster image in bits.
ulBitheight Height of the raster image in bits.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Necessary parameter for separating printout; generally pddc or hThread.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
pfnFlushBuffer can be either NULL or point to the function you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when the output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1)

GplFaxTIFF2EncodeBlock

Description

TIFF algorithm 2 compression. This function essentially provides a " Modified Huffman" G3 compression with no size limits. It is limited only by the byte representation of ULONG (4 Gigabytes).

Format

GplFaxTIFF2EncodeBlock( PVOID pHandle,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Parameters
pHandle (PVOID)
Pointer to a handle.
pbOutput (PBYTE)
Pointer to output buffer to fill.
ulOutputSize (ULONG)
Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition (PULONG)
Pointer to position in output buffer.
pbInput (PBYTE)
Input pointer to upper left corner of raster image to compress.
ulBitwide (ULONG)
Width of raster image to compress in bits.
ulBitheight (ULONG)
Height of raster image to compress in bits.
pfnFlushBuffer (PFN)
Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Necessary parameter for separating printout; generally pddc or hThread.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success:Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1)
Related Functions
The following are TIFF2-compression related functions.
GplFaxTIFF2StartBlock()
TIFF algorithm 2 compression start-of-document function.
Inputs
pulBitposition Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc; function just sets pulBitposition to 0 (you can do this yourself).
Returns Void
GplFaxTIFF2EndBlock()
TIFF algorithm 2 compression to flush output buffer.

GplFaxTIFF2EndBlock( PVOID pHandle,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Inputs
hThread HTHREAD.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; must be a multiple of 32 bits; gets updated and must be stored off the pddc.
pbInput Input pointer to upper left corner of raster image to compress.
ulBitwide Width of the raster image in bits.
ulBitheight Height of the raster image in bits.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

int pfnFlushBuffer( HTHREAD,
PBYTE,
ULONG );

HTHREAD Gotten from GplThreadCreateInstance().
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
TRUE If OK
FALSE If error

Exception Management

This package contains functions for setting errors with WinSetErrorInfo and logging appropriate warnings, exceptions, and error codes.

Four ascending severity levels are defined for error messages:

Warning The function detected a problem, took corrective action, and was able to complete processing successfully.
Error The function detected a problem for which no sensible corrective action is possible. The function is not executed and the system remains at the same state as when the function was requested.
Severe Error The function detected a problem from which the system cannot reestablish its state. The function has partially executed and the application must now make some corrective action to restore the system to a known state.
Unrecoverable Error The function detected an error from which it is impossible for the system to reestablish the state that it held at the time that the function was called. It is also impossible for the application to restore the system to a known state.

To use the GenPLib Exception-Management package, you must include:

INCL_GENPLIB_ERROR

The following functions are included in the package:

GplErrSetDosError

Description

This function saves DOS error.

Format
ERRORID APIENTRY GplErrSetDosError( USHORT usDosError );
Parameter
usDosError (USHORT)
Any error code defined in BSEERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure: None

GplErrSetError

Description

This function saves an error.

Format
ERRORID APIENTRY GplErrSetError( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure None

GplErrSetSevereError

Description

This function saves a severe error.

Format
ERRORID APIENTRY GplErrSetSevereError( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure: None

GplErrSetWarning

Description

This function saves the error warning.

Format
ERRORID APIENTRY GplErrSetWarning( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success:ERRORID
Failure: None

GplErrSetUnrecoverableError

Description

This function saves an unrecoverable error.

Format
ERRORID APIENTRY GplErrSetUnrecoverableError( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure: None

Memory Management

This package manages global and process (shared and non-shared) memory areas (heaps) using a single handle to a memory control block (HMCB) for all allocations. Therefore, you do not need to manage different heap or memory handles within your code.

This package calls appropriate OS/2 system APIs for memory creation and allocation. Dosxxx, SSxxx, and other API decisions are optimized for current OS/2 version performance. Examples include the underlying file system paging architecture and the number of processes in system.

Memory areas (heaps) are automatically grown when the initial memory size runs out. Committing more memory than needed is not required. Your code can use a single HMCB and underlying code will manage multiple memory areas ( heaps) if necessary.

Sets of debug trace information are displayed whenever memory is overwritten by other processes or writes to memory exceed the allocation size. Memory that has been allocated and not freed (memory leaks) is also shown as debug information.

To use the GenPLib Memory-Management package, you must include:

INCL_GENPLIB_MEMORY

The following APIs comprise the GenPLib Memory-Management package:

Call Flow

The GplMemoryCreateInstance() function should be the first memory API called in order to get a handle to a memory control block (HMCB). The HMCB handles all memory allocations. Using an HMCB, any number of memory allocations can be made using GplMemoryAlloc(). These allocations should be freed using GplMemoryFree.

GplMemoryDeleteInstance() is used when the HMCB is no longer needed for memory allocations.

Note: Any memory that was allocated using GplMemoryAlloc() using an HMCB being deleted by GplMemoryDeleteInstance and not freed by a corresponding GplMemoryFree() call will be shown as debug output (with location of function where allocation was done) for developers to correct memory leaks within their code.

GplMemoryCreateInstance

Description

This function creates an MCB and a user heap.

Format

HMCB APIENTRY GplMemoryCreateInstance( ULONG ulPrimarySize,
ULONG ulExtSize,
ULONG ulThreshold,
ULONG ulType );

Parameters
ulPrimarySize (ULONG)
Initial Heap size in bytes.
ulExtSize (ULONG)
Heap growth size fills in bytes.
ulThreshold (ULONG)
Threshold for heap allocation. Requests larger than this will be converted to DosAllocMem.
ulType (ULONG)
Heap type (process, shared). See the following notes for a description of process and shared heaps.
Returns
Success: Valid handle to memory control block.
Failure: NULL.
WinGetLastError() will return one of the following:
PMERR_MEMORY_ALLOCATION_ERROR
PMERR_INV_OR_INCOMPAT_OPTIONS
Notes

Treat creating a memory instance like creating a memory heap. The ulSize parameter should be a reasonable amount of memory for most of your processing. It is not necessary to set ulSize to a high number so that you do not run out of memory, because the memory area will automatically grow if your processing allocates more than the amount originally set by ulSize.

When the memory area is automatically grown, the ulExtSize value is used to determine how large an additional memory area should be created. A default value of 0 (zero) may be used, and the growth size will be tailored to a reasonable amount for the OS/2 version.

The #GplMemoryAlloc() API will allocate memory blocks differently based on the ulThreshold value. Typically, allocations of memory of sizes less than the threshold are allocated in a fast access memory area, and sizes larger than the threshold are allocated from a different memory pool. A default value of 0 (zero) may be used and the threshold will be automatically set to perform most efficiently on the current OS/2 version.

The ulType parameter allows you to specify the type of memory that you want to allocate. This defines the scope of accessibility to the memory within your driver or program and is one of the following:

SHARED_MEMORY
Used to create a global memory area (heap). For example, it is used for allocating global data like string tables in a printer driver.
PROCESS_MEMORY
Used to create a per process memory area (heap). For example , it is used for allocating a per device context (DC) memory in a printer driver.

GplMemoryDeleteInstance

Description

This function frees memory heap(s) for the user.

Format
APIRET APIENTRY GplMemoryDeleteInstance( HMCB  hMCB );
Parameter:hMCB (HMCB)
Pointer to memory allocated.
Returns
Success: NO_ERROR. Returns valid handle to memory control block.
Failure: ERROR_INVALID_PARA
or return values from:
SSFreeMem
DosFreeMem
DosSubUnsetMem
WinGetLastError() returns one of the following:
PM_INV_OR_INCOMPAT_OPTIONS
PMERR_MEMORY_DEALLOCATION_ERR

GplMemoryAlloc

Description

This function allocates memory for the user.

Format
PVOID APIENTRY GplMemoryAlloc( HMCB   hMCB,
                               ULONG  ulSize );
Parameters
hMCB (HMCB)
Handle to memory control block.
ulSize (ULONG)
Amount to allocate.
Returns
Success: Valid pointer to memory allocated.
Failure: NULL.
WinGetLastError() returns one of the following:
PMERR_INV_OR_INCOMPAT_OPTIONS
PMERR_INSUFFICIENT_MEMORY

GplMemoryFree

Description

This function frees memory for a user.

Format
APIRET APIENTRY GplMemoryFree( PVOID  pData );
Parameter
pData (PVOID)
Pointer to memory allocated.
Returns
Success: NO_ERROR.
Failure: ERROR_INVALID_PARA
or return values from:
SSFreeMem
DosFreeMem
DosSubFreeMem
WinGetLastError() returns one of the following:
PM_INV_OR_INCOMPAT_OPTIONS
PMERR_MEMORY_DEALLOCATION_ERR

GplMemoryGetObjectSize

Description

This function returns the size of a memory object allocated by #GplMemoryAlloc.

Format
LONG APIENTRY GplMemoryGetObjectSize( PVOID  pData );
Parameter
pData (PVOID)
Pointer to object.
Returns
Success :Size of memory object.
(LONG) lsize
Failure: 0, no size found for memory object. WinGetLastError will return PMERR_INVALID_PARAMETERS (meaning pData was invalid or NULL).

GplMemorySetUserID

Description

This function is optional, and it is helpful in debugging. You attach a pointer to the heap instance (like your pddc or pdb)-this extra pointer can then be viewed under the kernel debugger.

Format
VOID APIENTRY GplMemorySetUserID( HMCB   hMCB,
                                  ULONG  ulUserID );
Parameters
hMCB (HMCB)
Handle to memory control block.
ulUserID (ULONG)
Pointer to something helpful.

Implementation

The GenPLib Memory-Management package was created to provide consistency and to simplify the growth and management of heaps.

This package was one of the first developed for driver use. When writing a driver "from scratch" or modifying a driver to use GenPLib, begin with this package because many of the other GenPLib packages require memory allocated in the unique format of the memory-management package.

Previously, all drivers had to manage their own memory heaps, often using memory allocation packages exported from other parts of the OS/2 system-for example, the INCL_WINHEAP functions from PM WIN or the Subsystem Allocation routines in the PM Graphics Rendering Engine (GRE). It was never clear what routines should be used in a presentation driver or where they should be used (especially for portability). Many of these packages did not allow the caller to grow their heaps, provided no controls for memory leakage, and had no validation or recovery when a driver terminated unexpectedly.

A typical raster printer driver creates two types of heaps: a shared heap during Dynamic Link Library (DLL) initialization processing, and a per-DC heap when the PM GRE enables the driver during the FILL_PHYSICAL (02 Hex) subfunction. These heaps are freed during corresponding DLL termination and DISABLE_PHYSICAL calls to the driver.

In addition, a per-process heap is also created for the OS2_PM_DRV_DEVMODE entry point in a printer presentation driver. This entry point causes the driver to display its printer and job properties dialogs to the user or to retrieve printer and job property information for a specific output device/ destination.

A typical driver initially allocates a 256KB shared global heap and a 100KB per-DC heap and elects to use the memory package's default heap growth size and threshold for larger allocations (achieved by passing in 0L for parameters 2 and 3 for #GplMemoryDeleteInstance).

In case of unexpected driver termination, a driver should register a per- process exception handler for each of the major presentation driver entry points. This exception handler can contain cleanup code that frees any per- process heaps created using #GplMemoryCreateInstance in case any code problems cause an exception to occur.

One major benefit of the GenPLib memory code is the ability to detect memory leaks (memory a driver allocates but never frees). Whenever a call to #GplMemoryDeleteInstanceis made, all memory allocated from the heap is freed so that no leaks can occur. If the debug version of the GenPLib library is used, while #GplMemoryDeleteInstance is cleaning up memory areas that a developer forgot to free, it will inform the developer on a debugging terminal which line of code allocated the memory that was not freed. This is possible because the memory-management package records the function address (and other information) from where #GplMemoryAlloc is called.

An OS/2 presentation driver must consider the memory constraints of the user and other programs in the system. In the past, drivers allocated large heaps, hoping the memory an application needed would never overrun the amount initially allocated and cause an exception. Under the paging architecture of OS/2 Warp, however, all the heap memory is often committed during heap creation.

All this memory could be forced in and never used by an application (for example, an application queries the driver for the devices it supports and performs no output). In this case, the driver's memory pages would not be touched, so OS/2 would slowly page it out and give it back to the applications that had it in the first place. As a result, a good deal of swapping could occur for active addressable memory, especially on low-RAM systems. The alternative was to force the driver to manage its own heap growth and consolidate multiple heaps.

Using the GenPLib Memory-Management package, the heap is identified to the driver as a generic handle to a memory control block (HMCB), not as an address into memory. This handle is supplied by #GplMemoryCreateInstance and is used to identify the heap for all subsequent memory allocations and frees.

Therefore, a reasonable initial heap can be created that is just large enough to allow the driver to enable and respond to basic queries. As applications use the driver to render graphics (or other memory-intensive operations), the memory package detects when a given #GplMemoryAlloc call will overflow the current heap and grows it by a default or specified size. The caller does not ever need to know that the heap has grown because it continues to use the same HMCB, while the memory package chains these heaps together.

Example Code

Creating and Allocating From a Per-DC Heap

Following is an example of creating and allocating from a per-DC heap during a printer-driver enable via the OS2_PM_DRV_ENABLE entry point:

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )             // Determine which enable/disable subfunction
  {
    case FILL_PHYSICAL:           // Enable subfunction
    {
       // Create a per-DC memory area (heap)
       hmcbHeap = GplMemoryCreateInstance(
                           LEN_DCHEAP,            // Size of DC Heap (80K)
                           0L,                    // Default extent
                           0L,                    // Default threshold
                           PROCESS_MEMORY );      // Type of heap (define in GPLMEM.H)

       // If GplMemoryCreateInstance succeeded
       if(hmcbHeap != NULL);
       {
         // Allocate PDEVICEBLOCK
         pddc->pdb = ( PDEVICEBLOCK ) GplMemoryAlloc( hmcbHeap, sizeof( DEVICEBLOCK ) );

       } /* end if */
     } /* end case */
     break;
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */

Note: System memory must be used when passing data buffers to PrtWrite() calls.

Creating a Global Heap

Following is an example of creating a global heap:

ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )        // Flag to determine which load/unload module subfunction
  {
    case 0:               // Load module
    {
      // Create a global heap
      globals.pvHeap = GplMemoryCreateInstance(
                                 LEN_SHAREDHEAP,   // Initial size (256K)
                                 0,                // Default extent
                                 0,                // Default threshold
                                 SHARED_MEMORY);   // Type of heap (defined in GPLMEM.H)
    } /* end case */
    break;
  } /* end switch */
} /* end _DLL_InitTerm */
Allocating From a Global Heap

Following is an example of allocating from a global heap:

switch( ulSubFunc )                       // Determine which enable/disable subfunction
{
  case FILL_LOGICAL:                      // Enable subfunction
  {
    // For every string in our driver's string resource table
    for( i=0; i < STRING_TABLE_SIZE; i++ )
    {
      // Load each string from resource table (returns length)
      sLen = WinLoadString (szTemp);

      // Allocate memory to hold string just loaded from global heap
      globals.pbStringTable[i] = GplMemoryAlloc( globals.pvSharedHeap, sLen+1 );

      // Copy string from temporary buffer to our driver's global string table
      strcpy( globals.pbStringTable[i], szTemp );

     } /* end for */
     break;
   } /* end case */
} /* end switch */
Deleting a Per-DC Heap

Following is an example of deleting a per-DC heap:

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )
  {
    case DISABLE_PHYSICAL:
    {
      // Free per-DC heap after freeing per-DC memory objects
      GplMemoryDeleteInstance( hmcbHeap );

    } /* end case */
    break;
   } /* end switch */
} /* end OS2_PM_DRV_ENABLE */
Deleting a Global Heap

Following is an example of deleting a global heap:

ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )
  {
    case 1:
    {
      // Free global heap after freeing global memory objects
      GplMemoryDeleteInstance( globals.pvSharedHeap );

    } /* end case */
    break;
  } /* end switch */
} /* end _DLL_InitTerm */

Output-Thread Management

The output-thread management module provides a way to create multiple output threads that will manage the sending of data buffers to the printer. This functionality has also been called second-thread code; however, the implementation allows a separate thread per physical output address (for example, LPT, COM, and file).

This package seamlessly manages multiple buffer allocations and chains them together. It adjusts the internal data buffering to always try to keep the printer busy processing data. The functions assure that buffer sizes and their reuse align for the best performance with the system-memory memory model (for example, the OS/2 paging architecture).

Note: You may override the default buffer size and the maximum number of buffers to create.

Other benefits of using the separate output-thread code in your driver include:

  • Ability to pass off data buffers as fast as you can fill them without waiting for a device with slower transfer speeds
  • Unknowingly, the OS/2 PM graphics engine (and other system components) might have locked down system resources, such as fonts, while calling your driver. Therefore, implementing the thread code allows you to return to the graphics engine and free up the resource for other parts of the system, such as the display. This makes the system and applications more responsive to the user.
  • The package uses the GenPLib Exception-Management package for simple debug tracing.
  • The package uses the GenPLib Memory-Management package for allocation of instance data.

To use the GenPLib Output-Thread Management package, you must include:

INCL_GENPLIB_THREAD

Following are the Output-Thread Management APIs:

Call Flow

The thread instance (data and semaphores) is initialized; but a real thread is not spun off until you are certain that the application is really sending data to be printed. Therefore, you can systematically use GplThreadCreateInstance() at enable and GplThreadStart() at the first data write. This keeps the code straightforward.

GplThreadCreateInstance

Description

This function is used to allocate and initialize thread instance data. This function must be called before making any other thread calls in order to get a thread instance handle (HTHREAD). Semaphores and buffers are not created or allocated by this call.

Format

BOOL APIENTRY GplThreadCreateInstance(
  HMCB     hMcb,
  HMODULE  hMod,
  HTHREAD *phThread,
  PVOID    pvUserData );

Parameters
hMcb (HMCB)
Handle to the memory control block (See #Memory Management).
hMod (HMODULE)
Handle of calling module. Used for exception management and error logging.
*phThread (HTHREAD)
-output Thread instance handle.
pvUserData (PVOID)
-output This is used in debugging and is optional. It is recommended that you place a helpful pointer, such as your pddc or pdb pointer.
Returns
Success: TRUE
Failure: FALSE

GplThreadStart

Description

This function starts or updates thread parameters. It creates semaphores and buffers, committing system resources.

Format

BOOL APIENTRY GplThreadStart( HTHREAD hThread,
LHANDLE hSpooler,
HFILE   hDev,
ULONG   ulBufferSize,
USHORT  usMaxBuffers,
UCHAR   ucAbortChar,
ULONG   ulAbortCharCount,
ULONG   ulAbortBufferSize,
PBYTE   pbAbortBuffer,
PSZ     pszAbortString,
PSZ     pszLogAddress,
PSZ     pszWriteError,
PSZ     pszDeviceDescription,
LONG    lDCType );

Parameters
hThread (HTHREAD)
Instance handle of thread to start. Returned by #GplThreadCreateInstance.
hSpooler (LHANDLE)
Handle to Spooler (OD_QUEUED). Returned from SplQmOpen. May be zero (0) if hDev is supplied.
hDev (HFILE)
Handle to Device (OD_DIRECT). Returned from PrtOpen. May be zero (0) if hSpooler is supplied.
ulBufferSize (ULONG)
Size of buffer allocations. May be 0 to use default size.
usMaxBuffers (USHORT)
Maximum number of buffers to create. May be 0 to use default # of buffers.
ucAbortChar (UCHAR)
Character to send before the abort string. ucAbortChar can be used for devices that need to be sent a number of bytes before the abort string can be sent. ulAbortChar is sent first during the abort sequence.
ulAbortCharCount (ULONG)
Number of ucAbortChars to send to the device.
ulAbortBufferSize (ULONG)
Abort buffer size in bytes.
pbAbortBuffer (PBYTE)
Pointer to buffer to send when aborting a job. Buffer must not be deleted until after #GplThreadEnd. ulAbortBuffer is sent after ulAbortChar.
pszAbortString (PSZ)
Abort job reset command for your specific printer language. May be NULL and no string will be sent. pszAbortString is sent last in the abort sequence.
pszLogAddress (PSZ)
File or port name (for example, LPT1). Used for error reporting. May be NULL.
pszWriteError (PSZ)
Error prompt string to display in a SplMsgBox (for example, "Unable to write to printer"). May be NULL.
pszDeviceDescription (PSZ)
Device description to display in a SplMsgBox ( for example, "Laserprinter"). May be NULL.
lDCType (LONG)
DC type (OD_QUEUED, OD_DIRECT).
Returns
Success: TRUE
Failure: FALSE
Notes
ulAbortChar is sent first during the abort sequence.
ulAbortBuffer is sent after ulAbortChar.
pszAbortString is sent last in the abort sequence.

GplThreadOutput

Description

This function is used to pass data to a thread instance (referenced by the thread instance handle passed in) for output. #GplThreadStart must be called prior to calling GplThreadOutput.

Format

BOOL APIENTRY GplThreadOutput(
  HTHREAD hThread,
  PBYTE pBytes,
  ULONG ulCount,
  ULONG fulDataType);

Parameters
hThread (HTHREAD)
Thread instance handle.
pBytes (PBYTE)
Pointer to data to buffer.
ulCount (ULONG)
Size of data (byte size) pointed to by pBytes parameter
fulDataType (ULONG)
Data type flag.
THREAD_DT_PRINTERLANGUAGE
Data is printer language commands and only requires an abort string to be sent when aborting during this buffer. See pszAbortString in #GplThreadStart.
THREAD_DT_BINARY
Data is binary (raster) data and requires a series of bytes to be sent to ensure the printer is not in binary transfer mode before the abort string can be sent. See ucAbortChar in GplThreadStart.
Returns
Success: TRUE
Failure: FALSE

GplThreadEnd

Description

This function frees up any resources allocated by the thread instance (semaphores and data buffers). A call to this function implies that any remaining data buffers are flushed before the return of this call. This call is the logical pair to GplThreadCreate, which allocates system resources.

Format
BOOL APIENTRY GplThreadEnd( HTHREAD  hThread );
Parameter
hThread (HTHREAD)
Handle to thread instance.
Returns
Success: TRUE
Failure: FALSE

GplThreadDeleteInstance

Description

This function is used to free thread instance data allocated by a corresponding call to #GplThreadCreateInstance().

Format
BOOL APIENTRY GplThreadDeleteInstance( HTHREAD *phThread );
Parameter
phThread (HTHREAD)
Pointer to the thread instance handle to be freed.
Returns
Success: TRUE
Failure: FALSE

GplThreadAbortDoc

Description

This function is called to abort output from the thread that is referenced by the thread instance handle that was passed in. After output is stopped, the function will attempt to restore the printer to a known state where it can process data from another print job.

The first issue to consider when using this function is that the device may be executing a command that has a large amount of data that it is expecting to receive as a block. Therefore the parameter ulAbortChar passed in on #GplThreadStart is sent ulAbortCharCount times.

The second issue to consider is that the abort string 'pszAbortString' provided in #GplThreadStart() is sent last.

Format
BOOL APIENTRY GplThreadAbortDoc( HTHREAD  hThread );
Parameter
hThread (HTHREAD)
Handle to thread instance.
Returns
Success: TRUE
Failure: FALSE

GplThreadResetAbortDoc

Description

This function is used to resolve any previous calls to #GplThreadAbortDoc() so that another job can start printing using the same thread instance handle.

This function will wait, if necessary, for the abort processing started by #GplThreadAbortDoc to be completed before returning so that the thread instance can be put into a state to receive data from a new print job.

Format
BOOL APIENTRY GplThreadResetAbortDoc( HTHREAD  hThread );
Parameter
hThread (HTHREAD)
Handle to thread instance.
Returns
Success: TRUE
Failure: FALSE

GplThreadFlushBuffer

Description

This function causes all data buffers currently being managed by the thread instance to be flushed (written) to their final output destination (for example, parallel port and spool file).

This call allows a flag to be set so that all the data buffers are sent (flushed) before this call returns (fwait == TRUE) or synchronously (fwait = = FALSE).

Format
BOOL APIENTRY GplThreadFlushBuffer(
  HTHREAD  hThread,
  BOOL     fWait );
Parameters
hThread (HTHREAD)
Handle to thread instance.
fWait (BOOL)
Flag indicating flush asynchronously or synchronously.
Returns
Success: TRUE
Failure: FALSE

GplThreadHookDeviceWrites

Description

This is an optional function. This call was created because a class of devices added to the Omni driver did not set status lines according to known standard protocols. Calling this function allows the Omni driver to interpret the status lines because it knows about the devices' special protocols.

Format

BOOL APIENTRY GplThreadHookDeviceWrites(
  HTHREAD hThread,
  PFN PfnPrtWrite,
  pddc);

Parameters
hThread (HTHREAD)
Handle to thread instance.
pfnPrtWrite (PFN)
Our function.
pddc
Parameter to our function.
Returns
Success: TRUE
Failure: FALSE

Implementation

After the memory-management package is implemented, the next logical package that can be added to a driver is the GenPLib Output-Thread Management package.

The best place to implement threads in a driver is when sending and buffering data to what is more than likely a slower output device (or file) . This allows the driver to return back to the application and PM GRE for rendering the next page immediately to improve system response.

The benefits of adding multiple threads are proven in tests performed on OS/2 printer drivers that have added the GenPLib thread-management package. Results have shown a 7%-12% performance increase.

A printer driver retrieves a thread handle (HTHREAD) from GenPLib by calling #GplThreadCreateInstance during the FILL_PHYSICAL enable subfunction . This call is made only if the DC type is OD_QUEUED or OD_DIRECT (the DC types that request output). This call does not start a thread at this time but causes the GenPLib thread code to initialize its internal structures with information you supply. Once again, a corresponding call to #GplThreadDeleteInstance is made during the driver's disable subfunction DISABLE_PHYSICAL to free the thread handle.

The actual thread is started in the driver when the start-document escape code is received from the application (DEVESC_STARTDOC from the DevEscape() device call) using #GplThreadStart. The #GplThreadEnd call that stops the thread is placed in disable subfunction BEGIN_CLOSE_DC by the driver. It is not placed in end-document (DEVESC_ENDDOC) code because some applications choose to call many DEVESC_STARTDOC/DEVESC_ENDDOC pairs for the same DC. Placing the call in a disable subfunction assures that a thread will not start and stop many times in a row. The side effect of this is that the GenPLib thread code will receive multiple #GplThreadStart calls for the same thread. In this case, subsequent GplThreadStart calls cause an implied flush of all data received at that time for the thread.

If you call #GplThreadStart multiple times, the thread code accepts any changed information for input parameters to this call. This allows the driver to dynamically change the buffer size and the maximum number of buffers used by the thread code.

The #GplThreadAbortDoc call is also added to the driver's escape code processing routine for the abort-document call (DEVESC_ABORTDOC). This call the thread code to clear data out of all its buffers and to ignore any other data received by the thread until a #GplThreadResetAbortDoc call is made to the thread. As soon as all data is flushed by #GplThreadAbortDoc, the abort sequences supplied during #GplThreadStart will place the printer in a known state for the next print job.

#GplThreadResetAbortDoc tells the thread code that the job has ended, that no more data will be sent for this print job, and to resume sending data. This call is added during the end-document escape processing (DEVESC_ENDDOC) in the driver. This allows applications that perform multiple DEVESC_ STARTDOC/DEVESC_ENDDOC calls for a single DC (that is, submitting multiple print jobs using one DC) to function normally.

#GplThreadOutput is used to add any data to the thread's output buffers. The call has been added for the DEVESC_RAWDATA escape code. This escape code is sent to a driver to pass through raw data received from DOS or Windows applications along with a data buffer and data count. The call is also implemented wherever the driver needs to send out its own escape codes for graphical or text generating output. This data includes printer/job initialization commands, raster image data (compressed or uncompressed), font/text data, and job/printer termination commands.

The last function in the thread-management package is #GplThreadFlushBuffer, which tells the thread code to send all data currently held in the thread's buffers before the call returns. This call appears just after #GplThreadResetAbortDoc in a driver's DEVESC_ENDDOC processing code.

Example Code

Starting a Thread With Output to a Printer

Following is an example of starting a thread with output to a printer:

  case DEVESC_STARTDOC:                           // Escape code signals start of document
  {
    // If going to a file or direct to a printer
    if( OD_DIRECT == ulDCType )
    {
      // Open output port (printer-driver version of DosOpen)
      rc  =  PrtOpen( hFile, pszLogAddress );

      // Create/Update the second print thread
      rc = GplThreadStart( hThread,                 // Instance handle
                           0,                       // Handle to spooler
                           hFile,                   // Handle to device
                           0,                       // Default buffer size
                           0,                       // Default number of buffers
                           '\0',                    // Abort character
                           1024,                    // Abort character repeat count
                           sizeof( pszCmdAbort );   // Length of abort command
                           pszCmdAbort,             // Our abort command
                           pszLogAddress,           // For example, LPT1
                           "Error",                 // Error string if write fails
                           pszDeviceName,           // For example, "Epson LQ2550"
                           ulDCType );              // DC type
    } /* end if */
  } /* end case */
Starting a Thread With Output Queuing "Raw" Data

Following is an example of starting a thread with output queuing "raw" printer escapes:

  case DEVESC_STARTDOC:                           // Escape code signals start of document
  {
    // Else DC type is queued, not direct to a printer
    elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_RAW )
      {
        if( !hspl )
        {
          // Open appropriate queue (from DevOpenData) for output
          hspl  =  SplQmOpen( "*", 9, (PQMOPENDATA)&DevOpenData );
          SplQmStartDoc( hspl, szDocumentName );

        } /* end if */

        // Create/Update the second print thread
        rc = GplThreadStart( hThread,                 // Instance handle
                             hspl,                    // Handle to spooler
                             0,                       // Handle to device
                             0,                       // Default buffer size
                             0,                       // Default number of buffers
                             '\0',                    // Abort character
                             1024,                    // Abort character repeat count
                             sizeof( pszCmdAbort );   // Length of abort command
                             pszCmdAbort,             // Our abort command
                             pszLogAddress,           // For example, LPT1
                             "Error",                 // Error string if write fails
                             pszDeviceName,           // For example, "Epson LQ2550"
                             ulDCType );              // DC Type
      } /* end if */
    } /* end if */
  } /* end case */
Starting a Thread With Output Queuing "Standard" Data

Following is an example of starting a thread with output queuing "standard" data:

  case DEVESC_STARTDOC:                           // Escape code signals start of document
  {
    // Else DC type is queued, not direct to a printer
    elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_STD )
      {
        SplStdStart( pddc->pdb->hdc );

        // Open appropriate queue (from DevOpenData) for output
        hspl = SplQmOpen( "*", 9, (PQMOPENDATA)&DevOpenData );

        // Signal start of document (no thread is started by Omni driver)
        SplQmStartDoc( hspl, szDocumentName );

      } /* end if */
    } /* end if */
  } /* end case */
GplThreadCreateInstance and GplThreadDeleteInstance Usage

The enable and disable subfunctions FILL_PHYSICAL and DISABLE_PHYSICAL are ideal points at which to create and delete a thread instance. Following is an example of #GplThreadCreateInstance() and #GplThreadDeleteInstance() usage :

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                                 // Determine enable subfunction
  {
      case FILL_PHYSICAL:                             // An enable subfunction
      {
        // Only create a thread if we expect to output data
        if( (OD_QUEUED == pdb->ulType )  ||  (OD_DIRECT == pdb->ulType ) )
        {
          // Create an output thread instance (do not start thread yet)
          bRC = GplThreadCreateInstance( hmcbHeap, globals.hmod, &hThread, pddc );

        } /* end if */
      } /* end case */
      break;

      case DISABLE_PHYSICAL:                          // A disable subfunction
      {
        // Delete the output thread instance if allocated
        if( hThread )
        {
          bRC = GplThreadDeleteInstance( &hThread );

        } /* end if */
      } /* end case */
      break;

  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */
GplThreadOutput Usage

Following is an example of #GplThreadOutput() usage:

  case DEVESC_NEWFRAME:                            // Page eject requested
  {
     CHAR achRasterCmd[] = "*Esc*r1Q";

     // When getting a DEVESC_NEWFRAME or DEVESC_ENDDOC,
     // The current page's raster image can be retrieved from the
     // device surface and transferred to the printer

     // Enter raster mode by sending down escape code for current device
     bRC = GplThreadOutput( hThread,                 // Thread instance handle
                            achRasterCmd,            // Pointer to data buffer
                            strlen(achRasterCmd),    // Size of data buffer
                            THREAD_DT_BINARY );      // Data type

     // Dither and/or compress raster data and send to output thread
     bRC = GplThreadOutput( hThread,
                            pbRasterData,
                            ulRasterDataLength,
                            THREAD_DT_BINARY );

  } /* end case */
GplThreadEnd Usage

Following is an example of #GplThreadEnd usage during OS2_PM_DRV_ENABLE:

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                              // Determine enable subfunction
  {
    case BEGIN_CLOSE_DC:
    {
      // If we created an output thread
      if( hThread )
      {
        // End the second thread implied flush with wait
        bRC = GplThreadEnd( hThread );
      } /* end if */
    } /* end case */
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */

GplThreadAbortDoc Usage

Following is an example of #GplThreadAbortDoc usage for handling an abort- document escape:

  case DEVESC_ABORTDOC:
  {
    // Tell second thread that an abort doc occurred
    GplThreadAbortDoc( hThread );

    // Store a state flag in our pddc to indicate abort is in effect
    pddc->fAbortDocCalled = TRUE;

  } /* end case */
GplThreadResetAbortDoc Usage

Following is an example of using #GplThreadResetAbortDoc:

case DEVESC_ENDDOC:
{
  // If we received a DEVESC_ABORTDOC
  if( pddc->fAbortDocCalled )
  {
    // Notify the output thread that the abort condition is over
    GplThreadResetAbortDoc( hThread );

    // Reset the abort state so thread will begin processing data again
    pddc->fAbortDocCalled = FALSE;

  } /* end if */
} /* end case */
GplThreadFlushBuffer Usage

Following is an example of using #GplThreadFlushBuffer:

case DEVESC_ENDDOC:
{
  // If going to a file or direct to a printer
  if( OD_DIRECT == ulDCType )
  {
    // Flush all data in second thread; we are done
    GplThreadFlushBuffer( hThread, TRUE );

   } /* end if */

   elseif( OD_QUEUED == ulDCType  &&  PM_Q_RAW == ulDataType )
   {
     // Flush all data in second thread; we are done
     GplThreadFlushBuffer( hThread, TRUE );

   } /* end if */
} /* end case */
GplThreadHookDeviceWrites Usage

Following is an example of using #GplThreadHookDeviceWrites:

case DEVESC_STARTDOC:
{
  // We have just called thread Start
  GplThreadStart();

  // We inform the thread that we want to subclass the write of data
  // to the output destination, replacing it with our callback function
  GplThreadHookDeviceWrites( hThread,        // Thread handle
                             pfnPrtWrite,    // Our "write" function
                             pddc );         // Parameter to our "write" function
} /* end case */

Pattern Creation

This module contains functions to create bitmap patterns.

To use the GenPLib Pattern-Creation package, you must include:

INCL_GENPLIB_MEMORY
INCL_GENPLIB_PATTERNS

The following functions are used to create bitmap patterns.

GplPatternCreateBitmaps

Description

This function fills in an array of BMAPINFO structures with the PATSYM patterns. This function is designed to be used to fill in the DEVICESURFACE structure with patterns that match the resolution of the target device. GplPattern functions use the GenPLib Memory-Management package and require that a HMCB had been created with #GplMemoryCreateInstance.

Format

LONG APIENTRY GplPatternCreateBitmaps(
  HMCB        hMcb,           // Memory instance handle
  ULONG       ulXResolution,  // X device resolution
  ULONG       ulYResolution,  // Y device resolution
  ULONG       ulWidth,        // Desired pattern width
  ULONG       ulHeight,       // Desired pattern height
  PBMAPINFO   paBmapInfo,     // Array of pattern bitmaps (output)
  LONG        lCount,         // Number of bitmaps to create
  ULONG       ulFlip );       // Flip bitmaps horiz/vertically

Parameters
hMcb (HMCB)
Handle to initialized memory control block.
ulXResolution (ULONG)
Target device x resolution.
ulYResolution (ULONG)
Target device y resolution.
ulWidth (ULONG)
Size of bitmap X.
ulHeigh (ULONG)
Size of bitmap Y.
paBmapInfo (PBMAPINFO)
Pointer to an array of BMAPINFO.
lCount (LONG)
Number of bitmap information structures.
ulFlip (ULONG)
Bit vector. If GPLPAT_INVERT_BITS is set, the bitmaps are inverted.
Returns
TRUE: Success
FALSE Failure

GplPatternDeleteBitmaps

Description

This function deletes the bits and clears the array of BMAPINFO.

Format

LONG APIENTRY GplPatternDeleteBitmaps(
  HMCB       hMcb,        // Memory instance handle
  PBMAPINFO  paBmapInfo,  // Pointer to bitmaps to be freed
  LONG       lCount );    // Count of bitmaps to be freed

Parameters
hMcb (HMCB)
Handle to initialized memory control block.
paBmapInfo (PBMAPINFO)
Pointer to an array of BMAPINFO.
lCount (LONG)
Number of bitmap information structures.
Returns
TRUE: Success
FALSE: Failure

GplPatternCreate

Description

This function can be used by a presentation driver to create a bitmap representation of any one of the predefined OS/2 patterns. Typically, it is called to create all or a subset of patterns a device driver will support at the start of a print job.

Format

LONG APIENTRY GplPatternCreate(
  HMCB      hMcb,          // Memory instance handle
  ULONG     ulXResolution, // X device resolution
  ULONG     ulYResolution, // Y device resolution
  ULONG     ulPatSym,      // OS/2 pattern define
  PBMAPINFO pBmapInfo );   // A pattern bitmap (output)

Parameters
hMcb (HMCB)
Handle to initialized Memory control block.
ulXResolution (ULONG)
Target device x resolution.
ulYResolution (ULONG)
Target device y resolution.
ulPatSym (ULONG)
Pattern number as defined for OS/2 in PMDDI.H:
PATSYM_DEFAULT
PATSYM_DENSE1
PATSYM_DENSE2
PATSYM_DENSE3
PATSYM_DENSE4
PATSYM_DENSE5
PATSYM_DENSE6
PATSYM_DENSE7
PATSYM_DENSE8
PATSYM_VERT
PATSYM_HORIZ
PATSYM_DIAG1
PATSYM_DIAG2
PATSYM_DIAG3
PATSYM_DIAG4
PATSYM_NOSHADE
PATSYM_SOLID
PATSYM_BLANK
PATSYM_HALFTONE
PATSYM_HATCH
PATSYM_DIAGHATCH
pBmapInfo (PBMAPINFO)
Number of bitmap information structures.
Returns
TRUE: Success
FALSE Failure

Example Code

GplPatternCreateBitmaps Usage

Following is an example of using #GplPatternCreateBitmaps:

// PM Graphics Rendering Engine v2.2 Device Surface Function from the Omni driver

LONG APIENTRY QueryDeviceSurface( PDDC pddc, PVOID pv )
{
  // Initialize all fields of the device surface structure, then...
  if( !pddc->pdb->bAllocatedPatterns )
  {
      if( ORIENTATION_PORTRAIT == pdb->pJobProperties->ulOrientation )
      {
        if( GplPatternCreateBitmaps ( pddc->hmcbHeap,
                                      pddc->pResInfo->ulXRes,
                                      pddc->pResInfo->ulYRes,
                                      64L,
                                      64L,
                                      (PBMAPINFO)&pds->abmapinfoDefPattern,
                                      DEFAULT_PATTERNS_NUMBER,
                                      0L ) )
        {
          pddc->pdb->bAllocatedPatterns = TRUE;

        } /* end if */
      } /* end if */
  } /* end if */
} /* end QueryDeviceSurface */
GplPatternDeleteBitmaps Usage

Following is an example of using #GplPatternDeleteBitmaps:

case BEGIN_CLOSE_DC:

  // Perform other disable processing, then near end of case...
  if( GRE_22 <= globals.ulGreVersion )  // PM Graphics Rendering Engine v2.2 or higher
  {
    PDEVICESURFACE pds;

    // Note:  We only allocate patterns in QueryDeviceSurface for GRE v2.2 and higher
    pds = (PDEVICESURFACE)pddc->pdb->pDeviceSurface;
    if( GplPatternDeleteBitmaps( pddc->pdb->hmcbHeap,
                                 (PBMAPINFO)&pds->abmapinfoDefPattern,
                                 DEFAULT_PATTERNS_NUMBER ) )
    {
       pddc->pdb->bAllocatedPatterns = FALSE;

    } /* end if */
  } /* end if */

Semaphores

Printer drivers are responsible for serializing threads' access to any of their device contexts (DCs). OS/2 mutual exclusion (MUTEX) semaphores are a possible choice to implement this serialization. When two threads attempt to use the same printer DC at the same time, the second thread should receive a failing result code and PMERR_HDC_BUSY from WinGetLastError.

Given this behavior of OS/2 and its presentation drivers, most OS/2 applications use just one thread when printing. Whenever there is just one thread, there is little contention for the DC; but presentation drivers must safely manage this contention.

There is some performance overhead to consider whenever a thread calls into the OS/2 kernel, as is the case for calls to DosRequest/ReleaseMutexSem. The overhead occurs because the kernel reloads code and data selectors on kernel entry and exit. OS/2 MUTEX semaphores are always safe semaphores, however, because of this transition to the kernel.

Semaphores in the GenPLib

The semaphore package in the GenPLib implements a Ring-3 RAM semaphore when running on the 486 processor. The 486 has the CMPXCHG instruction that is not present on the 386. This single instruction implements a fast, safe, RAM semaphore at Ring 3.

On a 486, a thread requesting an unowned semaphore will remain at Ring 3 and not enter the kernel. If the semaphore is owned and the thread intends to wait, however, then it will enter the kernel to block on an event semaphore.

On a 386, the GenPLib simply uses OS/2 MUTEX semaphores. Implementing a semaphore on the 386 requires multiple instructions; thus, a switch to the kernel is required to disable interrupts while these multiple instructions execute.

For either processor, a thread may repeatedly request the same semaphore without an intervening release (nested requests). Use #GplSemQueryMutexSem to query the nested use count.

The GenPLib detects the processor type at runtime.

All GenPLib semaphore APIs return 0 on success. When not zero, they return the same result codes documented for their OS/2 MUTEX semaphore counterparts.

The GenPLib semaphore APIs require a pointer to a MTXSEM data structure instead of a semaphore handle like OS/2 MUTEX semaphores. It is the responsibility of the client to allocate one of these structures per semaphore and supply its pointer to the GenPLib semaphore API.

When the MTXSEM semaphore structure resides on shared memory, it may be created with the DC_SEM_SHARED flag. Additional processes may obtain access to this shared memory and open the semaphore.

All GenPLib semaphores are created in the unowned state.

To use the GenPLib Semaphores package, you must include:

INCL_GENPLIB_SEMAPHORES

The following APIs are included in the semaphore package of GenPLib:

With the exception of the first parameter, PMTXSEM, the parameters to the GenPLib semaphore APIs are as documented for OS/2 MUTEX semaphores.

GplSemCreateMutexSem

Description

This call creates a MUTEX semaphore.

Format
APIRET APIENTRY GplSemCreateMutexSem( PMTXSEM  pmtxsem,
                                      ULONG    flAttr )
Parameters
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
flAttr (ULONG)
Creation flags. DC_SEM_SHARED is the only flag.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER
or return code from:
DosQueryMem
DosCreateEventSem
DosCreateMutexSem
Notes

The caller must first allocate memory for the MTXSEM structure. See GPLSEM. H, where this structure is defined. If the memory is allocated from shared memory, the caller may specify the DC_SEM_SHARED flag for flAttr parameter.

GplSemOpenMutexSem

Description

This call opens a shared GPL Mutex semaphore.

Format
APIRET APIENTRY GplSemOpenMutexSem( PMTXSEM  pmtxsem );
Parameter
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
Notes
When multiple processes are using a shared semaphore, then subsequent processes must open the semaphore. It is not necessary for the creating process to perform an open.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER
or return code from:
DosOpenEventSem
DosOpenMutexSem

GplSemRequestMutexSem

Description

This call is used to request an OS/2 PM MUTEX semaphore.

Format
APIRET APIENTRY GplSemRequestMutexSem( PMTXSEM  pmtxsem,
                                       LONG     lTime );
Parameters
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
lTime (LONG)
Time out value in milliseconds.
Returns
Success: NO_ERROR
Failure: Typical error returns for this function include ERROR_INVALID_ PARAMETER or return codes from DosRequestMutexSem.
ERROR_TIMEOUT (RC 640d) means that the semaphore is owned.
Notes

The caller can specify the timeout value for the request. When timeout is 0 , the call returns immediately. When timeout is -1, the call is an indefinite wait. Otherwise the call will block up to the timeout specified.

GplSemReleaseMutexSem

Description

This call will release an owned MUTEX semaphore.

Format
APIRET APIENTRY GplSemReleaseMutexSem( PMTXSEM  pmtxsem );
Parameter
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
Returns
Success: NO_ERROR
Failure:
ERROR_INVALID_PARAMETER
ERROR_NOT_OWNER (not the owner of the MUTEX semaphore)
DosReleaseMutexSem
Notes

This call decrements the nested use count for the semaphore. When the nested use count goes to zero, the semaphore is truly released.

GplSemCloseMutexSem

Description

This call closes a GPL MUTEX semaphore for the process.

Format
APIRET APIENTRY GplSemCloseMutexSem( PMTXSEM  pmtxsem );

Parameter:pmtxsem (PMTXSEM)

Address of MTXSEM structure.
Returns
Success: NO_ERROR
Failure:
ERROR_INVALID_PARAMETER
DosCloseEventSem
DosCloseMutexSem
Notes

Every process using GPL semaphores should close each one with this API.

GplSemQueryMutexSem

Description

This call returns information about the semaphore.

Format

APIRET APIENTRY GplSemQueryMutexSem( PMTXSEM pmtxsem,
PID *ppid,
TID *ptid,
PULONG pulCount);

Parameters
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
*ppid (PPID)
Address where process ID is returned.
*ptid (PTID)
Address where thread ID is returned.
pulCount (PULONG)
Request count.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER
or the return codes from:
DosQueryMutexSem
Notes

This API will return information about the semaphore, such as what process and thread currently owns it, and if owned, what is the nested use count.

It is important for asynchronously killed threads to make this call to determine if they own the DC semaphore. If they own the semaphore, they should release it the appropriate number of times before exiting.

Differences Between GenPLib Semaphores and Kernel Semaphores

If a nonzero request time times out, it will timeout to within 30 milliseconds of the supplied time out.

DosQueryMutexSem returns a process and thread ID even if the MUTEX sem is unowned; that is, the count returned is zero. This semaphore package returns process and thread ID of zero when the semaphore is unowned regardless of 386 or 486.

String Management

The string-management package provides string-sorting functionality for printer output devices.

Certain printer output devices can output data only in small bands, yet they are expected to keep track of strings that might be positioned anywhere on a physical output page. These devices need an independent mechanism for keeping those strings in sorted "page" order until the device is actually processing the band of that string.

This is true for raster printers that utilize a print head and must only send text strings that are drawn using device (printer) fonts within a certain range of the print head when printing graphics data. The following diagrams represent this concept:

                        Output Page
                        +----------+
                        |          |
                        |          |
                        |xxxxxxxxxx|
                        |xxxxxxxxxx| <--- Current band of raster output
                        |          |      that can be processed by the
                        |          |      output device.
                        | Hello    |
                        |          |
                        |          | <--- Text is below current band so
                        |    ABCD  |      we can't output it yet
                        |          |
                        |          |
                        |          |
                        +----------+


                        Output Page (later)
                        +----------+
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
                        |xHelloxxxx| <--- Current band of raster output
                        |xxxxxxxxxx|      that can be processed by the
                        |          |      output device.
 Lower string still --> |    ABCD  |      This time the text string is in
 not in range           |          |      the band region and it should be
                        |          |      processed at this time
                        |          |
                        |          |
                        +----------+


                        Output Page (later)
                        +----------+
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
   String already   --> | Hello    |
   processed            |          |
                        |          |
                        |          |
                        |xxxxABCDxx|   <--- Current band of raster output
                        |xxxxxxxxxx|        that can be processed by the
                        |          |        output device.
                        |          |        This time the second string is in
                        +----------+        the band region and it should be
                                            processed at this time.

The package sorts text strings passed in page order. Top-to-bottom and left -to-right is the current default page order.

Once strings are inserted, you can extract strings in respect to a rectangular area (typically the current graphics band rectangle) that you pass in.

The string-sorting package utilizes the GenPLib Memory-Management and Exception-Management packages, thereby providing very efficient system resource usage and consistent error reporting and logging.

To use the GenPLib String-Management package, you must include:

INCL_GENPLIB_STRING

The following functions comprise the string-management package:

GplStringSorterCreateInstance

Description

This function creates a string sorter instance (memory resource allocated) and returns a handle that must be used by all other string sorter package functions.

The default heap size that is created is based on current printer driver media sizes and application text usage. A parameter is supplied so that the user can increase the initial memory area that is created to hold text strings. Otherwise a default value, based on test averages, will be used.

Format

APIRET APIENTRY GplStringSorterCreateInstance( HDC hdc,
  PHSORTER phStrSort,
  ULONG    ulHeapSize,
  ULONG    ulOptions );

Parameters
hdc (HDC)
Handle to current DC.
phStrSort (PHSORTER)
Pointer to the location where string-sorter handle will be placed.
ulHeapSize (ULONG)
Initial size of heap to allocate for strings. This can be zero. A default memory area will be created.
ulOptions (ULONG)
Creation options (for example, DBCS and TTY). This parameter is reserved for future use. This parameter must be zero.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterInsertString

Description

This function adds a text string to a string instance. Information, including the bounding rectangle passed in and a pointer to the data, is stored with the string. You can store with the string (for example, a pointer to a structure containing font name, font ID, and color).

Currently, the sort order is determined by the top-left corner of the text box passed in. The strings are sorted in page order. Page order is the order the text would be printed by a banding device on a hardcopy page.

Format

APIRET APIENTRY GplStringSorterInsertString( HSORTER hStrSort,
  PSZ   pszStr,
  RECTL rectlStr,
  PVOID pUserDef,
  PVOID pUnused );

Parameters
hStrSort (HSORTER)
Handle of the string sorter to be used for insert operation.
pszStr (PSZ)
Pointer to text string to insert.
rectlStr (RECTL)
Bounding rectangle of string being inserted.
pUserDef (PVOID)
Pointer to user-defined data to be stored with this string .
pUnused (PVOID)
NULL. Reserved for future use.
Returns
Success: NO_ERROR
Failure:
ERROR_INVALID_PARAMETER
ERROR_xxx

GplStringSorterQueryNumStrings

Description

This function returns the current number of strings in the string sorter instance (HSORTER) referenced on the call.

Format
APIRET APIENTRY GplStringSorterQueryNumStrings( HSORTER  hStrSort,
                                                PULONG   pulNumStr );
Parameters
hStrSort (HSORTER)
Handle of string sorter instance to be queried.
pulNumStr (PULONG)
Pointer to the location where number is to be returned.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterGetFirstString

Description

This function is used to get a linked list of strings from the string sorter in relation to a band rectangle passed in.

The strings are not deleted from the linked list on this call because we allocated the information and will de-allocate on a #GplStringSorterReset() call or a #GplStringSorterRemoveStrings() call.

Currently, the defined relationships that can be selected are:

Option Description
STR_INCL_PART_IN_BAND Return any string whose text box intersects any part of the band passed in.
STR_INCL_ALL_IN_BAND Return any string whose text box is entirely inside the band passed in.
STR_INCL_TOPLEFT_IN_BAND Return any string whose top-left coordinate is inside the band passed in.
Notes
  1. All retrievals are inclusive on all boundaries of the rectlBand passed in.
  2. GplStringSorterGetFirstString retrievals are non-destructive. (the string remains in the string-sorter instance and are only removed by #GplStringSorterRemoveStrings or #GplStringSorterReset calls).
Format

APIRET APIENTRY GplStringSorterGetFirstString(
  HSORTER hStrSort,
  ULONG   ulOption,
  RECTL   rectlBand,
  PSZ    *ppszStr,
  PRECTL  prectlStr,
  PPVOID  ppUserDef );

Parameters
hStrSort (HSORTER)
Handle of string-sorter instance to be used for string retrieval.
ulOption (ULONG)
Option passed in to indicate the relationship to test for on the "Get" operation. See options above.
rectlBand (RECTL)
The band rectangle passed in that will be used to test all text-string rectangles against.
*ppszStr (PSZ)
-output Pointer to first text string meeting retrieve criteria (ulOption).
prectlStr (PRECTL)
-output Pointer to the bounding rectangle of first string retrieved (passed in during #GplStringSorterInsertString).
ppUserDef (PPVOID)
-output Pointer to the user-defined data that was associated with text string retrieved (passed in during #GplStringSorterInsertString).
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterGetNextString

Description

This function is used to retrieve the next string matching the retrieve criteria passed in on the last call to #GplStringSorterGetFirstString(). The criteria is based on the ulOption and rectlBand parameters from that function). This function can be called multiple times until an ERROR_NO_ MORE_ITEMS return code is detected. Each successful call (NO_ERROR) returns another string from the string sorter instance meeting the criteria until no more strings meet the criteria.

Format

APIRET APIENTRY GplStringSorterGetNextString(
  HSORTER hStrSort
  PSZ *ppszStr,
  PRECTL prectlStr
  PPVOID ppUserDef );

Parameters
hStrSort (HSORTER)
Handle of the string-sorter instance to be used for the retrieve operation.
*ppszStr (PSZ)
- output Pointer to the location that the text string will be retrieved to.
prectlStr (PRECTL)
- output Bounding rectangle of the string being retrieved.
ppUserDef (PPVOID)
- output Pointer to user-defined data stored with string during the insert operation.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterRemoveStrings

Description

This function is used to delete all strings from the string sorter instance, based on a relationship (ulOption) to a rectangle (rectlBand) both passed.

See StringGetFirstStringFromSorter() and StringGetNextStringFromSorter() calls for non-destructive string retrievals.

Remove options include:

Option Description
STR_INCL_PART_IN_BAND Return any string whose text box intersects any part of the band passed in.
STR_INCL_ALL_IN_BAND Return any string whose text box is entirely inside the band passed in.
STR_INCL_TOPLEFT_IN_BAND Return any string whose top-left coordinate is inside the band passed in.

Removes are inclusive on all sides of rectlBand passed in.

Format
APIRET APIENTRY GplStringSorterRemoveStrings(
    HSORTER  hStrSort,
    ULONG    ulOption,
    RECTL    rectlBand );
Parameters
hStrSort (HSORTER)
Handle of the string sorter to be used for remove operation.
ulOption (ULONG)
Remove options.
rectlBand (RECTL)
Rectangle that is used for comparison by the remove option.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterReset

Description

This function resets the string sorter instance by removing all text strings inserted into the sorter.

Format
APIRET APIENTRY GplStringSorterReset( HSORTER  hStrSort );
Parameter
hStrSort (HSORTER)
Handle of the string sorter to be reset.
Returns
Success: NO_ERROR
Failure:ERROR_xxx

GplStringSorterDeleteInstance

Description

This function destroys a string sorter instance by freeing any system resources allocated on the corresponding call to #GplStringSorterCreateInstance(). This function implies a #GplStringSorterReset() call.

Format
APIRET APIENTRY GplStringSorterDeleteInstance( HSORTER  hStrSort );
Parameter
hStrSort (HSORTER)
Handle of string sorter to be destroyed. The handle is no longer valid after the call.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER

Example Code

Retrieving Strings From String Sorter During Band Processing

Following is an example of retrieving strings from string sorter during band processing in a raster printer driver:

 HSORTER hSorter;
 CharString( PDDC pddc, ..., LONG cChars, PCH pchString, ... )
 {
   if( bDeviceFont )                        // If we have a device font to manage
   {
     if( ! hSorter )                        // If we have not yet created a sorter
     {
       // Create a string sorter to handle our device fonts
       apiret = GplStringSorterCreateInstance( pddc,&hSorter, 0L, 0L );

     } /* end if */

     // Calculate text box of string from our character-width tables
     // Insert character string into sorter
     apiret = GplStringSorterInsertString( hSorter,
                                           (PSZ)pchString,
                                           rectlTextBox,
                                           (PVOID)NULL,
                                           (PVOID)NULL );

     // Example: check number of strings currently in sorter
     apiret = GplStringSorterQueryNumStrings( hSorter,&ulNumStr );

   } /* end if */
 } /* end CharString */
Usage Notes
Used default values for heap size and options during 'StringCreate' API (LTR, TTB).
Used default value for during 'StringInsert' API.
For 'StringInsert' API, we could have allocated and filled in our own structure that helps us process this string (for example, font name, identifier, color, etc.).
Removing Strings From a String Sorter During Processing of a Graphics Band Following is an example of removing strings from a string sorter during processing of a graphics band (banding)
 ProcessBand( PDDC pddc, ...)
 {
   APIRET  apiret;
   RECTL   rectlString, rectlBand;
   PSZ     pszString;

   // Retrieve first sting in the raster band we are processing
   apiret = GplStringSorterGetFirstString(
                hSorter,
                STR_INCL_TOP_LEFT_IN_BAND,
                                 // Retrieve strings whose Top-Left corner are in band
                rectlBand,
                                 // Rectangle of the graphic band we are processing
                &pszString,
                                 // Pointer that first string will be returned in
                &rectlString,
                                 // Text box of the string
                (PVOID)NULL );
                                 // We had no user-defined data to retrieve

   // if we have a string meeting retrieval criteria
   while( apiret != ERROR_NO_MORE_ITEMS )
   {
     // Get next string (uses same retrieve option as "GetFirst")
     apiret = GplStringSorterGetNextString( hSorter,
                                            &pszString,
                                            &rectlString,
                                            (PVOID)NULL );
   } /* end while */

 } /* end ProcessBand */
Removing Strings From a String Sorter After Retrieving Them

Following is an example of removing strings from a string sorter after retrieving them (during graphics band processing):

 ProcessBand( PDDC pddc, ... )
 {
   // After retrieving all strings from band and sending them to printer
   // See the previous example

   // We can remove these strings from the sorter
   // since we do not need to process them again
   apiret = GplStringSorterRemoveStrings( hSorter,
                                          STR_INCL_TOP_LEFT_IN_BAND,
                                          rectlBand );
 } /* end ProcessBand */
Resetting and Destroying the String Sorter During Escape Processing

Following is an example of resetting and destroying the string sorter during escape processing in a printer driver:

 Escape( PDDC pddc, ... )
 {
   APIRET apiret;

   case DEVESC_NEWFRAME:
   {
     // Process all graphics bands on page

     // Reset sorter for next page
     apiret = GplStringSorterReset( hSorter );

    } /* end case */
    break;

    case DEVESC_ENDDOC:
    {
      // Process the last page (implied NewFrame)

      // Sorter no longer needed for this print job
      apiret = GplStringSorterDeleteInstance( hSorter );

    } /* end case */
    break;
 } /* end Escape */