PDDR/2 - Generic Printer Library

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:

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: This code is included before any GenPLib memory-management functions or structures are referenced. The compiler automatically includes GPLMEM.H within GENPLIB.H.
 * 1) define INCL_GENPLIB_MEMORY
 * 2) include 

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.
 * }
 * 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:

Use ASSERTT or ASSERTF after:
 * 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.
 * }
 * Copying||Assert that enough space is present before copying.
 * }
 * }

Do not use ASSERTT or ASSERTF in:
 * 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
 * }
 * Switch statements||Use ASSERTT (TRUE) in the default case of switch statements
 * }
 * 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: 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
 * 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.

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: To use the GenPLib Banding package, you must include: INCL_GENPLIB_JOURNAL
 * 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.

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:
 * 
 * 
 * 
 * 
 * 
 * 
 * 

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. Following are the four cases in which this function may be called: 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 to stop recording and play the journal file. 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. 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.

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 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
 * 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
 * PLAY_BOTTOM_TO_TOP||Bands are played from 0 to the maximum Y value.
 * }
 * Returns:Success TRUE
 * Failure FALSE
 * Failure FALSE

GplJournalDeleteInstance
Description

This function is the opposite of. 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
 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 */  Usage

Following is an example of 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 */  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 */  and  Usage

Following are examples of and  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 */  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 */  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.
 * 
 * 
 * 
 * 

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.
 * 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
 * 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.
 * 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
 * lBias


 * pSrcInfo||Must have the following two fields set:
 * cx
 * cy
 * cx
 * cy
 * 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.
 * 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. 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.
 * 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 can be freed.

Format APIRET APIENTRY GplDitherDeleteInstance( PHDITHER pdh );
 * Parameter:pdh(PHDITHER)
 * Pointer to the dither handler that was created by.

Example Code
 * Returns:Success NO_ERROR

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:
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * GplFaxG3StartBlock
 * GplFaxG3EndBlock
 * GplFaxG3EndDoc
 * GplFaxG3NewFrame
 * 
 * GplFaxG4EndBlock
 * 
 * GplFaxTIFF2StartBlock
 * GplFaxTIFF2EndBlock

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.

Failure GPLCOMPRESS_ERROR (-1)
 * Returns:Success Count of bytes put in destination.

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 ; 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.
 * }
 * ||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, 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.
 * }
 * ||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 or. 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 or.

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 and . 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 and, 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: 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
 * 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:




 * 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)
 * 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
 * }
 * Returns||Void
 * }
 * }


 * GplFaxG3EndBlock:End of block flushed.


 * 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:
 * }
 * 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:
 * }
 * pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 * }


 * 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.
 * }
 * 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:Modified Huffman (MH) Group 3 return to control (RTC) sent at the end of document.


 * 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:
 * }
 * 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:
 * }
 * pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 * }


 * 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.
 * }
 * 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: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.




 * 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:
 * }
 * pulBitposition||Bit-position count in the output buffer.
 * pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 * }
 * pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 * }


 * 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.
 * }
 * ULONG||Unsigned long; size of pbOutput buffer; same as ulOutputSize.
 * }


 * Returns
 * Success|| Count of data written out (compressed data count).
 * Failure||GPLCOMPRESS_ERROR (-1)
 * }
 * Failure||GPLCOMPRESS_ERROR (-1)
 * }

GplFaxG4EncodeBlock
Description

This function performs G4 "Fax"-type compression.

It is similar to, 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
 * 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:




 * 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.
 * 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.


 * 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:
 * }
 * 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:
 * }
 * pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 * }
 * }


 * 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.
 * }
 * 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)
 * }
 * Failure|| GPLCOMPRESS_ERROR (-1)
 * }

GplFaxTIFF2EncodeBlock
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).
 * Description
 * Format
 * 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:




 * 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)
 * 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:TIFF algorithm 2 compression to flush output buffer.


 * 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:
 * }
 * 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:
 * }
 * pfnFlushBuffer||Flush buffer function; prototype for this function must be:
 * }
 * }


 * 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
 * }
 * TRUE||If OK
 * FALSE||If error
 * }
 * 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: To use the GenPLib Exception-Management package, you must include: INCL_GENPLIB_ERROR The following functions are included in the package:
 * 
 * 
 * 
 * 
 * 

GplErrSetDosError
This function saves DOS error. ERRORID APIENTRY GplErrSetDosError( USHORT usDosError );
 * Description
 * Format
 * Parameter:usDosError (USHORT)
 * Any error code defined in BSEERR.H that best identifies the failing event.


 * Returns:Success: ERRORID
 * Failure: None

GplErrSetError
This function saves an error. ERRORID APIENTRY GplErrSetError( USHORT usError );
 * Description
 * Format
 * Parameter:usError (USHORT)
 * Any error code defined in PMERR.H that best identifies the failing event.


 * Returns:Success: ERRORID
 * Failure None

GplErrSetSevereError
This function saves a severe error. ERRORID APIENTRY GplErrSetSevereError( USHORT usError );
 * Description
 * Format
 * Parameter:usError (USHORT)
 * Any error code defined in PMERR.H that best identifies the failing event.


 * Returns:Success: ERRORID
 * Failure: None

GplErrSetWarning
This function saves the error warning. ERRORID APIENTRY GplErrSetWarning( USHORT usError );
 * Description
 * Format
 * Parameter:usError (USHORT)
 * Any error code defined in PMERR.H that best identifies the failing event.


 * Returns:Success:ERRORID
 * Failure: None

GplErrSetUnrecoverableError
This function saves an unrecoverable error. ERRORID APIENTRY GplErrSetUnrecoverableError( USHORT usError );
 * Description
 * Format
 * 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
This function creates an MCB and a user heap.
 * Description
 * Format
 * 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

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.
 * Notes

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 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
This function frees memory heap(s) for the user. APIRET APIENTRY GplMemoryDeleteInstance( HMCB hMCB );
 * Description
 * Format
 * 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
This function allocates memory for the user. PVOID APIENTRY GplMemoryAlloc( HMCB  hMCB,                                ULONG  ulSize );
 * Description
 * Format
 * 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
This function frees memory for a user. APIRET APIENTRY GplMemoryFree( PVOID pData );
 * Description
 * Format
 * 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
This function returns the size of a memory object allocated by. LONG APIENTRY GplMemoryGetObjectSize( PVOID pData );
 * Description
 * Format
 * 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
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. VOID APIENTRY GplMemorySetUserID( HMCB  hMCB,                                   ULONG  ulUserID );
 * Description
 * Format
 * 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 ).

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 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 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 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 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 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 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
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)
 * Creating and Allocating From a Per-DC Heap

// 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.

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 */ 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);
 * Creating a Global Heap
 * Allocating From a Global Heap

// 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 */ 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 );
 * Deleting a Per-DC Heap

} /* end case */ break; } /* end switch */ } /* end OS2_PM_DRV_ENABLE */ 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 );
 * Deleting a Global Heap

} /* 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: To use the GenPLib Output-Thread Management package, you must include: INCL_GENPLIB_THREAD Following are the Output-Thread Management APIs:
 * 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.
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 

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
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.
 * Description
 * Format
 * Parameters:hMcb (HMCB)
 * Handle to the memory control block (See ).
 * 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
This function starts or updates thread parameters. It creates semaphores and buffers, committing system resources.
 * Description
 * Format
 * Parameters:hThread (HTHREAD)
 * Instance handle of thread to start. Returned by.
 * 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 . 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
This function is used to pass data to a thread instance (referenced by the thread instance handle passed in) for output. must be called prior to calling GplThreadOutput.
 * Description
 * Format
 * 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.
 * 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
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. BOOL APIENTRY GplThreadEnd( HTHREAD hThread );
 * Description
 * Format
 * Parameter:hThread (HTHREAD)
 * Handle to thread instance.


 * Returns:Success: TRUE
 * Failure: FALSE

GplThreadDeleteInstance
This function is used to free thread instance data allocated by a corresponding call to. BOOL APIENTRY GplThreadDeleteInstance( HTHREAD *phThread );
 * Description
 * Format
 * Parameter:phThread (HTHREAD)
 * Pointer to the thread instance handle to be freed.


 * Returns:Success: TRUE
 * Failure: FALSE

GplThreadAbortDoc
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.
 * Description

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 is sent ulAbortCharCount times.

The second issue to consider is that the abort string 'pszAbortString' provided in is sent last. BOOL APIENTRY GplThreadAbortDoc( HTHREAD hThread );
 * Format
 * Parameter:hThread (HTHREAD)
 * Handle to thread instance.


 * Returns:Success: TRUE
 * Failure: FALSE

GplThreadResetAbortDoc
This function is used to resolve any previous calls to so that another job can start printing using the same thread instance handle.
 * Description

This function will wait, if necessary, for the abort processing started by to be completed before returning so that the thread instance can be put into a state to receive data from a new print job. BOOL APIENTRY GplThreadResetAbortDoc( HTHREAD hThread );
 * Format
 * Parameter:hThread (HTHREAD)
 * Handle to thread instance.


 * Returns:Success: TRUE
 * Failure: FALSE

GplThreadFlushBuffer
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).
 * Description

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). BOOL APIENTRY GplThreadFlushBuffer(  HTHREAD  hThread,   BOOL     fWait );
 * Format
 * Parameters:hThread (HTHREAD)
 * Handle to thread instance.
 * fWait (BOOL)
 * Flag indicating flush asynchronously or synchronously.


 * Returns:Success: TRUE
 * Failure: FALSE

GplThreadHookDeviceWrites
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.
 * Description
 * Format
 * 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 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 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. The 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 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 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 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 call is made to the thread. As soon as all data is flushed by, the abort sequences supplied during will place the printer in a known state for the next print job.

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.

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, 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 in a driver's DEVESC_ENDDOC processing code.

Example Code
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 );
 * Starting a Thread With Output to a Printer

// 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 */ 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 );
 * Starting a Thread With Output Queuing "Raw" Data

} /* 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 */ 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 );
 * Starting a Thread With Output Queuing "Standard" Data

// 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 */ 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 and  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 );
 * GplThreadCreateInstance and GplThreadDeleteInstance Usage

} /* 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 */ Following is an example of usage: case DEVESC_NEWFRAME:                           // Page eject requested {    CHAR achRasterCmd[] = "*Esc*r1Q";
 * GplThreadOutput Usage

// 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 */ Following is an example of usage during OS2_PM_DRV_ENABLE: Following is an example of usage for handling an abort- document escape: case DEVESC_ABORTDOC: {   // Tell second thread that an abort doc occurred GplThreadAbortDoc( hThread );
 * GplThreadEnd Usage
 * GplThreadAbortDoc Usage

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

} /* end case */ Following is an example of using : case DEVESC_ENDDOC: { // If we received a DEVESC_ABORTDOC if( pddc->fAbortDocCalled ) {   // Notify the output thread that the abort condition is over GplThreadResetAbortDoc( hThread );
 * GplThreadResetAbortDoc Usage

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

} /* end if */ } /* end case */ Following is an example of using : 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 );
 * GplThreadFlushBuffer Usage

} /* 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 */ Following is an example of using : case DEVESC_STARTDOC: { // We have just called thread Start GplThreadStart;
 * GplThreadHookDeviceWrites Usage

// 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
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.
 * Description
 * Format
 * 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
This function deletes the bits and clears the array of BMAPINFO.
 * Description
 * Format
 * 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
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.
 * Description
 * Format
 * 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
Following is an example of using : // PM Graphics Rendering Engine v2.2 Device Surface Function from the Omni driver
 * GplPatternCreateBitmaps Usage

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 */ Following is an example of using : case BEGIN_CLOSE_DC:
 * GplPatternDeleteBitmaps Usage

// 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 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
This call creates a MUTEX semaphore. APIRET APIENTRY GplSemCreateMutexSem( PMTXSEM pmtxsem,                                       ULONG    flAttr )
 * Description
 * Format
 * 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

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.
 * Notes

GplSemOpenMutexSem
This call opens a shared GPL Mutex semaphore. APIRET APIENTRY GplSemOpenMutexSem( PMTXSEM pmtxsem );
 * Description
 * Format
 * 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
This call is used to request an OS/2 PM MUTEX semaphore. APIRET APIENTRY GplSemRequestMutexSem( PMTXSEM pmtxsem,                                        LONG     lTime );
 * Description
 * Format
 * 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.

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.
 * Notes

GplSemReleaseMutexSem
This call will release an owned MUTEX semaphore. APIRET APIENTRY GplSemReleaseMutexSem( PMTXSEM pmtxsem );
 * Description
 * Format
 * 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

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

GplSemCloseMutexSem
This call closes a GPL MUTEX semaphore for the process. APIRET APIENTRY GplSemCloseMutexSem( PMTXSEM pmtxsem ); Parameter:pmtxsem (PMTXSEM)
 * Description
 * Format
 * Address of MTXSEM structure.


 * Returns:Success: NO_ERROR
 * Failure:
 * ERROR_INVALID_PARAMETER
 * DosCloseEventSem
 * DosCloseMutexSem

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

GplSemQueryMutexSem
This call returns information about the semaphore.
 * Description
 * Format
 * 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

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.
 * Notes

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
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.
 * Description

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
 * 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
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).
 * Description

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
 * 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
This function returns the current number of strings in the string sorter instance (HSORTER) referenced on the call. APIRET APIENTRY GplStringSorterQueryNumStrings( HSORTER hStrSort,                                                 PULONG   pulNumStr );
 * Description
 * Format
 * 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
This function is used to get a linked list of strings from the string sorter in relation to a band rectangle passed in.
 * Description

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

Currently, the defined relationships that can be selected are:
 * 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 or  calls).
 * Format
 * 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 ).
 * ppUserDef (PPVOID)
 * -output Pointer to the user-defined data that was associated with text string retrieved (passed in during ).


 * Returns:Success: NO_ERROR
 * Failure: ERROR_xxx

GplStringSorterGetNextString
This function is used to retrieve the next string matching the retrieve criteria passed in on the last call to. 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.
 * Description
 * Format
 * 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
This function is used to delete all strings from the string sorter instance, based on a relationship (ulOption) to a rectangle (rectlBand) both passed.
 * Description

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

Remove options include: Removes are inclusive on all sides of rectlBand passed in. APIRET APIENTRY GplStringSorterRemoveStrings(    HSORTER  hStrSort,     ULONG    ulOption,     RECTL    rectlBand );
 * Format
 * 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
This function resets the string sorter instance by removing all text strings inserted into the sorter. APIRET APIENTRY GplStringSorterReset( HSORTER hStrSort );
 * Description
 * Format
 * Parameter:hStrSort (HSORTER)
 * Handle of the string sorter to be reset.


 * Returns:Success: NO_ERROR
 * Failure:ERROR_xxx

GplStringSorterDeleteInstance
This function destroys a string sorter instance by freeing any system resources allocated on the corresponding call to. This function implies a call. APIRET APIENTRY GplStringSorterDeleteInstance( HSORTER hStrSort );
 * Description
 * Format
 * 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
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 );
 * Retrieving Strings From String Sorter During Band Processing

} /* 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.).

ProcessBand( PDDC pddc, ...) {  APIRET  apiret; RECTL  rectlString, rectlBand; PSZ    pszString;
 * 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):

// 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 */ 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
 * Removing Strings From a String Sorter After Retrieving Them

// 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 */ Following is an example of resetting and destroying the string sorter during escape processing in a printer driver: Escape( PDDC pddc, ... ) {  APIRET apiret;
 * Resetting and Destroying the String Sorter During Escape Processing

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 */