diff options
author | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-13 01:42:35 +0900 |
---|---|---|
committer | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-13 01:42:35 +0900 |
commit | 72835b3d805ac6c7cdaac7d3aff107567e938314 (patch) | |
tree | 0f2a04dc3d0672c0960a62804c6e7758673e393c /ip | |
parent | eb5e5ee9adb02776056d1b4494f66150a2fc45f1 (diff) | |
download | hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.tar.gz hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.tar.bz2 hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.zip |
Tizen 2.1 base
Diffstat (limited to 'ip')
-rw-r--r-- | ip/hpip.h | 918 | ||||
-rw-r--r-- | ip/ipdefs.h | 116 | ||||
-rw-r--r-- | ip/ipmain.c | 1342 | ||||
-rw-r--r-- | ip/xbi2gray.c | 453 | ||||
-rw-r--r-- | ip/xchgbpp.c | 627 | ||||
-rw-r--r-- | ip/xcolrspc.c | 1191 | ||||
-rw-r--r-- | ip/xconvolve.c | 658 | ||||
-rw-r--r-- | ip/xcrop.c | 470 | ||||
-rw-r--r-- | ip/xfakemono.c | 465 | ||||
-rw-r--r-- | ip/xfax.c | 3566 | ||||
-rw-r--r-- | ip/xform.h | 340 | ||||
-rw-r--r-- | ip/xgamma.c | 760 | ||||
-rw-r--r-- | ip/xgray2bi.c | 614 | ||||
-rw-r--r-- | ip/xgrayout.c | 495 | ||||
-rw-r--r-- | ip/xinvert.c | 459 | ||||
-rw-r--r-- | ip/xjpg_dct.c | 393 | ||||
-rw-r--r-- | ip/xjpg_dct.h | 50 | ||||
-rw-r--r-- | ip/xjpg_dec.c | 2838 | ||||
-rw-r--r-- | ip/xjpg_enc.c | 2184 | ||||
-rw-r--r-- | ip/xjpg_fix.c | 839 | ||||
-rw-r--r-- | ip/xjpg_huf.c | 558 | ||||
-rw-r--r-- | ip/xjpg_huf.h | 83 | ||||
-rw-r--r-- | ip/xjpg_mrk.h | 91 | ||||
-rw-r--r-- | ip/xmatrix.c | 547 | ||||
-rw-r--r-- | ip/xpad.c | 523 | ||||
-rw-r--r-- | ip/xpcx.c | 1318 | ||||
-rw-r--r-- | ip/xpnm.c | 591 | ||||
-rw-r--r-- | ip/xrotate.c | 819 | ||||
-rw-r--r-- | ip/xsaturation.c | 463 | ||||
-rw-r--r-- | ip/xscale.c | 1277 | ||||
-rw-r--r-- | ip/xskel.c | 456 | ||||
-rw-r--r-- | ip/xtable.c | 742 | ||||
-rw-r--r-- | ip/xthumb.c | 558 | ||||
-rw-r--r-- | ip/xtiff.c | 1338 | ||||
-rw-r--r-- | ip/xtonemap.c | 496 | ||||
-rw-r--r-- | ip/xyxtract.c | 451 |
36 files changed, 29089 insertions, 0 deletions
diff --git a/ip/hpip.h b/ip/hpip.h new file mode 100644 index 0000000..10ca913 --- /dev/null +++ b/ip/hpip.h @@ -0,0 +1,918 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * hpojip.h - Interface into the Image Processing module + * + ***************************************************************************** + * + * Mark Overton, Dec 1997 + * +\*****************************************************************************/ + + +#if !defined HPIP_INC +#define HPIP_INC + +#if defined(__cplusplus) + extern "C" { +#endif + +#include <stdlib.h> + +/* TODO: Fix this! */ +// #include <endian.h> +#undef LITTLE_ENDIAN + +#define FAR +#define WINAPI +#define EXPORT(x) x + +typedef unsigned char BYTE, *PBYTE, FAR *LPBYTE; +typedef unsigned short WORD, *PWORD, FAR *LPWORD; +typedef unsigned short USHORT, *PUSHORT, FAR *LPUSHORT; +typedef unsigned int DWORD, *PDWORD, FAR *LPDWORD; +typedef unsigned int UINT, *PUINT, FAR *LPUINT; +typedef unsigned long ULONG, *PULONG, FAR *LPULONG; +typedef enum { FALSE=0, TRUE=1 } BOOL; +typedef void VOID, *PVOID, FAR *LPVOID; +typedef long long int __int64; + +typedef struct { + BYTE rgbRed:8; + BYTE rgbGreen:8; + BYTE rgbBlue:8; +} __attribute__((packed)) RGBQUAD; + +/****************************************************************************\ + **************************************************************************** + * + * COMMON DEFINITIONS for both xform drivers and ip functions + * + **************************************************************************** +\****************************************************************************/ + +#define IP_MAX_XFORMS 20 /* Max number of xforms we can handle. */ + +/* These bit-values are returned by all transform driver and ip functions. + * Zero or more of these bits is set, telling you if anything interesting + * happened. + */ +#define IP_READY_FOR_DATA 0X0001u +#define IP_PARSED_HEADER 0x0002u +#define IP_CONSUMED_ROW 0x0004u +#define IP_PRODUCED_ROW 0x0008u +#define IP_INPUT_ERROR 0x0010u +#define IP_FATAL_ERROR 0x0020u +#define IP_NEW_INPUT_PAGE 0x0040u +#define IP_NEW_OUTPUT_PAGE 0x0080u +#define IP_WRITE_INSERT_OK 0x0100u +#define IP_DONE 0x0200u + + +/* IP_IMAGE_TRAITS describes everything about an image. If an item is not + * known, a negative number is used, meaning "I don't know." So all items + * are signed. + */ +typedef struct { + int iPixelsPerRow; + int iBitsPerPixel; + int iComponentsPerPixel; + long lHorizDPI; /* 32.0 or 16.16 fixed-point */ + long lVertDPI; /* 32.0 or 16.16 fixed-point */ + long lNumRows; + int iNumPages; + int iPageNum; +} IP_IMAGE_TRAITS, *PIP_IMAGE_TRAITS, FAR*LPIP_IMAGE_TRAITS; + +typedef union { + DWORD dword; + PVOID pvoid; + __int64 i64; + RGBQUAD rgbquad; + float fl; +} DWORD_OR_PVOID; + +//#ifdef HPIP_INTERNAL +#include "xform.h" // this file uses the above definitions +//#else +//typedef struct IP_XFORM_TBL_s FAR *LPIP_XFORM_TBL; +//#endif + + +/****************************************************************************\ + **************************************************************************** + * + * Definitions for Exported IP Routines + * + **************************************************************************** +\****************************************************************************/ + + +/* Synopsis of the interface: + * + * Main routines: + * + * ipOpen - opens a new conversion-job + * ipConvert - converts some data + * ipClose - closes the conversion-job + * + * Ancillary routines: + * + * ipGetFuncPtrs - loads table with ptrs to these global routines + * ipGetClientDataPtr - returns ptr to client data-area in instance + * ipInsertedData - client inserted some data in output data stream + * ipGetImageTraits - returns traits of input and output images + */ + +/* handle for a conversion job */ +typedef void FAR*IP_HANDLE, *FAR*PIP_HANDLE, FAR*FAR*LPIP_HANDLE; + +typedef void (WINAPI *LPIP_PEEK_FUNC)( + IP_HANDLE hJob, /* handle for job making the callback */ + LPIP_IMAGE_TRAITS pTraits, /* traits of the data being peeked at */ + int nBytes, /* # bytes in buffer below */ + LPBYTE pBuf, /* data being peeked at */ + long nFilePos, /* file-position where data belongs */ + PVOID pUserdata); /* Data passed to the user in peek calls. */ + + +/* IP_XFORM - The list of the standard xforms supplied in image processor + * + * Warning: If the list below changes, you must also change an array in + * ipmain.c that is indexed by this enum. + */ +typedef enum { + X_FAX_ENCODE, X_FAX_DECODE, /* MH, MR and MMR formats */ + X_PCX_ENCODE, X_PCX_DECODE, + /* X_BMP_ENCODE, X_BMP_DECODE, */ + X_JPG_ENCODE, X_JPG_DECODE, X_JPG_FIX, + X_TIF_ENCODE, X_TIF_DECODE, + X_PNM_ENCODE, X_PNM_DECODE, + X_SCALE, + X_GRAY_2_BI, X_BI_2_GRAY, + X_CNV_COLOR_SPACE, + X_Y_EXTRACT, + /* X_HEADER, */ + X_THUMB, + X_TABLE, /* tables are: user1,user3,mirror,gamma,threshold,pass-thru */ + X_CROP, + X_TONEMAP, + X_SATURATION, + X_ROTATE, + X_PAD, + X_FAKE_MONO, + X_GRAYOUT, + X_CHANGE_BPP, + X_MATRIX, + X_CONVOLVE, + X_INVERT, + X_SKEL, +} IP_XFORM, *PIP_XFORM, FAR*LPIP_XFORM; + + +#define IP_MAX_XFORM_INFO 8 + +/* IP_XFORM_SPEC - Fully specifies an xform + * Each transform driver documents what goes into aXformInfo. + */ +typedef struct { + LPIP_XFORM_TBL pXform; /* ptr to jmp-table for xform to do */ + IP_XFORM eXform; /* which xform (used if pXform is NULL) */ + LPIP_PEEK_FUNC pfReadPeek; /* callback when xform dvr reads data */ + LPIP_PEEK_FUNC pfWritePeek; /* callback when xform dvr writes data */ + PVOID pUserData; /* Data passed to user in peek functions. */ + DWORD_OR_PVOID aXformInfo[IP_MAX_XFORM_INFO]; /* xform-specific info */ +} IP_XFORM_SPEC, *PIP_XFORM_SPEC, FAR*LPIP_XFORM_SPEC; + + + +/*****************************************************************************\ + * + * ipOpen - Opens a new conversion job + * + ***************************************************************************** + * + * This routine allocates some instance data, including space for nClientData. + * More memory is allocated once row-lengths are such are known. + * To deallocate all memory, ipClose must be called. + * + * The nXforms and pXforms parameters specify the sequence of transforms + * to go from input to output. The handle for the new job is returned + * in *pXform. + * + * A restriction on the list of xforms: the data-flows between xforms must be + * fixed-length buffers. This restricts you to raw raster rows between xforms. + * The data-flows into and out of ipConvert can be variable-length in nature. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipOpen ( + int nXforms, /* in: number of xforms in lpXforms below */ + LPIP_XFORM_SPEC lpXforms, /* in: list of xforms we should perform */ + int nClientData, /* in: # bytes of additional client data */ + LPIP_HANDLE phJob); /* out: handle for conversion job */ + + + +/*****************************************************************************\ + * + * ipMirrorBytes - Swaps bits in each byte of buffer + * (bits 0<->7, 1<->6, etc.) + * +\*****************************************************************************/ + +VOID ipMirrorBytes(PBYTE pbInputBuf,DWORD dwInputAvail); + + + +/*****************************************************************************\ + * + * ipConvert - Converts some input data (work-horse function) + * + ***************************************************************************** + * + * This function consumes input data and produces output data via the + * input- and output-buffer parameters. And it tells you what's happening + * via its function return value. + * + * On entry, pbInputBuf and wInputAvail specify the location and number of + * data-bytes in the input buffer. On return, pwInputUsed tells you how + * many of those input bytes were consumed. pdwInputNextPos tells you + * where in the input file you should read next for the following call; + * 0 is the beginning of the file. This is almost always the current file + * position plus pwInputUsed; if not, a file-seek is being requested. + * + * The output buffer parameters are analogous to the input parameters, + * except that pdwOutputThisPos tells you where the bytes just output + * should be written in the output file. That is, it applies to *this* + * write, not the *next* write, unlike the input arrangement. + * The output buffer pointers are allowed to be NULL, in which case + * the output is discarded. + * + * The function return value is a bit-mask that tells you if anything + * interesting happened. Multiple bits can be set. This information + * should be treated as independent of the data-transfers occuring via + * the parameters. The IP_CONSUMED_ROW and IP_PRODUCED_ROW bits can + * be used to count how many rows have been input and output. + * + * The IP_NEW_OUTPUT_PAGE bit is set when or after the last row of the + * page has been sent, and before the first row of the following page (if + * any) is sent. + * + * You may wish to insert secret data, such as thumbnails, into the + * output stream. When ipConvert returns the IP_WRITE_INSERT_OK bit, + * it is giving you permission to write stuff AFTER you write the output + * buffer it gave you. After adding your secret data, you must call + * ipInsertedData to tell us how many bytes were added. + * + * When there is no more input data, ipConvert must be called repeatedly + * with a NULL pbInputBuf parameter, which tells the processor to flush out + * any buffered rows. Keep calling it until it returns the IP_DONE bit. + * + * Do not call ipConvert again after it has returned either error bit or + * IP_DONE. + * + * At any time after ipConvert returns the IP_PARSED_HEADER bit, + * ipGetImageTraits may be called to obtain the input and output traits. + * IP_PARSED_HEADER is returned in *every* call after the header was parsed. + * + * Return value: Zero or more of these bits may be set: + * + * IP_PARSED_HEADER = input header has been parsed; traits are known + * IP_CONSUMED_ROW = an input row was parsed + * IP_PRODUCED_ROW = an output row was produced + * IP_INPUT_ERROR = syntax error in input data + * IP_FATAL_ERROR = misc error (internal error or bad param) + * IP_NEW_INPUT_PAGE = just encountered end of page on input + * IP_NEW_OUTPUT_PAGE = just finished outputting a page + * IP_WRITE_INSERT_OK = okay to insert data in output file + * IP_DONE = conversion is completed. + * +\*****************************************************************************/ + +EXPORT(WORD) ipConvert ( + IP_HANDLE hJob, /* in: handle to conversion job */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + LPBYTE pbInputBuf, /* in: ptr to input buffer */ + LPDWORD pdwInputUsed, /* out: # bytes used from input buf */ + LPDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in output buf */ + LPBYTE pbOutputBuf, /* in: ptr to output buffer */ + LPDWORD pdwOutputUsed, /* out: # bytes written in out buf */ + LPDWORD pdwOutputThisPos); /* out: file-pos to write this data */ + + + +/*****************************************************************************\ + * + * ipClose - Destroys the given conversion job + * + ***************************************************************************** + * + * This routine deallocates all memory associated with the given conversion + * job. It may be called at any time, which is how you do an abort. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipClose ( + IP_HANDLE hJob); /* in: handle to conversion job */ + + +/****************************************************************************\ + * + * ipResultMask - Selects bit-results to be returned by ipConvert + * + **************************************************************************** + * + * The mask parameter is the OR of the IP_... bits you want returned by + * calls to ipConvert. A 1 means you want that bit; 0 means you don't. + * By disabling frequently-returned bits, efficiency will improve because + * fewer ipConvert calls will be made because each call can do more work. + * + * The mask is all zeroes by default. The IP_DONE, IP_FATAL_ERROR, and + * IP_INPUT_ERROR bits are always enabled, regardless of their bit-values + * in the mask. + * +\*****************************************************************************/ + +EXPORT(WORD) ipResultMask ( + IP_HANDLE hJob, /* in: handle to conversion job */ + WORD wMask); /* in: result bits you are interested in */ + + +/*****************************************************************************\ + * + * ipGetClientDataPtr - Returns ptr to client's data in conversion instance + * + ***************************************************************************** + * + * ipOpen accepts an nClientData parameter which is the number of extra bytes + * we allocate for the client for his own use. This function returns the + * pointer to that memory in the given conversion instance. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetClientDataPtr ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPVOID FAR*ppvClientData); /* out: ptr to client's memory area */ + + + +/*****************************************************************************\ + * + * ipSetDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the first transform might not + * include *all* the image traits we'd like to know. Those not specified + * in the file-header are filled in from info provided by this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipSetDefaultInputTraits ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPIP_IMAGE_TRAITS pTraits); /* in: default image traits */ + + + +/*****************************************************************************\ + * + * ipGetOutputTraits - Returns the output traits before ipConvert is called + * + ***************************************************************************** + * + * If the first xform does not have a header, then you can call this function + * *after* calling ipSetDefaultInputTraits to get the output image traits. + * Ordinarily, you'd have to call ipConvert a few times and wait until it tells + * you that the (non-existent) header has been parsed. But if you need the + * output traits before calling ipConvert, this function will return them. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetOutputTraits ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPIP_IMAGE_TRAITS pTraits); /* out: output image traits */ + + + +/*****************************************************************************\ + * + * ipGetImageTraits - Returns traits of input and output images + * + ***************************************************************************** + * + * At any time after ipConvert has returned the IP_PARSED_HEADER bit, this + * function may be called to obtain the traits of the input and output images. + * If a pointer parameter is NULL, that traits record is not returned. + * + * After the conversion job is done, these traits will contain the actual + * number of rows input and output. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetImageTraits ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPIP_IMAGE_TRAITS pInputTraits, /* out: traits of input image */ + LPIP_IMAGE_TRAITS pOutputTraits); /* out: traits of output image */ + + + +/*****************************************************************************\ + * + * ipInsertedData - Client inserted some bytes into our output stream + * + ***************************************************************************** + * + * After ipConvert returns the IP_WRITE_INSERT_OK bit, and after the client + * writes out the output buffer, he is permitted to write out some additional + * data for his own use, such as a thumbnail image. After writing the added + * data, the client calls this function to tell us how much data he wrote. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipInsertedData ( + IP_HANDLE hJob, /* in: handle to conversion job */ + DWORD dwNumBytes); /* in: # of bytes of additional data written */ + + + +/*****************************************************************************\ + * + * ipOverrideDPI - Force a different DPI to be reported in the output + * + ***************************************************************************** + * + * The values supplied only change the DPI that's *reported* in the output + * traits and in the header (if any) of the output file. These DPI values + * do *not* affect the transforms, and do *not* affect or change any scaling. + * + * The image processor code supplies these values in the input traits of + * the last transform in the list of transforms so that they'll make it + * in the output file header. + * + * The DPI values are in fixed-point with 16 bits of fraction. A value of + * 0 means "no override; use the normal value". + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipOverrideDPI ( + IP_HANDLE hJob, /* in: handle to conversion job */ + DWORD dwHorizDPI, /* in: horiz DPI as 16.16; 0 means no override */ + DWORD dwVertDPI); /* in: vert DPI as 16.16; 0 means no override */ + + + +/*****************************************************************************\ + * + * ipGetFuncPtrs - Loads jump-table with pointers to the ip entry points + * + ***************************************************************************** + * + * This function loads a jump-table (typedef'ed below) for your convenience + * so you won't have to call GetProcAddress on every function. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +/*****************************************************************************\ + * + * ipGetXformTable - Returns ptr to IP_XFORM_TBL for transform. + * + ***************************************************************************** + * + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL phXform); + + + +/*** Prototype for ipGetFuncPtrs function ***/ +typedef WORD (WINAPI *IPOPEN) (int, LPIP_XFORM_SPEC, + int, LPIP_HANDLE); +typedef WORD (WINAPI *IPCONVERT) (IP_HANDLE, DWORD, LPBYTE, + LPDWORD, LPDWORD, DWORD, + LPBYTE, LPDWORD, LPDWORD); +typedef WORD (WINAPI *IPCLOSE) (IP_HANDLE); +typedef WORD (WINAPI *IPGETCLIENTDATAPTR) (IP_HANDLE, LPVOID FAR *); +typedef WORD (WINAPI *IPRESULTMASK) (IP_HANDLE, WORD); +typedef WORD (WINAPI *IPSETDEFAULTINPUTTRAITS) (IP_HANDLE, + LPIP_IMAGE_TRAITS); +typedef WORD (WINAPI *IPGETIMAGETRAITS) (IP_HANDLE, + LPIP_IMAGE_TRAITS, + LPIP_IMAGE_TRAITS); +typedef WORD (WINAPI *IPINSERTEDDATA) (IP_HANDLE, DWORD); +typedef WORD (WINAPI *IPOVERRIDEDPI) (IP_HANDLE, DWORD, DWORD); + +typedef WORD (WINAPI *IPGETXFORMTABLE) (LPIP_XFORM_TBL); + +typedef WORD (WINAPI *IPGETOUTPUTTRAITS) (IP_HANDLE, + LPIP_IMAGE_TRAITS); + +typedef struct { + WORD wStructSize; /* # of bytes in this struct */ + IPOPEN ipOpen; + IPCONVERT ipConvert; + IPCLOSE ipClose; + IPGETCLIENTDATAPTR ipGetClientDataPtr; + IPRESULTMASK ipResultMask; + IPSETDEFAULTINPUTTRAITS ipSetDefaultInputTraits; + IPGETIMAGETRAITS ipGetImageTraits; + IPINSERTEDDATA ipInsertedData; + IPOVERRIDEDPI ipOverrideDPI; + IPGETOUTPUTTRAITS ipGetOutputTraits; +} IP_JUMP_TBL, * PIP_JUMP_TBL, FAR * LPIP_JUMP_TBL; + +/*** Prototype for ipGetFuncPtrs function ***/ +typedef WORD (WINAPI *LPFNIPGETFUNCPTRS) (LPIP_JUMP_TBL); + +EXPORT(WORD) ipGetFuncPtrs (LPIP_JUMP_TBL lpJumpTbl); + +/****************************************************************************\ + **************************************************************************** + * + * OPTION DEFINITIONS for xform drivers + * + **************************************************************************** +\****************************************************************************/ + +/* xbi2gray.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_BI_2_GRAY_OUTPUT_BPP 0 +#define IP_BI_2_GRAY_WHITE_PIXEL 1 +#define IP_BI_2_GRAY_BLACK_PIXEL 2 + + +#if 0 + +/* xbmp.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_BMP_NEGATIVE_HEIGHT 0 + +#endif + + +/* xchgbpp.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_CHANGE_BPP_OUTPUT_BPP 0 + + +/* xcolrspc.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_CNV_COLOR_SPACE_WHICH_CNV 0 +#define IP_CNV_COLOR_SPACE_GAMMA 1 + +/* The following conversions are possible: */ + +typedef enum { + IP_CNV_YCC_TO_CIELAB = 0, + IP_CNV_CIELAB_TO_YCC = 1, + IP_CNV_YCC_TO_SRGB = 2, + IP_CNV_SRGB_TO_YCC = 3, + IP_CNV_LHS_TO_SRGB = 4, + IP_CNV_SRGB_TO_LHS = 5, + IP_CNV_BGR_SWAP = 100 +} IP_WHICH_CNV; + + +/* xconvolve.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_CONVOLVE_NROWS 0 +#define IP_CONVOLVE_NCOLS 1 +#define IP_CONVOLVE_MATRIX 2 +#define IP_CONVOLVE_DIVISOR 3 + +#define IP_CONVOLVE_MAXSIZE 9 /* up to 9x9 matrix */ + + +/* xcrop.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_CROP_LEFT 0 +#define IP_CROP_RIGHT 1 +#define IP_CROP_TOP 2 +#define IP_CROP_MAXOUTROWS 3 + + +/* xfakemono.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_FAKE_MONO_BPP 0 + + +/* xfax.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_FAX_FORMAT 0 +#define IP_FAX_NO_EOLS 1 +#define IP_FAX_MIN_ROW_LEN 2 + +enum {IP_FAX_MH, IP_FAX_MR, IP_FAX_MMR}; + + +/* xgray2bi.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_GRAY_2_BI_THRESHOLD 0 + + +/* xgrayout.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_GRAYOUT_LEFT 0 +#define IP_GRAYOUT_RIGHT 1 +#define IP_GRAYOUT_TOP 2 +#define IP_GRAYOUT_BOTTOM 3 + + +#if 0 + +/* xheader.h */ + +/* xheader.h - aXformInfo[0] struct given to xheader.c, the header generator + * + * Mark Overton, March 1998 + */ + +#define IP_HEADER_SPEC 0 + +/* aXformInfo[IP_HEADER_SPEC] shall be a pointer pointing to this: */ + +typedef struct { + PSTR pszLeftStr; // ptr to left-justified string; 0 -> none + PSTR pszCenterStr; // ptr to centered string; 0 -> none + PSTR pszRightStr; // ptr to right-justified string; 0 -> none + WORD wCharSet; // character set (may be a two-byte set) + PSTR pszTypeFace; // ptr to name of typeface (required) + float fHeightPoints; // point-size of font + float fMarginPoints; // left and right margin, in points + BOOL bOverlay; // 0=append header, 1=overlay it on top of page + RGBQUAD rgbWhite; // a white pixel + RGBQUAD rgbBlack; // a black pixel +} __attribute__((packed)) XHEADER_SPEC, *PXHEADER_SPEC, FAR*LPXHEADER_SPEC; + +/* A header is one line of text appearing at the top of faxes. This is known + * as a "TTI" in faxland. We implement this as a left-justified portion, a + * centered portion, and a right-justified portion. All three portions are + * optional. + * + * Note that xheader.c copies over the string fields in the struct. Therefore, + * those strings may go away after the call to cvtOpen. + * + * The bOverlay field controls whether the header is concatenated + * to the top of the page (0), or overlays the top of the page (1). + * + * xheader.c needs to know what values to use for white and black pixels, + * so rgbWhite and rgbBlack are a white pixel and black pixel repectively. + * If the page data is gray (8 bits/pixel), only the rgbRed field is used. + * If the page data is bilevel, then only the least significant bit of rgbRed + * is used. + */ +#endif + + +/* xjpg.h */ + +/* This .h file contains symbols xjpg_dec.c and xjpg_enc.c + * See those .c files for instructions on using these transforms. + */ + +/* Specifications for decoder: */ + +#define IP_JPG_DECODE_OUTPUT_SUBSAMPLED 0 +#define IP_JPG_DECODE_FROM_DENALI 1 + +/* Specifications for encoder: */ + +#define IP_JPG_ENCODE_QUALITY_FACTORS 0 +#define IP_JPG_ENCODE_SAMPLE_FACTORS 1 +#define IP_JPG_ENCODE_ALREADY_SUBSAMPLED 2 +#define IP_JPG_ENCODE_FOR_DENALI 3 +#define IP_JPG_ENCODE_OUTPUT_DNL 4 +#define IP_JPG_ENCODE_FOR_COLOR_FAX 5 +#define IP_JPG_ENCODE_DUMMY_HEADER_LEN 6 + + +/* xpad.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_PAD_LEFT 0 +#define IP_PAD_RIGHT 1 +#define IP_PAD_TOP 2 +#define IP_PAD_BOTTOM 3 +#define IP_PAD_VALUE 4 +#define IP_PAD_MIN_HEIGHT 5 + + +/* xrotate.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_ROTATE_UPPER_LEFT 0 +#define IP_ROTATE_UPPER_RIGHT 1 +#define IP_ROTATE_LOWER_LEFT 2 +#define IP_ROTATE_OUTPUT_SIZE 3 +#define IP_ROTATE_FAST 4 + + +/* xsaturation.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_SATURATION_FACTOR 0 + + +/* xscale.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_SCALE_HORIZ_FACTOR 0 +#define IP_SCALE_VERT_FACTOR 1 +#define IP_SCALE_FAST 2 + + +/* xskel.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_SKEL_SPEC_1 0 +#define IP_SKEL_SPEC_2 1 + + +/* xtable.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_TABLE_WHICH 0 +#define IP_TABLE_OPTION 1 + +#define IP_TABLE_COLOR_1 1 +#define IP_TABLE_COLOR_2 2 +#define IP_TABLE_COLOR_3 3 + +typedef enum { + IP_TABLE_USER, + IP_TABLE_PASS_THRU, + IP_TABLE_GAMMA, + IP_TABLE_THRESHOLD, + IP_TABLE_MIRROR, + IP_TABLE_USER_THREE, + IP_TABLE_BW_CLIP, + IP_TABLE_USER_WORD, + IP_TABLE_USER_THREE_WORD +} IP_TABLE_TYPE; + + +/* xthumb.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_THUMB_SCALE_SPEC 0 + + +/* xtiff.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_TIFF_FILE_PATH 0 + + +/* xtonemap.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_TONEMAP_POINTER 0 +#define IP_TONEMAP_LUM_SPACE 1 + + +/* xyxtract.h */ + +/* This .h file contains symbols for the transform in the corresponding .c file. + * See that .c file for instructions on using this transform. + */ + +#define IP_Y_EXTRACT_COLOR_SPACE 0 + +typedef enum { + IP_Y_EXTRACT_LUM_CHROME, + IP_Y_EXTRACT_RGB, + IP_Y_EXTRACT_BGR +} IP_Y_EXTRACT_WHICH_SPACE; + +#if defined(__cplusplus) + } +#endif + +#endif + + +/* End of File */ diff --git a/ip/ipdefs.h b/ip/ipdefs.h new file mode 100644 index 0000000..6611c6a --- /dev/null +++ b/ip/ipdefs.h @@ -0,0 +1,116 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * ipdefs.h - Definitions common among image processor files + * + * Mark Overton, Jan 1998 + * +\*****************************************************************************/ + +#if ! defined IPDEFS_INC +#define IPDEFS_INC + +#ifdef EXPORT_TRANSFORM +#define fatalBreakPoint() assert(0); +#else +extern void fatalBreakPoint(void); +#endif + +#define INSURE(must_be_true) \ +do { \ + if (! (must_be_true)) { \ + fatalBreakPoint(); \ + goto fatal_error; \ + } \ +} while (0) + + +#define HANDLE_TO_PTR(hJob_macpar, inst_macpar) \ +do { \ + inst_macpar = (void*)hJob_macpar; \ + INSURE (inst_macpar->dwValidChk == CHECK_VALUE); \ +} while (0) + + +#define IP_MEM_ALLOC(nBytes_macpar, ptr_macpar) \ +do { \ + /* the weirdness below is equivalent to a type cast */ \ + /* the 12 below is buffer-overrun allowance */ \ +/* *(void**)&(ptr_macpar) = (void*) malloc(nBytes_macpar+12); */ \ + (ptr_macpar) = malloc(nBytes_macpar+12); \ + INSURE (ptr_macpar != NULL); \ +} while (0) + + +#define IP_MEM_FREE(ptr_macpar) \ +do { \ + if (ptr_macpar != NULL) \ + free (ptr_macpar); \ +} while (0) + + +#define IP_MAX(valOne,valTwo) \ + ((valOne)>(valTwo) ? (valOne) : (valTwo)) + +#define IP_MIN(valOne,valTwo) \ + ((valOne)<(valTwo) ? (valOne) : (valTwo)) + + +/* We use approx NTSC weights of 5/16, 9/16, 2/16 for R, G and B respectively. */ +#define NTSC_LUMINANCE(Rval,Gval,Bval) \ + ((((Rval)<<2) + (Rval) + ((Gval)<<3) + (Gval) + ((Bval)<<1)) >> 4) + + +/* The MUL32HIHALF macro does a signed 32x32->64 multiply, and then + * discards the low 32 bits, giving you the high 32 bits. The way + * this is done is compiler-dependent. + */ +#if 0 +#define MUL32HIHALF(firstpar, secondpar, hihalfresult) { \ + __int64 prod64; \ + prod64 = (__int64)(firstpar) * (secondpar); \ + hihalfresult = ((int*)&prod64)[1]; \ + /* above, use a [0] for big endian */ +#endif +#define MUL32HIHALF(firstpar, secondpar, hihalfresult) { \ + hihalfresult = ((__int64)(firstpar) * (secondpar)) >> 32; \ +} + +#endif + +/* End of File */ diff --git a/ip/ipmain.c b/ip/ipmain.c new file mode 100644 index 0000000..598ead3 --- /dev/null +++ b/ip/ipmain.c @@ -0,0 +1,1342 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * ipmain.c - main control code and entry points for image processor + * + ***************************************************************************** + * + * Mark Overton, Dec 1997 + * +\*****************************************************************************/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> /* for memcpy, memset, etc. */ +#include <unistd.h> +#include "hpip.h" +#include "ipdefs.h" + +//#define HPIP_DEBUG + +#ifdef HPIP_DEBUG + #include <stdio.h> + #include <assert.h> + + #define _T(msg) msg + + #define PRINT0(args...) fprintf(stderr, args) + + #if 0 + #define PRINT1(args...) fprintf(stderr, args) + #else + #define PRINT1(args...) + #endif + + #undef INSURE + #define INSURE(boolexp) \ + do { if (0) goto fatal_error; assert(boolexp); } while(0) + + int infd; + int outfd; + +#else + #define PRINT0(args...) + #define PRINT1(args...) +#endif + + + +/*****************************************************************************\ + * + * Constants + * +\*****************************************************************************/ + + +#define CHECK_VALUE 0xACEC0DE4U /* for checking validity of instance struc */ +#define MIN_GENBUF_LEN 4000 /* arbitrary, but higher boosts speed some */ +#define PERMANENT_RESULTS \ + (IP_INPUT_ERROR | IP_FATAL_ERROR | IP_DONE) + + + +/*****************************************************************************\ + * + * Types + * +\*****************************************************************************/ + + +/* XFORM_STATE enum - all possible states of an xform */ + +typedef enum { + XS_NONEXISTENT=0, /* xform is not yet instantiated */ + XS_PARSING_HEADER, /* parsing header (always goes thru this state) */ + XS_CONVERTING, /* inputting and outputting */ + XS_CONV_NOT_RFD, /* only outputting; not ready for input */ + XS_FLUSHING, /* outputting buffered stuff; no more input */ + XS_DONE /* done and de-instantiated */ +} XFORM_STATE; + + +/* XFORM_INFO type - everything we know about an xform */ + +typedef struct { + XFORM_STATE eState; /* state of this xform */ + PIP_XFORM_TBL pXform; /* ptr to jmp-table for xform */ + LPIP_PEEK_FUNC pfReadPeek; /* callback when xform dvr reads data */ + LPIP_PEEK_FUNC pfWritePeek; /* callback when xform dvr writes data */ + PVOID pUserData; /* Data passed to user in peek functions. */ + DWORD_OR_PVOID aXformInfo[8]; /* xform-specific information */ + IP_XFORM_HANDLE hXform; /* handle for the xform */ + IP_IMAGE_TRAITS inTraits; /* traits of input data into xform */ + IP_IMAGE_TRAITS outTraits; /* traits of output data from xform */ + DWORD dwMinInBufLen; /* min # bytes in input buf */ + DWORD dwMinOutBufLen; /* min # bytes in output buf */ +} XFORM_INFO, *PXFORM_INFO; + + +/* GENBUF type - a general-purpose buffer which allows one to write or read + * varying amounts of data. + */ +typedef struct { + PBYTE pbBuf; /* ptr to beginning of buffer */ + DWORD dwBufLen; /* size of entire buffer (# of bytes) */ + DWORD dwValidStart; /* index of first valid data byte in buffer */ + DWORD dwValidLen; /* number of valid data bytes in buffer */ + DWORD dwFilePos; /* file-pos of start of valid data (starts at 0) */ +} GENBUF, *PGENBUF; + + +/* INST type - all variables in an instance of the image processor */ + +typedef struct { + + /* genbufs are used for input into the first xform, and to store output + * from the last xform. The client's input data is first put into gbIn, + * which is then fed into the first xform. The last xform's output is put + * into gbOut, which is then copied out to the client's output buffer. + */ + GENBUF gbIn; + GENBUF gbOut; + + /* mid-buffers are simple buffers that handle fixed-length raster-rows + * passed between xforms in the xform-list. For an xform, there's an input + * and an output buffer. For the next xform, they swap roles, so the old + * output buffer becomes the new input buffer, and vice versa. When the + * roles are swapped, the two pointers below are swapped. + */ + PBYTE pbMidInBuf; /* ptr to beginning of input mid-buf */ + PBYTE pbMidOutBuf; /* ptr to beginning of output mid-buf */ + DWORD dwMidLen; /* size of either buffer (# of bytes) */ + DWORD dwMidValidLen; /* # of bytes of good data in input mid-buf */ + int iOwner; /* index into xfArray of xform owning (using) + * the input mid-buf. negative means no owner + * and that pbMidInBuf is empty */ + + /* variables pertaining to the array of xforms */ + + XFORM_INFO xfArray[IP_MAX_XFORMS]; /* the array of xforms */ + WORD xfCount; /* number of xforms */ + + /* misc variables */ + + DWORD dwValidChk; /* struct validity check value */ + DWORD dwForcedHorizDPI; /* horiz DPI override as 16.16; 0=none */ + DWORD dwForcedVertDPI; /* vert DPI override as 16.16; 0=none */ + WORD wResultMask; /* desired ipConvert result bits */ + long lInRows; /* number of rows we've input */ + long lOutRows; /* number of rows we've output */ + int iInPages; /* number of pages we've received */ + int iOutPages; /* number of pages we've output */ + BOOL pendingInsert; /* ret IP_WRITE_INSERT_OK after outbuf empty? */ + +} INST, *PINST; + + + +/*****************************************************************************\ + * + * xformJumpTables - Array of ptrs to all driver jump tables + * + * Warning: This array is indexed by the enum IP_XFORM. If one changes, + * the other must change too. + * +\*****************************************************************************/ + +extern IP_XFORM_TBL faxEncodeTbl, faxDecodeTbl; +extern IP_XFORM_TBL pcxEncodeTbl, pcxDecodeTbl; +/* extern IP_XFORM_TBL bmpEncodeTbl, bmpDecodeTbl; */ +extern IP_XFORM_TBL jpgEncodeTbl, jpgDecodeTbl, jpgFixTbl; +extern IP_XFORM_TBL tifEncodeTbl, tifDecodeTbl; +extern IP_XFORM_TBL pnmEncodeTbl, pnmDecodeTbl; +extern IP_XFORM_TBL scaleTbl; +extern IP_XFORM_TBL gray2biTbl, bi2grayTbl; +extern IP_XFORM_TBL colorTbl; +extern IP_XFORM_TBL yXtractTbl; +/* extern IP_XFORM_TBL headerTbl; */ +extern IP_XFORM_TBL thumbTbl; +extern IP_XFORM_TBL tableTbl; +extern IP_XFORM_TBL cropTbl; +extern IP_XFORM_TBL tonemapTbl; +extern IP_XFORM_TBL saturationTbl; +extern IP_XFORM_TBL rotateTbl; +extern IP_XFORM_TBL padTbl; +extern IP_XFORM_TBL fakeMonoTbl; +extern IP_XFORM_TBL grayOutTbl; +extern IP_XFORM_TBL changeBPPTbl; +extern IP_XFORM_TBL matrixTbl; +extern IP_XFORM_TBL convolveTbl; +extern IP_XFORM_TBL invertTbl; +extern IP_XFORM_TBL skelTbl; + +static IP_XFORM_TBL * const xformJumpTables[] = { + &faxEncodeTbl, &faxDecodeTbl, /* MH, MR and MMR formats */ + &pcxEncodeTbl, &pcxDecodeTbl, + /* &bmpEncodeTbl, &bmpDecodeTbl, */ + &jpgEncodeTbl, &jpgDecodeTbl, &jpgFixTbl, + &tifEncodeTbl, &tifDecodeTbl, + &pnmEncodeTbl, &pnmDecodeTbl, + &scaleTbl, + &gray2biTbl, &bi2grayTbl, + &colorTbl, + &yXtractTbl, + /* &headerTbl, */ + &thumbTbl, + &tableTbl, + &cropTbl, + &tonemapTbl, + &saturationTbl, + &rotateTbl, + &padTbl, + &fakeMonoTbl, + &grayOutTbl, + &changeBPPTbl, + &matrixTbl, + &convolveTbl, + &invertTbl, + &skelTbl, +}; + + + +/*****************************************************************************\ + * + * fatalBreakPoint - Called when INSURE fails, used for debugger breakpoint + * +\*****************************************************************************/ + +void fatalBreakPoint (void) +{ + /* do nothing */ +#if defined _DEBUG + __asm int 3; +#endif + PRINT0 (_T("\nhit fatalBreakPoint!\n")); +} + + + +/*****************************************************************************\ + * + * deleteMidBufs - Frees the two mid-buffers, if they've been allocated + * +\*****************************************************************************/ + +static void deleteMidBufs (PINST g) +{ + PRINT0 (_T("deleteMidBufs\n")); + + if (g->pbMidInBuf != NULL) + IP_MEM_FREE (g->pbMidInBuf); + + if (g->pbMidOutBuf != NULL) + IP_MEM_FREE (g->pbMidOutBuf); + + g->pbMidInBuf = NULL; + g->pbMidOutBuf = NULL; +} + + + +/*****************************************************************************\ + * + * ipOpen - Opens a new conversion job + * +\*****************************************************************************/ + +EXPORT(WORD) ipOpen ( + int nXforms, /* in: number of xforms in lpXforms below */ + LPIP_XFORM_SPEC lpXforms, /* in: the xforms we should perform */ + int nClientData, /* in: # bytes of additional client data */ + PIP_HANDLE phJob) /* out: handle for conversion job */ +{ + PINST g; + int i; + PIP_XFORM_SPEC src; + PXFORM_INFO dest; + +#ifdef HPIP_DEBUG + char *ipIn = "/tmp/ipIn.dib"; +#endif + + PRINT0 (_T("ipOpen: nXforms=%d\n"), nXforms); + INSURE (nXforms>0 && lpXforms!=NULL && nClientData>=0 && phJob!=NULL); + +#ifdef HPIP_DEBUG + for (i=0; i<nXforms; i++) + { + switch (lpXforms[i].eXform) + { + case X_FAX_DECODE: + PRINT0("Fax_format=%d\n", lpXforms[i].aXformInfo[IP_FAX_FORMAT].dword); + ipIn = "/tmp/ipIn.pbm"; + break; + case X_JPG_DECODE: + PRINT0("JPG_decode=%d\n", lpXforms[i].aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword); + ipIn = "/tmp/ipIn.jpg"; + break; + case X_CNV_COLOR_SPACE: + PRINT0("Color_space conversion=%d\n", lpXforms[i].aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword); + PRINT0("Color_space gamma=%d\n", lpXforms[i].aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword); + break; + case X_CROP: + PRINT0("Crop_left=%d\n", lpXforms[i].aXformInfo[IP_CROP_LEFT].dword); + PRINT0("Crop_right=%d\n", lpXforms[i].aXformInfo[IP_CROP_RIGHT].dword); + PRINT0("Crop_top=%d\n", lpXforms[i].aXformInfo[IP_CROP_TOP].dword); + PRINT0("Crop_maxoutrows=%d\n", lpXforms[i].aXformInfo[IP_CROP_MAXOUTROWS].dword); + break; + case X_PAD: + PRINT0("Pad_left=%d\n", lpXforms[i].aXformInfo[IP_PAD_LEFT].dword); + PRINT0("Pad_right=%d\n", lpXforms[i].aXformInfo[IP_PAD_RIGHT].dword); + PRINT0("Pad_top=%d\n", lpXforms[i].aXformInfo[IP_PAD_TOP].dword); + PRINT0("Pad_bottom=%d\n", lpXforms[i].aXformInfo[IP_PAD_BOTTOM].dword); + PRINT0("Pad_value=%d\n", lpXforms[i].aXformInfo[IP_PAD_VALUE].dword); + PRINT0("Pad_minheight=%d\n", lpXforms[i].aXformInfo[IP_PAD_MIN_HEIGHT].dword); + break; + default: + PRINT0("Unknown xform\n"); + break; + } + } + + infd = creat(ipIn, 0600); + outfd = creat("/tmp/ipOut.ppm", 0600); +#endif + + /**** Create Instance and Init Misc Variables ****/ + + IP_MEM_ALLOC (sizeof(INST) + nClientData, g); + *phJob = g; + + memset (g, 0, sizeof(INST)); + g->dwValidChk = CHECK_VALUE; + g->iOwner = -1; + g->wResultMask = PERMANENT_RESULTS; + + /**** Transfer the Xforms to xfArray ****/ + + g->xfCount = (WORD)nXforms; + + for (i=0; i<nXforms; i++) { + src = &(lpXforms[i]); + dest = &(g->xfArray[i]); + + dest->eState = XS_NONEXISTENT; + dest->pXform = (src->pXform != NULL) + ? src->pXform + : xformJumpTables[src->eXform]; + INSURE (dest->pXform != NULL); + dest->pfReadPeek = src->pfReadPeek; + dest->pfWritePeek = src->pfWritePeek; + dest->pUserData = src->pUserData; + memcpy (dest->aXformInfo, src->aXformInfo, sizeof(dest->aXformInfo)); + } + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipClose - Destroys the given conversion job, deallocating all its memory + * +\*****************************************************************************/ + +EXPORT(WORD) ipClose (IP_HANDLE hJob) +{ + PINST g; + PXFORM_INFO pXform; + WORD n; + + PRINT0 (_T("ipClose: hJob=%p\n"), (void*)hJob); + HANDLE_TO_PTR (hJob, g); + + /**** Delete All Buffers ****/ + + deleteMidBufs (g); + g->dwMidLen = 0; + g->dwMidValidLen = 0; + + if (g->gbIn.pbBuf != NULL) IP_MEM_FREE (g->gbIn.pbBuf); + if (g->gbOut.pbBuf != NULL) IP_MEM_FREE (g->gbOut.pbBuf); + + /**** Delete All Xform Instances ****/ + + for (n=0; n<g->xfCount; n++) { + pXform = &(g->xfArray[n]); + if (pXform->hXform != NULL) + pXform->pXform->closeXform (pXform->hXform); + } + + IP_MEM_FREE (g); /* Delete our instance, and we're done */ + +#ifdef HPIP_DEBUG + close(infd); + close(outfd); +#endif + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipGetClientDataPtr - Returns ptr to client's data in conversion instance + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetClientDataPtr ( + IP_HANDLE hJob, /* in: handle to conversion job */ + PVOID *ppvClientData) /* out: ptr to client's memory area */ +{ + PINST g; + + PRINT0 (_T("ipGetClientDataPtr\n")); + HANDLE_TO_PTR (hJob, g); + *ppvClientData = (PVOID)((PBYTE)g + sizeof(INST)); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * ipResultMask - Selects bit-results to be returned by ipConvert + * +\****************************************************************************/ + +EXPORT(WORD) ipResultMask ( + IP_HANDLE hJob, /* in: handle to conversion job */ + WORD wMask) /* in: result bits you are interested in */ +{ + PINST g; + + PRINT0 (_T("ipResultMask: hJob=%p, wMask=%04x\n"), (void*)hJob, wMask); + HANDLE_TO_PTR (hJob, g); + g->wResultMask = wMask | PERMANENT_RESULTS; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipSetDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the first transform might not + * include *all* the image traits we'd like to know. Those not specified + * in the file-header are filled in from info provided by this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipSetDefaultInputTraits ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PINST g; + PIP_IMAGE_TRAITS p; + + PRINT0 (_T("ipSetDefaultInputTraits: hJob=%p PixelsPerRow=%d BitsPerPixel=%d ComponentsPerPixel=%d HorzDPI=%ld VertDPI=%ld Rows=%ld Pages=%d PageNum=%d\n"), + (void*)hJob, pTraits->iPixelsPerRow, pTraits->iBitsPerPixel, pTraits->iComponentsPerPixel, pTraits->lHorizDPI, + pTraits->lVertDPI, pTraits->lNumRows, pTraits->iNumPages, pTraits->iPageNum); + HANDLE_TO_PTR (hJob, g); + INSURE (g->xfArray[0].eState == XS_NONEXISTENT); + g->xfArray[0].inTraits = *pTraits; /* a structure copy */ + + /* Convert DPI from integer to 16.16 fixed-pt if necessary */ + p = &(g->xfArray[0].inTraits); + if (p->lHorizDPI < 0x10000) p->lHorizDPI <<= 16; + if (p->lVertDPI < 0x10000) p->lVertDPI <<= 16; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipGetImageTraits - Returns traits of input and output images + * + ***************************************************************************** + * + * After the conversion job is done, these traits will contain the actual + * number of rows input and output (ipConvert patches traits upon completion). + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetImageTraits ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPIP_IMAGE_TRAITS pInputTraits, /* out: traits of input image */ + LPIP_IMAGE_TRAITS pOutputTraits) /* out: traits of output image */ +{ + PINST g; + PXFORM_INFO pTail; + + PRINT0 (_T("ipGetImageTraits: hJob=%p\n"), (void*)hJob); + HANDLE_TO_PTR (hJob, g); + INSURE (g->xfCount > 0); + pTail = &(g->xfArray[g->xfCount-1]); + + if (pInputTraits != NULL) { + INSURE (g->xfArray[0].eState > XS_PARSING_HEADER); + *pInputTraits = g->xfArray[0].inTraits; + PRINT0 (_T("InputTraits: hJob=%p PixelsPerRow=%d BitsPerPixel=%d ComponentsPerPixel=%d HorzDPI=%ld VertDPI=%ld Rows=%ld Pages=%d PageNum=%d\n"), + (void*)hJob, pInputTraits->iPixelsPerRow, pInputTraits->iBitsPerPixel, pInputTraits->iComponentsPerPixel, pInputTraits->lHorizDPI, + pInputTraits->lVertDPI, pInputTraits->lNumRows, pInputTraits->iNumPages, pInputTraits->iPageNum); + } + + if (pOutputTraits != NULL) { + INSURE (pTail->eState > XS_PARSING_HEADER); + *pOutputTraits = pTail->outTraits; + PRINT0 (_T("OutputTraits: hJob=%p PixelsPerRow=%d BitsPerPixel=%d ComponentsPerPixel=%d HorzDPI=%ld VertDPI=%ld Rows=%ld Pages=%d PageNum=%d\n"), + (void*)hJob, pOutputTraits->iPixelsPerRow, pOutputTraits->iBitsPerPixel, pOutputTraits->iComponentsPerPixel, pOutputTraits->lHorizDPI, + pOutputTraits->lVertDPI, pOutputTraits->lNumRows, pOutputTraits->iNumPages, pOutputTraits->iPageNum); + } + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipInsertedData - Client inserted some bytes into our output stream + * +\*****************************************************************************/ + +EXPORT(WORD) ipInsertedData ( + IP_HANDLE hJob, /* in: handle to conversion job */ + DWORD dwNumBytes) /* in: # of bytes of additional data written */ +{ + PINST g; + PXFORM_INFO pTail; + + PRINT0 (_T("ipInsertedData: hJob=%p, dwNumBytes=%d\n"), (void*)hJob, dwNumBytes); + HANDLE_TO_PTR (hJob, g); + INSURE (g->xfCount > 0); + pTail = &(g->xfArray[g->xfCount-1]); + INSURE (pTail->eState > XS_PARSING_HEADER); + INSURE (g->gbOut.dwValidLen == 0); /* output genbuf must be empty */ + + pTail->pXform->insertedData (pTail->hXform, dwNumBytes); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipOverrideDPI - Force a different DPI to be reported in the output + * +\*****************************************************************************/ + +EXPORT(WORD) ipOverrideDPI ( + IP_HANDLE hJob, /* in: handle to conversion job */ + DWORD dwHorizDPI, /* in: horiz DPI as 16.16; 0 means no override */ + DWORD dwVertDPI) /* in: vert DPI as 16.16; 0 means no override */ +{ + PINST g; + + PRINT0 (_T("ipOverrideDPI: dwHorizDPI=%x, dwVertDPI=%x\n"), + dwHorizDPI, dwVertDPI); + HANDLE_TO_PTR (hJob, g); + + /* Convert from integer to fixed-pt if necessary */ + if (dwHorizDPI < 0x10000) dwHorizDPI <<= 16; + if (dwVertDPI < 0x10000) dwVertDPI <<= 16; + + g->dwForcedHorizDPI = dwHorizDPI; + g->dwForcedVertDPI = dwVertDPI; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipGetFuncPtrs - Loads jump-table with pointers to the ip entry points + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetFuncPtrs (LPIP_JUMP_TBL lpJumpTbl) +{ + PRINT0 (_T("ipGetFuncPtrs\n")); + INSURE (lpJumpTbl!=NULL && lpJumpTbl->wStructSize==sizeof(IP_JUMP_TBL)); + + lpJumpTbl->ipOpen = (LPVOID) ipOpen; + lpJumpTbl->ipConvert = (LPVOID) ipConvert; + lpJumpTbl->ipClose = (LPVOID) ipClose; + lpJumpTbl->ipGetClientDataPtr = (LPVOID) ipGetClientDataPtr; + lpJumpTbl->ipResultMask = (LPVOID) ipResultMask; + lpJumpTbl->ipSetDefaultInputTraits = (LPVOID) ipSetDefaultInputTraits; + lpJumpTbl->ipGetImageTraits = (LPVOID) ipGetImageTraits; + lpJumpTbl->ipInsertedData = (LPVOID) ipInsertedData; + lpJumpTbl->ipOverrideDPI = (LPVOID) ipOverrideDPI; + lpJumpTbl->ipGetOutputTraits = (LPVOID) ipGetOutputTraits; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipGetOutputTraits - Returns the output traits before ipConvert is called + * + ***************************************************************************** + * + * If the first xform does not have a header, then you can call this function + * *after* calling ipSetDefaultInputTraits to get the output image traits. + * Ordinarily, you'd have to call ipConvert a few times and wait until it tells + * you that the (non-existent) header has been parsed. But if you need the + * output traits before calling ipConvert, this function will return them. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +EXPORT(WORD) ipGetOutputTraits ( + IP_HANDLE hJob, /* in: handle to conversion job */ + LPIP_IMAGE_TRAITS pTraits) /* out: output image traits */ +{ + PINST g; + IP_IMAGE_TRAITS inTraits, outTraits; + int iXform; + PXFORM_INFO pXform; + WORD result; + DWORD dwHeaderLen; + DWORD dwInUsed, dwInNextPos; + + HANDLE_TO_PTR (hJob, g); + INSURE (g->xfCount>=1); + inTraits = g->xfArray[0].inTraits; /* set by SetDefaultInputTraits */ + + for (iXform=0; iXform<g->xfCount; iXform++) { + pXform = &(g->xfArray[iXform]); + INSURE (pXform->eState == XS_NONEXISTENT); + + /* Open the xform, set it up, get the traits, and close it */ + + result = pXform->pXform->openXform (&pXform->hXform); + INSURE (result == IP_DONE); + + result = pXform->pXform->setDefaultInputTraits ( + pXform->hXform, &inTraits); + INSURE (result == IP_DONE); + + result = pXform->pXform->setXformSpec ( + pXform->hXform, pXform->aXformInfo); + INSURE (result == IP_DONE); + + result = pXform->pXform->getHeaderBufSize ( + pXform->hXform, &dwHeaderLen); + INSURE (result == IP_DONE); + INSURE (dwHeaderLen == 0); + + result = pXform->pXform->getActualTraits ( + pXform->hXform, + 0, NULL, &dwInUsed, &dwInNextPos, + &inTraits, &outTraits); + INSURE (result & IP_DONE); + + result = pXform->pXform->closeXform (pXform->hXform); + INSURE (result == IP_DONE); + + inTraits = outTraits; + pXform->hXform = NULL; + } + + *pTraits = outTraits; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipConvert - Converts some input data (work-horse function) + * +\*****************************************************************************/ + +EXPORT(WORD) ipConvert ( + IP_HANDLE hJob, /* in: handle to conversion job */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in output buf */ + PBYTE pbOutputBuf, /* in: ptr to output buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write this data */ +{ + PINST g; + WORD ipResult; + int iXform; + WORD result; + PGENBUF pgbIn, pgbOut; + BOOL atTheHead, atTheTail; + PXFORM_INFO pXform, pPriorXform, pNextXform; + BOOL needInput, selectCnvState; + DWORD dwBytes; + int i; + DWORD n; + DWORD dwInUsed, dwOutUsed; + DWORD dwInNextPos, dwOutThisPos; + DWORD dwTheInLen, dwTheOutLen; + PBYTE pbTemp; + PBYTE pbTheInBuf, pbTheOutBuf; + DWORD dwJunk; + + if (pbOutputBuf == NULL) { + /* client wants us to discard all data */ + pdwOutputUsed = &dwJunk; + pdwOutputThisPos = &dwJunk; + dwOutputAvail = 0xfffffffu; + } + + PRINT0 (_T("ipConvert: hJob=%p, pbInputBuf=%p InputBufSize=%d pbOutputBuf=%p OutputBufSize=%d\n"), + (void*)hJob, pbInputBuf, dwInputAvail, pbOutputBuf, dwOutputAvail); + INSURE (pdwInputUsed !=NULL && pdwInputNextPos !=NULL && + pdwOutputUsed!=NULL && pdwOutputThisPos!=NULL); + HANDLE_TO_PTR (hJob, g); + INSURE (g->xfCount>=1); + + pgbIn = &g->gbIn; + pgbOut = &g->gbOut; + + *pdwInputUsed = 0; + *pdwOutputUsed = 0; + ipResult = 0; + + /**************************/ + /* Beginning of Main Loop */ + /**************************/ + + while (TRUE) { + + /**** Output any data in the output genbuf ****/ + + if (*pdwOutputUsed == 0) + *pdwOutputThisPos = pgbOut->dwFilePos; + + if (pgbOut->dwValidLen != 0) { /* output genbuf is not empty */ + /* Logic below: + * + * 1. If next output file-pos doesn't match output genbuf, exit loop. + * + * 2. Copy as much as possible from output genbuf to clients output buf. + * 2.1 decide number of bytes to copy + * 2.2 copy them + * 2.3 update misc variables + * + * 3. If output genbuf is not empty, exit loop (client's buf is full), + * else set wValidStart to 0 because output genbuf is empty. + */ + + if ((*pdwOutputThisPos+*pdwOutputUsed) != pgbOut->dwFilePos) { + PRINT0 (_T("ipConvert: output seek to %d\n"), pgbOut->dwFilePos); + goto exitLoop; + } + + dwBytes = pgbOut->dwValidLen; + if (dwOutputAvail < dwBytes) + dwBytes = dwOutputAvail; + + if (pbOutputBuf != NULL) { + memcpy (pbOutputBuf + *pdwOutputUsed, + pgbOut->pbBuf + pgbOut->dwValidStart, + dwBytes); + } + + *pdwOutputUsed += dwBytes; + dwOutputAvail -= dwBytes; + pgbOut->dwValidStart += dwBytes; + pgbOut->dwFilePos += dwBytes; + pgbOut->dwValidLen -= dwBytes; + + if (pgbOut->dwValidLen != 0) + goto exitLoop; + + pgbOut->dwValidStart = 0; /* output genbuf is now empty */ + } + + if (g->pendingInsert) { + g->pendingInsert = FALSE; + ipResult |= IP_WRITE_INSERT_OK; + } + + /**** If ipResult has any returnable bits set, exit loop now ****/ + + if (ipResult & g->wResultMask) + goto exitLoop; + + /**** Select an xform to run ****/ + + /* select the owner of input midbuf, or negative means 'none selected' */ + iXform = g->iOwner; + + if (iXform < 0) { + for (i=g->xfCount-1; i>=0; i--) { + if (g->xfArray[i].eState == XS_CONV_NOT_RFD) { + /* select last xform that's convnotrfd; this xform + * is outputting (or thinking) but not inputting */ + iXform = i; + break; + } + } + } + + if (iXform < 0) { + for (i=0; i<g->xfCount; i++) { + if (g->xfArray[i].eState != XS_DONE) { + /* select first xform that's not done */ + iXform = i; + break; + } + } + } + + if (iXform < 0) { + /* all xforms are done: discard all input, and report that we're done */ + *pdwInputUsed = dwInputAvail; + ipResult |= IP_DONE; + /* patch the last outtraits with actual # of rows output, so that + * ipGetImageTraits will return actual row-count after we're done */ + g->xfArray[g->xfCount-1].outTraits.lNumRows = g->lOutRows; + /* patch first intraits for the same reason */ + g->xfArray[0].inTraits.lNumRows = g->lInRows; + goto exitLoop; + } + + /**** Miscellaneous preparation ****/ + + pXform = &(g->xfArray[iXform]); + atTheHead = (iXform == 0); + atTheTail = (iXform == g->xfCount-1); + pPriorXform = atTheHead ? NULL : &(g->xfArray[iXform-1]); + pNextXform = atTheTail ? NULL : &(g->xfArray[iXform+1]); + + /**** Create the Xform if necessary ****/ + + if (pXform->eState == XS_NONEXISTENT) { + PRINT0 (_T("ipConvert: creating xform %d\n"), iXform); + INSURE (atTheHead || pPriorXform->eState>=XS_CONVERTING); + + if (atTheHead) { + /* this xform's input traits were set by ipSetDefaultInputTraits */ + } else { + /* this xform's input traits are prior xform's output traits */ + memcpy (&pXform->inTraits, &pPriorXform->outTraits, + sizeof(IP_IMAGE_TRAITS)); + + if (atTheTail) { + /* apply any DPI overrides */ + if (g->dwForcedHorizDPI != 0) + pXform->inTraits.lHorizDPI = (long)g->dwForcedHorizDPI; + if (g->dwForcedVertDPI != 0) + pXform->inTraits.lVertDPI = (long)g->dwForcedVertDPI; + } + } + + result = pXform->pXform->openXform (&pXform->hXform); + INSURE (result == IP_DONE); + result = pXform->pXform->setDefaultInputTraits ( + pXform->hXform, &pXform->inTraits); + INSURE (result == IP_DONE); + result = pXform->pXform->setXformSpec ( + pXform->hXform, pXform->aXformInfo); + INSURE (result == IP_DONE); + result = pXform->pXform->getHeaderBufSize ( + pXform->hXform, &pXform->dwMinInBufLen); + INSURE (result == IP_DONE); + + if (! atTheHead) { + /* a header is only allowed on first xform */ + INSURE (pXform->dwMinInBufLen == 0); + } else { + /* allocate the input genbuf */ + if (pXform->dwMinInBufLen == 0) + pXform->dwMinInBufLen = 1; + PRINT0 (_T("ipConvert: alloc input genbuf, len=%d\n"), + pXform->dwMinInBufLen); + IP_MEM_ALLOC (pXform->dwMinInBufLen, pgbIn->pbBuf); + pgbIn->dwBufLen = pXform->dwMinInBufLen; + pgbIn->dwValidStart = 0; + pgbIn->dwValidLen = 0; + pgbIn->dwFilePos = 0; + } + + pXform->eState = XS_PARSING_HEADER; + } + + /**** Enter flushing state if appropriate ****/ + + if (pXform->eState == XS_CONVERTING && + (( atTheHead && pbInputBuf==NULL && pgbIn->dwValidLen==0) || + (!atTheHead && pPriorXform->eState==XS_DONE && g->iOwner<0))) { + /* there will never be any more input to this xform: start flushing */ + PRINT0 (_T("ipConvert: xform %d is now flushing\n"), iXform); + pXform->eState = XS_FLUSHING; + } + + /**** Check that input data and output space are available ****/ + + needInput = (pXform->eState==XS_PARSING_HEADER || + pXform->eState==XS_CONVERTING); + + if (needInput) { + if (! atTheHead) { + /* the input midbuf must contain data */ + INSURE (g->iOwner>=0 && g->dwMidValidLen>0); + PRINT1 (_T("not at head, pixels = %08x\n"), *(DWORD*)(g->pbMidInBuf)); + } else if (pbInputBuf != NULL) { + DWORD dwUnusedStart; + + /* left-justify data in input genbuf if necessary */ + + if (pgbIn->dwBufLen-pgbIn->dwValidStart < pXform->dwMinInBufLen) { + /* too much wasted space on left end, so left justify */ + memmove (pgbIn->pbBuf, pgbIn->pbBuf + pgbIn->dwValidStart, + pgbIn->dwValidLen); + pgbIn->dwValidStart = 0; + PRINT1 (_T("left just, pixels = %08x\n"), *(DWORD*)(pgbIn->pbBuf)); + } + + /* put as much client input as possible into the input genbuf */ + + dwUnusedStart = pgbIn->dwValidStart + pgbIn->dwValidLen; + dwBytes = pgbIn->dwBufLen - dwUnusedStart; + if (dwBytes > dwInputAvail) + dwBytes = dwInputAvail; + + memcpy (pgbIn->pbBuf + dwUnusedStart, + pbInputBuf + *pdwInputUsed, + dwBytes); + + pgbIn->dwValidLen += dwBytes; + *pdwInputUsed += dwBytes; + dwInputAvail -= dwBytes; + *pdwInputNextPos += dwBytes; + + /* if input genbuf has insufficient data, exit loop */ + if (pgbIn->dwValidLen < pXform->dwMinInBufLen) + goto exitLoop; + PRINT1 (_T("at head, pixels = %08x\n"), *(DWORD*)(pgbIn->pbBuf)); + } + } + + if (atTheTail && pXform->eState!=XS_PARSING_HEADER) { + /* output might be produced; output genbuf must be empty */ + INSURE (pgbOut->dwValidLen == 0); + } + + /**** Call the Conversion Routine ****/ + + pbTheInBuf = atTheHead ? pgbIn->pbBuf + pgbIn->dwValidStart : g->pbMidInBuf; + dwTheInLen = atTheHead ? pgbIn->dwValidLen : g->dwMidValidLen; + pbTheOutBuf = atTheTail ? pgbOut->pbBuf : g->pbMidOutBuf; + dwTheOutLen = atTheTail ? pgbOut->dwBufLen : g->dwMidLen; + + if (pXform->eState == XS_PARSING_HEADER) { + result = pXform->pXform->getActualTraits ( + pXform->hXform, + dwTheInLen, pbTheInBuf, &dwInUsed, &dwInNextPos, + &pXform->inTraits, + &pXform->outTraits); + dwOutUsed = 0; + dwOutThisPos = 0; + } else { + if (pXform->eState == XS_FLUSHING) + pbTheInBuf = NULL; + result = pXform->pXform->convert ( + pXform->hXform, + dwTheInLen, pbTheInBuf, &dwInUsed, &dwInNextPos, + dwTheOutLen, pbTheOutBuf, &dwOutUsed, &dwOutThisPos); + } + + PRINT1 (_T("ipConvert: xform %d returned %04x\n"), iXform, result); + PRINT1 (_T("ipConvert: consumed %d and produced %d bytes\n"), + dwInUsed, dwOutUsed); + + if (pbTheOutBuf != NULL) + PRINT1 (_T("ipConvert: out data = %08x\n"), *(DWORD*)pbTheOutBuf); + + INSURE ((result & IP_FATAL_ERROR) == 0); + + /**** Update Input and Output Buffers ****/ + + if (dwInUsed > 0) { + if (pXform->pfReadPeek != NULL) { + #if defined _WIN32 + __try { + #endif + pXform->pfReadPeek (hJob, &(pXform->inTraits), + dwInUsed, pbTheInBuf, pgbIn->dwFilePos, + pXform->pUserData); + #if defined _WIN32 + } __except (EXCEPTION_EXECUTE_HANDLER) { + goto fatal_error; + } + #endif + } + + if (! atTheHead) { + /* We _assume_ that the xform consumed all the data in the midbuf */ + g->iOwner = -1; /* input midbuf is consumed and un-owned now */ + g->dwMidValidLen = 0; + } + } + + if (needInput && atTheHead) { + if (dwInUsed >= pgbIn->dwValidLen) { + /* consumed all input; mark input genbuf as empty */ + pgbIn->dwValidLen = 0; + pgbIn->dwValidStart = 0; + pgbIn->dwFilePos = dwInNextPos; + } else { + /* advance counters in genbuf */ + pgbIn->dwValidLen -= dwInUsed; + pgbIn->dwValidStart += dwInUsed; + pgbIn->dwFilePos += dwInUsed; + } + + /* if new genbuf file-pos doesn't match what xform wants, + * discard remainder of buffer */ + if (pgbIn->dwFilePos != dwInNextPos) { + PRINT0 (_T("ipConvert: input seek to %d\n"), dwInNextPos); + pgbIn->dwValidLen = 0; + pgbIn->dwValidStart = 0; + pgbIn->dwFilePos = dwInNextPos; + } + } + + if (dwOutUsed > 0) { + if (pXform->pfWritePeek != NULL) { + #if defined _WIN32 + __try { + #endif + pXform->pfWritePeek (hJob, &(pXform->outTraits), + dwOutUsed, pbTheOutBuf, dwOutThisPos, + pXform->pUserData); + #if defined _WIN32 + } __except (EXCEPTION_EXECUTE_HANDLER) { + goto fatal_error; + } + #endif + } + + if (atTheTail) { + pgbOut->dwFilePos = dwOutThisPos; + pgbOut->dwValidStart = 0; + pgbOut->dwValidLen = dwOutUsed; + } else { + INSURE (g->iOwner < 0); /* mid inbuf must be unowned here */ + g->iOwner = iXform + 1; /* next xform hereby owns mid inbuf */ + /* swap input and output midbuf pointers */ + pbTemp = g->pbMidInBuf; + g->pbMidInBuf = g->pbMidOutBuf; + g->pbMidOutBuf = pbTemp; + g->dwMidValidLen = dwOutUsed; + } + } + + /**** Handle Results of Conversion Call ****/ + + selectCnvState = FALSE; + + if (pXform->eState == XS_PARSING_HEADER) { + + if (result & IP_DONE) { + PRINT0 (_T("ipConvert: xform %d is done parsing header\n"), iXform); + pXform->pXform->getActualBufSizes (pXform->hXform, + &pXform->dwMinInBufLen, &pXform->dwMinOutBufLen); + + if (atTheHead) { + /* allocate new input genbuf, and xfer data into it */ + n = pXform->dwMinInBufLen; + if (n < MIN_GENBUF_LEN) + n = MIN_GENBUF_LEN; + if (n < pgbIn->dwValidLen) + n = pgbIn->dwValidLen; + PRINT0 (_T("ipConvert: alloc new input genbuf, ") + _T("old len=%d, new len=%d\n"), pgbIn->dwBufLen, n); + PRINT0 (_T(" dwMinInBufLen=%d, dwValidLen=%d\n"), + pXform->dwMinInBufLen, pgbIn->dwValidLen); + IP_MEM_ALLOC (n, pbTemp); + memcpy (pbTemp, + pgbIn->pbBuf + pgbIn->dwValidStart, + pgbIn->dwValidLen); + IP_MEM_FREE (pgbIn->pbBuf); + pgbIn->pbBuf = pbTemp; + pgbIn->dwBufLen = n; + pgbIn->dwValidStart = 0; + } + + /* boost size of midbufs if necessary (also 1st malloc of them) */ + n = atTheHead ? 0 : pXform->dwMinInBufLen; + if (!atTheTail && pXform->dwMinOutBufLen > n) + n = pXform->dwMinOutBufLen; + /* note: the code below (correctly) does not create mid-bufs if + * n is 0, which occurs if there's only one xform in the list */ + if (n > g->dwMidLen) { + /* delete both mid-bufs, and (re) allocate them */ + /* copy data from old to new, if necessary */ + PBYTE pbOldMidInBuf = g->pbMidInBuf; + g->pbMidInBuf = NULL; + PRINT0 (_T("ipConvert: alloc mid-bufs, old len=%d, new len=%d\n"), + g->dwMidLen, n); + deleteMidBufs (g); + IP_MEM_ALLOC (n, g->pbMidInBuf ); + IP_MEM_ALLOC (n, g->pbMidOutBuf); + if (pbOldMidInBuf != NULL) { + memcpy (g->pbMidInBuf, pbOldMidInBuf, g->dwMidLen); + IP_MEM_FREE (pbOldMidInBuf); + } + g->dwMidLen = n; + } + + if (atTheTail) { + /* allocate output genbuf */ + n = pXform->dwMinOutBufLen; + if (n < MIN_GENBUF_LEN) + n = MIN_GENBUF_LEN; + PRINT0 (_T("ipConvert: alloc output genbuf, len=%d, minlen=%d\n"), + n, pXform->dwMinOutBufLen); + IP_MEM_ALLOC (n, pgbOut->pbBuf); + pgbOut->dwBufLen = n; + pgbOut->dwValidStart = 0; + pgbOut->dwValidLen = 0; + + /* At this point it is permissible to call ipGetImageTraits + * to obtain output traits because all xforms are past the + * parsing-header state, and thus the output traits are known. + * So tell caller that we're done parsing the header. */ + ipResult |= IP_PARSED_HEADER; + } + + selectCnvState = TRUE; + } + + } else { /* state is XS_CONVERTING or XS_CONV_NOT_RFD or XS_FLUSHING */ + + if (atTheHead) { + /* handle status bits pertaining to the input data */ + if (result & IP_CONSUMED_ROW) { + g->lInRows += 1; + ipResult |= IP_CONSUMED_ROW; + } + if (result & IP_NEW_OUTPUT_PAGE) { + /* a new *output* page for the input xform is a new *input* page + * for the IP as a whole */ + g->iInPages += 1; + ipResult |= IP_NEW_INPUT_PAGE; + } + } + + if (atTheTail) { + /* handle status bits pertaining to the output data */ + ipResult |= (result & (IP_PRODUCED_ROW | IP_NEW_OUTPUT_PAGE)); + if (result & IP_PRODUCED_ROW) + g->lOutRows += 1; + if (result & IP_NEW_OUTPUT_PAGE) + g->iOutPages += 1; + if (result & IP_WRITE_INSERT_OK & g->wResultMask) + g->pendingInsert = TRUE; + } else if (result & IP_NEW_OUTPUT_PAGE) { + /* this xform hit end of page, so tell next xform about it */ + PRINT0 (_T("ipConvert: xform %d hit end of page\n"), iXform); + pNextXform->pXform->newPage (pNextXform->hXform); + } + + if (result & IP_DONE) { + PRINT0 (_T("ipConvert: xform %d is done\n"), iXform); + pXform->eState = XS_DONE; + } else if (pXform->eState != XS_FLUSHING) + selectCnvState = TRUE; + } /* if state is 'parsing header' */ + + if (selectCnvState) { + /* go to one of the two 'converting' states */ + if ((result & IP_READY_FOR_DATA) == 0) + PRINT1 (_T("ipConvert: xform %d is not ready for data\n"), iXform); + pXform->eState = (result & IP_READY_FOR_DATA) + ? XS_CONVERTING : XS_CONV_NOT_RFD; + } + + } /* end of while (TRUE) */ + exitLoop: ; + + /********************/ + /* End of Main Loop */ + /********************/ + + *pdwInputNextPos = pgbIn->dwFilePos + pgbIn->dwValidLen; + + /* After headers are parsed, parsed-header bit should always be set */ + if (g->xfArray[g->xfCount-1].eState >= XS_CONVERTING) + ipResult |= IP_PARSED_HEADER; + + PRINT0 (_T("ipConvert: ipResult=%04x, returning %04x, InputUsed=%d InputNextPos=%d OutputUsed=%d OutputThisPos=%d\n"), + ipResult, ipResult & g->wResultMask, *pdwInputUsed, *pdwInputNextPos, *pdwOutputUsed, *pdwOutputThisPos); + +#ifdef HPIP_DEBUG + if (pbInputBuf && *pdwInputUsed) + write(infd, pbInputBuf, *pdwInputUsed); + + if (*pdwOutputUsed) + write(outfd, pbOutputBuf, *pdwOutputUsed); +#endif + + return ipResult & g->wResultMask; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * ipMirrorBytes - Swaps bits in each byte of buffer + * (bits 0<->7, 1<->6, etc.) + * +\*****************************************************************************/ + +static const BYTE baMirrorImage[256] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +VOID ipMirrorBytes(PBYTE pbInputBuf,DWORD dwInputAvail) { + while (dwInputAvail>0) { + *pbInputBuf=baMirrorImage[*pbInputBuf]; + pbInputBuf++; + dwInputAvail--; + } +} + +/* End of File */ diff --git a/ip/xbi2gray.c b/ip/xbi2gray.c new file mode 100644 index 0000000..a250811 --- /dev/null +++ b/ip/xbi2gray.c @@ -0,0 +1,453 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xbi2gray.c - Converts bilevel into gray (8-bit gray or 24-bit gray) + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * yBi2GrayTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_BI_2_GRAY_OUTPUT_BPP] = format of output: + * 8 = output 8-bit gray, + * 24 = output 24-bit gray. + * aXformInfo[IP_BI_2_GRAY_WHITE_PIXEL] = + * an RGBQUAD representing a white output pixel. + * aXformInfo[IP_BI_2_GRAY_BLACK_PIXEL] = + * an RGBQUAD representing a black output pixel. + * + * Each bilevel pixel is output as a full white or full black pixel. + * This xform needs to know what values to use for those white and + * black output pixels. Hence the two RGBQUAD items above. + * For 8-bit gray output, only the rgbRed field is used in each. + * + * Capabilities and Limitations: + * + * Translates each white or black bilevel input pixel into an 8-bit + * or 24-bit output white or black output pixel. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 1 8 or 24 + * iComponentsPerPixel * must be 1 1 or 3 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Mar 1998 Mark Overton -- wrote code + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + +typedef struct { + IP_IMAGE_TRAITS inTraits; /* traits of the input image */ + UINT uRowsDone; /* number of rows converted so far */ + WORD wOutBitsPerPixel; /* bits/pixel to output (8 or 24) */ + RGBQUAD rgbWhite; /* value of an output white */ + RGBQUAD rgbBlack; /* value of an output black */ + DWORD dwInRowBytes; /* # bytes per input row */ + DWORD dwOutRowBytes; /* # bytes per output row */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} B2G_INST, *PB2G_INST; + + + +/*****************************************************************************\ + * + * bi2gray_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD bi2gray_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PB2G_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(B2G_INST), g); + *pXform = g; + memset (g, 0, sizeof(B2G_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * bi2gray_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD bi2gray_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PB2G_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + INSURE (pTraits->iBitsPerPixel == 1); + INSURE (pTraits->iComponentsPerPixel == 1); + INSURE (pTraits->iPixelsPerRow > 0); + + g->inTraits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * bi2gray_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD bi2gray_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PB2G_INST g; + UINT nBits; + + HANDLE_TO_PTR (hXform, g); + nBits = aXformInfo[IP_BI_2_GRAY_OUTPUT_BPP].dword; + INSURE (nBits==8 || nBits==24); + g->wOutBitsPerPixel = (WORD)nBits; + g->rgbWhite = aXformInfo[IP_BI_2_GRAY_WHITE_PIXEL].rgbquad; + g->rgbBlack = aXformInfo[IP_BI_2_GRAY_BLACK_PIXEL].rgbquad; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * bi2gray_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD bi2gray_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * bi2gray_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD bi2gray_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PB2G_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pInTraits = g->inTraits; /* structure copies */ + *pOutTraits = g->inTraits; + pOutTraits->iBitsPerPixel = g->wOutBitsPerPixel; + pOutTraits->iComponentsPerPixel = g->wOutBitsPerPixel==8 ? 1 : 3; + + g->dwInRowBytes = (g->inTraits.iPixelsPerRow+7) / 8; + g->dwOutRowBytes = g->inTraits.iPixelsPerRow * pOutTraits->iComponentsPerPixel; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * bi2gray_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD bi2gray_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PB2G_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->dwInRowBytes; + *pdwMinOutBufLen = g->dwOutRowBytes; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * bi2gray_convert - Converts one row + * +\*****************************************************************************/ + +static WORD bi2gray_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PB2G_INST g; + PBYTE pIn, pOut, pInAfter; + BYTE bMask, bBilevel; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("bi2gray_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + INSURE (dwInputAvail >= g->dwInRowBytes ); + INSURE (dwOutputAvail >= g->dwOutRowBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pInAfter = pIn + g->dwInRowBytes; + + while (pIn < pInAfter) { + bBilevel = *pIn++; + + if (g->wOutBitsPerPixel == 24) { + for (bMask=0x80u; bMask!=0; bMask>>=1) { + *(RGBQUAD*)pOut = (bMask & bBilevel) + ? g->rgbBlack + : g->rgbWhite; + pOut += 3; + } + } else { /* 8 bits per output pixel */ + for (bMask=0x80u; bMask!=0; bMask>>=1) { + *pOut++ = (bMask & bBilevel) + ? g->rgbBlack.rgbRed + : g->rgbWhite.rgbRed; + } + } + } + + *pdwInputUsed = g->dwInRowBytes; + g->dwInNextPos += g->dwInRowBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = g->dwOutRowBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += g->dwOutRowBytes; + + g->uRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * bi2gray_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD bi2gray_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * bi2gray_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD bi2gray_newPage ( + IP_XFORM_HANDLE hXform) +{ + PB2G_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * bi2gray_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD bi2gray_closeXform (IP_XFORM_HANDLE hXform) +{ + PB2G_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * bi2grayTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL bi2grayTbl = { + bi2gray_openXform, + bi2gray_setDefaultInputTraits, + bi2gray_setXformSpec, + bi2gray_getHeaderBufSize, + bi2gray_getActualTraits, + bi2gray_getActualBufSizes, + bi2gray_convert, + bi2gray_newPage, + bi2gray_insertedData, + bi2gray_closeXform +}; + +/* End of File */ diff --git a/ip/xchgbpp.c b/ip/xchgbpp.c new file mode 100644 index 0000000..dd8a454 --- /dev/null +++ b/ip/xchgbpp.c @@ -0,0 +1,627 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xchangeBPP.c - Changes bits per pixel + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * changeBPPTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_CHANGE_BPP_OUTPUT_BPP] = bits/pixel to output + * + * Capabilities and Limitations: + * + * 1 bpp is assumed to be bilevel (0=white, 1=black). + * 8 and 16 bpp are assumed to be grayscale. + * 24 and 48 bpp are assumed to be 3-component color (rgb is assumed when + * we convert from color into grayscale). + * Among the above supported bpp values, any bpp can be changed into any + * other bpp. Changing into bi-level (bpp=1) performs simple thresholding. + * Use xgray2bi if you want error-diffusion. + * If the input and output bpp values are the same, this xform merely passes + * the pixels through unexamined. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * anything changed as specified + * iComponentsPerPixel * 1 or 3 can be changed to 3 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Mar 1998 Mark Overton -- wrote code (for 1 -> 8/24 only) + * Apr 2000 Mark Overton -- generalized as a change-BPP xform + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include "assert.h" + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + +typedef struct { + IP_IMAGE_TRAITS inTraits; /* traits of the input image */ + UINT uRowsDone; /* number of rows converted so far */ + WORD wOutBitsPerPixel; /* bits/pixel to output */ + DWORD dwInRowBytes; /* # bytes per input row */ + DWORD dwOutRowBytes; /* # bytes per output row */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} CBPP_INST, *PCBPP_INST; + + + +/*****************************************************************************\ + * + * changeBPP_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD changeBPP_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PCBPP_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(CBPP_INST), g); + *pXform = g; + memset (g, 0, sizeof(CBPP_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * changeBPP_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD changeBPP_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PCBPP_INST g; + int bpp; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + bpp = pTraits->iBitsPerPixel; + INSURE (bpp==1 || bpp==8 || bpp==16 || bpp==24 || bpp==48); + INSURE (pTraits->iComponentsPerPixel==1 || pTraits->iComponentsPerPixel==3); + INSURE (pTraits->iPixelsPerRow > 0); + + g->inTraits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * changeBPP_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD changeBPP_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PCBPP_INST g; + UINT nBits; + + HANDLE_TO_PTR (hXform, g); + nBits = aXformInfo[IP_CHANGE_BPP_OUTPUT_BPP].dword; + g->wOutBitsPerPixel = (WORD)nBits; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * changeBPP_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD changeBPP_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * changeBPP_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD changeBPP_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PCBPP_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pInTraits = g->inTraits; /* structure copies */ + *pOutTraits = g->inTraits; + pOutTraits->iBitsPerPixel = g->wOutBitsPerPixel; + pOutTraits->iComponentsPerPixel = g->wOutBitsPerPixel<24 ? 1 : 3; + + g->dwInRowBytes = (g->inTraits.iPixelsPerRow*g->inTraits.iBitsPerPixel + 7) / 8; + g->dwOutRowBytes = (g->inTraits.iPixelsPerRow*g->wOutBitsPerPixel + 7) / 8; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * changeBPP_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD changeBPP_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PCBPP_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->dwInRowBytes; + *pdwMinOutBufLen = g->dwOutRowBytes; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * changeBPP_convert - Converts one row + * +\*****************************************************************************/ + +static WORD changeBPP_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PCBPP_INST g; + PBYTE pIn, pOut, pInAfter; + unsigned rv, gv, bv; + BYTE bMask, bBilevel; + BYTE bPixels; + WORD wPixels; + DWORD dwPixels; + + #define OUTPUT_THRESHOLDED_BIT(grayParam) { \ + int gray = grayParam; \ + if (gray < 128) bBilevel |= bMask; \ + bMask >>= 1; \ + if (bMask == 0) { \ + *pOut++ = (BYTE)bBilevel; \ + bBilevel = 0; \ + bMask = (BYTE)0x80; \ + } \ + } + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("changeBPP_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + INSURE (dwInputAvail >= g->dwInRowBytes ); + INSURE (dwOutputAvail >= g->dwOutRowBytes); + + bMask = 0x80; + bBilevel = 0; + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pInAfter = pIn + g->dwInRowBytes; + + if (g->inTraits.iBitsPerPixel == g->wOutBitsPerPixel) { + /* no change in bpp; just copy the buffer */ + memcpy (pOut, pIn, g->dwInRowBytes); + } else if (g->inTraits.iBitsPerPixel == 1) { + while (pIn < pInAfter) { + bBilevel = *pIn++; + + if (g->wOutBitsPerPixel == 48) { + for (bMask=0x80u; bMask!=0; bMask>>=1) { + dwPixels = (bMask & bBilevel) + ? 0 /* black */ + : 0xffffffff; /* white */ + *(DWORD*)pOut = dwPixels; + *(WORD*)(pOut+4) = (WORD)dwPixels; + pOut += 6; + } + } else if (g->wOutBitsPerPixel == 24) { + for (bMask=0x80u; bMask!=0; bMask>>=1) { + *(DWORD*)pOut = (bMask & bBilevel) + ? 0 /* black */ + : 0xffffffff; /* white */ + pOut += 3; + } + } else if (g->wOutBitsPerPixel == 16) { + for (bMask=0x80u; bMask!=0; bMask>>=1) { + *(WORD*)pOut = (bMask & bBilevel) + ? 0 /* black */ + : 0xffff; /* white */ + pOut += 2; + } + } else if (g->wOutBitsPerPixel == 8) { + for (bMask=0x80u; bMask!=0; bMask>>=1) { + *pOut++ = (bMask & bBilevel) + ? 0 /* black */ + : 0xff; /* white */ + } + } else + assert (0); + } + } + else if (g->inTraits.iBitsPerPixel == 8) { + if (g->wOutBitsPerPixel == 48) { + while (pIn < pInAfter) { + wPixels = (*pIn++ << 8); + *(WORD*)(pOut+0) = wPixels; + *(WORD*)(pOut+2) = wPixels; + *(WORD*)(pOut+4) = wPixels; + pOut += 6; + } + } else if (g->wOutBitsPerPixel == 24) { + while (pIn < pInAfter) { + bPixels = *pIn++; + *pOut++ = bPixels; + *pOut++ = bPixels; + *pOut++ = bPixels; + } + } else if (g->wOutBitsPerPixel == 16) { + while (pIn < pInAfter) { + wPixels = (*pIn++ << 8); + *(WORD*)pOut = wPixels; + pOut += 2; + } + } else if (g->wOutBitsPerPixel == 1) { + while (pIn < pInAfter) { + OUTPUT_THRESHOLDED_BIT (*pIn); + pIn++; + } + } else + assert (0); + } + else if (g->inTraits.iBitsPerPixel == 16) { + if (g->wOutBitsPerPixel == 48) { + while (pIn < pInAfter) { + wPixels = *(WORD*)pIn; + *(WORD*)(pOut+0) = wPixels; + *(WORD*)(pOut+2) = wPixels; + *(WORD*)(pOut+4) = wPixels; + pIn += 2; + pOut += 6; + } + } else if (g->wOutBitsPerPixel == 24) { + while (pIn < pInAfter) { + bPixels = (*(WORD*)pIn) >> 8; + pIn += 2; + *pOut++ = bPixels; + *pOut++ = bPixels; + *pOut++ = bPixels; + } + } else if (g->wOutBitsPerPixel == 8) { + while (pIn < pInAfter) { + *pOut++ = (*(WORD*)pIn) >> 8; + pIn += 2; + } + } else if (g->wOutBitsPerPixel == 1) { + while (pIn < pInAfter) { + OUTPUT_THRESHOLDED_BIT ((*(WORD*)pIn) >> 8); + pIn += 2; + } + } else + assert (0); + } + else if (g->inTraits.iBitsPerPixel == 24) { + if (g->wOutBitsPerPixel == 48) { + while (pIn < pInAfter) { + *(WORD*)(pOut+0) = (WORD)(*pIn++) << 8; + *(WORD*)(pOut+2) = (WORD)(*pIn++) << 8; + *(WORD*)(pOut+4) = (WORD)(*pIn++) << 8; + pOut += 6; + } + } else if (g->wOutBitsPerPixel == 16) { + /* converting rgb color (24) to grayscale (16) */ + while (pIn < pInAfter) { + rv = (*pIn++) << 8; + gv = (*pIn++) << 8; + bv = (*pIn++) << 8; + *(WORD*)pOut = NTSC_LUMINANCE (rv, gv, bv); + pOut += 2; + } + } else if (g->wOutBitsPerPixel == 8) { + /* converting rgb color (24) to grayscale (8) */ + while (pIn < pInAfter) { + rv = *pIn++; + gv = *pIn++; + bv = *pIn++; + *pOut++ = NTSC_LUMINANCE (rv, gv, bv); + } + } else if (g->wOutBitsPerPixel == 1) { + /* converting rgb color (24) to bi-level */ + while (pIn < pInAfter) { + rv = *pIn++; + gv = *pIn++; + bv = *pIn++; + OUTPUT_THRESHOLDED_BIT (NTSC_LUMINANCE(rv,gv,bv)); + } + } else + assert (0); + } + else if (g->inTraits.iBitsPerPixel == 48) { + if (g->wOutBitsPerPixel == 24) { + while (pIn < pInAfter) { + *pOut++ = ((WORD*)pIn)[0] >> 8; + *pOut++ = ((WORD*)pIn)[1] >> 8; + *pOut++ = ((WORD*)pIn)[2] >> 8; + pIn += 6; + } + } else if (g->wOutBitsPerPixel == 16) { + /* converting rgb color (48) to grayscale (16) */ + while (pIn < pInAfter) { + rv = ((WORD*)pIn)[0]; + gv = ((WORD*)pIn)[1]; + bv = ((WORD*)pIn)[2]; + *(WORD*)pOut = NTSC_LUMINANCE (rv, gv, bv); + pIn += 6; + pOut += 2; + } + } else if (g->wOutBitsPerPixel == 8) { + /* converting rgb color (48) to grayscale (8) */ + while (pIn < pInAfter) { + rv = ((WORD*)pIn)[0]; + gv = ((WORD*)pIn)[1]; + bv = ((WORD*)pIn)[2]; + *pOut++ = NTSC_LUMINANCE (rv, gv, bv) >> 8; + pIn += 6; + } + } else if (g->wOutBitsPerPixel == 1) { + /* converting rgb color (48) to bi-level */ + while (pIn < pInAfter) { + rv = ((WORD*)pIn)[0]; + gv = ((WORD*)pIn)[1]; + bv = ((WORD*)pIn)[2]; + OUTPUT_THRESHOLDED_BIT (NTSC_LUMINANCE(rv,gv,bv) >> 8); + pIn += 6; + } + } else + assert (0); + } + + if (g->inTraits.iBitsPerPixel>1 && g->wOutBitsPerPixel==1 && bMask!=(BYTE)0x80) + *pOut = bBilevel; /* output any partially-filled byte */ + + *pdwInputUsed = g->dwInRowBytes; + g->dwInNextPos += g->dwInRowBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = g->dwOutRowBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += g->dwOutRowBytes; + + g->uRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * changeBPP_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD changeBPP_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * changeBPP_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD changeBPP_newPage ( + IP_XFORM_HANDLE hXform) +{ + PCBPP_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * changeBPP_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD changeBPP_closeXform (IP_XFORM_HANDLE hXform) +{ + PCBPP_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * changeBPPTbl - Jump-table for xform + * +\*****************************************************************************/ + +IP_XFORM_TBL changeBPPTbl = { + changeBPP_openXform, + changeBPP_setDefaultInputTraits, + changeBPP_setXformSpec, + changeBPP_getHeaderBufSize, + changeBPP_getActualTraits, + changeBPP_getActualBufSizes, + changeBPP_convert, + changeBPP_newPage, + changeBPP_insertedData, + changeBPP_closeXform +}; + +/* End of File */ diff --git a/ip/xcolrspc.c b/ip/xcolrspc.c new file mode 100644 index 0000000..98a60b8 --- /dev/null +++ b/ip/xcolrspc.c @@ -0,0 +1,1191 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xcolrspc.c - Converts between color spaces + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * colorTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV] = Which color-space conversion to do: + * IP_CNV_YCC_TO_CIELAB = ycc -> cielab + * IP_CNV_CIELAB_TO_YCC = cielab -> ycc + * IP_CNV_YCC_TO_SRGB = ycc -> srgb + * IP_CNV_SRGB_TO_YCC = srgb -> ycc + * IP_CNV_LHS_TO_SRGB = lhs -> srgb + * IP_CNV_SRGB_TO_LHS = srgb -> lhs + * IP_CNV_BGR_SWAP = rgb<->bgr swap + * + * aXformInfo[IP_CNV_COLOR_SPACE_GAMMA] = Gamma value for gamma correction, + * in 16.16 fixed-point. + * In the YCC->CIELab conversion, the data will be + * inverse-gamma corrected using 1/Gamma. + * In the CIELab->YCC conversion, it will be gamma corrected + * using Gamma. + * This Gamma value is ignored in the other conversions. + * A value of 0.0 makes us use a default Gamma of 2.2. + * You must set this to 1.0 if you want to disable Gamma. + * + * Gamma is done here because the pixels are in RGB at one + * point in the YCC<->CIELab conversions. Since you can't do + * Gamma on YCC or CIELab data, it can't be done outside + * this xform for those two conversions. + * + * Capabilities and Limitations: + * + * Does the space conversions listed above. Conversions will only be done on + * 24-bit data; monochrome data are passed through unchanged. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be <= 24 same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Feb 1998 Mark Overton -- wrote original code, with dummy conversions + * Apr 1998 Mark Overton -- added actual conversion code, adapted from + * Cindy Samson's code + * +\******************************************************************************/ + +#include "math.h" // needed for pow and floor +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include "assert.h" + +/* xsc_clean[Uu]p.h: */ + +int Send_yTable[100] = { +210,212,213,214,215,216, +217,218,219,220,221,223, +224,225,226,228,230,232, +233,234,235,237,238,239, +240,242,243,244,245,246, +247,248,249,250,251,252, +253,254,255,255,255,255, +255,255,255,255}; + +/* end xsc_clean[Uu]p.h */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input image */ + IP_WHICH_CNV which_cnv; /* which space-conversion to do */ + BYTE bGammaTbl[256]; /* Gamma-correction table */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} COL_INST, *PCOL_INST; + +static BOOL fInited = FALSE; + +//Neutral Shift Data Definition +#define NEUTRAL_SHIFT_SEND TRUE + + +// constants to convert [r,g,b] into Y +#define RGBTOY_R_FAC 0.30078125f +#define RGBTOY_G_FAC 0.5859375f +#define RGBTOY_B_FAC 0.11328125f + +// constants to convert [y,cb,cr] into [r,g,b] +#define YCCTORGB_CR_TO_R 1.39946f +#define YCCTORGB_CB_TO_G -0.344228f +#define YCCTORGB_CR_TO_G -0.717202f +#define YCCTORGB_CB_TO_B 1.77243f + + +// The above constants used for converting between RGB and YCC are based +// on the following conversion matrices. The code assumes that the +// 0.003 and 0.006 below are zero. +#if 0 + static float RGBtoYCC[3][3]= { // input is RGB + 0.30078125f, 0.5859375f, 0.11328125f, // Y + -0.16796875f, -0.33203125f, 0.5f, // Cb + 0.5f, -0.41796875f, -0.08203125f // Cr + }; + + static float YCCtoRGB[3][3]= { // input is YCbCr + 1.0f, 0.003f, 1.39946f, // R + 1.0f, -0.344228f, -0.717202f, // G + 1.0f, 1.77243f, 0.006f // B + }; +#endif + + +#define X_SCALE (255.0/244.0) +#define Y_SCALE (255.0/255.0) +#define Z_SCALE (255.0/210.0) + +#define DEFAULT_GAMMA 2.2f +#define SLIGHT_BOOST (255.0/253.0) // todo - get rid of this + + +/****************************************************************************\ + **************************************************************************** + ** ** + ** W O R K E R R O U T I N E S ** + ** ** + **************************************************************************** +\****************************************************************************/ + + + +#define CLIP(wilma) ((wilma>255) ? 255 : ((wilma<0) ? 0 : (wilma))) + +#define TERM_FRAC_BITS 4 // # frac bits in temp x/y/z terms +#define CONV_FRAC_BITS 4 // # frac bits in "_fix" conversion tables +#define CONV_FRAC_ROUND (1L<<(CONV_FRAC_BITS-1)) +#define TBL33_FRAC_BITS 16 // # bits of fraction in 3x3 tables +#define TABLE_FRAC_SCALE (1L << TBL33_FRAC_BITS) + + +// these tables are computed by initTables + +static BYTE YtoL [256]; // these are for to/from CIELab +static BYTE LtoY [256]; +static short cubeRoot [256]; +static BYTE cube [256]; + +static short cb2b [256]; // these are for [y,cb,cr]->[r,g,b] +static short cr2r [256]; +static short cr2g_fix [256]; +static short cb2g_fix [256]; + +static short r2y_fix [256]; // these are for [r,g,b]->Y +static short g2y_fix [256]; +static short b2y_fix [256]; + +static BYTE by2cb [2*256]; // these are for [r,g,b]->CbCr +static BYTE ry2cr [2*256]; + + + +// VectMult - Multiplies a pixel by a 3x3 matrix, with fixed-point math +// +static void VectMult( + int inPixel[3], // in: input pixel + int outPixel[3], // out: output pixel + long *pMat) // in: 3x3 matrix with TBL33_FRAC_BITS of fraction +{ + int i; + + for (i=0; i<3; i++) { + outPixel[i] = (int)( pMat[0]*inPixel[0] + + pMat[1]*inPixel[1] + + pMat[2]*inPixel[2] + (1L<<(TBL33_FRAC_BITS-1)) + ) >> TBL33_FRAC_BITS; + pMat += 3; + } +} + + + +// initTables - Computes look-up tables +// +// CIE Illuminant D50 white point Xn=96.422 Yn=100 Zn=82.521 +// CIE Illuminant D65 white point Xn=95.04 Yn=100 Zn=108.89 +// FAX gamut Range: +// L* = [0,100] +// a* = [-85, 85] +// b* = [-75, 125] +// +// NL = 255/100*l +// Na = 255/170 x a* + 128 +// Nb = 255/200 x b* + 96 +// + +static void initTables (void) +{ + int val; + int icr, icb; + float fval; + float t; + + for (val=0; val<=255; val++) + { + fval = (float)val / 255.0f; + + // compute Y->L array + + if (fval > 0.008856f) t = 116.0f*(float)pow(fval,1.0/3.0) - 16.0f; + else t = 903.3f*fval; + t = (255.0f/100.0f)*t; + if (t < 0.0f) t = 0.0f; + if (t > 255.0f) t = 255.0f; + YtoL[val] = (BYTE)(t + 0.5f); + + // compute L->Y array + + if (val <= 7) + t = val * (100.0f/903.3f); + else { + t = (fval*100.0f+16.0f) / 116.0f; + t = 255.0f*t*t*t; + } + LtoY[val] = (BYTE)(t + 0.5f); + + // compute cube-root array. + // Input is 0..255. We divide it by 255 so it's 0..1, take cube root, + // and scale result to 0..255. Adjustment is for small numbers. + + if (fval > 0.008856f) t = (float)pow(fval, 1.0f/3.0f); + else t = 7.7867f*fval + 16.0f/116.0f; + cubeRoot[val] = (short)(t*255.0f*(1L<<TERM_FRAC_BITS) + 0.5f); + + cube[val] = (BYTE)(255.0f*fval*fval*fval); + // above, we don't round because we won't reach full black in lab->ycc conv + + // compute ycc<->rgb conversion tables + + fval = (float)val; + + r2y_fix[val] = (short)(SLIGHT_BOOST*RGBTOY_R_FAC*fval*(1<<CONV_FRAC_BITS)); + g2y_fix[val] = (short)(SLIGHT_BOOST*RGBTOY_G_FAC*fval*(1<<CONV_FRAC_BITS)); + b2y_fix[val] = (short)(SLIGHT_BOOST*RGBTOY_B_FAC*fval*(1<<CONV_FRAC_BITS)); + + fval -= 128.0f; + + cb2b [val] = (short)(floor(YCCTORGB_CB_TO_B*fval + 0.5)); + cr2r [val] = (short)(floor(YCCTORGB_CR_TO_R*fval + 0.5)); + cb2g_fix[val] = (short)(YCCTORGB_CB_TO_G*fval*(1<<CONV_FRAC_BITS)); + cr2g_fix[val] = (short)(YCCTORGB_CR_TO_G*fval*(1<<CONV_FRAC_BITS)); + } + + + for (val=0; val<=2*255; val++) + { + fval = (float)val - 255.0f; + + icb = (int)floor(fval/YCCTORGB_CB_TO_B + 0.5); + if (icb>=-4 && icb<=4) // make sure white is white + icb = 0; + icb += 128; + by2cb[val] = (BYTE)CLIP(icb); + + icr = (int)floor(fval/YCCTORGB_CR_TO_R + 0.5); + if (icr>=-4 && icr<=4) // make sure white is white. + icr = 0; + icr += 128; + ry2cr[val] = (BYTE)CLIP(icr); + } +} + + + +static void calcGammaTable ( + PCOL_INST g, + DWORD dwGamma) /* Gamma value in 16.16 fixed-point */ +{ + #define MAX_GAMMA_SLOPE 4 + + int i; + int maxval; + float fGamma, f, gamval; + BYTE bGamVal; + + fGamma = (dwGamma == 0) ? DEFAULT_GAMMA : (float)dwGamma/(1L<<16); + + switch (g->which_cnv) { + case IP_CNV_YCC_TO_CIELAB: + /* YCC is assumed to have been Gamma corrected. So we must do + * inverse Gamma when converting to CIELab + */ + fGamma = 1.0f / fGamma; + break; + + case IP_CNV_CIELAB_TO_YCC: + /* CIELab has not been Gamma corrected, so we must do forward + * Gamma when going to YCC. + */ + break; + + default: + /* No Gamma for the other conversions */ + fGamma = 1.0f; + } + + if (fGamma == 1.0f) { + /* No gamma correction: use identity table */ + for (i=0; i<=255; i++) + g->bGammaTbl[i] = i; + } else { + fGamma = 1.0f / fGamma; + for (i=0; i<=255; i++) { + f = (float)i / 255.0f; + gamval = (float)pow(f, fGamma); + bGamVal = (BYTE)(255.0f*gamval + 0.5f); + maxval = (int)(i * MAX_GAMMA_SLOPE); + if (fGamma<1.0f && bGamVal>maxval) + bGamVal = maxval; + g->bGammaTbl[i] = bGamVal; + } + } +} + + + +/****************************************************************************\ + **************************************************************************** + ** ** + ** YCC -> sRGB and YCC -> CIELAB ** + ** ** + **************************************************************************** +\****************************************************************************/ + + +/* Table for Molokai (and Wizard, I think) */ +#if 0 +static long RGBtoXYZ50[9] = { + (long)(0.4358530*X_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.3840300*X_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.1431260*X_SCALE*TABLE_FRAC_SCALE + 0.5), + + (long)(0.2225640*Y_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.7200520*Y_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.0607176*Y_SCALE*TABLE_FRAC_SCALE + 0.5), + + (long)(0.0139307*Z_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.0973260*Z_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.7142870*Z_SCALE*TABLE_FRAC_SCALE + 0.5) +}; +#endif + + +/* Table for Polaris and Avalon */ +#if 1 +static long RGBtoXYZ50[9] = { + (long)(0.464700*X_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.339211*X_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.156961*X_SCALE*TABLE_FRAC_SCALE + 0.5), + + (long)(0.220615*Y_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.700919*Y_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.053199*Y_SCALE*TABLE_FRAC_SCALE + 0.5), + + (long)(0.005146*Z_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.050405*Z_SCALE*TABLE_FRAC_SCALE + 0.5), + (long)(0.774384*Z_SCALE*TABLE_FRAC_SCALE + 0.5) +}; +#endif + + + +// YCCToCIELab - Converts a pixel (3 unsigned bytes) from YCC to fax LAB +// +// Conversions done herein: +// YCC -> sRGB -> inverse Gamma -> XYZ(d65) -> XYZ(d50) -> LAB +// Some of the above steps are combined for speed. +// +static void YCCToCIELab ( + PBYTE pYCC, // in: YCC pixel (3 unsigned bytes) + PBYTE pCIELab, // out: LAB pixel (3 unsigned bytes) + PBYTE pGamma) // in: inverse Gamma table +{ + int iy, icb,icr; + int sR, sG, sB; + int sRGBval[3]; + int XYZ50val[3]; + int x, y, z; + int xterm, yterm, zterm; + int L, a, b; + int absCr, absCb; //Neutral Shift Purpose + + + iy = pYCC[0]; + icb = pYCC[1]; + icr = pYCC[2]; + +#ifdef NEUTRAL_SHIFT_SEND + + absCb = abs(icb-128); + absCr = abs(icr-128); + + if (iy>=210) + { + iy = Send_yTable[iy-210]; + } + + if (iy ==255) + { + + if ((absCb < 5) && (absCr <5)) + { + icr = icb = 128; + } + } + else if (iy>240) + { + if ((absCb < 4) && (absCr <4)) + { + icr=icb=128; + iy = 255; + } + else if ((absCb < 3) && (absCr <3)) + { + icr=icb=128; + } + } + else if (iy>230) + { + if ((absCb < 3) && (absCr <3)) + { + icr=icb=128; + iy = 250; + } + } + else if (iy>220) + { + if ((absCb < 3) && (absCr <3)) + { + icr=icb=128; + iy = 240; + } + } + else if (iy >60) + { + if ((absCb <3) && (absCr <3)) + { + icr=icb=128; + } + } + + #endif + +/* END NEUTRAL SHIFT */ + // iy=77; icb=85; icr=255; // solid red + // ycc=29,255,107 is solid blue + + + /**** Convert YCC to sRGB ****/ + + sR = iy + cr2r[icr]; + sR = CLIP(sR); + + sG = iy + ((cr2g_fix[icr] + cb2g_fix[icb] + CONV_FRAC_ROUND) >> CONV_FRAC_BITS); + sG = CLIP(sG); + + sB = iy + cb2b[icb]; + sB = CLIP(sB); + + /**** Inverse Gamma correction, and Convert sRGB(d65) to XYZ(d50) ****/ + + sRGBval[0] = (long)pGamma[sR]; + sRGBval[1] = (long)pGamma[sG]; + sRGBval[2] = (long)pGamma[sB]; + + VectMult (sRGBval, XYZ50val, RGBtoXYZ50); + + x = CLIP(XYZ50val[0]); + y = CLIP(XYZ50val[1]); + z = CLIP(XYZ50val[2]); + + /**** Convert XYZ(d50) to fax LAB ****/ + + pCIELab[0] = L = YtoL[y]; + xterm = cubeRoot[x]; + yterm = cubeRoot[y]; + zterm = cubeRoot[z]; + + b = yterm - zterm; + b += (96<<TERM_FRAC_BITS) + (1<<(TERM_FRAC_BITS-1)); + b >>= TERM_FRAC_BITS; + pCIELab[2] = (BYTE)CLIP(b); + + if (b < 0) { /* todo - adjustment for out-of-gamut bright blue */ + /* xterm and L factors below of 6,1 -> rgb of 32,31,229 */ + /* xterm and L factors below of 8,2 -> rgb of 84,80,255 */ + /* xterm and L factors below of 6,0.5 are visually best */ + xterm += 6*b; + L -= b>>1; + pCIELab[0] = (BYTE)CLIP(L); + } + + // a = (long)((1<<10)*500.0/170.0) * (xterm-yterm) >> 10; + // the 3*a - a/16 below is close enough to (500.0/170.0)*a + a = xterm - yterm; + a = a + a + a - (a>>4); // multiply by approx 500/170 + a += (128<<TERM_FRAC_BITS) + (1<<(TERM_FRAC_BITS-1)); + a >>= TERM_FRAC_BITS; + pCIELab[1] = (BYTE)CLIP(a); +} + + + +// YCCTosRGB - Converts a pixel (3 unsigned bytes) from YCC to sRGB +// +static void YCCTosRGB ( + PBYTE pYCC, // in: YCC pixel (3 unsigned bytes) + PBYTE psRGB) // out: sRGB pixel (3 unsigned bytes) +{ + int iy, icb, icr; + int sR, sG, sB; + + iy = pYCC[0]; + icb = pYCC[1]; + icr = pYCC[2]; + + sR = iy + cr2r[icr]; + psRGB[0] = (BYTE)CLIP(sR); + + sG = iy + ((cr2g_fix[icr] + cb2g_fix[icb] + CONV_FRAC_ROUND) >> CONV_FRAC_BITS); + psRGB[1] = (BYTE)CLIP(sG); + + sB = iy + cb2b[icb]; + psRGB[2] = (BYTE)CLIP(sB); +} + + + +/****************************************************************************\ + **************************************************************************** + ** ** + ** sRGB -> YCC and CIELAB -> YCC ** + ** ** + **************************************************************************** +\****************************************************************************/ + + +static long XYZ50tosRGB[9] = { + (long)( 3.1344500*TABLE_FRAC_SCALE /* *(255.0/248.0) */ /X_SCALE + 0.5), + (long)(-1.6177000*TABLE_FRAC_SCALE /* *(255.0/248.0) */ /Y_SCALE - 0.5), + (long)(-0.4905000*TABLE_FRAC_SCALE /* *(255.0/248.0) */ /Z_SCALE - 0.5), + + (long)(-0.9788600*TABLE_FRAC_SCALE /* *(255.0/258.0) */ /X_SCALE - 0.5), + (long)( 1.9164800*TABLE_FRAC_SCALE /* *(255.0/258.0) */ /Y_SCALE + 0.5), + (long)( 0.0334962*TABLE_FRAC_SCALE /* *(255.0/258.0) */ /Z_SCALE + 0.5), + + (long)( 0.0719813*TABLE_FRAC_SCALE /* *(255.0/254.0) */ /X_SCALE + 0.5), + (long)(-0.2290660*TABLE_FRAC_SCALE /* *(255.0/254.0) */ /Y_SCALE - 0.5), + (long)( 1.4050500*TABLE_FRAC_SCALE /* *(255.0/254.0) */ /Z_SCALE + 0.5) +}; + + + +// CIELabToYCC - Converts a pixel (3 unsigned bytes) from fax LAB to YCC +// +// Conversions done herein: +// LAB(d50) -> XYZ(d50) -> XYZ(d65) -> sRGB -> Gamma -> YCC +// Some of the above steps are combined for speed. +// +static void CIELabToYCC ( + PBYTE pCIELab, // in: LAB pixel (3 unsigned bytes) + PBYTE pYCC, // out: YCC pixel (3 unsigned bytes) + PBYTE pGamma) // in: Gamma table +{ + int a, b, xterm, yterm, zterm; + int Y; + long factor; + int XYZ50[3]; + int sRGB[3]; + int sR,sG,sB; + int iy; + int icr, icb; + + /**** LAB -> XYZ, both in d50 ****/ + + factor = (long)((1L<<16)*170.0/500.0); + a = (int)(((((long)pCIELab[1]-128) * factor) + 0x8000L) >> 16); + b = (int)pCIELab[2] - 96; + + XYZ50[1] = Y = LtoY[pCIELab[0]]; + yterm = (cubeRoot[Y] + (1<<(TERM_FRAC_BITS-1))) >> TERM_FRAC_BITS; + + xterm = a + yterm; + XYZ50[0] = cube[CLIP(xterm)]; + + zterm = yterm - b; + XYZ50[2] = cube[CLIP(zterm)]; + + /**** XYZ(d50)->XYZ(d65)->sRGB via 3x3 matrix, then Gamma correct ****/ + + VectMult (XYZ50, sRGB, XYZ50tosRGB); + + sR = pGamma[CLIP(sRGB[0])]; + sG = pGamma[CLIP(sRGB[1])]; + sB = pGamma[CLIP(sRGB[2])]; + + /**** sRGB -> YCC ****/ + + iy = r2y_fix[sR] + g2y_fix[sG] + b2y_fix[sB]; + iy = (iy + CONV_FRAC_ROUND) >> CONV_FRAC_BITS; + iy = CLIP(iy); + + //It is done inside the Neutral Shift area + //pYCC[0] = (BYTE)iy; + + //pYCC[1] = (by2cb+255)[sB-iy]; + // pYCC[2] = (ry2cr+255)[sR-iy]; + icb = (int)((by2cb+255)[sB-iy]); //it is pYCC[1] + icr = (int)((ry2cr+255)[sR-iy]); //it is pYCC[2] + + pYCC[0] = (BYTE)iy; + pYCC[1] = (BYTE)icb; + pYCC[2] = (BYTE)icr; + + + +} + + + +// sRGBToYCC - Converts a pixel (3 unsigned bytes) from sRGB to YCC +// +static void sRGBToYCC ( + PBYTE psRGB, // in: sRGB pixel (3 unsigned bytes) + PBYTE pYCC) // out: YCC pixel (3 unsigned bytes) +{ + int sR, sG, sB; + int iy; + + sR = psRGB[0]; + sG = psRGB[1]; + sB = psRGB[2]; + + iy = r2y_fix[sR] + g2y_fix[sG] + b2y_fix[sB]; + iy = (iy + CONV_FRAC_ROUND) >> CONV_FRAC_BITS; + iy = CLIP(iy); + pYCC[0] = (BYTE)iy; + + pYCC[1] = (by2cb+255)[sB-iy]; + pYCC[2] = (ry2cr+255)[sR-iy]; +} + + + +/****************************************************************************\ + **************************************************************************** + ** ** + ** sRGB -> HLS and HLS -> YCC ** + ** ** + **************************************************************************** +\****************************************************************************/ + + + +// sRGBToLHS - Converts a pixel (3 unsigned bytes) from sRGB to LHS +// +static void sRGBToLHS ( + PBYTE psRGB, // in: sRGB pixel (3 unsigned bytes) + PBYTE pLHS) // out: LHS pixel (3 unsigned bytes) +{ + int R, G, B; + int L, H, S; // these are in 0..255 + int maxVal, minVal, diff, sum, numerator; + + R = (unsigned)psRGB[0]; + G = (unsigned)psRGB[1]; + B = (unsigned)psRGB[2]; + + maxVal = IP_MAX (R, G); + maxVal = IP_MAX (maxVal, B); + minVal = IP_MIN (R, G); + minVal = IP_MIN (minVal, B); + diff = maxVal - minVal; + sum = maxVal + minVal; + L = sum >> 1; + + if (diff <= 1) { + S = 0; + H = 0; + } else { + // below is really 255*diff / (...), but avoiding the multiply + S = ((diff<<9) - diff - diff) / (L<=127 ? sum : 510-sum); + S = (S+1) >> 1; // round to 8 bits + + // determine the hue + if (R == maxVal) { + numerator = (maxVal-B) - (maxVal-G); + H = 0; // red is at 0 degrees + } else if (G == maxVal) { + numerator = (maxVal-R) - (maxVal-B); + H = (1<<12)*1/3; // green is at 120 degrees + } else { // blue-dominant + numerator = (maxVal-G) - (maxVal-R); + H = (1<<12)*2/3; // blue is at 240 degrees + } + + // The line below is same as: hue += ((1<<12)/6) * numerator / diff; + // but is faster and more accurate. + H += (numerator<<11) / (diff+diff+diff); + H = (H + (1<<3)) >> 4; // rounds 12 bits down to 8 bits + } + + pLHS[0] = (unsigned char)L; + pLHS[1] = (unsigned char)H; + pLHS[2] = (unsigned char)S; +} + + + +// LHSTosRGB - Converts a pixel (3 unsigned bytes) from LHS to sRGB +// +static void LHSTosRGB ( + PBYTE pLHS, // in: LHS pixel (3 unsigned bytes) + PBYTE psRGB) // out: sRGB pixel (3 unsigned bytes) +{ + int L, H, S; + int R=0, G=0, B=0; + int hbase, hfrac, product, maxVal, minVal, midVal; + + L = (unsigned)pLHS[0]; // 1.0 is at 255 + H = (unsigned)pLHS[1]; // 1.0 is at 255 (which is 360 degrees) + S = (unsigned)pLHS[2]; // 1.0 is at 255 + + // In RGB_to_HLS, L=sum/2, which truncates. The average error from this + // truncation is 0.25, which is the (1<<4) added below. + L = (L<<6) + (1<<4); // now 1.0 is at 255*(1<<6), 6 bits of frac + S = S<<6; + + H *= 6; + hbase = H >> 8; // in 0..5, and is the basic hue + hfrac = H & 0x00ff; // fractional offset from basic hue to next basic hue + + product = L*S; + product = (product + (product>>8)) >> (8+6); // approx division by 255*(1<<6) + if (L <= (127<<6)+(1<<4)) maxVal = L + product; + else maxVal = L + S - product; + + minVal = L + L - maxVal; + midVal = minVal + (((maxVal-minVal) * ((hbase&1) ? 256-hfrac : hfrac)) >> 8); + + // round the results to 8 bits by shifting out the 6 frac bits + minVal = (minVal+(1<<5)) >> 6; + midVal = (midVal+(1<<5)) >> 6; + maxVal = (maxVal+(1<<5)) >> 6; + + // I ran this routine with all possible h-l-s values (2^24 of them!), and + // none produced a value outside 0..255, so we don't do the checks below. + // if (maxVal < 0) maxVal=0; else if (maxVal > 255) maxVal = 255; + // if (midVal < 0) midVal=0; else if (midVal > 255) midVal = 255; + // if (minVal < 0) minVal=0; else if (minVal > 255) minVal = 255; + + switch (hbase) + { + case 0: R = maxVal; G = midVal; B = minVal; break; + case 1: R = midVal; G = maxVal; B = minVal; break; + case 2: R = minVal; G = maxVal; B = midVal; break; + case 3: R = minVal; G = midVal; B = maxVal; break; + case 4: R = midVal; G = minVal; B = maxVal; break; + case 5: R = maxVal; G = minVal; B = midVal; break; + } + + psRGB[0] = (BYTE)R; + psRGB[1] = (BYTE)G; + psRGB[2] = (BYTE)B; +} + + + +/****************************************************************************\ + **************************************************************************** + ** ** + ** E N T R Y P O I N T S ** + ** ** + **************************************************************************** +\****************************************************************************/ + + + +/*****************************************************************************\ + * + * color_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD color_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PCOL_INST g; + + if (! fInited) { + initTables (); + fInited = TRUE; + } + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(COL_INST), g); + *pXform = g; + memset (g, 0, sizeof(COL_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * color_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD color_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PCOL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + INSURE (pTraits->iBitsPerPixel <= 24); + INSURE (pTraits->iPixelsPerRow > 0); + + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * color_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD color_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PCOL_INST g; + + HANDLE_TO_PTR (hXform, g); + g->which_cnv = (IP_WHICH_CNV)aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword; + calcGammaTable (g, aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword); + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * color_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD color_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * color_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD color_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PCOL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * color_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD color_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PCOL_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = *pdwMinOutBufLen = + (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * color_convert - Converts one row + * +\*****************************************************************************/ + +static WORD color_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PCOL_INST g; + int nBytes; + PBYTE pIn, pOut, pOutAfter; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("color_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pOut + nBytes; + + if (g->traits.iBitsPerPixel < 24) { + /* grayscale data; pass through unchanged */ + memcpy (pOut, pIn, nBytes); + } else if (g->which_cnv == IP_CNV_BGR_SWAP) { + while (pOut < pOutAfter) { + pOut[0] = pIn[2]; + pOut[1] = pIn[1]; + pOut[2] = pIn[0]; + pIn += 3; + pOut += 3; + } + } else { + while (pOut < pOutAfter) { + switch (g->which_cnv) { + case IP_CNV_YCC_TO_CIELAB: YCCToCIELab (pIn, pOut, g->bGammaTbl); break; + case IP_CNV_CIELAB_TO_YCC: CIELabToYCC (pIn, pOut, g->bGammaTbl); break; + case IP_CNV_YCC_TO_SRGB: YCCTosRGB (pIn, pOut); break; + case IP_CNV_SRGB_TO_YCC: sRGBToYCC (pIn, pOut); break; + case IP_CNV_LHS_TO_SRGB: LHSTosRGB (pIn, pOut); break; + case IP_CNV_SRGB_TO_LHS: sRGBToLHS (pIn, pOut); break; + default: goto fatal_error; + } + + pIn += 3; + pOut += 3; + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * color_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD color_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * color_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD color_newPage ( + IP_XFORM_HANDLE hXform) +{ + PCOL_INST g; + + HANDLE_TO_PTR (hXform, g); + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * color_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD color_closeXform (IP_XFORM_HANDLE hXform) +{ + PCOL_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * colorTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL colorTbl = { + color_openXform, + color_setDefaultInputTraits, + color_setXformSpec, + color_getHeaderBufSize, + color_getActualTraits, + color_getActualBufSizes, + color_convert, + color_newPage, + color_insertedData, + color_closeXform +}; + +/* End of File */ diff --git a/ip/xconvolve.c b/ip/xconvolve.c new file mode 100644 index 0000000..56d420d --- /dev/null +++ b/ip/xconvolve.c @@ -0,0 +1,658 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xconvolve.c - convolution using any number or rows and columns up to max + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * convolveTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_CONVOLVE_NROWS ] = # rows in convolution matrix (odd) + * aXformInfo[IP_CONVOLVE_NCOLS ] = # columns in convolution matrix (odd) + * aXformInfo[IP_CONVOLVE_MATRIX ] = ptr to convolution matrix + * aXformInfo[IP_CONVOLVE_DIVISOR] = divide by this after summing products + * + * The matrix is an array of int's, ordered left to right, top to bottom. + * After the pixels are multiplied by the elements in the matrix and these + * products summed together, the sum is divided by the integer divisor. + * + * If you set nRows and nCols to 7, then this xform is identical to the + * "User defined filter" feature in Paint Shop Pro. + * + * This xform makes a copy of the given matrix, so its memory can be freed + * after you've called setXformSpec. + * + * IP_CONVOLVE_MAXSIZE is the max number of rows or columns. + * + * Capabilities and Limitations: + * + * The input data must be grayscale (8 or 16 bits/pixel), or color (24 or + * 48 bits/pixel) in a luminance-chrominance color-space. This xform only + * changes the first component of color data (assumed to be the luminance). + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * 8, 16, 24 or 48 same as default input + * iComponentsPerPixel * 1 or 3 same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +/* Use the #define below if this transform will exist in a dll outside of the + * image pipeline. This will allow the functions to be exported. + * #define EXPORT_TRANFORM 1 + */ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + int iBytesPerPixel; /* # bytes in each pixel */ + DWORD dwRowsRead; /* number of rows read so far */ + DWORD dwRowsWritten; /* number of rows output so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + int nCols; /* # columns in the matrix (must be odd) */ + int nRows; /* # rows in the matrix (must be odd) */ + int nRowsFilled; /* # rows filled so far in the matrix */ + int iDivisor; /* divide sum of products by this */ + int matrix[IP_CONVOLVE_MAXSIZE*IP_CONVOLVE_MAXSIZE]; /* the matrix */ + PBYTE apRows[IP_CONVOLVE_MAXSIZE]; /* ptrs to buffered rows */ + DWORD dwValidChk; /* struct validity check value */ +} CONV_INST, *PCONV_INST; + + + +/*****************************************************************************\ + * + * convolve_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PCONV_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(CONV_INST), g); + *pXform = g; + memset (g, 0, sizeof(CONV_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * convolve_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PCONV_INST g; + int bpp; + int comps; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are valid */ + bpp = pTraits->iBitsPerPixel; + comps = pTraits->iComponentsPerPixel; + INSURE ((comps==1 && (bpp==8 || bpp==16)) || + (comps==3 && (bpp==24 || bpp==48))); + INSURE (pTraits->iPixelsPerRow > 0); + + g->traits = *pTraits; /* a structure copy */ + g->iBytesPerPixel = g->traits.iBitsPerPixel / 8; + g->dwBytesPerRow = g->traits.iPixelsPerRow * g->iBytesPerPixel; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * convolve_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PCONV_INST g; + int i, n; + + HANDLE_TO_PTR (hXform, g); + + g->nRows = aXformInfo[IP_CONVOLVE_NROWS ].dword; + g->nCols = aXformInfo[IP_CONVOLVE_NCOLS ].dword; + g->iDivisor = aXformInfo[IP_CONVOLVE_DIVISOR].dword; + + INSURE ((g->nRows&1)!=0 && g->nRows>0 && g->nRows<=IP_CONVOLVE_MAXSIZE); + INSURE ((g->nCols&1)!=0 && g->nCols>0 && g->nCols<=IP_CONVOLVE_MAXSIZE); + INSURE (g->iDivisor != 0); + INSURE (aXformInfo[IP_CONVOLVE_MATRIX].pvoid != 0); + + n = g->nRows * g->nCols; + for (i=0; i<n; i++) + g->matrix[i] = ((int*)(aXformInfo[IP_CONVOLVE_MATRIX].pvoid))[i]; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * convolve_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * convolve_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PCONV_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * convolve_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD convolve_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PCONV_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * CopyRow - Copies row to row-buffer, filling over-run zone on sides + * +\*****************************************************************************/ + +static void CopyRow ( + PCONV_INST g, /* ptr to our instance variables */ + PBYTE pbSrc, /* input buffer */ + PBYTE pbDest) /* output buffer in apRows (with side-zones allocated) */ +{ + int nSidePixels; + int i; + + nSidePixels = g->nCols / 2; + + /* copy leftmost pixel into all pixels in left side-zone */ + for (i=0; i<nSidePixels; i++) { + memcpy (pbDest, pbSrc, g->iBytesPerPixel); + pbDest += g->iBytesPerPixel; + } + + /* copy the buffer */ + memcpy (pbDest, pbSrc, g->dwBytesPerRow); + pbDest += g->dwBytesPerRow; + pbSrc += g->dwBytesPerRow - g->iBytesPerPixel; /* leave at rightmost pixel */ + + /* copy rightmost pixel into all pixels in right side-zone */ + for (i=0; i<nSidePixels; i++) { + memcpy (pbDest, pbSrc, g->iBytesPerPixel); + pbDest += g->iBytesPerPixel; + } +} + + + +/*****************************************************************************\ + * + * convolve_bytes - performs convolution on a row pixels with 8 or 24 bits each + * +\*****************************************************************************/ + +static void convolve_bytes ( + PCONV_INST g, /* ptr to our instance variables */ + PBYTE pbDest) /* output buffer */ +{ + PBYTE pbSrc, pbDestAfter; + int iSrcOffset, iSideOffset, iSum; + int *pMatrix; + int row, col; + + pbDestAfter = pbDest + g->dwBytesPerRow; + iSrcOffset = 0; + iSideOffset = g->iBytesPerPixel * (g->nCols>>1); + + while (pbDest < pbDestAfter) /* for each output pixel ... */ + { + iSum = 0; + pMatrix = g->matrix; + + for (row=0; row<g->nRows; row++) { + pbSrc = g->apRows[row] + iSrcOffset; + for (col=0; col<g->nCols; col++) { + iSum += (*pMatrix++) * (int)(unsigned)(*pbSrc); + pbSrc += g->iBytesPerPixel; + } + } + + iSum = (iSum + (g->iDivisor>>1)) / g->iDivisor; + if (iSum < 0) iSum = 0; else if (iSum > 255) iSum = 255; + *pbDest++ = (BYTE)iSum; + + if (g->iBytesPerPixel == 3) { + /* copy chrominance values from the center pixel to the output pixel */ + pbSrc = g->apRows[g->nRows>>1] + (iSideOffset + iSrcOffset + 1); + *pbDest++ = *pbSrc++; + *pbDest++ = *pbSrc++; + } + + iSrcOffset += g->iBytesPerPixel; + } +} + + + +/*****************************************************************************\ + * + * convolve_words - performs convolution on a row pixels with 16 or 48 bits each + * +\*****************************************************************************/ + +static void convolve_words ( + PCONV_INST g, /* ptr to our instance variables */ + PWORD pwDest) /* output buffer */ +{ + PWORD pwSrc, pwDestAfter; + int iSrcOffset, iSideOffset, iSum; + int *pMatrix; + int row, col; + + pwDestAfter = pwDest + (g->dwBytesPerRow>>1); + iSrcOffset = 0; + iSideOffset = g->traits.iComponentsPerPixel * (g->nCols>>1); + + while (pwDest < pwDestAfter) /* for each output pixel ... */ + { + iSum = 0; + pMatrix = g->matrix; + + for (row=0; row<g->nRows; row++) { + pwSrc = (WORD*)(g->apRows[row]) + iSrcOffset; + for (col=0; col<g->nCols; col++) { + iSum += (*pMatrix++) * (int)(unsigned)(*pwSrc); + pwSrc += g->traits.iComponentsPerPixel; + } + } + + iSum = (iSum + (g->iDivisor>>1)) / g->iDivisor; + if (iSum < 0) iSum = 0; else if (iSum > 0x00ffff) iSum = 0x00ffff; + *pwDest++ = (WORD)iSum; + + if (g->traits.iComponentsPerPixel == 3) { + /* copy chrominance values from the center pixel to the output pixel */ + pwSrc = (WORD*)(g->apRows[g->nRows>>1]) + (iSideOffset + iSrcOffset + 1); + *pwDest++ = *pwSrc++; + *pwDest++ = *pwSrc++; + } + + iSrcOffset += g->traits.iComponentsPerPixel; + } +} + + + +/*****************************************************************************\ + * + * convolve_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PCONV_INST g; + WORD wFlags; + + HANDLE_TO_PTR (hXform, g); + + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + wFlags = 0; + + /**** We'll always consume an input row, so update its vars ****/ + + if (pbInputBuf != NULL) { + INSURE (dwInputAvail >= g->dwBytesPerRow); + *pdwInputUsed = g->dwBytesPerRow; + g->dwInNextPos += g->dwBytesPerRow; + *pdwInputNextPos = g->dwInNextPos; + g->dwRowsRead += 1; + wFlags |= IP_CONSUMED_ROW | IP_READY_FOR_DATA; + } + + /**** Fill the row-buffers ****/ + + if (g->nRowsFilled < g->nRows) { /* This is the initial fill */ + INSURE (pbInputBuf != NULL); /* not allowed to flush now */ + do { + IP_MEM_ALLOC (g->dwBytesPerRow + g->iBytesPerPixel*g->nCols, + g->apRows[g->nRowsFilled]); + CopyRow (g, pbInputBuf, g->apRows[g->nRowsFilled]); + g->nRowsFilled += 1; + } while (g->nRowsFilled < (g->nRows+1)/2); + + if (g->nRowsFilled < g->nRows) + return wFlags; /* we're not done with initial fill */ + } else { + /* rotate buffer-pointers, and copy new row to bottom row */ + int i; + PBYTE pBottomRow; + + if (pbInputBuf == NULL) { + /* flushing */ + if (g->dwRowsRead == g->dwRowsWritten) + return IP_DONE; + pbInputBuf = g->apRows[g->nRows-1]; /* duplicate prior row */ + } + + pBottomRow = g->apRows[0]; /* new bottom row overwrites oldest top row */ + for (i=1; i<g->nRows; i++) + g->apRows[i-1] = g->apRows[i]; + g->apRows[g->nRows-1] = pBottomRow; + + CopyRow (g, pbInputBuf, pBottomRow); + } + + /**** Output a Row ****/ + + INSURE (dwOutputAvail >= g->dwBytesPerRow); + + if (g->traits.iBitsPerPixel==8 || g->traits.iBitsPerPixel==24) + convolve_bytes (g, pbOutputBuf); + else + convolve_words (g, (WORD*)pbOutputBuf); + + *pdwOutputUsed = g->dwBytesPerRow; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += g->dwBytesPerRow; + g->dwRowsWritten += 1; + wFlags |= IP_PRODUCED_ROW; + + return wFlags; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * convolve_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * convolve_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_newPage ( + IP_XFORM_HANDLE hXform) +{ + PCONV_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * convolve_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD convolve_closeXform (IP_XFORM_HANDLE hXform) +{ + PCONV_INST g; + int i; + + HANDLE_TO_PTR (hXform, g); + + /* free any rows that were allocated */ + for (i=0; i<IP_CONVOLVE_MAXSIZE; i++) + if (g->apRows[i] != NULL) + IP_MEM_FREE (g->apRows[i]); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * convolveTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL convolveTbl = { + convolve_openXform, + convolve_setDefaultInputTraits, + convolve_setXformSpec, + convolve_getHeaderBufSize, + convolve_getActualTraits, + convolve_getActualBufSizes, + convolve_convert, + convolve_newPage, + convolve_insertedData, + convolve_closeXform +}; + +/* End of File */ + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL pXform) +{ + WORD wRet = IP_DONE; + + if (pXform) + *pXform = clrmapTbl; + else + wRet = IP_FATAL_ERROR; + + return wRet; +} +#endif diff --git a/ip/xcrop.c b/ip/xcrop.c new file mode 100644 index 0000000..2613b98 --- /dev/null +++ b/ip/xcrop.c @@ -0,0 +1,470 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xcrop.c - Crops all four sides of the input image + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * cropTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_CROP_LEFT ] = left: # of pixels to remove from left side + * aXformInfo[IP_CROP_RIGHT ] = right: # of pixels to remove from right side + * aXformInfo[IP_CROP_TOP ] = top: # of rows to remove from top + * aXformInfo[IP_CROP_MAXOUTROWS] = maxOutRows: max # of rows to output + * + * Any (or even all) of the above values may be zero. If maxOutRows is + * zero, then an unlimited number of rows can be output. + * + * Capabilities and Limitations: + * + * Crops all four sides of the image. + * The image data must be fixed-length rows of uncompressed pixels. + * For bilevel data, the "left" value is changed to the nearest multiple + * of 8, and the "right" value is changed so the resulting row-width + * does not change. + * If all crop-amounts above are 0, this xform becomes merely a pass-thru. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * used input width - horiz crop + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows used if known output height, if input known + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input image */ + DWORD dwLeft, dwRight; /* # pixels to crop, left and right sides */ + DWORD dwTop; /* # rows to crop from top */ + DWORD dwMaxOutRows; /* max # rows to output */ + DWORD dwInBytesPerRow; /* # bytes in each input row */ + DWORD dwOutBytesPerRow; /* # bytes in each output row */ + DWORD dwLeftCropBytes; /* # bytes to toss from left side of each row */ + DWORD dwInRows; /* number of rows input so far */ + DWORD dwOutRows; /* number of rows output so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} CROP_INST, *PCROP_INST; + + + +/*****************************************************************************\ + * + * crop_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD crop_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PCROP_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(CROP_INST), g); + *pXform = g; + memset (g, 0, sizeof(CROP_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * crop_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD crop_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PCROP_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0); + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * crop_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD crop_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PCROP_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwLeft = aXformInfo[IP_CROP_LEFT].dword; + g->dwRight = aXformInfo[IP_CROP_RIGHT].dword; + g->dwTop = aXformInfo[IP_CROP_TOP].dword; + g->dwMaxOutRows = aXformInfo[IP_CROP_MAXOUTROWS].dword; + + if (g->dwMaxOutRows == 0) + g->dwMaxOutRows = 0x7ffffffful; /* 0 -> infinite */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * crop_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD crop_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * crop_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD crop_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PCROP_INST g; + int left, right, shift; + int inWidth, outWidth; + int bpp; + long actualOut, maxOut; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Compute the crop info */ + + bpp = g->traits.iBitsPerPixel; + left = g->dwLeft; + right = g->dwRight; + inWidth = g->traits.iPixelsPerRow; + outWidth = inWidth - left - right; + INSURE (outWidth >= 0); + + if (bpp == 1) { + /* shift to start at nearest byte boundary */ + shift = ((left+4) & ~7) - left; + left += shift; /* this is now a multiple of 8 */ + right += shift; + } + + g->dwInBytesPerRow = (bpp*inWidth + 7) / 8; + g->dwOutBytesPerRow = (bpp*outWidth + 7) / 8; + g->dwLeftCropBytes = (bpp*left + 7) / 8; + + /* Report the traits */ + + *pInTraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + pOutTraits->iPixelsPerRow = outWidth; + + /* compute the output lNumRows, if possible */ + if (pInTraits->lNumRows > 0) { + maxOut = g->dwMaxOutRows; + actualOut = pInTraits->lNumRows - (long)g->dwTop; + INSURE (actualOut >= 0); + pOutTraits->lNumRows = actualOut<maxOut ? actualOut : maxOut; + } + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * crop_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD crop_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PCROP_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = g->dwInBytesPerRow; + *pdwMinOutBufLen = g->dwOutBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * crop_convert - Converts one row + * +\*****************************************************************************/ + +static WORD crop_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PCROP_INST g; + DWORD dwOutBytes; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("crop_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Check if we should discard the row (vertical cropping) ****/ + + dwOutBytes = (g->dwInRows < g->dwTop || g->dwOutRows >= g->dwMaxOutRows) + ? 0 : g->dwOutBytesPerRow; + + /**** Output a Row ****/ + + INSURE (dwInputAvail >= g->dwInBytesPerRow); + INSURE (dwOutputAvail >= dwOutBytes); + + if (dwOutBytes > 0) { + memcpy (pbOutputBuf, pbInputBuf+g->dwLeftCropBytes, dwOutBytes); + g->dwOutRows += 1; + } + + g->dwInRows += 1; + + *pdwInputUsed = g->dwInBytesPerRow; + g->dwInNextPos += g->dwInBytesPerRow; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = dwOutBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += dwOutBytes; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * crop_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD crop_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * crop_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD crop_newPage ( + IP_XFORM_HANDLE hXform) +{ + PCROP_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * crop_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD crop_closeXform (IP_XFORM_HANDLE hXform) +{ + PCROP_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * cropTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL cropTbl = { + crop_openXform, + crop_setDefaultInputTraits, + crop_setXformSpec, + crop_getHeaderBufSize, + crop_getActualTraits, + crop_getActualBufSizes, + crop_convert, + crop_newPage, + crop_insertedData, + crop_closeXform +}; + +/* End of File */ diff --git a/ip/xfakemono.c b/ip/xfakemono.c new file mode 100644 index 0000000..e21a862 --- /dev/null +++ b/ip/xfakemono.c @@ -0,0 +1,465 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xfakemono.c - Simulates grayscale or bilevel in 24-bit data. + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * fakeMonoTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_FAKE_MONO_BPP] = 8 or 1, to simulate grayscale or mono + * + * Capabilities and Limitations: + * + * The input and output image are 24-bits per pixel. But the output image + * will *appear* to be grayscale if aXformInfo[0] == 8, or bilevel if it == 1. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 24 24 + * iComponentsPerPixel * must be 3 3 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +// Use the #define below if this transform will exist in a dll outside of the +// image pipeline. This will allow the functions to be exported. +// #define EXPORT_TRANFORM 1 + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + int iFakeDPI; /* dpi to fake (8 or 1) */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} FMON_INST, *PFMON_INST; + + + +/*****************************************************************************\ + * + * fakeMono_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PFMON_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(FMON_INST), g); + *pXform = g; + memset (g, 0, sizeof(FMON_INST)); + g->dwValidChk = CHECK_VALUE; + g->iFakeDPI = 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * fakeMono_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PFMON_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow > 0 && + pTraits->iBitsPerPixel == 24 && + pTraits->iComponentsPerPixel == 3); + + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * fakeMono_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PFMON_INST g; + HANDLE_TO_PTR (hXform, g); + g->iFakeDPI = aXformInfo[IP_FAKE_MONO_BPP].dword; + INSURE (g->iFakeDPI==1 || g->iFakeDPI==8); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * fakeMono_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * fakeMono_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PFMON_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * fakeMono_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD fakeMono_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PFMON_INST g; + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * fakeMono_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PFMON_INST g; + int nBytes; + PBYTE pIn, pInAfter, pOut; + unsigned rv, gv, bv; + int gray; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("fakeMono_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pInAfter = pIn + nBytes; + + if (g->iFakeDPI == 1) { // faking bi-level + while (pIn < pInAfter) { + rv = *pIn++; + gv = *pIn++; + bv = *pIn++; + gray = NTSC_LUMINANCE (rv, gv, bv); + gray = (gray >= 128) ? 255 : 0; + *pOut++ = (BYTE)gray; + *pOut++ = (BYTE)gray; + *pOut++ = (BYTE)gray; + } + } else { // faking grayscale + while (pIn < pInAfter) { + rv = *pIn++; + gv = *pIn++; + bv = *pIn++; + gray = NTSC_LUMINANCE (rv, gv, bv); + *pOut++ = (BYTE)gray; + *pOut++ = (BYTE)gray; + *pOut++ = (BYTE)gray; + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * fakeMono_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * fakeMono_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_newPage ( + IP_XFORM_HANDLE hXform) +{ + PFMON_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * fakeMono_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD fakeMono_closeXform (IP_XFORM_HANDLE hXform) +{ + PFMON_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * fakeMonoTbl - Jump-table for transform driver + * +\*****************************************************************************/ +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL fakeMonoTbl = { + fakeMono_openXform, + fakeMono_setDefaultInputTraits, + fakeMono_setXformSpec, + fakeMono_getHeaderBufSize, + fakeMono_getActualTraits, + fakeMono_getActualBufSizes, + fakeMono_convert, + fakeMono_newPage, + fakeMono_insertedData, + fakeMono_closeXform +}; + +/* End of File */ + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL pXform) +{ + WORD wRet = IP_DONE; + + if (pXform) + *pXform = clrmapTbl; + else + wRet = IP_FATAL_ERROR; + + return wRet; +} +#endif diff --git a/ip/xfax.c b/ip/xfax.c new file mode 100644 index 0000000..1c23de9 --- /dev/null +++ b/ip/xfax.c @@ -0,0 +1,3566 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * xfax.c - encoder and decoder for fax data (MH, MR and MMR) + * + ***************************************************************************** + * + * Name of Global Jump-Table: + * + * faxEncodeTbl = the encoder, + * faxDecodeTbl = the decoder. + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_FAX_FORMAT]: Format of data (both encoder & decoder). + * Values are: IP_FAX_MH, IP_FAX_MR, IP_FAX_MMR. + * + * aXformInfo[IP_FAX_NO_EOLS]: No EOLs in the data? + * 0 = EOLs are in data as usual; + * 1 = no EOLs in data. + * This tells the encoder whether to output EOLs. + * This tells the decoder if there are EOLs in the data. + * + * aXformInfo[IP_FAX_MIN_ROW_LEN]: Minimum # bits to put in each output row + * (MH & MR only). + * Only the encoder needs this, and guarantees that each + * row it outputs contains at least this many bits. It + * inserts fill 0's as needed. The fax standard needs + * this to insure that a row consumes a minimum amout of + * time when sent over the modem, hence there's a minimum + * number of bits to be transmitted per row. + * + * Capabilities and Limitations: + * + * Bits per pixel must be 1 (bi-level only). + * Encodes and decodes MH, MR and MMR per the fax standard. + * Encoding MR uses a k-factor of 2 if vert dpi < 150, else k-factor is 4. + * If an error occurs in MH or MR data, the previous good row is returned. + * + * Default Input Traits, and Output Traits: + * + * For both encoder and decoder: + * + * trait default input output + * ------------------- ------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 1 1 + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Mark Overton, Jan 1998 + * +\*****************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include "assert.h" /* todo: eliminate all asserts */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stdout, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + + + + +/* +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@ @@ +@@ @@ +@@ U T I L I T I E S @@ +@@ @@ +@@ @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +*/ + + +/* baLeftZeroesTbl returns number of leading zeroes in byte index */ + +static const BYTE baLeftZeroesTbl[256] = +{ + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +/* baRightZeroesTbl returns number of trailing zeroes in byte index */ + +static const BYTE baRightZeroesTbl[256] = +{ + 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + + + +/*____________________________________________________________________________ + | | | + | scan_to | Scan pixels rightward until hitting desired color (white/black) | + |_________|__________________________________________________________________| + | | + | Pos, the initial search position, must not be negative nor greater than | + | pixels_in_row+1. This function will not return a value greater than | + | pixels_in_row. | + | | + | WARNING: Before calling this routine, the first byte after the end of the | + | buffer must be set to alternating zeroes and ones (such as 0x55).| + | This allows us to scan bytes for a pixel-change without doing | + | the end-of-buffer boundary-check. | + | | + | Function return value: index of first pixel of desired color. | + |____________________________________________________________________________| +*/ +static int scan_to ( + UINT skip, /* color to skip over (FF=black, 00=white) */ + BYTE *buf_p, /* buffer in which to search */ + int start_pos, /* start-index for search (0=leftmost pixel in buf) */ + int pixels_in_row) /* # pixels in the buffer */ +{ + /************************************************************************** + * + * PERFORMANCE NOTE + * + * This routine skips all-white and all-black areas a *byte* at a time + * using a three-instruction loop. The old routine did this a *word* + * (2 bytes) at a time using a three-instruction loop. But this routine + * results in faster G3/G4 encoding/decoding, even for mostly white areas, + * because it has so little overhead outside the loop. Also, its small + * size doesn't toss as much other G3/G4 code out of the cache, improving + * performance even more. + * + * Seconds to MMR encode+decode 1000 rows of 1728 pixels (bench_fax.c): + * + * black density: 0% 10% 20% 50% + * old routine: 2.1 10.2 18.8 45.1 + * this routine: 2.2 7.4 12.8 29.9 + * inlining this routine: 2.2 7.9 13.8 32.4 + * + * Inlining this routine *hurt* performance a little because more MMR + * code was kicked out of the cache. + * + *************************************************************************/ + + BYTE *cur_p; + UINT byte_mask; + UINT byte; + int pos; + + byte_mask = 0x00FFu; + cur_p = buf_p + (start_pos >> 3); + skip &= byte_mask; + byte = (*cur_p ^ skip) & (byte_mask >> (start_pos & 7)); + + if (byte == 0) { + do { + cur_p += 1; + byte = *cur_p; + } while (byte == skip); + byte ^= skip; + } + + pos = ((cur_p-buf_p)<<3) + baLeftZeroesTbl[byte]; + + if (pos > pixels_in_row) + pos = pixels_in_row; + return pos; +} + + + +/*____________________________________________________________________________ + | | | + | worst_buf_size | calculates worst buffer-usage for a compressed row | + |________________|___________________________________________________________| +*/ +/* worst-case failure of compression (which is expansion) */ +#define WORST_EXPAND_1D (9<<1) /* = 4.5 in 14.2 fixed-point */ +#define WORST_EXPAND_2D (6<<2) /* = 6.0 in 14.2 fixed-point */ + +static int worst_buf_size ( + WORD wFmt, /* fax format (IP_FAX_MH/MR/MMR) */ + int iRowWidth) /* width of each row of bitmap (# pixels) */ +{ + return ( ( (wFmt==IP_FAX_MH ? WORST_EXPAND_1D : WORST_EXPAND_2D) + * ((iRowWidth+7)/8) + ) >> 2 + ) + 4; +} + + + +/* +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@ @@ +@@ @@ +@@ E N C O D E R @@ +@@ @@ +@@ @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +*/ + + +/* ENC_INST - our instance variables */ + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the image */ + DWORD dwValidChk; /* struct validity check value */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + int iRowLen; /* # pixels in each uncompressed input row */ + BYTE wOutFmt; /* output format (IP_FAX_MH/FAX_MR/FAX_MMR) */ + BOOL fNoEOLs; /* don't output any EOLs? */ + UINT w12Cycle; /* (MR only) # rows in [1d,2d,2d...] cycle */ + UINT wMinBits; /* minimum # bits to output in each row */ + int iRowNum; /* current row-number of output, 0 is first */ + BYTE *prior_p; /* (MR/MMR only) the prior row */ + + /* Variables for "Outputting Bits" section */ + BYTE *pbBufStart; /* beginning of output buffer */ + BYTE *pbOutByte; /* current byte in output buffer */ + DWORD dwBitBuffer; /* buffer of bits (to be written to pbOutByte) */ + UINT wBitsAvail; /* # of unused bits in dwBitBuffer */ +} ENC_INST, *PENC_INST; + + + +/***************************************************************************** + * * + * O U T P U T T I N G B I T S * + * * + ***************************************************************************** + + +Interface into this section: + put_init - initializes this section + put_bits - outputs a variable-number of bits (buffered) + put_fill_bits - Outputs fill bits (if necessary) to reach the minimum + put_grab - gives all bytes except the partial one to caller + put_new_buf - tells us to use a new buffer (after put_grab was called) + put_done - writes any remaining bits in the buffer +*/ + + + +/*____________________________________________________________________________ + | | | + | put_init | Initializes this 'put' section | + |__________|_________________________________________________________________| +*/ +static void put_init ( + ENC_INST *g, + BYTE *pbOutBuf) +{ + g->dwBitBuffer = 0; + g->wBitsAvail = 32; + g->pbOutByte = pbOutBuf; + g->pbBufStart = pbOutBuf; +} + + + +/*____________________________________________________________________________ + | | | + | put_bits | Outputs (buffered) the low-order 'length' bits in 'bits' | + |__________|_________________________________________________________________| + | | + | 'length' must be no larger than 25. | + | The bits in 'bits' that are not to be written must be zeroes. | + | | + | This routine lets dwBitBuffer fill up until data won't fit. Then it | + | writes out as many bytes as possible from the buffer in a loop. I | + | did it this way so the byte-write code would be in cache for more of | + | the writes, and so 'write_bytes' will be called as seldom as possible. | + |____________________________________________________________________________| +*/ + +static void write_bytes (ENC_INST *g) +{ + /* PERFORMANCE NOTE: inlining this routine slows down the encoder + * due to worse cache usage. + */ + BYTE *byte_p; + DWORD bitbuf; + UINT bitsavail; + + /* assert (g->wBitsAvail <= 24); */ + byte_p = g->pbOutByte; + bitbuf = g->dwBitBuffer; + bitsavail = g->wBitsAvail; + + do { + *byte_p++ = (BYTE )(bitbuf >> 24); + bitbuf <<= 8; + bitsavail += 8; + } while (bitsavail <= 24); + + g->pbOutByte = byte_p; + g->dwBitBuffer = bitbuf;; + g->wBitsAvail = bitsavail; +} + + +#define put_bits(g, length_par, bits_par) \ +do { \ + UINT length_loc = length_par; \ + DWORD bits_loc = bits_par; \ + \ + if (length_loc > g->wBitsAvail) \ + write_bytes (g); \ + \ + g->wBitsAvail -= length_loc; \ + g->dwBitBuffer |= bits_loc << g->wBitsAvail; \ +} while (0) + + +static void put_bits_routine ( + ENC_INST *g, + UINT length, + DWORD bits) +{ + put_bits (g, length, bits); +} + + + +/*____________________________________________________________________________ + | | | + | put_flush | Writes out all bytes containing any data in dwBitBuffer | + |___________|________________________________________________________________| +*/ +static void put_flush (ENC_INST *g) +{ + if (g->wBitsAvail < 32) { + g->wBitsAvail &= ~7ul; /* reduce to a multiple of 8 (byte-boundary) */ + write_bytes (g); + } + + assert (g->wBitsAvail == 32); +} + + + +/*____________________________________________________________________________ + | | | + | put_fill_bits | Outputs fill bits (if necessary) to reach the minimum | + |_______________|____________________________________________________________| + | | + | We want put_grab to return the minimum # of bits, but it returns an | + | array of bytes (not bits). So this routine insures that at least | + | the minimum # of bits have been written (as bytes) into the output | + | buffer. | + |____________________________________________________________________________| +*/ +static void put_fill_bits (ENC_INST *g) +{ + int iMore; + + put_flush (g); + + /* write out zero-bytes until we're at (or past) the minimum # bits */ + + iMore = (int)g->wMinBits - 8*(g->pbOutByte - g->pbBufStart); + if (iMore > 0) { + iMore = (iMore+7) / 8; + memset (g->pbOutByte, 0, iMore); + g->pbOutByte += iMore; + } +} + + + +/*____________________________________________________________________________ + | | | + | put_grab | Returns # of bytes written so far, and restarts at buffer-start | + |__________|_________________________________________________________________| + | | + | The caller is expected to copy N bytes from the buffer, where N is the | + | number this function returns. | + |____________________________________________________________________________| +*/ +static int put_grab (ENC_INST *g) +{ + int n; + + n = g->pbOutByte - g->pbBufStart; + g->pbOutByte = g->pbBufStart; /* next byte goes into beginning of buffer */ + return n; +} + + + +/*____________________________________________________________________________ + | | | + | put_new_buf | Use a new buffer (must be called after put_grab) | + |_____________|______________________________________________________________| +*/ +static void put_new_buf ( + ENC_INST *g, + BYTE *pbOutBuf) +{ + assert (g->pbOutByte == g->pbBufStart); + g->pbOutByte = pbOutBuf; + g->pbBufStart = pbOutBuf; +} + + + +/*____________________________________________________________________________ + | | | + | put_done | Writes any buffered bits, and returns total # of bytes written | + |__________|_________________________________________________________________| +*/ +static int put_done ( + ENC_INST *g) +{ + put_flush (g); + return g->pbOutByte - g->pbBufStart; +} + + + +/* + ***************************************************************************** + * * + * E N C O D I N G R O W S * + * * + ***************************************************************************** + + +Interface into this section: + encode_row_1d - compresses a row into 1-dim format for MH and MR + encode_row_2d - compresses a row into 2-dim format for MR and MMR +*/ + + + +/* Structure for storing G3 codes */ + +typedef struct { + USHORT bits; + USHORT length; +} huff_t; + + +/* run-length = index, index is in 0..63 */ +static const huff_t MHWhiteRuns[] = { + {0x35, 8}, {0x7, 6}, {0x7, 4}, {0x8, 4}, + {0xb, 4}, {0xc, 4}, {0xe, 4}, {0xf, 4}, + {0x13, 5}, {0x14, 5}, {0x7, 5}, {0x8, 5}, + {0x8, 6}, {0x3, 6}, {0x34, 6}, {0x35, 6}, + {0x2a, 6}, {0x2b, 6}, {0x27, 7}, {0xc, 7}, + {0x8, 7}, {0x17, 7}, {0x3, 7}, {0x4, 7}, + {0x28, 7}, {0x2b, 7}, {0x13, 7}, {0x24, 7}, + {0x18, 7}, {0x2, 8}, {0x3, 8}, {0x1a, 8}, + {0x1b, 8}, {0x12, 8}, {0x13, 8}, {0x14, 8}, + {0x15, 8}, {0x16, 8}, {0x17, 8}, {0x28, 8}, + {0x29, 8}, {0x2a, 8}, {0x2b, 8}, {0x2c, 8}, + {0x2d, 8}, {0x4, 8}, {0x5, 8}, {0xa, 8}, + {0xb, 8}, {0x52, 8}, {0x53, 8}, {0x54, 8}, + {0x55, 8}, {0x24, 8}, {0x25, 8}, {0x58, 8}, + {0x59, 8}, {0x5a, 8}, {0x5b, 8}, {0x4a, 8}, + {0x4b, 8}, {0x32, 8}, {0x33, 8}, {0x34, 8} +}; + + +/* run-length = 64*(index+1), index is in 0..26 */ +static const huff_t MHMakeupWhite[] = { + {0x1b, 5}, {0x12, 5}, {0x17, 6}, {0x37, 7}, + {0x36, 8}, {0x37, 8}, {0x64, 8}, {0x65, 8}, + {0x68, 8}, {0x67, 8}, {0xcc, 9}, {0xcd, 9}, + {0xd2, 9}, {0xd3, 9}, {0xd4, 9}, {0xd5, 9}, + {0xd6, 9}, {0xd7, 9}, {0xd8, 9}, {0xd9, 9}, + {0xda, 9}, {0xdb, 9}, {0x98, 9}, {0x99, 9}, + {0x9a, 9}, {0x18, 6}, {0x9b, 9} +}; + + +/* run-length = index, index is in 0..63 */ +static const huff_t MHBlackRuns[] = { + {0x37, 10}, {0x2, 3}, {0x3, 2}, {0x2, 2}, + {0x3, 3}, {0x3, 4}, {0x2, 4}, {0x3, 5}, + {0x5, 6}, {0x4, 6}, {0x4, 7}, {0x5, 7}, + {0x7, 7}, {0x4, 8}, {0x7, 8}, {0x18, 9}, + {0x17, 10}, {0x18, 10}, {0x8, 10}, {0x67, 11}, + {0x68, 11}, {0x6c, 11}, {0x37, 11}, {0x28, 11}, + {0x17, 11}, {0x18, 11}, {0xca, 12}, {0xcb, 12}, + {0xcc, 12}, {0xcd, 12}, {0x68, 12}, {0x69, 12}, + {0x6a, 12}, {0x6b, 12}, {0xd2, 12}, {0xd3, 12}, + {0xd4, 12}, {0xd5, 12}, {0xd6, 12}, {0xd7, 12}, + {0x6c, 12}, {0x6d, 12}, {0xda, 12}, {0xdb, 12}, + {0x54, 12}, {0x55, 12}, {0x56, 12}, {0x57, 12}, + {0x64, 12}, {0x65, 12}, {0x52, 12}, {0x53, 12}, + {0x24, 12}, {0x37, 12}, {0x38, 12}, {0x27, 12}, + {0x28, 12}, {0x58, 12}, {0x59, 12}, {0x2b, 12}, + {0x2c, 12}, {0x5a, 12}, {0x66, 12}, {0x67, 12} +}; + + +/* run-length = 64*(index+1), index is in 0..26 */ +static const huff_t MHMakeupBlack[] = { + {0xf, 10}, {0xc8, 12}, {0xc9, 12}, {0x5b, 12}, + {0x33, 12}, {0x34, 12}, {0x35, 12}, {0x6c, 13}, + {0x6d, 13}, {0x4a, 13}, {0x4b, 13}, {0x4c, 13}, + {0x4d, 13}, {0x72, 13}, {0x73, 13}, {0x74, 13}, + {0x75, 13}, {0x76, 13}, {0x77, 13}, {0x52, 13}, + {0x53, 13}, {0x54, 13}, {0x55, 13}, {0x5a, 13}, + {0x5b, 13}, {0x64, 13}, {0x65, 13} +}; + + +/* run-length = 64*(index+28), index is in 0..12 */ +static const huff_t MHExtMakeup[] = { + {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12}, + {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12}, + {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12}, + {0x1f, 12} +}; + + +/* vertical-offset = index-3 */ +static const huff_t VertTbl[] = { + {2, 7}, + {2, 6}, + {2, 3}, + {1, 1}, + {3, 3}, + {3, 6}, + {3, 7} +}; + + + +/*____________________________________________________________________________ + | | | + | put_run | Outputs a white or black run | + |_________|__________________________________________________________________| +*/ +static void put_run_routine( + ENC_INST *g, + int iRunLen, + const huff_t *makeup_tbl, + const huff_t *code_tbl) +{ + huff_t te; + + while (iRunLen >= 1792) { + int tpos; + tpos = (iRunLen>>6) - (1792>>6); + if (tpos > 12) + tpos = 12; + te = MHExtMakeup [tpos]; + put_bits_routine (g, te.length, te.bits); + iRunLen -= (tpos+(1792>>6)) << 6; + } + + if (iRunLen >= 64) { + te = makeup_tbl [(iRunLen>>6) - 1]; + put_bits_routine (g, te.length, te.bits); + iRunLen &= 63; + } + + te = code_tbl [iRunLen]; + put_bits_routine (g, te.length, te.bits); +} + +#define put_run(g, par_run_len, par_makeup_tbl, par_code_tbl) \ +do { \ + huff_t te; \ + int loc_run_len = par_run_len; \ + \ + if (loc_run_len >= 64) \ + put_run_routine (g, loc_run_len, par_makeup_tbl, par_code_tbl); \ + else { \ + te = par_code_tbl [loc_run_len]; \ + put_bits (g, te.length, te.bits); \ + } \ +} while (0) + + + +#define PutWhiteRun(g, iRunLen) \ + put_run (g, iRunLen, MHMakeupWhite, MHWhiteRuns) + + +#define PutBlackRun(g, iRunLen) \ + put_run (g, iRunLen, MHMakeupBlack, MHBlackRuns) + + +static void PutEOL(ENC_INST *g) /* output the EOL code (11 zeroes and a one) */ +{ + put_bits_routine (g, 12, 0x001); +} + + + +/*____________________________________________________________________________ + | | | + | encode_row_1d | Converts a pixel-row into MH (CCITT G3) format | + |_______________|____________________________________________________________| + | | + | Before calling this routine, you must call put_init. | + | After calling this routine, you must call put_grab/put_done. | + | This routine puts the EOL at the beginning of the line. | + |____________________________________________________________________________| +*/ +static void encode_row_1d ( + ENC_INST *g, + BYTE *pbPixelRow, /* ptr to pixel-row */ + int iPixels, /* # of pixels in above row */ + BOOL fDoingMR) /* Sending MR? Ie, send a 1d/2d tag-bit after EOL? */ +{ + int iStartPos; + int iChange; + UINT skip; /* the color we're skipping over; 0x00=black, 0xFF=white */ + + PutEOL (g); + if (fDoingMR) + put_bits_routine (g,1,1); /* tag-bit after EOL means 1-dim row-data */ + + pbPixelRow[iPixels>>3] = 0x55u; /* scan_to requires this */ + iStartPos = 0; + skip = 0; + + while (iStartPos < iPixels) { + iChange = scan_to (skip, pbPixelRow, iStartPos, iPixels); + if (skip) PutBlackRun (g, iChange-iStartPos); + else PutWhiteRun (g, iChange-iStartPos); + iStartPos = iChange; + skip = ~skip; + } +} + + + +/*____________________________________________________________________________ + | | | + | encode_row_2d | Converts a pixel-row into 2-dimensional format | + |_______________|____________________________________________________________| + | | + | Before calling this routine, you must call put_init. | + | After calling this routine, you must call put_grab/put_done. | + | For MR data, this routine puts the EOL+tag at the beginning of the line. | + |____________________________________________________________________________| +*/ +/* + * The variable 'need' below is a bit-array telling us which values (a1, b0, + * etc) are needed. It is determined by the cases below. + * + * b0 is not in the standard. I've defined it as, "A pixel on the reference + * line above or to the right of a0, and to the left of b1." Since b1 is the + * first changing pixel, b0 must be the same color as a0. b0 is the point at + * which the search for b1 begins. + * + * In the code below, b0 uses the b1 variable because if we're using b0, then + * b1 is not known, so it's okay to clobber it. + * + * Below, a0, a1, b0, b1 etc denote positions before coding, and A0, A1, + * B0, B1, etc denote positions after coding. + * + * Pass Mode: + * + * B0 + * b1 b2 + * - - - X X X - - - + * X - - - - - - X - + * a0 a1 + * A0 A1 + * + * A0 = b2, specified by the standard. + * A1 = a1, because A0 is to the left of a1, a1 does not move. + * B0 = b2, which is known to be the same color as A0. + * + * Needed: B1 and B2. + * + * Vertical Mode: + * + * Normal case: + * + * B1 + * b1 b2 + * - - - X X X - - - + * - - X X - - - - - + * a0 a1 + * A0 + * + * A0 = a1, specified by the standard. + * B1 = b2, the first changing pixel of opposite color as A0. + * + * Needed: A1, B2. + * + * An exception (a1=b2): + * + * b1 b2 + * - - - X - - - - - + * - - - - X - - - - + * a0 a1 + * A0 + * + * Here, b2 is above A0, and therefore cannot be used as B1. + * And its color is opposite A0, so it's not even usable as B0. + * So B0 and B1 must be scanned. + * + * Needed: A1, B0, B1, B2. + * + * A subtle exception (a1 is at least 2 pixels to the left of b1): + * + * B1 B2 + * b1 b2 + * - - X X X - X - - + * - - - - X - - - - + * a0 a1 + * A0 + * + * b1 and b2 move *backwards* after coding. + * B1 = b1-1, and B2=b1. Since it's not worth the time to check + * for this rare pixel-arrangement, we'll just rescan B0, B1 and B2. + * + * Needed: A1, B0, B1, B2. + * + * Horizontal Mode: + * + * First case (a2 > b2): + * + * b1 b2 + * - - - - - X - - - + * - X X X X X X - - + * a0 a1 a2 + * A0 + * + * Since b2 is left of A0, we know nothing about what's above A0. + * So everything must be scanned. + * + * Needed: A1, B0, B1, B2. + * + * Second case: (a2 <= b2): + * + * B0 + * b1 b2 + * - - - - - X X X - + * - X X X X X X - - + * a0 a1 a2 + * A0 + * + * A0 = a2, specified by the standard. + * B0 = b2, because it's the same color as A0, and not to left of A0. + * + * Needed: A1, B1, B2. + * + * Exception to above case (a2 < b1): + * + * B1 B2 + * b1 b2 + * - - - - - - X X - + * - X X X X - - - - + * a0 a1 a2 + * A0 + * + * b1 is to the right of A0, so b1 and b2 don't move. + * This case is common because it occurs whenever a row with some + * data follows a blank row. + * + * Needed: A1. + */ +static void encode_row_2d ( + ENC_INST *g, + BYTE *pbPixelRow, /* ptr to pixel-row */ + BYTE *pbRefRow, /* ptr to reference-row */ + int iPixels, /* # of pixels in above row */ + BOOL fDoingMR) /* Sending MR? Ie, output an EOL + tag-bit? */ +{ + #define A1 1 + #define B0 2 + #define B1 4 + #define B2 8 + + int a0, a1, a2; + int b1, b2; + int iDelta; + UINT skip; /* the color we're skipping over; 00=white, FF=black */ + UINT need; + + if (fDoingMR) { + PutEOL (g); + put_bits_routine (g,1,0); /* tag-bit after EOL means 2-dim row-data */ + } + + pbPixelRow[iPixels>>3] = 0x55u; /* scan_to requires this */ + pbRefRow [iPixels>>3] = 0x55u; /* scan_to requires this */ + + /* The imaginary pixel before the first is considered a white pixel. + * So if the first pixel in the row is black, it is considered + * a "changing" pixel (white->black). + */ + a1 = scan_to ( 0x00, pbPixelRow, 0, iPixels); + b1 = scan_to ( 0x00, pbRefRow, 0, iPixels); + b2 = scan_to ((UINT)~0x00, pbRefRow, b1+1, iPixels); + skip = (UINT)~0x00u; /* white, initially */ + a0 = 0; + + while (TRUE) { + + /* output one of the modes */ + + iDelta = a1 - b1; + + if (b2 < a1) { /* pass mode */ + put_bits (g,4,1); + need = B1 | B2; + a0 = b2; + b1 = b2; + } else if (-3<=iDelta && iDelta<=3) { /* vertical mode */ + huff_t te = VertTbl[iDelta+3]; + put_bits (g, te.length, te.bits); + need = A1 | B2; + if (b2==a1 || iDelta<=-2) + need = A1 | B0 | B1 | B2; + a0 = a1; + b1 = b2; + skip = ~skip; + } else { /* horizontal mode */ + a2 = scan_to (skip, pbPixelRow, a1+1, iPixels); + put_bits (g,3,1); + if (skip) { + PutWhiteRun (g,a1-a0); + PutBlackRun (g,a2-a1); + } else { + PutBlackRun (g,a1-a0); + PutWhiteRun (g,a2-a1); + } + if (a2 > b2) { + need = A1 | B0 | B1 | B2; + } else if (a2 < b1) { + need = A1; + } else { + b1 = b2; + need = A1 | B1 | B2; + } + a0 = a2; + } + + if (a0 >= iPixels) + break; + + /* compute next a1, b1, b2 */ + + if (need & A1) a1 = scan_to (~skip, pbPixelRow, a0+1, iPixels); + if (need & B0) b1 = scan_to ( skip, pbRefRow, a0, iPixels); + if (need & B1) b1 = scan_to (~skip, pbRefRow, b1+1, iPixels); + if (need & B2) b2 = scan_to ( skip, pbRefRow, b1+1, iPixels); + } +} + + + +/* +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@ @@ +@@ @@ +@@ E N C O D E R @@ +@@ @@ +@@ @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +*/ + + + +/*****************************************************************************\ + * + * faxEncode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD faxEncode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PENC_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(ENC_INST), g); + *pXform = g; + memset (g, 0, sizeof(ENC_INST)); + g->dwValidChk = CHECK_VALUE; + put_init (g, NULL); /* put_new_buf will be called later */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxEncode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD faxEncode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PENC_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we actually use or care about are known */ + INSURE (pTraits->iPixelsPerRow > 0); /* we need the row-length */ + INSURE (pTraits->iBitsPerPixel == 1); /* image must be bi-level */ + + g->traits = *pTraits; /* a structure copy */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxEncode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD faxEncode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PENC_INST g; + + HANDLE_TO_PTR (hXform, g); + g->wOutFmt = (BYTE)aXformInfo[IP_FAX_FORMAT].dword; + g->fNoEOLs = (BOOL)aXformInfo[IP_FAX_NO_EOLS].dword; + g->wMinBits = (WORD)aXformInfo[IP_FAX_MIN_ROW_LEN].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxEncode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD faxEncode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * faxEncode_getActualTraits - Parses header, and returns input & output traits + * + ***************************************************************************** + * + * For this fax xform driver, this routine merely returns input traits. + * +\*****************************************************************************/ + +static WORD faxEncode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PENC_INST g; + int inBytes; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Since we don't change traits, just copy out the default traits */ + + *pInTraits = g->traits; + *pOutTraits = g->traits; + + /* Compute some stuff */ + + g->iRowLen = g->traits.iPixelsPerRow; /* todo: eliminate redundant var */ + + /* below, if vert dpi is unknown (negative), we use cycle-len of 2 */ + g->w12Cycle = (g->traits.lVertDPI < (150l<<16)) ? 2 : 4; + + /* Allocate the prior-row buffer, if needed */ + + if (g->wOutFmt != IP_FAX_MH) { + if (g->prior_p != NULL) + IP_MEM_FREE (g->prior_p); + inBytes = (g->iRowLen+7) / 8; + IP_MEM_ALLOC (inBytes, g->prior_p); + memset (g->prior_p, 0, inBytes); + } + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * faxEncode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD faxEncode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PENC_INST g; + UINT uWorstBuf, uMinBytes; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = (g->iRowLen+7) / 8; + + uWorstBuf = worst_buf_size (g->wOutFmt, g->iRowLen); + uMinBytes = (g->wMinBits+7) / 8; + *pdwMinOutBufLen = uWorstBuf > uMinBytes ? uWorstBuf : uMinBytes; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxEncode_convert - the work-horse routine + * + ***************************************************************************** + * + * This routine (actually put_bits) hangs onto the last 1-3 bytes of + * encoded row-data due to its buffering method. + * +\*****************************************************************************/ + +static WORD faxEncode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PENC_INST g; + int inBytes; + int i; + + HANDLE_TO_PTR (hXform, g); + + put_new_buf (g, pbOutputBuf); + + /********************************************************/ + /* If we're being told to flush, output the ending EOLs */ + /********************************************************/ + + if (dwInputAvail == 0) { + + switch (g->wOutFmt) { + case IP_FAX_MH: + for (i=6; i>0; i--) + PutEOL (g); + break; + + case IP_FAX_MR: + for (i=6; i>0; i--) { + PutEOL (g); + put_bits_routine (g,1,1); + } + break; + + case IP_FAX_MMR: + PutEOL (g); + PutEOL (g); + break; + } + + *pdwInputUsed = 0; + *pdwOutputUsed = put_done (g); + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /******************************/ + /* Normal Case (not flushing) */ + /******************************/ + + inBytes = (g->iRowLen+7) / 8; + INSURE (dwInputAvail >= (DWORD)inBytes); + INSURE (dwOutputAvail > 0); + + switch (g->wOutFmt) { + case IP_FAX_MH: + encode_row_1d (g, pbInputBuf, g->iRowLen, FALSE); + put_fill_bits (g); + break; + + case IP_FAX_MR: + if (g->iRowNum % g->w12Cycle == 0) + encode_row_1d (g, pbInputBuf, g->iRowLen, TRUE); + else + encode_row_2d (g, pbInputBuf, g->prior_p, g->iRowLen, TRUE); + put_fill_bits (g); + break; + + case IP_FAX_MMR: + encode_row_2d (g, pbInputBuf, g->prior_p, g->iRowLen, FALSE); + break; + } + + if (g->prior_p != NULL) + memcpy (g->prior_p, pbInputBuf, inBytes); + + *pdwInputUsed = inBytes; + g->dwInNextPos += inBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = put_grab (g); + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += *pdwOutputUsed; + g->iRowNum += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxEncode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD faxEncode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * faxEncode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD faxEncode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PENC_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: output EOLs to mark a new page */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * faxEncode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD faxEncode_closeXform (IP_XFORM_HANDLE hXform) +{ + PENC_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (g->prior_p != NULL) + IP_MEM_FREE (g->prior_p); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxEncodeTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL faxEncodeTbl = { + faxEncode_openXform, + faxEncode_setDefaultInputTraits, + faxEncode_setXformSpec, + faxEncode_getHeaderBufSize, + faxEncode_getActualTraits, + faxEncode_getActualBufSizes, + faxEncode_convert, + faxEncode_newPage, + faxEncode_insertedData, + faxEncode_closeXform +}; + + + +/* +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@ @@ +@@ @@ +@@ D E C O D E R @@ +@@ @@ +@@ @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +*/ + + +/* white/nonwhite might be put in interface later on */ +#define NONWHITE_ROW 0x0000 /* row is not all white */ +#define WHITE_ROW 0x0000 /* row is all white */ + +#define MAX_CODE_LEN 13 /* length of longest code */ +#define EOL_LEN 12 /* EOL is 11 zeroes and a one */ +#define EOLS_FOR_MH_MR 3 /* the std says 6, but some might be zapped */ +#define EOLS_FOR_MMR 1 /* the std says 2, but we'll stop at first because + * we might not be able to fetch the second EOL from + * the cache because we won't fetch anything unless + * it contains 13 bits, and an EOL is only 12 */ + +/* Huffman tables (at end of file): */ + +extern const BYTE fax_vert_huff_index[]; +extern const USHORT fax_vert_huff[]; +extern const BYTE fax_black_huff_index[]; +extern const USHORT fax_black_huff[]; +extern const BYTE fax_white_huff_index[]; +extern const USHORT fax_white_huff[]; + +#define MAX_BLACK_CODELEN 13 +#define MAX_WHITE_CODELEN 12 +#define MAX_VERT_CODELEN 7 + +#define CODELEN_SHIFT 12 +#define VALUE_MASK 0x0fffu + +/* items only in fax_vert_huff table: */ +#define LAST_VERT 6 +#define PASS_MODE 7 +#define HORIZ_MODE 8 + +enum { + RET_GOT_CODE, /* we parsed a good code (ret in *piResult) */ + RET_BAD_CODE, /* trash in row-data */ + RET_FILL, /* got some fill-zeroes */ + RET_HIT_EOL, /* hit EOL; no row-data was parsed or returned */ + RET_NEED_MORE /* need more input-bytes to complete the row */ +}; + + + +/*____________________________________________________________________________ + | | + | Type-definition of our instance-variables | + |____________________________________________________________________________| +*/ + +typedef enum { + NORMAL_2D, + HORIZ_1ST, + HORIZ_2ND +} STATE_2D; + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the image */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ + + /* Variables for getting bits: */ + BYTE *gb_buf_p; /* beginning of our buffer */ + BYTE *gb_buf_after_p; /* 1st byte after our buffer */ + BYTE *gb_byte_p; /* ptr to next byte */ + int gb_cache_cnt; /* # of available bits in gb_cache */ + DWORD gb_cache; /* 32-bit buffer to cache the next few bits */ + /* is also pos of next avail bit; msb=32, lsb=1 */ + int gb_num_zeroes; /* # of successive zero-bits we've gotten */ + + /* Variables for row-decoding functions: */ + int pixel_pos; /* coordinate of next pixel; 0 is leftmost */ + BYTE white; /* doing a white run? (00=black, FF=white) */ + STATE_2D state_2d; /* state of the 2-dim decoder */ + int a0; /* pixel before 1st is an imaginary white pixel */ + BYTE *prior_p; /* buffer containing prior row */ + BOOL ref_row_invalid; /* (MR only) reference row invalid due to error?*/ + + /* Variables for the exported functions: */ + BYTE input_format; /* input format (IP_FAX_MH/MR/MMR) */ + BYTE num_eols; /* number of successive EOLs we've gotten */ + BOOL no_eols; /* are EOLs not present in input? */ + BOOL toss_everything; /* are we discarding all data due to prior err? */ + BOOL flushing_to_eol; /* are we ignoring bits until an EOL? */ + BOOL got_fill; /* gotten any fill-zeroes? */ + BOOL got_black; /* set any black pixels in the row? */ + BOOL two_dim; /* next row is 2-dimensional encoding? */ + int row_len; /* # pixels in each row */ + int bytes_in_row; /* # bytes in each row */ +} DEC_INST, *PDEC_INST; + + + +/***************************************************************************** + * * + * F E T C H I N G B I T S * + * * + ***************************************************************************** + + +Interface into this section: + + bits_init - inits this section + bits_buf_open - gives us (this section) a buffer to consume + BITS_REFILL_CACHE - fills cache; must be called before parsing + BITS_IN_CACHE - returns # bits currently in the cache + BITS_LOAD - returns next N bits of input (no advance is done) + BITS_ADVANCE - advances input by the given # of bits + bits_buf_close - returns # bytes consumed in buffer + bits_flush - discards all unread bits + bits_flush_to_eol - flushes input bits until EOL is encountered +*/ + + + +/*____________________________________________________________________________ + | | | + | bits_init | initializes this "fetching bits" section | + |___________|________________________________________________________________| +*/ +static void bits_init (DEC_INST *g) +{ + g->gb_num_zeroes = 0; + g->gb_cache_cnt = 0; /* the cache is empty */ +} + + + +/*____________________________________________________________________________ + | | | + | bits_buf_open | gives us (this section) a buffer to consume | + |_______________|____________________________________________________________| +*/ +static void bits_buf_open ( + DEC_INST *g, + BYTE *buf_p, + int num_bytes) +{ + g->gb_byte_p = buf_p; + g->gb_buf_p = buf_p; + g->gb_buf_after_p = buf_p + num_bytes; +} + + + +/*____________________________________________________________________________ + | | | + | bits_buf_close | returns # bytes consumed in buffer | + |________________|___________________________________________________________| +*/ +static UINT bits_buf_close (DEC_INST *g) +{ + return (g->gb_byte_p - g->gb_buf_p); +} + + + +/*____________________________________________________________________________ + | | | + | bits_flush | discards all unread bits | + |____________|_______________________________________________________________| +*/ +static void bits_flush (DEC_INST *g) +{ + g->gb_cache_cnt = 0; + g->gb_byte_p = g->gb_buf_after_p; +} + + + +/*____________________________________________________________________________ + | | | + | BITS_REFILL_CACHE | fills cache as full as possible | + |___________________|________________________________________________________| +*/ +#define BITS_REFILL_CACHE(g) \ +{ \ + int cache_cnt = g->gb_cache_cnt; \ + DWORD cache = g->gb_cache; \ + BYTE *byte_p = g->gb_byte_p; \ + BYTE *buf_after_p = g->gb_buf_after_p; \ + \ + while (cache_cnt<=24 && byte_p<buf_after_p) { \ + cache = (cache << 8) | (*byte_p++); \ + cache_cnt += 8; \ + } \ + \ + g->gb_cache_cnt = cache_cnt; \ + g->gb_cache = cache; \ + g->gb_byte_p = byte_p; \ +} + + + +/*____________________________________________________________________________ + | | | + | BITS_IN_CACHE | returns # bits currently in the bit-cache | + |_______________|____________________________________________________________| +*/ +#define BITS_IN_CACHE(g) (g->gb_cache_cnt) + + + +/*____________________________________________________________________________ + | | | + | bits_flush_to_eol | flushes input bits until EOL is encountered | + |___________________|________________________________________________________| + | | + | If got_fill is TRUE, then we merely scan for a set bit. | + | Otherwise, we first count leading zeroes until it reaches 11. | + | | + | Return value: TRUE = We hit an EOL. In this case, if leave_a_bit is | + | TRUE, the cache will contain at least one bit so | + | you can fetch a 1-dim/2-dim bit. | + | FALSE = We need more input data. | + |____________________________________________________________________________| +*/ +static BOOL bits_flush_to_eol ( + DEC_INST *g, /* our instance vars */ + BOOL got_fill, /* have we gotten fill zeroes? */ + BOOL leave_a_bit) /* after hitting EOL, insure that cache isn't empty? */ +{ + #define CLEAR_UNUSED_CACHE_BITS \ + if (g->gb_cache_cnt < 32) \ + g->gb_cache &= (1lu << g->gb_cache_cnt) - 1lu; + + DWORD bit; + BYTE byt; + + /*********************************************/ + /* Scan input until 11 zeroes have been seen */ + /*********************************************/ + + if (g->gb_num_zeroes >= 11) + got_fill = TRUE; + + if (! got_fill) + { + if (g->gb_cache_cnt != 0) { + for (bit = (1lu<<(g->gb_cache_cnt-1)); + bit != 0; + bit >>= 1) { + g->gb_cache_cnt -= 1; + if (bit & g->gb_cache) + g->gb_num_zeroes =0; + else { + g->gb_num_zeroes += 1; + if (g->gb_num_zeroes >= 11) + break; + } + } + } + + /* the cache is now empty; start scanning bytes */ + + while (TRUE) { + if (g->gb_byte_p >= g->gb_buf_after_p) + return FALSE; + byt = *(g->gb_byte_p)++; + g->gb_num_zeroes += baLeftZeroesTbl[byt]; + if (g->gb_num_zeroes >= 11) { + g->gb_byte_p -= 1; + break; + } + if (byt != 0) + g->gb_num_zeroes = baRightZeroesTbl[byt]; + } + } + + /*******************************************/ + /* Scan input until a non-zero bit is seen */ + /*******************************************/ + + while (TRUE) + { + BITS_REFILL_CACHE (g) + if (g->gb_cache_cnt==0 || (leave_a_bit && g->gb_cache_cnt==1)) + return FALSE; + + CLEAR_UNUSED_CACHE_BITS + + if (g->gb_cache == 0) + g->gb_cache_cnt = 0; /* cache is all zeroes; discard it */ + else { + /* we hit the EOL */ + for (bit = (1lu<<(g->gb_cache_cnt-1)); + (bit & g->gb_cache) == 0; + bit >>= 1) + g->gb_cache_cnt -= 1; /* discard the 0's before the 1 */ + + /* After discarding the set bit, if leave_a_bit is TRUE, we want + * the cache to be non-empty so that a 1-dim/2-dim bit can then + * be fetched. Hence the check for 2 bits in cache below. + */ + if (!leave_a_bit || g->gb_cache_cnt>=2) { + g->gb_cache_cnt -= 1; /* discard the set bit we found above */ + g->gb_num_zeroes = 0; + return TRUE; + } + } + + /* discard zero bytes */ + + /* Warning: If fax_decode_convert_row was told to flush, both + * pointers gb_byte_p and gb_buf_after_p can be NULL. So the + * pointer-compare below must be *before* the dereference in the + * test of the while loop, to avoid dereferencing a NULL pointer. + */ + if (g->gb_cache_cnt == 0) + while (g->gb_byte_p<g->gb_buf_after_p && *(g->gb_byte_p)==0) + g->gb_byte_p++; + } +} + + + +/*____________________________________________________________________________ + | | | + | BITS_LOAD | returns the next num_bits of input, with NO advance | + |___________|________________________________________________________________| +*/ +#define BITS_LOAD(g, num_bits, par_result) { \ + int n_bits = (int)(num_bits); \ + \ + par_result = (g->gb_cache >> (g->gb_cache_cnt-n_bits)) \ + & ((1u<<n_bits) - 1u); \ +} + +#if 0 + +#define BITS_LOAD(g, num_bits, par_result) { \ + int n_bits = (int)(num_bits); \ + \ + par_result = g->gb_cache; \ + \ + asm ("extract %1,%2,%0" \ + : "=d" (par_result) \ + : "dI" (g->gb_cache_cnt - n_bits), "dI" (n_bits), "0" (par_result)); \ +} + +#endif + + + +/*____________________________________________________________________________ + | | | + | BITS_ADVANCE | advances input by num_bits bits | + |______________|_____________________________________________________________| +*/ +#define BITS_ADVANCE(g, num_bits) { \ + g->gb_cache_cnt -= (num_bits); \ +} + + + +/***************************************************************************** + * * + * U T I L I T I E S * + * * + *****************************************************************************/ + + + +/*____________________________________________________________________________ + | | | + | parse_code_routine | Parses a Huffman code using the given code tables | + |____________________|_______________________________________________________| + | | + | Function return values: RET_GOT_CODE | + | RET_BAD_CODE | + | RET_FILL | + | RET_HIT_EOL | + | RET_NEED_MORE | + | | + | This function must NOT be called to scan for an EOL after it has | + | returned RET_BAD_CODE or RET_FILL. | + | | + | Warning: When this returns RET_HIT_EOL, it must guarantee that the | + | bit-cache is not empty so a 1-dim/2-dim bit can be fetched. | + |____________________________________________________________________________| +*/ + +static UINT parse_code_routine ( + DEC_INST *g, + int bits_in_index, /* in: # bits in index for index_tbl */ + const BYTE index_tbl[], /* in: contains indices into value_tbl */ + const USHORT value_tbl[], /* in: contains [codelen, value] pairs */ + int *value_p) /* out: the value corresponding to the code */ +{ + UINT blob, values; + + BITS_REFILL_CACHE(g) + if (BITS_IN_CACHE(g) < bits_in_index) + return RET_NEED_MORE; + + BITS_LOAD (g, bits_in_index, blob); + values = value_tbl[index_tbl[blob]]; + + if (values != 0) { + /* normal case: we got a valid code */ + BITS_ADVANCE (g, values >> CODELEN_SHIFT); + *value_p = values & VALUE_MASK; + return RET_GOT_CODE; + } + + if (BITS_IN_CACHE(g) < MAX_CODE_LEN) + return RET_NEED_MORE; + + BITS_LOAD (g, EOL_LEN, blob); + + if (blob == 1) { + BITS_ADVANCE (g, EOL_LEN); + return RET_HIT_EOL; + } + + if (blob == 0) { + BITS_ADVANCE (g, EOL_LEN); + return RET_FILL; + } + + return RET_BAD_CODE; +} + + + +/*____________________________________________________________________________ + | | | + | PARSE_CODE | A fast macro for parsing Huffman codes | + |____________|_______________________________________________________________| +*/ +#define PARSE_CODE( \ + g, \ + bits_in_index, /* in: # bits in index for index_tbl */ \ + index_tbl, /* in: contains indices into value_tbl */ \ + value_tbl, /* in: contains [codelen, value] pairs */ \ + out_result, /* out: RET_GOT_CODE, RET_BAD_CODE, etc */ \ + out_value) /* out: the value corresponding to the code */ \ +do { \ + UINT blob, values; \ + \ + if (BITS_IN_CACHE(g) < bits_in_index) \ + goto call##value_tbl; \ + \ + BITS_LOAD (g, bits_in_index, blob); \ + values = value_tbl[index_tbl[blob]]; \ + \ + if (values == 0) { \ + call##value_tbl: \ + out_result = parse_code_routine \ + (g, bits_in_index, index_tbl, value_tbl, &out_value); \ + } else { \ + BITS_ADVANCE (g, values >> CODELEN_SHIFT); \ + out_result = RET_GOT_CODE; \ + out_value = values & VALUE_MASK; \ + } \ +} while (0) + + + +/*____________________________________________________________________________ + | | | + | runs_array | all possible runs that fit in 16 bits | + |____________|_______________________________________________________________| + | | + | This array is indexed by (leftbit<<4)+rightbit. | + | Leftbit and rightbit are in 0..15. 0=msb, 15=lsb. | + |____________________________________________________________________________| +*/ + +static const USHORT runs_array_le[256] = { /* little-endian array */ + 0x0080, 0x00c0, 0x00e0, 0x00f0, 0x00f8, 0x00fc, 0x00fe, 0x00ff, + 0x80ff, 0xc0ff, 0xe0ff, 0xf0ff, 0xf8ff, 0xfcff, 0xfeff, 0xffff, + 0x0000, 0x0040, 0x0060, 0x0070, 0x0078, 0x007c, 0x007e, 0x007f, + 0x807f, 0xc07f, 0xe07f, 0xf07f, 0xf87f, 0xfc7f, 0xfe7f, 0xff7f, + 0x0000, 0x0000, 0x0020, 0x0030, 0x0038, 0x003c, 0x003e, 0x003f, + 0x803f, 0xc03f, 0xe03f, 0xf03f, 0xf83f, 0xfc3f, 0xfe3f, 0xff3f, + 0x0000, 0x0000, 0x0000, 0x0010, 0x0018, 0x001c, 0x001e, 0x001f, + 0x801f, 0xc01f, 0xe01f, 0xf01f, 0xf81f, 0xfc1f, 0xfe1f, 0xff1f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x000c, 0x000e, 0x000f, + 0x800f, 0xc00f, 0xe00f, 0xf00f, 0xf80f, 0xfc0f, 0xfe0f, 0xff0f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0006, 0x0007, + 0x8007, 0xc007, 0xe007, 0xf007, 0xf807, 0xfc07, 0xfe07, 0xff07, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0003, + 0x8003, 0xc003, 0xe003, 0xf003, 0xf803, 0xfc03, 0xfe03, 0xff03, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, + 0x8001, 0xc001, 0xe001, 0xf001, 0xf801, 0xfc01, 0xfe01, 0xff01, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x2000, 0x3000, 0x3800, 0x3c00, 0x3e00, 0x3f00, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1000, 0x1800, 0x1c00, 0x1e00, 0x1f00, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x0c00, 0x0e00, 0x0f00, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0400, 0x0600, 0x0700, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, +}; + +static const USHORT runs_array_be[256] = { /* big-endian array */ + 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, + 0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, + 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00, + 0x7f80, 0x7fc0, 0x7fe0, 0x7ff0, 0x7ff8, 0x7ffc, 0x7ffe, 0x7fff, + 0x0000, 0x0000, 0x2000, 0x3000, 0x3800, 0x3c00, 0x3e00, 0x3f00, + 0x3f80, 0x3fc0, 0x3fe0, 0x3ff0, 0x3ff8, 0x3ffc, 0x3ffe, 0x3fff, + 0x0000, 0x0000, 0x0000, 0x1000, 0x1800, 0x1c00, 0x1e00, 0x1f00, + 0x1f80, 0x1fc0, 0x1fe0, 0x1ff0, 0x1ff8, 0x1ffc, 0x1ffe, 0x1fff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x0c00, 0x0e00, 0x0f00, + 0x0f80, 0x0fc0, 0x0fe0, 0x0ff0, 0x0ff8, 0x0ffc, 0x0ffe, 0x0fff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0400, 0x0600, 0x0700, + 0x0780, 0x07c0, 0x07e0, 0x07f0, 0x07f8, 0x07fc, 0x07fe, 0x07ff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, + 0x0380, 0x03c0, 0x03e0, 0x03f0, 0x03f8, 0x03fc, 0x03fe, 0x03ff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, + 0x0180, 0x01c0, 0x01e0, 0x01f0, 0x01f8, 0x01fc, 0x01fe, 0x01ff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0080, 0x00c0, 0x00e0, 0x00f0, 0x00f8, 0x00fc, 0x00fe, 0x00ff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0040, 0x0060, 0x0070, 0x0078, 0x007c, 0x007e, 0x007f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0020, 0x0030, 0x0038, 0x003c, 0x003e, 0x003f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0010, 0x0018, 0x001c, 0x001e, 0x001f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x000c, 0x000e, 0x000f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0006, 0x0007, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0003, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, +}; + +static const USHORT *runs_array = runs_array_le; + +static void initRunArray(void) { + int iTest=1; + char *pcTest=((char *)(&iTest)); + + if (*pcTest==1) { + runs_array = runs_array_le; + } else { + runs_array = runs_array_be; + } +} + + +/*____________________________________________________________________________ + | | | + | set_run | Sets a run of pixels to black | + |_________|__________________________________________________________________| + | | + | This routine does bounds-checking to avoid setting any pixels outside the | + | given buffer. | + | | + | Since this is called often, it's written to be as fast as I could make it. | + |____________________________________________________________________________| +*/ +static void set_run ( + BYTE *buf_p, /* buffer in which we're to set the run */ + int start_pos, /* pixel-index of left side of run */ + int run_len, /* # of pixels in the run (non-negative) */ + int row_len) /* # of pixels in the buffer */ +{ + /************************************************************************* + * + * PERFORMANCE NOTE + * + * Inlining this routine slows it down a little. + * Seconds to MMR encode+decode 1000 rows of 1728 pixels (bench_fax.c): + * + * black density: 0% 10% 20% 50% + * calling this routine: 2.2 7.4 12.8 29.9 + * inlining this routine: 2.2 7.5 13.2 30.6 + * + * Inlining this routine *hurt* performance a little because more MMR + * code was kicked out of the cache. + * + *************************************************************************/ + + int end_pos; /* pixel-index of rightmost pixel in run */ + int start_index; /* byte-index of first byte in run */ + BYTE *left_p; /* ptr to byte containing leftmost pixel in run */ + BYTE *right_p; /* ptr to byte containing rightmost pixel in run */ + + end_pos = start_pos + run_len - 1; + + if (end_pos >= row_len) + end_pos = row_len - 1; + + start_index = start_pos >> 4; + + if ((end_pos >> 4) == start_index) { + /* the run is contained within an even-aligned 2-byte word */ + ((USHORT*)buf_p)[start_index] |= + runs_array [((start_pos & 15)<<4) | (end_pos & 15)]; + } else if (end_pos > start_pos) { + /* the run spans two or more bytes */ + left_p = buf_p + (start_pos >> 3); + right_p = buf_p + (end_pos >> 3); + + *left_p |= (BYTE )0xFFu >> (UINT)( start_pos & 7u ); + *right_p = (BYTE )0xFFu << (UINT)(7u - (end_pos & 7u)); + left_p++; + + while (left_p < right_p) + *left_p++ = 0xFFu; + } +} + + +/***************************************************************************** + * * + * DECODING ROW-DATA * + * * + ***************************************************************************** + + + Interface into this section: + + decode_row_init - inits instance-vars for this section + decode_row_1d - parses a row of 1-dim data + decode_row_2d - parses a row of 2-dim data + + The parsing routines return these bit-values: + + IP_PRODUCED_ROW + IP_INPUT_ERROR + DECODE_HIT_EOL (defined below, not in the public interface) + DECODE_HIT_FILL (ditto) + + A return-value of zero (ie, none of the above bits are set) means that the + routine wants to be called again with more input-bytes. + + These routines fetch input by calling BITS_LOAD, so bits_init and + bits_buf_open must have already been called. +*/ + +#define DECODE_HIT_EOL 0x4000u +#define DECODE_HIT_FILL 0x8000u + + +/*____________________________________________________________________________ + | | | + | decode_row_init | inits instance-variables for this section | + |_________________|__________________________________________________________| +*/ +static void decode_row_init (DEC_INST *g) +{ + g->pixel_pos = 0; + g->white = 0xFFu; /* start with a white run */ + g->state_2d = NORMAL_2D; + g->a0 = -1; /* pixel before 1st is an imaginary white pixel */ + g->got_black = FALSE; /* haven't gotten any black pixels */ +} + + + +/*____________________________________________________________________________ + | | | + | decode_row_1d | Decodes a row of MH (1-dimensional G3) data | + |_______________|____________________________________________________________| +*/ +static UINT decode_row_1d ( + DEC_INST *g, /* in: pointer to our instance-variables */ + BYTE *pbPrevOutBuf, /* in: prev output row (for error-handling) */ + BYTE *pbOutBuf) /* out: output buf */ +{ + int run_len; /* # pixels in current run */ + UINT white; + int pixel_pos; + UINT result; + UINT ret_val = 0; /* init to zap a compiler warning */ + int index_length; + const BYTE *index_tbl_p; + const USHORT *value_tbl_p; + + if (g->pixel_pos == 0) { + memset (pbOutBuf, 0, g->bytes_in_row); /* set whole row to white */ + g->ref_row_invalid = TRUE; + } + + white = (UINT)(int )(signed char)g->white; /* must be all 0's or all 1's */ + pixel_pos = g->pixel_pos; + + while (TRUE) { + + if (white) { + index_length = MAX_WHITE_CODELEN; + index_tbl_p = fax_white_huff_index; + value_tbl_p = fax_white_huff; + } else { + index_length = MAX_BLACK_CODELEN; + index_tbl_p = fax_black_huff_index; + value_tbl_p = fax_black_huff; + } + + PARSE_CODE (g, index_length, index_tbl_p, value_tbl_p, result, run_len); + PRINT (_T("pc result=%d, len=%d\n"), result, run_len); + + if (result == RET_GOT_CODE) { + /* todo: below, we call set_run for EVERY make-up code (slow) */ + if (! white) { + set_run (pbOutBuf, pixel_pos, run_len, g->row_len); + g->got_black = TRUE; + } + + pixel_pos += run_len; + if (run_len <= 63) /* this is a final run (not a make-up) */ + white = ~white; + continue; /* go to top of main 'while' loop */ + } + + switch (result) { /* handle unusual condition */ + + case RET_NEED_MORE: + ret_val = 0; + goto bail_out; + break; + + case RET_BAD_CODE: + memcpy (pbOutBuf, pbPrevOutBuf, g->bytes_in_row); + ret_val = IP_PRODUCED_ROW | IP_INPUT_ERROR; + goto bail_out; + break; + + case RET_FILL: + case RET_HIT_EOL: + ret_val = result==RET_FILL ? DECODE_HIT_FILL : DECODE_HIT_EOL; + if (pixel_pos == 0) + goto bail_out; + if (pixel_pos == g->row_len) { + ret_val |= IP_PRODUCED_ROW; + g->ref_row_invalid = FALSE; + goto bail_out; + } + memcpy (pbOutBuf, pbPrevOutBuf, g->bytes_in_row); + ret_val |= IP_PRODUCED_ROW | IP_INPUT_ERROR; + goto bail_out; + break; + + default: + assert (FALSE); + } /* end of switch */ + } /* end of while */ + + bail_out: + + g->white = white; + g->pixel_pos = pixel_pos; + + return ret_val; +} + + + +/*____________________________________________________________________________ + | | | + | decode_row_2d | Decodes a row of MR/MMR (2-dimensional) data | + |_______________|____________________________________________________________| +*/ +static UINT decode_row_2d ( + DEC_INST *g, /* in: pointer to our instance-variables */ + BYTE *pbPrevBuf, /* in: prev output row ("reference row") */ + BYTE *pbOutBuf) /* out: output buffer */ +{ + UINT group4; + int iAction; + UINT white; + UINT result = 0; /* the 0 eliminates a compiler warning */ + int row_len; + int run_len; + int a0, a1; + int b1=0, b2; /* the 0 eliminates a compiler warning */ + UINT need_b0; + UINT ret_val = 0; /* the 0 eliminates a compiler warning */ + + group4 = (g->input_format == IP_FAX_MMR); + row_len = g->row_len; + white = (UINT)(int)(signed char)g->white; /* must be all 0's or all 1's so sign extend */ + a0 = g->a0; + + if (a0 < 0) { + memset (pbOutBuf, 0, g->bytes_in_row); /* set whole row to white */ + pbPrevBuf[g->bytes_in_row] = 0x55u; /* scan_to requires this */ + } + + while (TRUE) { + + /****************************************/ + /* Process a usual code -- normal state */ + /****************************************/ + + need_b0 = TRUE; + + while (g->state_2d == NORMAL_2D) { + + if (a0>=row_len && group4) { + /* ret_val = IP_PRODUCED_ROW; goto bail_out; + * todo: undelete the line above? */ + if (a0 == row_len) ret_val = IP_PRODUCED_ROW; + else ret_val = IP_INPUT_ERROR; + goto bail_out; + } + + PARSE_CODE (g, MAX_VERT_CODELEN, fax_vert_huff_index, + fax_vert_huff, result, iAction); + if (result != RET_GOT_CODE) + goto unusual_condition; + + if (iAction == HORIZ_MODE) { + g->state_2d = HORIZ_1ST; + break; + } + + if (a0 < 0) { + a0 = 0; + b1 = -1; /* a kludge so scan below will start at pixel 0 */ + } else if (need_b0) + b1 = scan_to (white, pbPrevBuf, a0, row_len); + + b1 = scan_to (~white, pbPrevBuf, b1+1, row_len); + + if (iAction <= LAST_VERT) { /* vertical mode */ + iAction -= 3; + a1 = b1 + iAction; + need_b0 = (iAction<-1 || iAction>0); + if (! white) { + set_run (pbOutBuf, a0, a1-a0, row_len); + g->got_black = TRUE; + } + if (a1<a0 || a1>row_len) goto corrupt; + a0 = a1; + white = ~white; + } else { /* pass mode */ + b2 = scan_to (white, pbPrevBuf, b1+1, row_len); + if (! white) { + set_run (pbOutBuf, a0, b2-a0, row_len); + g->got_black = TRUE; + } + need_b0 = FALSE; + a0 = b2; + b1 = b2; + } + } /* while NORMAL_2D */ + + /*******************************************/ + /* Process a usual code -- horizontal mode */ + /*******************************************/ + + while (TRUE) { /* HORIZ_1ST or HORIZ_2ND */ + int index_length; + const BYTE *index_tbl_p; + const USHORT *value_tbl_p; + + do { + if (white) { + index_length = MAX_WHITE_CODELEN; + index_tbl_p = fax_white_huff_index; + value_tbl_p = fax_white_huff; + } else { + index_length = MAX_BLACK_CODELEN; + index_tbl_p = fax_black_huff_index; + value_tbl_p = fax_black_huff; + } + + PARSE_CODE (g, index_length, index_tbl_p, value_tbl_p, + result, run_len); + + if (result != RET_GOT_CODE) + goto unusual_condition; + if (a0 < 0) /* Exception: See Fascicle VII.3, */ + a0 = 0; /* rec T.4, section 4.2.1.3.4 */ + /* todo: below, we call set_run for EVERY make-up code (slow) */ + if (! white) { + set_run (pbOutBuf, a0, run_len, row_len); + g->got_black = TRUE; + } + a0 += run_len; + } while (run_len > 63); + + white = ~white; + if (g->state_2d == HORIZ_1ST) + g->state_2d = HORIZ_2ND; + else { + g->state_2d = NORMAL_2D; + if (a0 > row_len) goto corrupt; + break; /* exit while */ + } + } + + continue; /* no unusual condition, so resume main while loop */ + + /*****************************/ + /* Handle unusual conditions */ + /*****************************/ + + corrupt: + result = RET_BAD_CODE; + + unusual_condition: + + switch (result) { + + case RET_GOT_CODE: + /* normal case: was handled in the block of code above */ + break; + + case RET_NEED_MORE: + ret_val = 0; + goto bail_out; + break; + + case RET_BAD_CODE: + g->state_2d = NORMAL_2D; + memcpy (pbOutBuf, pbPrevBuf, g->bytes_in_row); + ret_val = IP_PRODUCED_ROW | IP_INPUT_ERROR; + g->ref_row_invalid = TRUE; + goto bail_out; + break; + + case RET_FILL: + case RET_HIT_EOL: + ret_val = result==RET_FILL ? DECODE_HIT_FILL : DECODE_HIT_EOL; + g->state_2d = NORMAL_2D; + + if (group4) { + /* We should only hit EOL when no row-data is present */ + /* todo: Always report error if fill-zeroes were seen? */ + if (a0 >= 0) + ret_val |= IP_INPUT_ERROR; + } else { + if (!g->ref_row_invalid && a0==row_len) + ret_val |= IP_PRODUCED_ROW; + else if (a0 >= 0) { + memcpy (pbOutBuf, pbPrevBuf, g->bytes_in_row); + ret_val |= IP_PRODUCED_ROW | IP_INPUT_ERROR; + g->ref_row_invalid = TRUE; + } + } + goto bail_out; + break; + + default: + assert (FALSE); + + } /* end of switch to handle unusual condition */ + } /* end of while */ + + bail_out: + + /* save variables needed in next call */ + g->a0 = a0; + g->white = white; + + return ret_val; +} + + + +/*****************************************************************************\ + * + * faxDecode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD faxDecode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PDEC_INST g; + + initRunArray(); + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(DEC_INST), g); + *pXform = g; + memset (g, 0, sizeof(DEC_INST)); + g->dwValidChk = CHECK_VALUE; + + bits_init (g); + decode_row_init (g); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxDecode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD faxDecode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we actually use or care about are known */ + INSURE (pTraits->iPixelsPerRow > 0); /* we need the row-length */ + INSURE (pTraits->iBitsPerPixel == 1); /* image must be bi-level */ + + g->traits = *pTraits; /* a structure copy */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxDecode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD faxDecode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + g->input_format = (BYTE)aXformInfo[IP_FAX_FORMAT].dword; + g->no_eols = (BOOL)aXformInfo[IP_FAX_NO_EOLS].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxDecode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD faxDecode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * faxDecode_getActualTraits - Parses header, and returns input & output traits + * + ***************************************************************************** + * + * There is no header, so this routine merely computes some stuff + * +\*****************************************************************************/ + +static WORD faxDecode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PDEC_INST g; + int inBytes; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Since we don't change traits, just copy out the default traits */ + + *pInTraits = g->traits; + *pOutTraits = g->traits; + + /* Compute some stuff */ + + g->row_len = g->traits.iPixelsPerRow; /* todo: zap redundant var */ + g->bytes_in_row = inBytes = (g->row_len+7) / 8; + g->two_dim = (g->input_format == IP_FAX_MMR); + + /* For MH and MR: Discard bits before the first EOL */ + g->flushing_to_eol = ! g->two_dim; + + /* allocate the prior-row buffer */ + + if (g->prior_p != NULL) + IP_MEM_FREE (g->prior_p); + IP_MEM_ALLOC (inBytes, g->prior_p); + memset (g->prior_p, 0, inBytes); + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * faxDecode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD faxDecode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = 1; + *pdwMinOutBufLen = g->bytes_in_row; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxDecode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD faxDecode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PDEC_INST g; + UINT bit; + UINT ret_val; /* return-value of this function */ + + HANDLE_TO_PTR (hXform, g); + + *pdwOutputUsed = 0; + + if (dwInputAvail==0 && BITS_IN_CACHE(g)<MAX_CODE_LEN) { + /* We're being told to flush; indicate we're done. */ + /* Our only buffer is the cache */ + *pdwInputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + if (g->toss_everything) { + *pdwInputUsed = dwInputAvail; + *pdwInputNextPos = g->dwInNextPos = g->dwInNextPos + dwInputAvail; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_READY_FOR_DATA; + } + + bits_buf_open (g, pbInputBuf, dwInputAvail); + + if (g->flushing_to_eol) { + ret_val = bits_flush_to_eol(g, g->got_fill, + (BOOL)(g->input_format==IP_FAX_MR)) + ? DECODE_HIT_EOL : 0; + } else { + if (g->two_dim) ret_val = decode_row_2d(g, g->prior_p, pbOutputBuf); + else ret_val = decode_row_1d(g, g->prior_p, pbOutputBuf); + PRINT (_T("decoder returned %x\n"), ret_val, 0); + } + + if (ret_val & IP_INPUT_ERROR) { + if (g->input_format == IP_FAX_MMR) { + bits_flush (g); + g->toss_everything = TRUE; + /* This isn't fatal any more. We merely toss all following data */ + /* ret_val |= IP_INPUT_ERROR; */ + } else { + g->flushing_to_eol = TRUE; + g->num_eols = 0; + } + } + + if (ret_val & IP_PRODUCED_ROW) { + if (g->got_black) ret_val |= NONWHITE_ROW | IP_CONSUMED_ROW; + else ret_val |= WHITE_ROW | IP_CONSUMED_ROW; + *pdwOutputUsed = g->bytes_in_row; + memcpy (g->prior_p, pbOutputBuf, g->bytes_in_row); + decode_row_init (g); + g->num_eols = 0; /* ignore all EOLs before the row */ + } + + if (ret_val & DECODE_HIT_FILL) { + g->flushing_to_eol = TRUE; + g->got_fill = TRUE; + } + + if (ret_val & DECODE_HIT_EOL) { + if (g->input_format == IP_FAX_MR) { + /* read the tag-bit for 1-dim/2-dim */ + BITS_LOAD (g, 1, bit) + g->two_dim = (bit == 0); + BITS_ADVANCE (g, 1) + } + + decode_row_init (g); + g->flushing_to_eol = FALSE; + g->got_fill = FALSE; + g->num_eols += 1; + + if (g->num_eols>=EOLS_FOR_MH_MR || + (g->input_format==IP_FAX_MMR && g->num_eols>=EOLS_FOR_MMR)) { + /* WE HIT END OF PAGE */ + g->num_eols = 0; + bits_flush (g); + ret_val |= IP_NEW_OUTPUT_PAGE; + } + } + + *pdwInputUsed = bits_buf_close (g); + g->dwInNextPos += *pdwInputUsed; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = (ret_val & IP_PRODUCED_ROW) ? g->bytes_in_row : 0; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += *pdwOutputUsed; + +#if 0 + { + int i; + for (i=0; i<*pdwInputUsed; i++) + PRINT (_T("%02x "), pbInputBuf[i], 0); + } +#endif + + return (ret_val | IP_READY_FOR_DATA) + & (IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA | + /* NONWHITE_ROW | WHITE_ROW | */ + IP_NEW_OUTPUT_PAGE | + IP_INPUT_ERROR | IP_FATAL_ERROR); + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxDecode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD faxDecode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * faxDecode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD faxDecode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: flush bits until we see a page boundary */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * faxDecode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD faxDecode_closeXform (IP_XFORM_HANDLE hXform) +{ + PDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (g->prior_p != NULL) + IP_MEM_FREE (g->prior_p); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * faxDecodeTbl - Jump-table for Decoder + * +\*****************************************************************************/ + +IP_XFORM_TBL faxDecodeTbl = { + faxDecode_openXform, + faxDecode_setDefaultInputTraits, + faxDecode_setXformSpec, + faxDecode_getHeaderBufSize, + faxDecode_getActualTraits, + faxDecode_getActualBufSizes, + faxDecode_convert, + faxDecode_newPage, + faxDecode_insertedData, + faxDecode_closeXform +}; + + + + +/* +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ @ +@ T A B L E S F O R D E C O D E R @ +@ @ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +*/ + + + +const USHORT fax_white_huff[105] = { 0, + 0x4002, 0x4003, 0x4004, 0x4005, 0x4006, 0x4007, 0x500a, 0x500b, + 0x5080, 0x5008, 0x5009, 0x5040, 0x600d, 0x6001, 0x600c, 0x60c0, + 0x6680, 0x6010, 0x6011, 0x600e, 0x600f, 0x7016, 0x7017, 0x7014, + 0x7013, 0x701a, 0x7015, 0x701c, 0x701b, 0x7012, 0x7018, 0x7019, + 0x7100, 0x801d, 0x801e, 0x802d, 0x802e, 0x802f, 0x8030, 0x8021, + 0x8022, 0x8023, 0x8024, 0x8025, 0x8026, 0x801f, 0x8020, 0x8035, + 0x8036, 0x8027, 0x8028, 0x8029, 0x802a, 0x802b, 0x802c, 0x803d, + 0x803e, 0x803f, 0x8000, 0x8140, 0x8180, 0x803b, 0x803c, 0x8031, + 0x8032, 0x8033, 0x8034, 0x8037, 0x8038, 0x8039, 0x803a, 0x81c0, + 0x8200, 0x8280, 0x8240, 0x95c0, 0x9600, 0x9640, 0x96c0, 0x92c0, + 0x9300, 0x9340, 0x9380, 0x93c0, 0x9400, 0x9440, 0x9480, 0x94c0, + 0x9500, 0x9540, 0x9580, 0xb700, 0xb740, 0xb780, 0xc7c0, 0xc800, + 0xc840, 0xc880, 0xc8c0, 0xc900, 0xc940, 0xc980, 0xc9c0, 0xca00, +}; + + +const BYTE fax_white_huff_index[4096] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 92, 92, 95, 96, 97, 98, 99, 100, 93, 93, 94, 94, 101, 102, 103, 104, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, + 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 80, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 81, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 82, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 83, + 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, + 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, + 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, + 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + + +const USHORT fax_black_huff[105] = { 0, + 0x2003, 0x2002, 0x3001, 0x3004, 0x4006, 0x4005, 0x5007, 0x6009, + 0x6008, 0x700a, 0x700b, 0x700c, 0x800d, 0x800e, 0x900f, 0xa012, + 0xa040, 0xa010, 0xa011, 0xa000, 0xb700, 0xb740, 0xb780, 0xb018, + 0xb019, 0xb017, 0xb016, 0xb013, 0xb014, 0xb015, 0xc7c0, 0xc800, + 0xc840, 0xc880, 0xc8c0, 0xc900, 0xc940, 0xc980, 0xc9c0, 0xca00, + 0xc034, 0xc037, 0xc038, 0xc03b, 0xc03c, 0xc140, 0xc180, 0xc1c0, + 0xc035, 0xc036, 0xc032, 0xc033, 0xc02c, 0xc02d, 0xc02e, 0xc02f, + 0xc039, 0xc03a, 0xc03d, 0xc100, 0xc030, 0xc031, 0xc03e, 0xc03f, + 0xc01e, 0xc01f, 0xc020, 0xc021, 0xc028, 0xc029, 0xc080, 0xc0c0, + 0xc01a, 0xc01b, 0xc01c, 0xc01d, 0xc022, 0xc023, 0xc024, 0xc025, + 0xc026, 0xc027, 0xc02a, 0xc02b, 0xd280, 0xd2c0, 0xd300, 0xd340, + 0xd500, 0xd540, 0xd580, 0xd5c0, 0xd600, 0xd640, 0xd680, 0xd6c0, + 0xd200, 0xd240, 0xd380, 0xd3c0, 0xd400, 0xd440, 0xd480, 0xd4c0, +}; + + +const BYTE fax_black_huff_index[8192] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 21, 21, 21, 21, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, + 22, 22, 22, 22, 23, 23, 23, 23, 37, 37, 38, 38, 39, 39, 40, 40, + 16, 16, 16, 16, 16, 16, 16, 16, 41, 41, 85, 86, 87, 88, 42, 42, + 43, 43, 89, 90, 91, 92, 44, 44, 45, 45, 93, 94, 24, 24, 24, 24, + 25, 25, 25, 25, 95, 96, 46, 46, 47, 47, 48, 48, 97, 98, 49, 49, + 50, 50, 99, 100, 101, 102, 103, 104, 17, 17, 17, 17, 17, 17, 17, 17, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 26, 26, 26, 26, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, + 57, 57, 58, 58, 59, 59, 60, 60, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 61, 61, 62, 62, 63, 63, 64, 64, + 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 27, 27, 27, 27, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 28, 28, 28, 28, + 29, 29, 29, 29, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, + 30, 30, 30, 30, 83, 83, 84, 84, 20, 20, 20, 20, 20, 20, 20, 20, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + + +const USHORT fax_vert_huff[10] = { 0, + 0x1003, 0x3008, 0x3002, 0x3004, 0x4007, 0x6001, 0x6005, 0x7000, + 0x7006, +}; + + +const BYTE fax_vert_huff_index[128] = { + 0, 0, 8, 9, 6, 6, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +/* End of File */ diff --git a/ip/xform.h b/ip/xform.h new file mode 100644 index 0000000..4ce4829 --- /dev/null +++ b/ip/xform.h @@ -0,0 +1,340 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/* xform.h - Interface into the transform drivers + * + * Mark Overton, Jan 2000 - Extracted from hpojip.h + */ + +#if ! defined XFORM_H_INC +#define XFORM_H_INC + + +/*****************************************************************************\ + ***************************************************************************** + * + * TRANSFORM DRIVER interface + * + ***************************************************************************** +\*****************************************************************************/ + + +/* In some ways, this driver interface is very similar to the main ip + * interface. Please don't get them confused. + * + * One of the parameters of the ipOpen function is an array of structures. + * Each such structure defines a transform, and contains, among other things, + * a pointer to a jump-table for the transform-driver. The jump-table is + * a structure containing function-pointers for all the driver's functions. + * These functions and the jump-table are defined below. + * + * Function Call Order + * + * openXform; + * setDefaultInputTraits, setXformSpec, getHeaderBufSize (any order); + * getActualTraits (possibly multiple calls); + * getActualBufSizes; + * convert, insertedData, newPage (usually multiple calls); + * closeXform. + * + * Raw Data Format + * + * A decoder outputs raw data, an encoder inputs raw data, and all data passed + * between xforms consists of raw data. + * Such raw raster data consists of fixed-length raster rows of packed pixels. + * The pixels are packed as follows: + * + * bi-level: 8 pixels/byte, left pixel in msb, 0=white, 1=black. + * 4-bit gray: 2 pixels/byte, left pixel in hi nibble, 0=black, 15=white. + * 8-bit gray: 1 pixel/byte, 0=black, 255=white. + * color: three bytes per pixel, in some color space. + * + * Driver Documentation + * + * The .c file for an xform driver starts with a comment-section documenting + * the following items: + * + * - the capabilities of the driver, and its limitations. + * - the name of the global jump-table (of type IP_XFORM_TBL). + * - what should be put in the aXformInfo array passed to setXformSpec. + * - which items in default input traits are ignored versus used. + * - what the output image traits are. + * + * Note that image traits, such as pixels per row, should *not* be put in + * aXformInfo because such info is provided by setDefaultInputTraits. + * Things like the JPEG quality-factor should be in aXformInfo. + * + * An xform driver is allowed to overrun its input or output buffer by + * up to 12 bytes. This is allowed because some image processing algorithms + * are faster if they operate on multiple pixels at a time, which will cause + * them to read or write a little past the end of the buffer. Reading or + * writing before the beginning of a buffer is not allowed. + */ + + +typedef void* IP_XFORM_HANDLE; /* handle for an xform driver */ + + +/* IP_XFORM_TBL - Jump-table for a transform driver (all the entry points) + */ +typedef struct IP_XFORM_TBL_s { + + /* openXform - Creates a new instance of the transformer + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*openXform) ( + IP_XFORM_HANDLE *pXform); /* out: returned handle */ + + + /* setDefaultInputTraits - Specifies default input image traits + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*setDefaultInputTraits) ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits); /* in: default image traits */ + + + /* setXformSpec - Provides xform-specific information + * + * The aXformInfo array provides the transform-specific info. + * For example, for a scaling transform, this array would contain + * the horizontal and vertical scaling factors and possibly additional + * info about whether to scale quickly by simple pixel-replication. + * For a JPEG-encode transform, the array would contain the quality + * factor and subsampling information. + * + * Each transform documents what it needs in aXformInfo. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*setXformSpec) ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]); /* in: xform information */ + + + /* getHeaderBufSize- Returns size of input buf needed to hold header + * + * Returns size of input buffer that's guaranteed to hold the file + * header. If that's too big, the xform code will have to parse the + * header across several calls, and return a suitable buffer size. + * If there is no header, this function returns 0 in pwInBufLen. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*getHeaderBufSize) ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen); /* out: buf size for parsing header */ + + + /* getActualTraits - Parses header, and returns input & output traits + * + * This function may need to be called multiple times to parse the + * file header. And it can consume input in each call. + * Once the header has been parsed, it returns IP_DONE, meaning that + * it is not to be called again and that the in-traits and out-traits + * structures have been filled in. The output traits are computed based + * on the input traits and the xform information provided by setXformSpec. + * + * See the description of 'convert' below for how the input-buffer + * parameters are used. The final value returned in pdwInputNextPos + * (where this returns IP_DONE) is the one that applies to the + * 'convert' function. This function MUST be called even if there is + * no header because you need that pdwInputNextPos value. + * + * NOTE: In addition to IP_DONE, the IP_READY_FOR_DATA bit must also + * be set if the xform will want data on the first call to 'convert'. + * + * Return value: + * 0 = call again with more input, + * IP_DONE = normal end (IP_READY_FOR_DATA set if data needed) + * IP_INPUT_ERROR = error in input data, + * IP_FATAL_ERROR = misc error. + */ + WORD (*getActualTraits) ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits); /* out: output image traits */ + + + /* getActualBufSizes - Returns buf sizes needed for remainder of session + * + * Since the input and output row-lengths are now known because + * getActualTraits has parsed the header, and actual buffer sizes needed + * for the remainder of the conversion session can now be fetched. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*getActualBufSizes) ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pwMinInBufLen, /* out: min input buf size */ + PDWORD pwMinOutBufLen); /* out: min output buf size */ + + + /* convert - The work-horse conversion routine + * + * This function consumes input data and produces output data via the + * input- and output-buffer parameters. And it tells you what's happening + * via its function return value. + * + * On entry, pbInputBuf and wInputAvail specify the location and number of + * data-bytes in the input buffer. On return, pwInputUsed tells you how + * many of those input bytes were consumed. pdwInputNextPos tells you + * where in the input file you should read next for the following call; + * 0 is the beginning of the file. This is almost always the current file + * position plus pwInputUsed; if not, a file-seek is being requested. + * + * The output buffer parameters are analogous to the input parameters, + * except that pdwOutputThisPos tells you where the bytes just output + * should be written in the output file. That is, it applies to *this* + * write, not the *next* write, unlike the input arrangement. + * + * The function return value is a bit-mask that tells you if anything + * interesting happened. Multiple bits can be set. This information + * should be treated as independent of the data-transfers occuring via + * the parameters. The IP_CONSUMED_ROW and IP_PRODUCED_ROW bits can + * be used to count how many rows have been input and output. + * + * The IP_READY_FOR_DATA bit indicates that the next call to 'convert' + * definitely will consume data. If this bit is 0, the next call + * definitely will NOT consume data. The main converter code that calls + * these xform functions uses this ready-for-data info to control the + * order of xform calls. The IP_READY_FOR_DATA bit must be set correctly. + * + * The IP_NEW_INPUT_PAGE bit is set when or after the last row of the + * input page has been parsed. It may be a few rows before you get the + * corresponding IP_NEW_OUTPUT_PAGE bit. + * + * The IP_NEW_OUTPUT_PAGE bit is set when or after the last row of the + * page has been sent, and before the first row of the following page (if + * any) is sent. + * + * You may wish to insert secret data, such as thumbnails, into the + * output stream. When 'convert' returns the IP_WRITE_INSERT_OK bit, + * it is giving you permission to write stuff AFTER you write the output + * buffer it gave you. After adding your secret data, you must call + * insertedData to tell us how many bytes were added. + * + * When there is no more input data, 'convert' must be called repeatedly + * with a NULL pbInputBuf parameter, which tells the xform to flush out + * any buffered rows. Keep calling it until it returns the IP_DONE bit. + * + * Do not call 'convert' again after it has returned either error bit or + * IP_DONE. + * + * If the input or output data consists of fixed-length uncompressed rows, + * then it is permissible for the xform to read up to 8 bytes past the + * end of the (fixed-length) input row, or to write up to 8 bytes past the + * end of the (fixed-length) output row. The caller of the xform routines + * allocates at least this many extra overrun-zone bytes so that algorithms + * can process pixels multiple-bytes at a time without worrying about + * running past the end of the row. These overrun-zone bytes must NOT be + * reported by getActualBufSizes, which should report only what's needed + * for the actual row-data. + * + * Return value: Zero or more of these bits may be set: + * + * IP_CONSUMED_ROW = an input row was parsed + * IP_PRODUCED_ROW = an output row was produced + * IP_INPUT_ERROR = syntax error in input data + * IP_FATAL_ERROR = misc error (internal error or bad param) + * IP_NEW_INPUT_PAGE = just finished parsing the input page + * IP_NEW_OUTPUT_PAGE = just finished outputting a page + * IP_WRITE_INSERT_OK = okay to insert data in output file + * IP_DONE = conversion is completed. + */ + WORD (*convert) ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos); /* out: file-pos to write the data */ + + + /* newPage - Tells xform to flush rest of this page, and start a new page + * + * Uncompressed input-data formats do not have page-boundary info, so + * calling this function between rows fed into 'convert' is how you tell + * xform to output a page-boundary. After this is called, 'convert' will + * first flush any buffered rows, then return the IP_NEW_OUTPUT_PAGE bit, + * and finally start a new page. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*newPage) (IP_XFORM_HANDLE hXform); + + + /* insertedData - Tells us that bytes were inserted into output stream + * + * See IP_WRITE_INSERT_OK discussion above. You call this routine to tell + * the xform code how many bytes were secretly added to the output stream. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*insertedData) ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes); + + + /* closeXform - destroys instance of xform + * + * This may be called at any time to remove an instance of the xform. + * It deallocates all dynamic memory associated with the xform. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + */ + WORD (*closeXform)(IP_XFORM_HANDLE hXform); + +} IP_XFORM_TBL, *PIP_XFORM_TBL, FAR*LPIP_XFORM_TBL; + +#endif diff --git a/ip/xgamma.c b/ip/xgamma.c new file mode 100644 index 0000000..4058b91 --- /dev/null +++ b/ip/xgamma.c @@ -0,0 +1,760 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xgamma.c - Applies Gamma transform + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * gammaTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[0] = gamma value, in 16.16 fixed point + * + * Capabilities and Limitations: + * + * Gamma values must range between 0.0 and 3.5. + * Only operates on 8-bit grayscale data. + * Uses cubic splines for speed. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 8 8 + * iComponentsPerPixel * must be 1 1 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Feb 1998 Mark Overton -- ported code to software + * early 1997 Mark Overton -- wrote original code for Kodiak firmware + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #define PRINT(msg,arg1,arg2) \ + fprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input image */ + BYTE gammaTable[256]; /* the gamma table */ + WORD wRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} GAM_INST, *PGAM_INST; + + + + +/*____________________________________________________________________________ + | | | + | fast_sin fast_cos | fast sine and cosine functions | + |___________________|________________________________________________________| + | | + | Input: Units of angle is fraction of circle (e.g., 1.0 is 360 deg). | + | Format is 0.32 fixed-point (i.e., 32 bits of fraction). | + | | + | Returns: Sine or cosine expressed as 16.16 signed fixed-point. | + | | + | Accuracy: Max error is 0.000054 (i.e., 14 good bits of fraction). | + |____________________________________________________________________________| +*/ +static long fast_sin (ULONG ang) +{ + ULONG ang31, ang30, delta_ang, result; + UINT index, base, next; + + /* 90 deg is divided into this many intervals */ + #define INDEX_BITS 6 + #define N_INTERVALS (1<<INDEX_BITS) + #define HALF_WORST_ERR 3 + + static const USHORT sin_table[N_INTERVALS+2] = { + 0, 1608, 3216, 4821, 6424, 8022, 9616, 11204, + 12785, 14359, 15924, 17479, 19024, 20557, 22078, 23586, + 25080, 26558, 28020, 29466, 30893, 32303, 33692, 35062, + 36410, 37736, 39040, 40320, 41576, 42806, 44011, 45190, + 46341, 47464, 48559, 49624, 50660, 51665, 52639, 53581, + 54491, 55368, 56212, 57022, 57798, 58538, 59244, 59914, + 60547, 61145, 61705, 62228, 62714, 63162, 63572, 63944, + 64277, 64571, 64827, 65043, 65220, 65358, 65457, 65516, + 65535, 65535 /* <-- these should be 65536 (won't fit in 16 bits) */ + }; + + /* zero the msb (representing 180 deg), leaving 31 bits */ + ang31 = (ang << 1) >> 1; + + /* fold angle to first quadrant, leaving 30 bits */ + ang30 = ang31; + if (ang30 >= 0x40000000u) + ang30 = 0x80000000u - ang30; /* sin(180-a) = sin(a) */ + + index = ang30 >> (30-INDEX_BITS); + base = sin_table [index]; + next = sin_table [index+1]; + delta_ang = ang30>>(14-INDEX_BITS) & 0x0000ffffu; + result = ((next-base)*delta_ang >> 16) + base + HALF_WORST_ERR; + + /* negate result if 180-bit was set in original angle */ + if ((long)ang < 0) + result = (ULONG)(-(long)result); + + return (long)result; + + #undef INDEX_BITS + #undef N_INTERVALS + #undef HALF_WORST_ERR +} + + +static /* inline */ int fast_cos (UINT ang) +{ + return fast_sin(0x40000000u-ang); +} + + +#if 0 + +#include <stdio.h> +#include <math.h> + +void main (void) +{ + int i; + float err, max_err; + + max_err = 0.0; + + for (i=0; i<=16*65536; i++) { + err = sin ((float)i * (2.0*3.1415926535897932384626/(16*65536.0))) + - fast_sin(i<<12)/65536.0; + if (err < 0) err = -err; + if (err > max_err) max_err = err; + } + + printf ("max err = %f\n", max_err); +} + +#endif + + +#if 0 + +#include <stdio.h> +#include <math.h> + +void main (void) +{ + int i; + float s; + + for (i=0; i<=64; i++) { + s = sin ((float)i * (3.1415926535897932384626/(2.0*64.0))); + printf ("%5d, ", (int)(s*(1<<16) + 0.5)); + if (i%8 == 7) puts(""); + } +} + +#endif + + + +typedef struct { + long a, b, c; /* x = at + bt^2 + ct^3 */ + long d, e, f; /* y = dt + et^2 + ft^3 */ +} cubic_t; + + + +/*____________________________________________________________________________ + | | | + | calc_cubic_coeffs | calcs coefficients given start/end angles/velocities | + |___________________|________________________________________________________| + | | + | Units of angle is fraction of circle (e.g., 1.0 is 360 deg). | + | Format is 0.32 fixed-point (i.e., 32 bits of fraction). | + |____________________________________________________________________________| +*/ +static void calc_cubic_coeffs ( + ULONG start_ang, + ULONG final_ang, + long start_vel, /* velocities are in 16.16 fixed point */ + long final_vel, + cubic_t *p) +{ + long start_x, start_y, final_x, final_y; + + /* For both start and final below: vel = vel * 2.0 / (1.0 + cos(ang)), + * where vel is 16.16 before calc, and is 20.12 after the calc below. + */ + start_vel = (start_vel<<11) / ((1lu<<14) + (fast_cos(start_ang)>>2)); + final_vel = (final_vel<<11) / ((1lu<<14) + (fast_cos(final_ang)>>2)); + + /* Below: (vel is 20.12) * (cos>>2 is 18.14) yields a 6.26; + * the >>10 changes 6.26 into a 16.16. + */ + start_x = start_vel*(fast_cos(start_ang)>>2) >> 10; + start_y = start_vel*(fast_sin(start_ang)>>2) >> 10; + final_x = final_vel*(fast_cos(final_ang)>>2) >> 10; + final_y = final_vel*(fast_sin(final_ang)>>2) >> 10; + + p->a = start_x; + p->d = start_y; + p->b = (3<<16) - 2*start_x - final_x; + p->e = -2*start_y - final_y; + p->c = start_x + final_x - (2<<16); + p->f = start_y + final_y; +} + + + +/*____________________________________________________________________________ + | | | + | transform_cubic | makes cubic end at (x_end,y_end) instead of (1,0) | + |_________________|__________________________________________________________| +*/ +static void transform_cubic ( + int x_end, + int y_end, + cubic_t *p) +{ + cubic_t q; + + q.a = p->a*x_end - p->d*y_end; + q.b = p->b*x_end - p->e*y_end; + q.c = p->c*x_end - p->f*y_end; + + q.d = p->a*y_end + p->d*x_end; + q.e = p->b*y_end + p->e*x_end; + q.f = p->c*y_end + p->f*x_end; + + *p = q; +} + + + +/*____________________________________________________________________________ + | | | + | calc_gamma_from_coeffs | calcs gamma table, given the coefficients | + |________________________|___________________________________________________| +*/ +static void calc_gamma_from_coeffs ( + cubic_t *p, /* in: coefficients of cubic curve */ + BYTE tbl[]) /* out: array to receive the gamma table */ +{ + #define LG_N_INTERVALS 7 /* 7 is the max; 8 causes overflow */ + #define N_INTERVALS (1u<<LG_N_INTERVALS) + #define RND_PROD(p) (((p) + ((1<<LG_N_INTERVALS)-1)) >> LG_N_INTERVALS) + + int t, x, y, xprev=0, yprev=0, xtmp, ytmp; + + for (t=0; t<=N_INTERVALS; t+=1) { + x = (((RND_PROD((RND_PROD(p->c*t) + p->b)*t) + p->a)*t + >> (LG_N_INTERVALS+15)) + 1) >> 1; + y = (((RND_PROD((RND_PROD(p->f*t) + p->e)*t) + p->d)*t + >> (LG_N_INTERVALS+15)) + 1) >> 1; + + if (t==0 || x!=xprev) tbl[x] = y; + + if (t > 0) { + xtmp = xprev + 1; + ytmp = (yprev+y) / 2; + while (xtmp < x) + tbl[xtmp++] = ytmp; + } + + xprev = x; yprev = y; + } +} + + + +/*____________________________________________________________________________ + | | | + | calc_gamma_table | calculates gamma table using a cubic curve | + |__________________|_________________________________________________________| +*/ +static void calc_gamma_table ( + long gamma, /* in: gamma value as 24.8 (0.7 .. 3.5 is useful range) */ + BYTE tbl[]) /* out: array to receive the gamma table */ +{ + #define FLOAT_TO_FIX(f) ((short)((f)*0x1000 + 0.5)) /* output is 8.8 */ + #define FLOAT_TO_ANG(f) ((USHORT)(long)((f)*0x10000/360.0 + 0.5)) + + #define MIN_INDEX 0 /* corresponds to gamma=0.0 */ + #define MAX_INDEX 6 /* corresponds to gamma=3.0 */ + + typedef struct { /* ang_base=0.16; all others are 4.12 */ + short start_vel_base; short start_vel_slope; + USHORT start_ang_base; short start_ang_slope; + short final_vel_base; short final_vel_slope; + USHORT final_ang_base; short final_ang_slope; + } spec_t; + + static const spec_t specs[MAX_INDEX-MIN_INDEX+1] = { + { /* 0.0 .. 0.5 (just does a straight line) */ + FLOAT_TO_FIX(1.0), FLOAT_TO_FIX(0.0), /* start vel */ + FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(0.0), /* start ang */ + FLOAT_TO_FIX(1.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(0.0), /* final ang */ + }, + { /* 0.5 .. 1.0 (bogus below about 0.7) */ + FLOAT_TO_FIX(0.05), FLOAT_TO_FIX(0.5), /* start vel */ + FLOAT_TO_ANG(-75.0), FLOAT_TO_FIX(150.0/360.0), /* start ang */ + FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(15.0), FLOAT_TO_FIX(-30.0/360.0), /* final ang */ + }, + { /* 1.0 .. 1.5 */ + FLOAT_TO_FIX(0.3), FLOAT_TO_FIX(0.2), /* start vel */ + FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(60.0/360.0), /* start ang */ + FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(-20.0/360.0), /* final ang */ + }, + { /* 1.5 .. 2.0 */ + FLOAT_TO_FIX(0.4), FLOAT_TO_FIX(0.2), /* start vel */ + FLOAT_TO_ANG(30.0), FLOAT_TO_FIX(20.0/360.0), /* start ang */ + FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(-10.0), FLOAT_TO_FIX(-16.0/360.0), /* final ang */ + }, + { /* 2.0 .. 2.5 */ + FLOAT_TO_FIX(0.5), FLOAT_TO_FIX(0.3), /* start vel */ + FLOAT_TO_ANG(40.0), FLOAT_TO_FIX(4.0/360.0), /* start ang */ + FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(-18.0), FLOAT_TO_FIX(-8.0/360.0), /* final ang */ + }, + { /* 2.5 .. 3.0 */ + FLOAT_TO_FIX(0.65), FLOAT_TO_FIX(0.4), /* start vel */ + FLOAT_TO_ANG(42.0), FLOAT_TO_FIX(4.0/360.0), /* start ang */ + FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(-22.0), FLOAT_TO_FIX(-6.0/360.0), /* final ang */ + }, + { /* 3.0 .. 3.5 */ + FLOAT_TO_FIX(0.85), FLOAT_TO_FIX(0.5), /* start vel */ + FLOAT_TO_ANG(44.0), FLOAT_TO_FIX(1.0/360.0), /* start ang */ + FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */ + FLOAT_TO_ANG(-25.0), FLOAT_TO_FIX(-4.0/360.0), /* final ang */ + }, + }; + + #define BEGIN_BLACKS 0 /* # of 0's at beginning of gamma table */ + #define END_WHITES 4 /* # of 255's at final of gamma table */ + + const spec_t *p; + long start_vel, final_vel; + ULONG start_ang, final_ang; + cubic_t cubic; + int i; + + i = gamma >> 7; + if (i > MAX_INDEX) i = MAX_INDEX; + p = &specs[i-MIN_INDEX]; + gamma -= i << 7; + + start_vel = ((long)p->start_vel_base << 4) + + (p->start_vel_slope*gamma >> 4); + final_vel = ((long)p->final_vel_base << 4) + + (p->final_vel_slope*gamma >> 4); + start_ang = ((long)p->start_ang_base << 16) + + (p->start_ang_slope*gamma << 12); + final_ang = ((long)p->final_ang_base << 16) + + (p->final_ang_slope*gamma << 12); + + calc_cubic_coeffs (start_ang, final_ang, start_vel, final_vel, &cubic); + transform_cubic (255-BEGIN_BLACKS-END_WHITES, 254, &cubic); + calc_gamma_from_coeffs (&cubic, tbl); + + memmove (&tbl[BEGIN_BLACKS], &tbl[0], (256-BEGIN_BLACKS)*sizeof(BYTE)); + tbl[0] = 0; + for (i=0; i<BEGIN_BLACKS; i++) + tbl[i] = 0; + + for (i=256-END_WHITES; i<=255; i++) + tbl[i] = 255; +} + + + + +/*****************************************************************************\ + * + * gamma_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD gamma_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PGAM_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(GAM_INST), g); + *pXform = g; + memset (g, 0, sizeof(GAM_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gamma_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD gamma_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PGAM_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + INSURE (pTraits->iBitsPerPixel == 8); + INSURE (pTraits->iComponentsPerPixel == 1); + INSURE (pTraits->iPixelsPerRow > 0); + + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gamma_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD gamma_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PGAM_INST g; + DWORD gamma; + + HANDLE_TO_PTR (hXform, g); + gamma = aXformInfo[0].dword; + INSURE (gamma <= 0x38000u); /* 3.5 is our limit */ + + calc_gamma_table ((gamma+0x0080u)>>8, g->gammaTable); + /* todo: make calc_gamma_table accept a 16.16 gamma value */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gamma_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD gamma_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * gamma_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD gamma_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD wInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PGAM_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * gamma_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD gamma_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pwMinInBufLen, /* out: min input buf size */ + PDWORD pwMinOutBufLen) /* out: min output buf size */ +{ + PGAM_INST g; + + HANDLE_TO_PTR (hXform, g); + *pwMinInBufLen = *pwMinOutBufLen = g->traits.iPixelsPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gamma_convert - Converts one row + * +\*****************************************************************************/ + +static WORD gamma_convert ( + IP_XFORM_HANDLE hXform, + DWORD wInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD wOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PGAM_INST g; + int nBytes; + PBYTE pIn, pOut, pOutAfter; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT ("gamma_convert: Told to flush.\n", 0, 0); + *pwInputUsed = *pwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->traits.iPixelsPerRow; + INSURE (wInputAvail >= nBytes ); + INSURE (wOutputAvail >= nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pOut + nBytes; + + while (pOut < pOutAfter) { + pOut[0] = g->gammaTable[pIn[0]]; + pOut[1] = g->gammaTable[pIn[1]]; + pOut[2] = g->gammaTable[pIn[2]]; + pOut[3] = g->gammaTable[pIn[3]]; + pOut[4] = g->gammaTable[pIn[4]]; + pOut[5] = g->gammaTable[pIn[5]]; + pOut[6] = g->gammaTable[pIn[6]]; + pOut[7] = g->gammaTable[pIn[7]]; + + pIn += 8; + pOut += 8; + } + + *pwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->wRowsDone += 1; + + PRINT ("gamma_convert: Returning, out used = %d\n", out_used, 0); + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gamma_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD gamma_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD wNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * gamma_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD gamma_newPage ( + IP_XFORM_HANDLE hXform) +{ + PGAM_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * gamma_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD gamma_closeXform (IP_XFORM_HANDLE hXform) +{ + PGAM_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gammaTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL gammaTbl = { + gamma_openXform, + gamma_setDefaultInputTraits, + gamma_setXformSpec, + gamma_getHeaderBufSize, + gamma_getActualTraits, + gamma_getActualBufSizes, + gamma_convert, + gamma_newPage, + gamma_insertedData, + gamma_closeXform +}; + +/* End of File */ diff --git a/ip/xgray2bi.c b/ip/xgray2bi.c new file mode 100644 index 0000000..1946e1b --- /dev/null +++ b/ip/xgray2bi.c @@ -0,0 +1,614 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xgray2bi.c - Error-diffuser and thresholder that's designed to be fast + * + ****************************************************************************** + * + * Jan 1998 Mark Overton -- ported code into an xform driver + * June 1995 Mark Overton -- developed and benchmarked algorithm + * + * Name of Global Jump-Table: + * + * gray2biTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_GRAY_2_BI_THRESHOLD] = Threshold. + * If threshold is zero, error-diffusion is done. + * If non-zero, it is the black/white threshold: + * A gray pixel >= to threshold becomes white, else black. + * + * Capabilities and Limitations: + * + * Inputs rows of 8-bit gray pixels, and outputs rows of bi-level pixels. + * The formats are the standard raw formats described in hpojip.h. + * The error-diffusion weights are perturbed by small internally-generated + * amounts to break up any pixel patterns that would otherwise occur. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 8 1 + * iComponentsPerPixel * must be 1 1 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Error-diffusion timings (seconds for one page): + * + * 386-33 486-33 486-66 P-90 735 image dim task + * ------ ------ ------ ---- ----- --------- --------------------------------- + * 21.6 9.0 4.4 2.3 1.4 1728x2200 200x200 fax in photo mode + * 43. 17.9 8.9 4.6 2.9 2400x3150 300x300 local copy, 1/4" margins + * 86. 36. 17.9 9.3 5.7 4800x3150 600x300 local copy, 1/4" margins + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + + +typedef struct { + IP_IMAGE_TRAITS inTraits; /* traits of the input image */ + DWORD dwRowsDone; /* number of rows converted so far */ + BYTE bThreshold; /* white/black threshold; 0 -> diffuse */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ + short *pErrBuf; /* error-term buffer */ +} G2B_INST, *PG2B_INST; + + + +/****************************************************************************\ + * + * thresholdRow - work-horse thresholding routine + * +\****************************************************************************/ + +static void thresholdRow ( + int iPixelsPerRow, /* in: # of pixels in the row */ + BYTE bThreshold, /* in: white/black threshold value */ + BYTE baInBuf [], /* in: input pixels (0=black, 255=white) */ + BYTE baOutBuf []) /* out: output pixels (0=white, 1=black, 8 per byte) */ +{ + int iMore; + PBYTE pbIn, pbOut; + BYTE bOut, bMask; + + pbIn = baInBuf; + pbOut = baOutBuf; + + for (iMore=iPixelsPerRow; iMore>0; iMore-=8) + { + bOut = 0; + + for (bMask=0x80u; bMask!=0; bMask>>=1) + { + if (*pbIn++ < bThreshold) + bOut |= bMask; + } + + *pbOut++ = bOut; + } +} + + + +/****************************************************************************\ + * + * diffuseRow - work-horse error-diffusion routine + * +\****************************************************************************/ + +static void diffuseRow ( + int iPixelsPerRow, /* in: # of pixels in the row */ + short iaErrBuf [], /* in/out: error-term buffer from prior call */ + BYTE baInBuf [], /* in: input pixels (0=black, 255=white) */ + BYTE baOutBuf []) /* out: output pixels (0=white, 1=black, 8 per byte) */ +{ + int rr, r, cur; + int br, b, bl, bll; + int err; + int weight4, weight2, weight1; + short *eptr; + int noise; + BYTE *inptr, *inafter; + BYTE *outptr; + BYTE outvalue; + int mask; + BOOL second; /* computing 2nd nibble in byte? */ + + /* The diffusion algorithm below uses the following 6 weights: + * + * X 4 2 + * 1 3 4 2 + * + * These weights add to 16, so there are shifts by 4 in the code. + * weight4 is the 4 above. Likewise with weight2 and weight1. + * eptr is positioned at the 1 above. + * The macro reads the error at the top 4 above, and writes it at the 1. + * bll is positioned at the 1 above. bleft at the 3. below at the low 4. + * The noise perturbations add to zero, so no net noise is injected. + */ +#if 0 + #define DIFFUSE(par_outmask) { \ + mask = cur >> (8*sizeof(cur)-1); /* signed shift */ \ + outvalue |= par_outmask & mask; \ + weight4 = (cur - (~mask&0x0ff0)) >> 2; \ + \ + noise = (cur & (0x7f<<1)) - 0x7f; \ + cur = ((unsigned)(*inptr++) << 4) \ + + eptr[3] + weight2 + weight4 + noise ; \ + weight2 = (weight4>>1); \ + weight1 = (weight2>>1); \ + *eptr++ = bll + weight1 - noise ; \ + bll = bleft + weight2 + weight1 /* + noise */ ; \ + bleft = below + weight4 - noise ; \ + below = weight2 + noise ; \ + } + + cur = iaErrBuf[2] + ((unsigned)(*inptr++) << 4); + bll = 0; + bleft = 0; + below = 0; + weight2 = 0; + +#endif + + #define DIFFUSE(par_outmask) { \ + /* decide if output pixel is black or white */ \ + mask = (cur-0x800) >> (8*sizeof(cur)-1); /* all 0's or all 1's */ \ + outvalue |= par_outmask & mask; \ + \ + /* compute error, and weights of 4/16, 2/16 and 1/16 */ \ + err = cur - (~mask&0x0ff0); \ + /* multiply error by 15/16 so it won't propagate a long distance */ \ + err = err - (err>>4); \ + weight4 = err >> 2; \ + weight2 = weight4 >> 1; \ + weight1 = weight2 >> 1; \ + \ + /* distribute error to neighboring pixels */ \ + noise = err & 0x00ff; \ + rr += weight2; \ + r += weight4 + noise; \ + br = weight2 + noise; \ + b += weight4 - noise; \ + bl += weight2 + weight1; /* the 3 weight */ \ + bll += weight1 - noise; \ + \ + /* advance right one pixel, so move values left one pixel */ \ + cur = r; \ + r = rr; \ + rr = ((unsigned)(*inptr++) << 4) + eptr[2]; \ + eptr[-2] = bll; \ + bll = bl; \ + bl = b; \ + b = br; \ + eptr += 1; \ + } + + inptr = baInBuf; + inafter = baInBuf + iPixelsPerRow; + outptr = baOutBuf; + eptr = iaErrBuf + 2; + outvalue = 0; + second = FALSE; + + cur = ((unsigned)inptr[0] << 4) + eptr[0]; + r = ((unsigned)inptr[1] << 4) + eptr[1]; + rr = ((unsigned)inptr[2] << 4) + eptr[2]; + inptr += 3; + bll = bl = b = br = 0; + + while (inptr < inafter) { + DIFFUSE (0x08); + DIFFUSE (0x04); + DIFFUSE (0x02); + DIFFUSE (0x01); + + if (! second) { + /* we just computed the left half of the byte */ + outvalue <<= 4; + second = TRUE; + } else { + /* we just computed the right half of the byte, so store it */ + *outptr++ = outvalue; + outvalue = 0; + second = FALSE; + } + } /* end of for */ + + if (second) + *outptr = outvalue; /* store final nibble */ + + eptr[-2] = bll; + eptr[-1] = bl; + eptr[ 0] = b; + + #undef DIFFUSE +} + + + +/*****************************************************************************\ + * + * gray2bi_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD gray2bi_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PG2B_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(G2B_INST), g); + *pXform = g; + memset (g, 0, sizeof(G2B_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gray2bi_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD gray2bi_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PG2B_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + INSURE (pTraits->iBitsPerPixel == 8); + INSURE (pTraits->iComponentsPerPixel == 1); + INSURE (pTraits->iPixelsPerRow > 0); + + g->inTraits = *pTraits; /* a structure copy */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gray2bi_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD gray2bi_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PG2B_INST g; + + HANDLE_TO_PTR (hXform, g); + INSURE ((DWORD)aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword <= 255); + g->bThreshold = (BYTE)aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gray2bi_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD gray2bi_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * gray2bi_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD gray2bi_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PG2B_INST g; + int nBytes; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pInTraits = g->inTraits; + *pOutTraits = g->inTraits; + pOutTraits->iBitsPerPixel = 1; /* this xform only changes bits/pixel */ + + if (g->bThreshold == 0) { + nBytes = sizeof(short) * g->inTraits.iPixelsPerRow; + IP_MEM_ALLOC (nBytes, g->pErrBuf); + memset (g->pErrBuf, 0, nBytes); + } + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * gray2bi_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD gray2bi_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PG2B_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->inTraits.iPixelsPerRow; + *pdwMinOutBufLen = (g->inTraits.iPixelsPerRow + 7) / 8; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gray2bi_convert - error-diffuses one row + * +\*****************************************************************************/ + +static WORD gray2bi_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PG2B_INST g; + int inBytes, outBytes; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("gray2bi_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Convert and Output a Row ****/ + + inBytes = g->inTraits.iPixelsPerRow; + outBytes = (inBytes + 7) / 8; + INSURE (dwInputAvail >= (DWORD)inBytes ); + INSURE (dwOutputAvail >= (DWORD)outBytes); + + if (g->bThreshold == 0) { + INSURE (g->pErrBuf != NULL); + diffuseRow (inBytes, g->pErrBuf, pbInputBuf, pbOutputBuf); + } else + thresholdRow (inBytes, g->bThreshold, pbInputBuf, pbOutputBuf); + + *pdwInputUsed = inBytes; + g->dwInNextPos += inBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = outBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += outBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gray2bi_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD gray2bi_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * gray2bi_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD gray2bi_newPage ( + IP_XFORM_HANDLE hXform) +{ + PG2B_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * gray2bi_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD gray2bi_closeXform (IP_XFORM_HANDLE hXform) +{ + PG2B_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (g->pErrBuf != NULL) + IP_MEM_FREE (g->pErrBuf); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * gray2biTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL gray2biTbl = { + gray2bi_openXform, + gray2bi_setDefaultInputTraits, + gray2bi_setXformSpec, + gray2bi_getHeaderBufSize, + gray2bi_getActualTraits, + gray2bi_getActualBufSizes, + gray2bi_convert, + gray2bi_newPage, + gray2bi_insertedData, + gray2bi_closeXform +}; + +/* End of File */ diff --git a/ip/xgrayout.c b/ip/xgrayout.c new file mode 100644 index 0000000..aa379a6 --- /dev/null +++ b/ip/xgrayout.c @@ -0,0 +1,495 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xgrayOut.c - Grays out everything outside the given rectangle. + * + * "Graying out" means changing pixels toward a middle gray value. Everything + * outside the given rectangle is grayed-out by this xform. This is useful for + * displaying a selected-area in an image (the rectangle). + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * grayOutTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_GRAYOUT_LEFT ] = left + * aXformInfo[IP_GRAYOUT_RIGHT ] = right + * aXformInfo[IP_GRAYOUT_TOP ] = top + * aXformInfo[IP_GRAYOUT_BOTTOM] = bottom + * + * The four numbers above give the locations of the sides of the rectangle + * which is *not* to be grayed-out. Everything outside these boundaries + * will be grayed-out. If these coordinates are outside the image, this + * xform will behave sensibly. + * + * Capabilities and Limitations: + * + * 24-bit, 3-component data only. + * + * Default Input Traits, and Output Traits: + * + * Describe what you do with the default input traits, and how the + * output traits are determined. + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 24 24 + * iComponentsPerPixel * must be 3 3 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + + +#define TARGET_GRAY 0xB0 /* grayed pixels are pushed toward this gray-level */ + +// Use the #define below if this transform will exist in a dll outside of the +// image pipeline. This will allow the functions to be exported. +// #define EXPORT_TRANFORM 1 + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + int iLeft, iRight; /* the rectangle to not gray-out */ + int iTop, iBottom; + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} GRAYOUT_INST, *PGRAYOUT_INST; + + + +/*****************************************************************************\ + * + * grayOut_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PGRAYOUT_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(GRAYOUT_INST), g); + *pXform = g; + memset (g, 0, sizeof(GRAYOUT_INST)); + g->dwValidChk = CHECK_VALUE; + g->iRight = 1000000; + g->iBottom = 1000000; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * grayOut_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PGRAYOUT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow > 0 && + pTraits->iBitsPerPixel == 24 && + pTraits->iComponentsPerPixel == 3); + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * grayOut_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PGRAYOUT_INST g; + + HANDLE_TO_PTR (hXform, g); + g->iLeft = aXformInfo[IP_GRAYOUT_LEFT ].dword; + g->iRight = aXformInfo[IP_GRAYOUT_RIGHT ].dword; + g->iTop = aXformInfo[IP_GRAYOUT_TOP ].dword; + g->iBottom = aXformInfo[IP_GRAYOUT_BOTTOM].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * grayOut_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * grayOut_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PGRAYOUT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * grayOut_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD grayOut_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PGRAYOUT_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * grayOut_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PGRAYOUT_INST g; + int nBytes; + PBYTE pIn, pInAfter, pOut, pLeft, pRight; + + #define GRAYOUT_PIXEL \ + *pOut++ = (BYTE)(((int)(*pIn++)+TARGET_GRAY) >> 1); /* R */ \ + *pOut++ = (BYTE)(((int)(*pIn++)+TARGET_GRAY) >> 1); /* G */ \ + *pOut++ = (BYTE)(((int)(*pIn++)+TARGET_GRAY) >> 1); /* B */ + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("grayOut_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pInAfter = pIn + nBytes; + + if ((int)g->dwRowsDone<g->iTop || (int)g->dwRowsDone>g->iBottom) { + /* Gray-out the entire row */ + while (pIn < pInAfter) { + GRAYOUT_PIXEL + } + } else { + /* Gray-out portion of row between iLeft and iRight */ + pLeft = pIn + 3*g->iLeft; + pRight = pIn + 3*g->iRight; + + if (pLeft >= pInAfter) + pLeft = pInAfter - 3; /* clamp to rightmost pixel */ + if (pRight >= pInAfter) + pRight = pInAfter - 3; /* clamp to rightmost pixel */ + + while (pIn < pLeft) { /* gray-out left portion */ + GRAYOUT_PIXEL + } + while (pIn <= pRight) { /* copy portion within rect */ + *pOut++ = *pIn++; + *pOut++ = *pIn++; + *pOut++ = *pIn++; + } + while (pIn < pInAfter) { /* gray-out right portion */ + GRAYOUT_PIXEL + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * grayOut_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * grayOut_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_newPage ( + IP_XFORM_HANDLE hXform) +{ + PGRAYOUT_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * grayOut_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD grayOut_closeXform (IP_XFORM_HANDLE hXform) +{ + PGRAYOUT_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * grayOutTbl - Jump-table for transform driver + * +\*****************************************************************************/ +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL grayOutTbl = { + grayOut_openXform, + grayOut_setDefaultInputTraits, + grayOut_setXformSpec, + grayOut_getHeaderBufSize, + grayOut_getActualTraits, + grayOut_getActualBufSizes, + grayOut_convert, + grayOut_newPage, + grayOut_insertedData, + grayOut_closeXform +}; + +/* End of File */ + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL pXform) +{ + WORD wRet = IP_DONE; + + if (pXform) + *pXform = clrmapTbl; + else + wRet = IP_FATAL_ERROR; + + return wRet; +} +#endif diff --git a/ip/xinvert.c b/ip/xinvert.c new file mode 100644 index 0000000..a187fa9 --- /dev/null +++ b/ip/xinvert.c @@ -0,0 +1,459 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: David Paschal (based on Mark Overton's "xskel" template). */ + +/******************************************************************************\ + * + * xinvert.c - Inverts bilevel, grayscale, or color data. + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * invertTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * None. + * + * Capabilities and Limitations: + * + * For 1-3 bpp, does a NOT operation (complements the bits). + * For >3 bpp, does a NEG operation (NEG plus 1). + * + * Default Input Traits, and Output Traits: + * + * Describe what you do with the default input traits, and how the + * output traits are determined. + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel * passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +// Use the #define below if this transform will exist in a dll outside of the +// image pipeline. This will allow the functions to be exported. +// #define EXPORT_TRANFORM 1 + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwAddToNot; /* 0=NOT, 1=NEG operation. */ + DWORD dwValidChk; /* struct validity check value */ +} INVERT_INST, *PINVERT_INST; + + + +/*****************************************************************************\ + * + * invert_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PINVERT_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(INVERT_INST), g); + *pXform = g; + memset (g, 0, sizeof(INVERT_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * invert_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PINVERT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0); + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + if (pTraits->iBitsPerPixel>3) { + g->dwAddToNot=1; + } else { + g->dwAddToNot=0; + } + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * invert_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PINVERT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Check your options in aXformInfo here, and save them. + * Use the INSURE macro like you'd use assert. INSURE jumps to + * fatal_error below if it fails. + */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * invert_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * invert_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PINVERT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + g->dwInNextPos = 0; + + *pInTraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * invert_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD invert_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PINVERT_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * invert_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PINVERT_INST g; + int nBytes,i; + PBYTE pIn, pOut; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("invert_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + + /* At this point, pIn is your input buffer, and pOut is your output buffer. + * Do whatever you are going to do here. + */ + for (i=0;i<nBytes;i++) { + pOut[i]=~pIn[i]+g->dwAddToNot; + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * invert_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * invert_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_newPage ( + IP_XFORM_HANDLE hXform) +{ + PINVERT_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * invert_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD invert_closeXform (IP_XFORM_HANDLE hXform) +{ + PINVERT_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * invertTbl - Jump-table for transform driver + * +\*****************************************************************************/ +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL invertTbl = { + invert_openXform, + invert_setDefaultInputTraits, + invert_setXformSpec, + invert_getHeaderBufSize, + invert_getActualTraits, + invert_getActualBufSizes, + invert_convert, + invert_newPage, + invert_insertedData, + invert_closeXform +}; + +/* End of File */ + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL pXform) +{ + WORD wRet = IP_DONE; + + if (pXform) + { + *pXform = clrmapTbl; + } + else + { + wRet = IP_FATAL_ERROR; + } + + return wRet; +} +#endif diff --git a/ip/xjpg_dct.c b/ip/xjpg_dct.c new file mode 100644 index 0000000..b20e17e --- /dev/null +++ b/ip/xjpg_dct.c @@ -0,0 +1,393 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*____________________________________________________________________________ + | | | + | xjpg_dct.c | Computes forward and inverse DCT for JPEG | + |____________|_______________________________________________________________| + | | + | Mark Overton, May 1997 | + |____________________________________________________________________________| +*/ + +#include "xjpg_dct.h" + + +/*____________________________________________________________________________ + | | | + | SUB_AND_ADD | replaces a with a-b, and b with a+b using no temp registers | + |_____________|______________________________________________________________| +*/ +#define SUB_AND_ADD(a,b) { \ + b += a; \ + a += a; \ + a -= b; \ +} + + + +/*____________________________________________________________________________ + | | | + | MUL_ROUND | computes x = round(x*c), using no temp registers | + |___________|________________________________________________________________| + | | + | c is assumed to have CONST_FRAC_BITS bits of fraction. | + |____________________________________________________________________________| +*/ +#if (defined _WINDOWS) && !(defined _WIN32) + + /* We are compiling for 16-bit Windows 3.1 */ + + #define MUL_ROUND(c,x) { \ + long product; \ + product = (long)(x) * ((long)(c) << (16-CONST_FRAC_BITS)); \ + x = (product+0x8000L) >> 16; \ + } + +#else + + #define MUL_ROUND(c,x) { \ + x = (short)(x) * (short)(c); \ + x = ((x)+(1l<<(CONST_FRAC_BITS-1))) >> CONST_FRAC_BITS; \ + } + +#endif + + + +/*____________________________________________________________________________ + | | | + | dct_forward | computes DCT for JPEG | + |_____________|______________________________________________________________| + | | + | This is the DCT algorithm based on the small FFT Winograd transform | + | from Trans. IEICE, vol. E 71(11), 1095-1097, Nov. 1988 | + | | + | Input: 'block' is 64 level-shifted pixels (-128..127 each). | + | | + | Output: 'block' is the DCT (64 words). | + | These values need to be scaled by the forward correction matrix | + | for the Winograd DCT. | + |____________________________________________________________________________| +*/ +void dct_forward (register int *block_p) +{ + #define CONST_FRAC_BITS 14 /* bits of frac in CONST_1-CONST_5 below */ + + #define CONST_1 (23170/2) /* 15 bits of frac shifted down to 14 */ + #define CONST_2 (17734/2) + #define CONST_3 (23170/2) + #define CONST_4 (42813/2) /* this one wouldn't fit with 15 bits of frac */ + #define CONST_5 (12540/2) + + int *data_p; + int d0, d1, d2, d3, d4, d5, d6, d7; + + /******************/ + /* Transform Rows */ + /******************/ + + for (data_p=block_p; data_p<block_p+64; data_p+=8) + { + d0 = data_p[0]; + d1 = data_p[1]; + d2 = data_p[2]; + d3 = data_p[3]; + d4 = data_p[4]; + d5 = data_p[5]; + d6 = data_p[6]; + d7 = data_p[7]; + + SUB_AND_ADD (d0, d7) + SUB_AND_ADD (d1, d6) + SUB_AND_ADD (d2, d5) + SUB_AND_ADD (d4, d3) + + SUB_AND_ADD (d7, d3) + SUB_AND_ADD (d6, d5) + + SUB_AND_ADD (d3, d5) + data_p[4] = d3; + data_p[0] = d5; + + d6 += d7; + MUL_ROUND (CONST_1, d6) + SUB_AND_ADD (d7, d6) + data_p[6] = d7; + data_p[2] = d6; + + /* At this point, the only live math vars are in: d0, d1, d2, d4 */ + + d7 = d1 + d2; + MUL_ROUND (CONST_3, d7) + d1 += d0; + SUB_AND_ADD (d0, d7) + d4 -= d2; + d6 = d1 + d4; + MUL_ROUND (CONST_5, d6) + MUL_ROUND (CONST_4, d1) + d1 -= d6; + + SUB_AND_ADD (d7, d1) + data_p[7] = d7; + data_p[1] = d1; + + MUL_ROUND (CONST_2, d4) + d4 += d6; + SUB_AND_ADD (d0, d4) + data_p[5] = d0; + data_p[3] = d4; + } + + /*********************/ + /* Transform Columns */ + /*********************/ + + for (data_p=block_p; data_p<block_p+8; data_p++) + { + d0 = data_p[0*8]; + d7 = data_p[7*8]; + SUB_AND_ADD (d0, d7) + + d4 = data_p[4*8]; + d3 = data_p[3*8]; + SUB_AND_ADD (d4, d3) + + d1 = data_p[1*8]; + d6 = data_p[6*8]; + SUB_AND_ADD (d1, d6) + + d2 = data_p[2*8]; + d5 = data_p[5*8]; + SUB_AND_ADD (d2, d5) + + SUB_AND_ADD (d7, d3) + SUB_AND_ADD (d6, d5) + + SUB_AND_ADD (d3, d5) + data_p[4*8] = d3; + data_p[0*8] = d5; + + d6 += d7; + MUL_ROUND (CONST_1, d6) + SUB_AND_ADD (d7, d6) + data_p[6*8] = d7; + data_p[2*8] = d6; + + /* At this point, the only live math vars are in: d0, d1, d2, d4 */ + + d7 = d1 + d2; + MUL_ROUND (CONST_3, d7) + d1 += d0; + SUB_AND_ADD (d0, d7) + d4 -= d2; + d6 = d1 + d4; + MUL_ROUND (CONST_5, d6) + MUL_ROUND (CONST_4, d1) + d1 -= d6; + + SUB_AND_ADD (d7, d1) + data_p[7*8] = d7; + data_p[1*8] = d1; + + MUL_ROUND (CONST_2, d4) + d4 += d6; + SUB_AND_ADD (d0, d4) + data_p[5*8] = d0; + data_p[3*8] = d4; + } + + #undef CONST_FRAC_BITS + #undef CONST_1 + #undef CONST_2 + #undef CONST_3 + #undef CONST_4 + #undef CONST_5 +} /* end of dct_forward */ + + + +/*____________________________________________________________________________ + | | | + | dct_inverse | computes inverse DCT for JPEG | + |_____________|______________________________________________________________| + | | + | This is the DCT algorithm based on the small FFT Winograd transform | + | from Trans. IEICE, vol. E 71(11), 1095-1097, Nov. 1988 | + | | + | Input: 'block' is the DCT (64 words). | + | These values are assumed to have been scaled by the inverse | + | correction matrix for the Winograd DCT. | + | | + | Output: 'block' is 64 level-shifted pixels. These values will have | + | as many bits of fraction as the input DCT had. After rounding | + | and level-shifting, you must clamp these values to 0..255. | + |____________________________________________________________________________| +*/ +void dct_inverse (register int *block_p) +{ + #define CONST_FRAC_BITS 13 /* bits of frac in CONST_1-CONST_5 below */ + + #define CONST_1 ((46341+2)/4) /* 15 bits of frac shifted down to 13 */ + #define CONST_2 ((85627+2)/4) + #define CONST_3 ((46341+2)/4) + #define CONST_4 ((35468+2)/4) + #define CONST_5 ((25080+2)/4) + + int *data_p; + int d0, d1, d2, d3, d4, d5, d6, d7, tmp; + + /*********************/ + /* Transform Columns */ + /*********************/ + + for (data_p=block_p; data_p<block_p+8; data_p++) + { + d0 = data_p[0*8]; + d4 = data_p[4*8]; + SUB_AND_ADD (d0, d4) + + d1 = data_p[1*8]; + d7 = data_p[7*8]; + SUB_AND_ADD (d1, d7) + + d2 = data_p[2*8]; + d6 = data_p[6*8]; + SUB_AND_ADD (d2, d6) + + d5 = data_p[5*8]; + d3 = data_p[3*8]; + SUB_AND_ADD (d5, d3) + + MUL_ROUND (CONST_1, d2) + d2 -= d6; + SUB_AND_ADD (d0, d2) + SUB_AND_ADD (d4, d6) + SUB_AND_ADD (d7, d3) + + tmp = d3; + SUB_AND_ADD (d6, d3) + data_p[7*8] = d6; + data_p[0*8] = d3; + + d6 = d5 - d1; + MUL_ROUND (CONST_5, d6); + MUL_ROUND (CONST_4, d1) + d1 -= d6; + d1 -= tmp; + MUL_ROUND (CONST_3, d7) + d7 -= d1; + MUL_ROUND (CONST_2, d5) + d6 -= d5; + d6 += d7; + + SUB_AND_ADD (d2, d1) + data_p[6*8] = d2; + data_p[1*8] = d1; + + SUB_AND_ADD (d0, d7) + data_p[5*8] = d0; + data_p[2*8] = d7; + + SUB_AND_ADD (d4, d6) + data_p[3*8] = d4; + data_p[4*8] = d6; + } + + /******************/ + /* Transform Rows */ + /******************/ + + for (data_p=block_p; data_p<block_p+64; data_p+=8) + { + d0 = data_p[0]; + d1 = data_p[1]; + d2 = data_p[2]; + d3 = data_p[3]; + d4 = data_p[4]; + d5 = data_p[5]; + d6 = data_p[6]; + d7 = data_p[7]; + + SUB_AND_ADD (d0, d4) + SUB_AND_ADD (d1, d7) + SUB_AND_ADD (d2, d6) + SUB_AND_ADD (d5, d3) + + MUL_ROUND (CONST_1, d2) + d2 -= d6; + SUB_AND_ADD (d0, d2) + SUB_AND_ADD (d4, d6) + SUB_AND_ADD (d7, d3) + + tmp = d3; + SUB_AND_ADD (d6, d3) + data_p[7] = d6; + data_p[0] = d3; + + d6 = d5 - d1; + MUL_ROUND (CONST_5, d6) + MUL_ROUND (CONST_4, d1) + d1 -= d6; + d1 -= tmp; + MUL_ROUND (CONST_3, d7) + d7 -= d1; + MUL_ROUND (CONST_2, d5) + d6 -= d5; + d6 += d7; + + SUB_AND_ADD (d2, d1) + data_p[6] = d2; + data_p[1] = d1; + + SUB_AND_ADD (d0, d7) + data_p[5] = d0; + data_p[2] = d7; + + SUB_AND_ADD (d4, d6) + data_p[3] = d4; + data_p[4] = d6; + } + + #undef CONST_FRAC_BITS + #undef CONST_1 + #undef CONST_2 + #undef CONST_3 + #undef CONST_4 + #undef CONST_5 +} /* end of dct_inverse */ + +/* End of File */ diff --git a/ip/xjpg_dct.h b/ip/xjpg_dct.h new file mode 100644 index 0000000..15fedb8 --- /dev/null +++ b/ip/xjpg_dct.h @@ -0,0 +1,50 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*____________________________________________________________________________ + | | | + | xjpg_dct.h | Computes forward and inverse DCT for JPEG | + |____________|_______________________________________________________________| + | | + | Mark Overton, May 1997 | + |____________________________________________________________________________| +*/ + +void dct_forward (register int *block_p); + +void dct_inverse (register int *block_p); + +/* End of File */ diff --git a/ip/xjpg_dec.c b/ip/xjpg_dec.c new file mode 100644 index 0000000..507e11a --- /dev/null +++ b/ip/xjpg_dec.c @@ -0,0 +1,2838 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * xjpg_dec.c - Decodes a JPEG file into a raw gray image + * + ***************************************************************************** + * + * Name of Global Jump-Table: + * + * jpgDecodeTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_JPG_DECODE_OUTPUT_SUBSAMPLED]: + * Output only subsampled raw data? 0=no, 1=yes. + * aXformInfo[IP_JPG_DECODE_FROM_DENALI]: + * Data came from a Denali? 0=no, 1=yes. + * + * The aXformInfo items above may all be set to 0 for typical JPEG files. + * + * For Denali, we assume the following: + * - Every 8x8 block ends with an EOB + * - A slight change in the Huffman tables (no 15-bit code) + * - Denali sends us a proprietary short header (APP1 marker) + * + * Capabilities and Limitations: + * + * Decodes a standard JPEG file. Also handles JFIF 1.0 (APP0 marker). + * Also handles the non-standard short header output by OfficeJet firmware + * (APP1 marker). Also handles APP1 markers defined by color fax standard. + * Will *not* decode a non-interleaved file; it must be interleaved. + * Handles 1-4 components per pixel, and 4 Huffman tables, so SOF1 with + * 8 bits/component is okay. + * + * Default Input Traits, and Output Traits: + * + * For decoder: + * + * trait default input output + * ------------------- ------------------- ------------------------ + * iPixelsPerRow ignored based on header + * iBitsPerPixel ignored based on header + * iComponentsPerPixel ignored based on header + * lHorizDPI ignored based on header + * lVertDPI ignored based on header + * lNumRows ignored based on header + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Kludges: + * + * Chromafax and other software inserts fill 0's instead of fill 1's + * before a marker, such as the all-important EOI marker. So when + * we get a syntax error when parsing those 0's, we do not report + * an error if there's a following marker, but simply proceed to + * process the marker as usual. + * + * Chromafax uses an SOF1 instead of the correct SOF0, so we allow + * that, and also handle four Huffman tables that SOF1 requires. + * + * Jan 1998 Mark Overton -- Ported to new software Image Processor + * Apr 1996 Mark Overton -- Finished software-only JPEG decoder + * Feb 1996 Mark Overton -- initial code + * +\*****************************************************************************/ + +#include <string.h> +#include <assert.h> + +#include "hpip.h" +#include "ipdefs.h" +#include "setjmp.h" +#include "xjpg_dct.h" +#include "xjpg_mrk.h" + + +#define DUMP_JPEG 0 + +#if DUMP_JPEG + #include "stdio.h" + #include <tchar.h> + + #define DUMP(msg,arg1,arg2,arg3) \ + _ftprintf(stdout, msg, (int)(arg1), (int)(arg2), (int)(arg3)) +#else + #define DUMP(msg,arg1,arg2,arg3) +#endif + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stdout, _T("(jpeg) ") msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + + +/*____________________________________________________________________________ + | | + | Constants | + |____________________________________________________________________________| +*/ + +#define MAX_HUFF_TBLS 4 + +#define UNEXPECTED_MARKER 1 +#define BAD_MARKER_ID 2 +#define BAD_MARKER_DATA 3 +#define NO_RESTART_MARKER 4 +#define BAD_HUFF_CODE 5 +#define UNEXPECTED_END_OF_DATA 6 +#define NOT_IMPLEMENTED 7 + +#define MAX_MARKER_LEN 15000 /* large in case marker holds a thumbnail */ +#define MAX_HEADER_SIZE (MAX_MARKER_LEN+2000) +#define MAX_BLOCKS_IN_MCU 6 +#define MAX_MCU_SIZE (MAX_BLOCKS_IN_MCU*304) + /* max encoded MCU size, plus stuff-bytes */ +#define INBUF_NUM_MCUS 2 /* workbuf will be this multiple of max MCU */ + +#define DC_TBL_INDEX_LEN 9 +#define AC_TBL_INDEX_LEN 12 + +#define CHECK_VALUE 0x1ce5ca7e + + + +/*____________________________________________________________________________ + | | + | Instance Variables | + |____________________________________________________________________________| +*/ + +typedef struct { + BYTE size; /* number of bits in the Huff code (code is the index) */ + BYTE value; /* value that was coded */ +} main_huff_elem_t; + +typedef struct { + WORD code; /* Huff code to use for the 'value' below */ + BYTE size; /* number of bits in above Huff 'code' */ + BYTE value; /* value that was coded */ +} aux_huff_elem_t; + +typedef struct { + BYTE *index_p; + main_huff_elem_t *main_p; + aux_huff_elem_t *aux_p; +} huff_tbl_t; + + +/* Decoding is centered around out_rows_ap. It is indexed by + * [color_component_number][row_number]. + * color_component_number 0 is Y (intensity); mono only has this component. + * + * Each component in out_rows_ap has a height (# rows) equal to the number of + * samples in the MCU, and a width equal to the number of samples in all the + * MCUs in a row. That is, pixels are stored in out_rows_ap with NO REPLICATION. + * So if a component has sample factors of H and V, it will have 8*V rows and + * pixels_in_row*H/max_horiz_samp_fac columns in out_rows_ap. + */ + +typedef struct { + BYTE *out_rows_ap[4][32]; /* row-buffers [component][row] */ + + /***** Items from SOF, Start Of Frame *****/ + + IP_IMAGE_TRAITS traits; /* traits of the image */ + BYTE num_comps; /* # of components (1 => mono) */ + BYTE horiz_samp_facs[4]; /* horizontal sampling factors */ + BYTE vert_samp_facs [4]; /* vertical sampling factors */ + BYTE max_horiz_samp_fac; /* max sample factors */ + BYTE max_vert_samp_fac; + BYTE which_quant_tbl[4]; /* selects q tbl for component */ + UINT rows_per_mcu; /* # rows & cols in each MCU */ + UINT cols_per_mcu; + UINT mcus_per_row; /* # of MCUs in each row */ + UINT rowCountOffset; + + /***** Items from other markers *****/ + + WORD restart_interval; /* restart interval (0 -> none) */ + long quant_tbls[4][64]; /* quantization tables */ + BYTE which_dc_tbl[4]; /* selects DC tbl for component */ + BYTE which_ac_tbl[4]; /* selects AC tbl for component */ + BOOL fColorFax; /* is this from a fax? */ + + /***** Huffman tables *****/ + + huff_tbl_t dc_tbls[MAX_HUFF_TBLS]; + huff_tbl_t ac_tbls[MAX_HUFF_TBLS]; + + /***** Configuration variables *****/ + + BOOL output_subsampled; /* output subsampled data? */ + BOOL fDenali; /* data is from a Denali? */ + + /***** Variables used while decoding *****/ + + DWORD dwInNextPos; /* next read pos in input file */ + DWORD dwOutNextPos; /* next write pos in output file */ + long rows_done; /* # rows decoded and output */ + UINT mcus_done; /* # MCUs decoded so far in row */ + BOOL sending_rows; /* returning the decoded rows? */ + BOOL got_short_header; /* got an OfficeJet short header? */ + BOOL got_EOI; /* hit the end-of-image marker? */ + BYTE restart_cur_marker; /* index of next expected marker */ + WORD restart_cur_mcu; /* current MCU-count in interval */ + int prior_dc[4]; /* DC values of prior block */ + jmp_buf syntax_error; /* jump-target for syntax errors */ + jmp_buf old_syntax_error; + DWORD dwValidChk; /* struct validity check value */ + + /***** Reading bits variables *****/ + + DWORD rd_bit_buf; + /* Bits to be read from inbuf (read left-to-right). */ + + int rd_bits_avail; + /* Number of bits not yet read in rd_bit_buf (= 32 - number read). */ + + BYTE *rd_inbuf_beg; + /* The beginning of the input buffer. */ + + BYTE *rd_inbuf_next; + /* Next byte in inbuf to be read. */ + + /***** Decoding 8x8 blocks *****/ + + int block[64]; /* scratch-pad 8x8 block */ + int *block_zz[64+16]; /* zig-zag ptrs into above block */ + +} JDEC_INST, *PJDEC_INST; + + + +/*____________________________________________________________________________ + | | + | Forward Routines | + |____________________________________________________________________________| +*/ + +static void huff_define_table ( + PJDEC_INST g, + BOOL ac, /* defining an AC table? (else DC) */ + UINT id, /* which table is being defined (0-3) */ + const BYTE counts[16], /* number of Huffman codes of each length 1-16 */ + const BYTE values[]); /* values associated with codes of above lengths */ + +void wino_scale_table (long *tbl_p); + + + + +/****************************************************************************** + ****************************************************************************** + + R E A D I N G + + + Interface into this section: + + read_init - inits this section + read_buf_open - we are being given a (new) input buffer + read_buf_close - done with current input buffer; return # bytes used + read_byte - returns next input byte (syncs to byte-boundary) + read_uint - returns next 2-byte integer (syncs) + read_skip_forward - discards the given number of input bytes + read_skip_backward - backs up the given number of bytes + READ_BITS_LOAD - loads N bits of input into lsb's of a var (no advance) + READ_BITS_ADVANCE - advance input N bits; you must call this to eat input + + ****************************************************************************** + ******************************************************************************/ + + + +/*____________________________________________________________________________ + | | | + | read_init | Inits this section | + |___________|________________________________________________________________| +*/ +static void read_init (PJDEC_INST g) +{ + g->rd_bits_avail = 0; + g->rd_bit_buf = 0; +} + + + +/*____________________________________________________________________________ + | | | + | read_buf_open | We are being given a (new) buffer to read input | + |_______________|____________________________________________________________| + | | + | This routine records the location of the new input buffer. | + |____________________________________________________________________________| +*/ +static void read_buf_open (PJDEC_INST g, BYTE *buf_p) +{ + g->rd_inbuf_beg = buf_p; + g->rd_inbuf_next = buf_p; +} + + + +/*____________________________________________________________________________ + | | | + | read_buf_close | We are done with the current input buffer | + |________________|___________________________________________________________| + | | + | This function returns # bytes read from the input buffer. | + |____________________________________________________________________________| +*/ +static int read_buf_close (PJDEC_INST g) +{ + return g->rd_inbuf_next - g->rd_inbuf_beg; +} + + + +/*____________________________________________________________________________ + | | | + | rd_sync | Empties the bit-cache, and syncs to a byte-boundary | + |_________|__________________________________________________________________| +*/ +static void rd_sync (PJDEC_INST g) +{ + g->rd_bits_avail = 0; +} + + + +/*____________________________________________________________________________ + | | | + | read_byte | returns next input byte (syncs to byte-boundary) | + |___________|________________________________________________________________| +*/ +static BYTE read_byte (PJDEC_INST g) +{ + rd_sync (g); + return *(g->rd_inbuf_next)++; +} + + + +/*____________________________________________________________________________ + | | | + | read_uint | returns next 2-byte integer (syncs) | + |___________|________________________________________________________________| +*/ +static unsigned read_uint (PJDEC_INST g) +{ + UINT uval; + + rd_sync (g); + uval = (unsigned)*(g->rd_inbuf_next)++ << 8; + return uval | *(g->rd_inbuf_next)++; +} + + + +/*____________________________________________________________________________ + | | | + | read_skip_forward | discards the given number of input bytes | + |___________________|________________________________________________________| +*/ +static void read_skip_forward (PJDEC_INST g, UINT n) +{ + if (n > MAX_MARKER_LEN) + longjmp (g->syntax_error, BAD_MARKER_DATA); + rd_sync (g); + g->rd_inbuf_next += n; +} + + + +/*____________________________________________________________________________ + | | | + | read_skip_backward | backs up N bytes in the input buffer | + |____________________|_______________________________________________________| +*/ +static void read_skip_backward (PJDEC_INST g, UINT n) +{ + rd_sync (g); + g->rd_inbuf_next -= n; +} + + + +/*____________________________________________________________________________ + | | | + | READ_BITS_LOAD | loads next par_n_bits of input into par_value (no advance)| + |________________|___________________________________________________________| + | | + | If a marker is encountered: | + | - this macro will goto hit_marker | + | - the marker will be the next two bytes in the buffer | + | - the cache will be empty (ie, any fill 1's were discarded) | + | | + | If the JPEG file erroneously has fill 0's (instead of 1's) before a | + | marker, we'll parse them as Huffman codes. If such a bogus Huffman code | + | has more bits than were in our bit-buffer, the extra non-existent bits | + | will be returned as zeroes. | + |____________________________________________________________________________| +*/ + +#define CANT_LOAD_NEAR_MARKER(g) \ + (g->rd_bits_avail<=0 || \ + (g->rd_bits_avail<=7 && (((1u<<g->rd_bits_avail)-1) & ~g->rd_bit_buf) == 0)) + /* we just parsed past erroneous fill 0's OR */ + /* cached bits are just fill 1's; toss them */ + + +#define READ_BITS_LOAD(g, fixed_len, par_n_bits, par_value, hit_marker) \ +{ \ + if ((int)(par_n_bits) > g->rd_bits_avail) { \ + BYTE a_byte; \ + \ + do { \ + a_byte = *(g->rd_inbuf_next)++; \ + DUMP (_T("<%02x>"), a_byte, 0, 0); \ + \ + if (a_byte == (BYTE )0xffu) { \ + if (*(g->rd_inbuf_next)++ != 0) { /* we hit a marker */ \ + g->rd_inbuf_next -= 2; \ + if (CANT_LOAD_NEAR_MARKER(g)) \ + goto hit_marker; \ + break; /* exit 'while' loop */ \ + } \ + } \ + \ + g->rd_bit_buf = (g->rd_bit_buf<<8) | a_byte; \ + g->rd_bits_avail += 8; \ + } while (g->rd_bits_avail <= 24); \ + } \ + \ + par_value = (g->rd_bit_buf << (32-g->rd_bits_avail)) >> (32-(par_n_bits)); \ +} + + + +/*____________________________________________________________________________ + | | | + | READ_BITS_ADVANCE | discards next par_n_bits of input | + |___________________|________________________________________________________| +*/ +#define READ_BITS_ADVANCE(g, par_n_bits) \ +{ \ + g->rd_bits_avail -= par_n_bits; \ +} + + + +/****************************************************************************** + ****************************************************************************** + + M A R K E R S + + + Interface into this section: + + mar_get - parses and returns next marker-id + mar_parse - parses the data associated with the marker + + ****************************************************************************** + ******************************************************************************/ + + + +/*____________________________________________________________________________ + | | | + | parse_factors | parses sample-factors as nibbles into array | + |_______________|____________________________________________________________| + | | + | This is only called by parse_app_short_header below. | + | Function return value = maximum sample factor. | + |____________________________________________________________________________| +*/ +static UINT parse_factors ( + UINT factors, /* in: sample factors, one nibble each, l-to-r */ + BYTE fac_array[4]) /* out: array of sample factors */ +{ + int i; + UINT fac; + UINT max_fac; + + max_fac = 0; + + for (i=3; i>=0; i--) { + fac = factors & 0x000fu; + factors >>= 4; + if (fac > max_fac) max_fac = fac; + fac_array[i] = fac; + } + + return max_fac; +} + + + +/*____________________________________________________________________________ + | | | + | calc_quant_table | calculates a quantization table | + |__________________|_________________________________________________________| + | | + | This is only called by parse_app_short_header below. | + | Warning: The calculation below should match the firmware. | + |____________________________________________________________________________| +*/ +static void calc_quant_table ( + PJDEC_INST g, + UINT dc_q_fac, /* orig DC is scaled by (dc_q_fac/50) */ + UINT ac_q_fac, /* orig ACs are scaled by (ac_q_fac/50) */ + const BYTE *orig_tbl_p, /* original table */ + UINT which_q_tbl) /* which table is being defined (0-3) */ +{ + int i; + UINT quant; + long *tbl_p; + + tbl_p = g->quant_tbls[which_q_tbl]; + + for (i=0; i<64; i++) { + quant = ((*orig_tbl_p++)*(i==0 ? dc_q_fac : ac_q_fac) + 25u) / 50u; + if (quant == 0) quant = 1; + if (quant > 255) quant = 255; + tbl_p[i] = quant; + } + + wino_scale_table (tbl_p); +} + + + +/*____________________________________________________________________________ + | | | + | mar_get | parses and returns next marker-id | + |_________|__________________________________________________________________| +*/ +static UINT mar_get (PJDEC_INST g) +{ + UINT marker = 0; /* init to eliminate a compiler warning */ + BOOL got_ff; + + got_ff = FALSE; + + while (TRUE) { + marker = read_byte (g); + if (marker == 0xff) got_ff = TRUE; + else break; + } + + if (!got_ff || marker==0) + longjmp (g->syntax_error, BAD_MARKER_ID); + + return marker; +} + + + +/*____________________________________________________________________________ + | | | + | mar_flush | discards data associated with current marker | + |___________|________________________________________________________________| +*/ +static void mar_flush (PJDEC_INST g, UINT marker) +{ + if (marker==MARKER_SOI || marker==MARKER_EOI || + (marker>=MARKER_RST0 && marker<=MARKER_RST7)) + return; /* marker has no associated segment */ + + read_skip_forward (g, read_uint(g) - 2); +} + + + +/*____________________________________________________________________________ + | | | + | parse_app_jfif | parses a JFIF APP0 marker | + |________________|___________________________________________________________| + | | + | Sets these fields in 'traits': lHorizDPI, lVertDPI | + |____________________________________________________________________________| +*/ +static void parse_app_jfif (PJDEC_INST g) +{ + UINT len; + BYTE byt; + UINT h_dpi, v_dpi; + + len = read_uint (g); + byt = read_byte (g); + if (! (len==16 && (byt==(BYTE )'J' || byt==(BYTE )'j'))) { + /* This is not a JFIF APP-marker, so silently discard it */ + read_skip_forward (g, len-3); + return; + } + + read_skip_forward (g,6); /* discard "FIF\0" + 0x01 + 0x00 */ + byt = read_byte (g); + h_dpi = read_uint (g); + v_dpi = read_uint (g); + read_skip_forward (g,2); /* discard thumbnail X and Y */ + + if (byt == 1) { /* 1 means that units are dots/inch */ + g->traits.lHorizDPI = (DWORD)h_dpi << 16; + g->traits.lVertDPI = (DWORD)v_dpi << 16; + } +} + + + +/*____________________________________________________________________________ + | | | + | parse_app_g3fax | parses a color fax APP1 marker | + |_________________|__________________________________________________________| + | | + | Can set these fields in 'traits': lHorizDPI, lVertDPI | + |____________________________________________________________________________| + */ +static void parse_app_g3fax (PJDEC_INST g) +{ + int i; + UINT len, dpi; + BYTE id_ver[8]; + const BYTE valid_id_ver[8] = { + 0x47, 0x33, 0x46, 0x41, 0x58, 0x00, /* "G3FAX" plus a 0 byte */ + 0x07, 0xca }; /* version is year of std, 1994 */ + + len = read_uint (g); + if (len != 12) { + /* wrong version, or a gamut or illuminant spec which we ignore */ + read_skip_forward (g, len-2); + return; + } + + for (i=0; i<8; i++) /* fax id is 6 bytes; version is 2 bytes */ + id_ver[i] = read_byte (g); + + dpi = read_uint (g); + + if (memcmp(id_ver,valid_id_ver,8) == 0 + && (dpi==200 || dpi==300 || dpi==400)) { + /* everything is valid (whew!), so save dpi */ + g->traits.lHorizDPI = g->traits.lVertDPI = (long)dpi << 16; + g->fColorFax = TRUE; + } +} + + + +/*____________________________________________________________________________ + | | | + | parse_app_short_header | An APP1 marker output by OfficeJet firmware | + |________________________|___________________________________________________| +*/ +static void parse_app_short_header (PJDEC_INST g) +{ + /* Since the firmware does not supply tables in its header, + * the tables used in the firmware are supplied below. */ + + static const BYTE orig_lum_quant[64] = { + 16, 11, 12, 14, 12, 10, 16, 14, + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99 + }; + + + static const BYTE orig_chrom_quant[64] = { + 17, 18, 18, 24, 21, 24, 47, 26, + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }; + + static const BYTE lum_DC_counts[16] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const BYTE lum_DC_values[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b + }; + + static const BYTE chrom_DC_counts[16] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const BYTE chrom_DC_values[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b + }; + + static const BYTE lum_AC_counts[16] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d + }; + + static const BYTE lum_AC_counts_Denali[16] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x00, 0x7e + /* Above, the [01,7d] in the normal table was changed to [00,7e]. + * This alteration eliminates the sole 15-bit code, and yields + * Huffman codes as follows: + * - common codes are 12 bits wide or less, + * - uncommon codes are exactly 16 bits wide, and all those codes + * start with nine '1' bits, leaving seven bits of useful info. + * Denali uses a 4K-entry table for the common codes, and a + * quick lookup for the 7-bit leftover codes. So parsing of all + * codes is simple and fast. + */ + }; + + static const BYTE lum_AC_values[162] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa + }; + + static const BYTE chrom_AC_counts[16] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77 + }; + + static const BYTE chrom_AC_values[162] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa + }; + + UINT len; + UINT dc_q_fac, ac_q_fac; + UINT reserved; + + /***** default items not in the short header *****/ + + g->restart_interval = 0; + + g->which_quant_tbl[0] = 0; + g->which_quant_tbl[1] = 1; + g->which_quant_tbl[2] = 1; + + g->which_dc_tbl[0] = 0; + g->which_ac_tbl[0] = 0; + g->which_dc_tbl[1] = 1; + g->which_ac_tbl[1] = 1; + g->which_dc_tbl[2] = 1; + g->which_ac_tbl[2] = 1; + + huff_define_table (g, FALSE, 0, lum_DC_counts , lum_DC_values); + huff_define_table (g, TRUE , 0, g->fDenali ? lum_AC_counts_Denali + : lum_AC_counts, lum_AC_values); + huff_define_table (g, FALSE, 1, chrom_DC_counts, chrom_DC_values); + huff_define_table (g, TRUE , 1, chrom_AC_counts, chrom_AC_values); + + /***** parse the short header *****/ + + len = read_uint (g); + if (len != 18) + longjmp (g->syntax_error, BAD_MARKER_DATA); + + g->traits.lNumRows = read_uint (g); + g->traits.iPixelsPerRow = read_uint (g); + g->traits.lHorizDPI = (long)read_uint(g) << 16; + g->traits.lVertDPI = (long)read_uint(g) << 16; + ac_q_fac = read_byte (g); + g->num_comps = read_byte (g); + g->max_horiz_samp_fac = parse_factors (read_uint(g), g->horiz_samp_facs); + g->max_vert_samp_fac = parse_factors (read_uint(g), g->vert_samp_facs); + dc_q_fac = read_byte (g); + reserved = read_byte (g); /* should be 0 */ + + g->traits.iComponentsPerPixel = g->num_comps; + g->traits.iBitsPerPixel = g->num_comps * 8; + if (g->traits.lNumRows == 0) + g->traits.lNumRows = -1; /* -1 means 'unknown' */ + if (dc_q_fac == 0) + dc_q_fac = ac_q_fac; + + calc_quant_table (g, dc_q_fac, ac_q_fac, orig_lum_quant, 0); + calc_quant_table (g, dc_q_fac, ac_q_fac, orig_chrom_quant, 1); + + g->got_short_header = TRUE; +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_app | application-specific marker | + |_______________|____________________________________________________________| + | | + | Can set these fields in 'traits': lHorizDPI, lVertDPI | + |____________________________________________________________________________| + */ +static void mar_parse_app (PJDEC_INST g, UINT marker) +{ + UINT len; + BYTE id1, id2, id3; + + len = read_uint (g); + if (len <= 5) { + /* unknown marker; discard it */ + read_skip_forward (g, len-2); + return; + } + + id1 = read_byte (g); + id2 = read_byte (g); + id3 = read_byte (g); + read_skip_backward (g, 5); + + if (marker==MARKER_APP+1 && id1==0x47 && id2==0x33 && id3==0x46) { + /* G3 color fax APP1 marker */ + parse_app_g3fax (g); + } else if (marker==MARKER_APP+0 && (id1=='J' || id1=='j') + && (id2=='F' || id2=='f') && (id3=='I' || id3=='i')) { + /* JFIF APP0 marker for generic JPEG files */ + parse_app_jfif (g); + } else if (marker==MARKER_APP+1 && len==18) { + /* assume that APP1 marker is a short header */ + parse_app_short_header (g); + } else { + /* unrecognized APP marker; discard it */ + read_skip_forward (g, len); + } +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_sof | start of frame | + |_______________|____________________________________________________________| + | | + | Sets these fields in 'traits': lNumRows, iPixelsPerRow, iBitsPerPixel, | + | iComponentsPerPixel. | + | | + | Sets these variables: num_comps (equal to iComponentsPerPixel), | + | horiz_samp_facs, vert_samp_facs, which_quant_tbl, | + | max_horiz_samp_fac, max_vert_samp_fac | + |____________________________________________________________________________| +*/ +static void mar_parse_sof (PJDEC_INST g, UINT marker) +{ + UINT len; + UINT uBitsPerComp; + UINT comp; + BYTE comp_id; + BYTE hv_samp; + BYTE q_table; + BYTE h, v; + + len = read_uint (g); + uBitsPerComp = read_byte (g); + g->rowCountOffset=g->rd_inbuf_next-g->rd_inbuf_beg; + g->traits.lNumRows = read_uint (g); + g->traits.iPixelsPerRow = read_uint (g); + g->traits.iComponentsPerPixel = g->num_comps = read_byte (g); + g->traits.iBitsPerPixel = g->num_comps * uBitsPerComp; + + if (g->traits.lNumRows == 0) + g->traits.lNumRows = -1; /* -1 means 'unknown' */ + + if (len != (8u + 3u*g->num_comps) || g->num_comps==0) + longjmp (g->syntax_error, BAD_MARKER_DATA); + + /* Below, we also check for SOF1 because Chromafax erroneously outputs it */ + if ((marker!=MARKER_SOF0 && marker!=MARKER_SOF0+1) + || uBitsPerComp!=8 || g->num_comps>4) + longjmp (g->syntax_error, NOT_IMPLEMENTED); + + g->max_horiz_samp_fac = 1; + g->max_vert_samp_fac = 1; + + #if DUMP_JPEG + _ftprintf (stderr, _T("\nSOF marker:\n")); + _ftprintf (stderr, _T(" bits per sample = %d\n"), uBitsPerComp); + _ftprintf (stderr, _T(" number of rows = %d\n"), g->traits.lNumRows); + _ftprintf (stderr, _T(" pixels per row = %d\n"), g->traits.iPixelsPerRow); + _ftprintf (stderr, _T(" # components = %d\n"), g->num_comps); + #endif + + for (comp=0; comp<g->num_comps; comp++) { + comp_id = read_byte (g); + hv_samp = read_byte (g); + q_table = read_byte (g); + + #if DUMP_JPEG + _ftprintf (stderr, + _T(" %d: comp id = %d, hv samp fac = %02x, which q = %d\n"), + comp, comp_id, hv_samp, q_table); + #endif + + g->horiz_samp_facs[comp] = h = hv_samp >> 4; + g->vert_samp_facs [comp] = v = hv_samp & 0x0fu; + g->which_quant_tbl[comp] = q_table; + + if (h > g->max_horiz_samp_fac) g->max_horiz_samp_fac = h; + if (v > g->max_vert_samp_fac ) g->max_vert_samp_fac = v; + } +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_dqt | discrete quantization table | + |_______________|____________________________________________________________| + | | + | Sets the following variables: quant_tbls. | + |____________________________________________________________________________| +*/ +static void mar_parse_dqt (PJDEC_INST g) +{ + int len, i; + BYTE pt; + long *tbl_p; + + len = read_uint(g) - 2; + + while (len >= 65) { + len -= 65; + pt = read_byte (g); + if ((pt & 0xfcu) != 0) + longjmp (g->syntax_error, NOT_IMPLEMENTED); + tbl_p = g->quant_tbls[pt & 3]; + DUMP (_T("\nDQT marker: table=%d"), pt & 3, 0, 0); + for (i=0; i<64; i++) { + if ((i & 15) == 0) DUMP(_T("\n "), 0,0,0); + tbl_p[i] = read_byte (g); + DUMP (_T("%2d "), tbl_p[i], 0, 0); + } + DUMP (_T("\n"), 0,0,0); + wino_scale_table (tbl_p); + } + + if (len != 0) + longjmp (g->syntax_error, BAD_MARKER_DATA); +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_dht | define Huffman tables | + |_______________|____________________________________________________________| + | | + | Sets the following variables: dc_tbls, ac_tbls. | + |____________________________________________________________________________| +*/ +static void mar_parse_dht (PJDEC_INST g) +{ + BYTE num_codes[16]; + BYTE values[256]; + + int len, i, tot_codes; + BYTE class_id; + + len = read_uint(g) - 2; + + while (len > 17) { + class_id = read_byte (g); + + for (tot_codes=0, i=0; i<=15; i++) { + num_codes[i] = read_byte (g); + tot_codes += num_codes[i]; + } + + len -= 17; + if (len < tot_codes) + longjmp (g->syntax_error, BAD_MARKER_DATA); + + for (i=0; i<tot_codes; i++) + values[i] = read_byte (g); + len -= tot_codes; + + #if DUMP_JPEG + _ftprintf (stderr, _T("\nDHT marker: class=%d, id=%d\n counts = "), + (BOOL )(class_id>>4), class_id & 0x0f); + for (i=0; i<16; i++) + _ftprintf (stderr, _T("%2d "), num_codes[i]); + _ftprintf (stderr, _T("\n values = ")); + for (i=0; i<tot_codes; i++) + _ftprintf (stderr, _T("%02x "), values[i]); + _ftprintf (stderr, _T("\n")); + #endif + + huff_define_table (g, (BOOL)(class_id>>4), class_id & 0x0f, + num_codes, values); + } + + if (len != 0) + longjmp (g->syntax_error, BAD_MARKER_DATA); +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_dri | define restart interval | + |_______________|____________________________________________________________| + | | + | Sets the following variable: restart_interval. | + |____________________________________________________________________________| +*/ +static void mar_parse_dri (PJDEC_INST g) +{ + UINT len; + + len = read_uint (g); + if (len != 4) + longjmp (g->syntax_error, BAD_MARKER_DATA); + + g->restart_interval = read_uint (g); + DUMP (_T("\nDRI marker: restart interval = %d\n"), g->restart_interval, 0,0); +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_sos | start of scan | + |_______________|____________________________________________________________| +*/ +static void mar_parse_sos (PJDEC_INST g) +{ + UINT len; + UINT comp; + UINT cs; + UINT dc_ac; + + len = read_uint (g); + if (len != 6u+2u*g->num_comps) + longjmp (g->syntax_error, BAD_MARKER_DATA); + + read_byte (g); /* skip Ns value (number of components in scan) */ + + DUMP (_T("\nSOS marker:\n"), 0,0,0); + + for (comp=0; comp<g->num_comps; comp++) { + cs = read_byte (g); /* skip Cs value (component selector) */ + dc_ac = read_byte (g); + DUMP (_T(" %d: cs = %d, which dc_ac tbl = %02x\n"), comp, cs, dc_ac); + g->which_dc_tbl[comp] = dc_ac >> 4; + g->which_ac_tbl[comp] = dc_ac & 0x0fu; + } + + read_byte (g); /* skip Ss value (start selection) */ + read_byte (g); /* skip Se value (end selection) */ + read_byte (g); /* skip AhAl value (approximation bit positions) */ +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse_dnl | Define Number of Lines | + |_______________|____________________________________________________________| + | | + | Sets the following variable: traits.lNumRows | + |____________________________________________________________________________| +*/ +static void mar_parse_dnl (PJDEC_INST g) +{ + UINT len, nRows; + + len = read_uint (g); + nRows = read_uint (g); + + if (len!=4u || nRows==0) + longjmp (g->syntax_error, BAD_MARKER_DATA); + + DUMP (_T("\nDNL marker: %d\n"), nRows,0,0); + g->traits.lNumRows = nRows; +} + + + +/*____________________________________________________________________________ + | | | + | mar_parse | parses the marker, storing results in global variables | + |___________|________________________________________________________________| +*/ +static void mar_parse (PJDEC_INST g, UINT marker) +{ + PRINT (_T("mar_parse: marker = %02xh\n"), marker, 0); + + switch (marker) + { + case MARKER_APP+0: + case MARKER_APP+1: + case MARKER_APP+2: + case MARKER_APP+3: + case MARKER_APP+4: + case MARKER_APP+5: + case MARKER_APP+6: + case MARKER_APP+7: + case MARKER_APP+8: + case MARKER_APP+9: + case MARKER_APP+10: + case MARKER_APP+11: + case MARKER_APP+12: + case MARKER_APP+13: + case MARKER_APP+14: + case MARKER_APP+15: + mar_parse_app (g, marker); + break; + + case MARKER_COM: /* comment marker */ + case MARKER_JPG+0: + case MARKER_JPG+1: + case MARKER_JPG+2: + case MARKER_JPG+3: + case MARKER_JPG+4: + case MARKER_JPG+5: + case MARKER_JPG+6: + case MARKER_JPG+7: + case MARKER_JPG+8: + case MARKER_JPG+9: + case MARKER_JPG+10: + case MARKER_JPG+11: + case MARKER_JPG+12: + case MARKER_JPG+13: + mar_flush (g, marker); + break; + + case MARKER_SOF0: + case MARKER_SOF1: + case MARKER_SOF2: + case MARKER_SOF3: + case MARKER_SOF5: + case MARKER_SOF6: + case MARKER_SOF7: + case MARKER_SOF8: + case MARKER_SOF9: + case MARKER_SOFA: + case MARKER_SOFB: + case MARKER_SOFD: + case MARKER_SOFE: + case MARKER_SOFF: + mar_parse_sof (g, marker); + break; + + case MARKER_RST0: + case MARKER_RST1: + case MARKER_RST2: + case MARKER_RST3: + case MARKER_RST4: + case MARKER_RST5: + case MARKER_RST6: + case MARKER_RST7: + DUMP (_T("\nRST marker.\n"), 0,0,0); + break; + + case MARKER_DHT: mar_parse_dht (g); break; + case MARKER_DQT: mar_parse_dqt (g); break; + case MARKER_DRI: mar_parse_dri (g); break; + case MARKER_SOS: mar_parse_sos (g); break; + case MARKER_DNL: mar_parse_dnl (g); break; + + case MARKER_DAC: + case MARKER_DHP: + case MARKER_EXP: + longjmp (g->syntax_error, NOT_IMPLEMENTED); + break; + + /* The following markers have no data following them */ + + case MARKER_SOI: + DUMP (_T("\nSOI marker.\n"), 0,0,0); + break; + + case MARKER_EOI: + DUMP (_T("\nEOI marker.\n"), 0,0,0); + break; + + default: + longjmp (g->syntax_error, BAD_MARKER_DATA); + } +} + + + +/****************************************************************************** + ****************************************************************************** + + H U F F M A N + + + Interface into this section: + + huff_init - inits this section + huff_free_tbl - deallocates memory for one table + huff_free_all - deallocates memory for all tables + huff_define_table - defines a new Huffman table + DECODE_HUFF - decodes and returns next Huffman code + + ****************************************************************************** + ******************************************************************************/ + + + +/*____________________________________________________________________________ + | | | + | DECODE_HUFF | Decodes a Huffman code, returning corresponding value | + |_____________|______________________________________________________________| +*/ +#define DECODE_HUFF( \ + g, \ + huff_tbl_p, \ + main_ix_len, \ + par_result, \ + hit_marker) \ +{ \ + UINT tbl_index, code, size; \ + main_huff_elem_t *elem; \ + \ + READ_BITS_LOAD (g, FALSE, main_ix_len, code, hit_marker) \ + tbl_index = huff_tbl_p->index_p[code]; \ + elem = &(huff_tbl_p->main_p[tbl_index]); \ + par_result = elem->value; \ + size = elem->size; \ + \ + if (size == 0) { \ + par_result = parse_aux_code (g, huff_tbl_p->aux_p); \ + } else { \ + DUMP (_T(" %2d-%04x-%3d(main) "), size, code, par_result); \ + READ_BITS_ADVANCE (g, size) \ + } \ +} + + + +/*____________________________________________________________________________ + | | | + | parse_aux_code | Code is not in short-width table; look in aux table | + |________________|___________________________________________________________| + | | + | Returns the value associated with the code. | + |____________________________________________________________________________| +*/ +static UINT parse_aux_code ( + PJDEC_INST g, + aux_huff_elem_t *aux_tbl_par_p) +{ + UINT code, size, val; + UINT diff; + UINT excess; + aux_huff_elem_t *lo_p, *hi_p, *mid_p; + + READ_BITS_LOAD (g, FALSE, 16, code, syntax_err) + +#if 0 /* we are no longer using ROM tables */ + if ((BYTE *)aux_tbl_par_p == dec_AC_aux_tbl) + { + /* We are using our default ROM table */ + val = dec_AC_aux_tbl [code & 0x7f]; + DUMP (_T(" %2d-%4x-%3d(aux-rom) "), 16, code, val); + if (val==0 || (code & 0xff80)!=0xff80) goto syntax_err; + READ_BITS_ADVANCE (g,16) + } +#endif + + lo_p = aux_tbl_par_p; + hi_p = lo_p + lo_p->size - 1; + lo_p += 1; /* 1st table-entry is a dummy containing above table-size */ + + while ((diff=(hi_p-lo_p)) > 1) { + mid_p = lo_p + (diff>>1); + if (mid_p->code > code) hi_p = mid_p; + else lo_p = mid_p; + } + + size = lo_p->size; + excess = 16u - size; + if ((code>>excess) != (UINT)(lo_p->code>>excess)) { + lo_p = hi_p; + size = lo_p->size; + excess = 16u - size; + if ((code>>excess) != (UINT)(lo_p->code>>excess)) { + PRINT (_T("aux code of %x not found\n"), code, 0); + goto syntax_err; + } + } + + val = lo_p->value; + READ_BITS_ADVANCE (g,size) + DUMP (_T(" %2d-%4x-%3d(aux) "), size, code, val); + + return val; + + syntax_err: + PRINT (_T("parse_aux_code: syntax error\n"), 0, 0); + longjmp (g->syntax_error, BAD_HUFF_CODE); +} + + + +/*____________________________________________________________________________ + | | | + | huff_init | Inits this section | + |___________|________________________________________________________________| +*/ +static void huff_init (PJDEC_INST g) +{ + memset (g->dc_tbls, 0, sizeof(g->dc_tbls)); + memset (g->ac_tbls, 0, sizeof(g->ac_tbls)); +} + + + +/*____________________________________________________________________________ + | | | + | huff_free_tbl | Frees memory allocated for the given table | + |_______________|____________________________________________________________| +*/ +static void huff_free_tbl (PJDEC_INST g, huff_tbl_t *tbl_p) +{ + if (tbl_p->index_p != NULL) + IP_MEM_FREE (tbl_p->index_p); + if (tbl_p->main_p != NULL) + IP_MEM_FREE (tbl_p->main_p); + if (tbl_p->aux_p != NULL) + IP_MEM_FREE (tbl_p->aux_p); + + tbl_p->index_p = NULL; + tbl_p->main_p = NULL; + tbl_p->aux_p = NULL; +} + + + +/*____________________________________________________________________________ + | | | + | huff_free_all | Frees all memory allocated for Huffman tables | + |_______________|____________________________________________________________| +*/ +static void huff_free_all (PJDEC_INST g) +{ + int i; + + for (i=0; i<MAX_HUFF_TBLS; i++) { + huff_free_tbl (g, &(g->dc_tbls[i])); + huff_free_tbl (g, &(g->ac_tbls[i])); + } +} + + + +/*____________________________________________________________________________ + | | | + | calc_table | Defines a Huffman table | + |____________|_______________________________________________________________| +*/ +static void calc_table ( + const BYTE counts[16], /* in: # Huffman codes of each length 1-16 */ + const BYTE huffval[], /* in: values for codes of above lengths */ + UINT main_ix_len, /* in: # bits in main tbl index (0=use max) */ + huff_tbl_t *huff_tbl_p) /* out: the three tables */ +{ + BYTE huffsize[257]; + WORD huffcode[257]; + int tot_codes; + int i; + BYTE *index_p; + main_huff_elem_t *main_p; + aux_huff_elem_t *aux_p; + + /***************************************************/ + /* Compute a complete Huffman table. */ + /* output: huffval, huffsize, huffcode, tot_codes */ + /***************************************************/ + + { + int i, j, k, code, siz; + + /* Generate size array -- see JPEG document + * + * Note that list BITS in JPEG doc. has index from 1-16, but this list + * is called 'counts', indexed 0-15. This thus BITS[i] is replaced + * by counts[i-1] + */ + tot_codes = 0; + for (i=1; i<=16; i++) + for (j=1; j<=counts[i-1]; j++) + huffsize[tot_codes++] = i; + + huffsize[tot_codes] = 0; + + #if 0 /* we now only use RAM tables */ + if (memcmp(counts, rom_counts, 16) == 0 && + memcmp(huffval, rom_val, tot_codes) == 0) { + PRINT (_T("calc_table: using ROM Huffman tables\n"), 0, 0); + return FALSE; /* tell caller to use ROM-tables instead */ + } + #endif + + /* Generate code array -- see JPEG document + * + * The resulting table is sorted by increasing 'code', and also by + * increasing 'size'. + */ + k = 0; + code = 0; + siz = huffsize[0]; + while (TRUE) { + do { + huffcode[k++] = code++; + } while (huffsize[k]==siz && k<257); /* Overflow Detection */ + if (huffsize[k] == 0) + break; /* all done */ + do { /* Shift next code to expand prefix */ + code <<= 1; + siz += 1; + } while (huffsize[k] != siz); + } + } + + /****************************************/ + /* Make the main table (output: main_p) */ + /****************************************/ + + { /* This "main" table is indexed by the output of the "table table", + * which is indexed by 'main_ix_len' bits of input. + * If the table-entry has a 'size' of 0, the aux table is examined. + */ + int i, nbytes; + int extra_bits; + UINT first, final, code_plus_junk; + + if (main_ix_len == 0) + main_ix_len = huffsize[tot_codes-1]; + + nbytes = (tot_codes+1) * sizeof(main_huff_elem_t); + IP_MEM_ALLOC (nbytes, main_p); + memset (main_p, 0, nbytes); + + nbytes = 1lu << main_ix_len; + IP_MEM_ALLOC (nbytes, index_p); + memset (index_p, 0, nbytes); + + for (i=0; i<tot_codes && huffsize[i]<=main_ix_len; i++) { + main_p[i+1].value = huffval [i]; + main_p[i+1].size = huffsize[i] ; + + extra_bits = main_ix_len - huffsize[i]; + first = huffcode[i] << extra_bits; + final = first + (1lu << extra_bits) - 1; + for (code_plus_junk = first; + code_plus_junk <= final; + code_plus_junk++) + index_p[code_plus_junk] = i+1; + } + } + + /********************************************/ + /* Make the auxiliary table (output: aux_p) */ + /********************************************/ + + { /* This is used when an entry in the main table was 0, meaning that + * the code is longer than 'main_ix_len'. The aux table consists of + * all [code, size, value] triples for sizes > main_ix_len. A binary + * search is used to locate the code. + * The first table-entry is a dummy whose 'size' field is the number + * of table-entries (including the dummy). + */ + int first, n_entries; + aux_huff_elem_t *p; + + /* locate first huffsize > main_ix_len */ + for (first=0; first<tot_codes && huffsize[first]<=main_ix_len; first++); + + if (first == tot_codes) { + /* the main table captured everything; no aux table is needed */ + IP_MEM_ALLOC (1, aux_p); + } else { + n_entries = tot_codes - first + 1; /* +1 because of dummy entry */ + IP_MEM_ALLOC (n_entries * sizeof(aux_huff_elem_t), aux_p); + + /* fill-in the dummy entry (contains # entries in table) */ + p = aux_p; + p->size = (UINT) n_entries; + p->code = p->value = 0; + p += 1; + + for (i=first; i<tot_codes; i++) { + p->size = huffsize[i]; + p->code = huffcode[i] << (16u - p->size); + p->value = huffval [i]; + p += 1; + } + } + } + + #if DUMP_JPEG + { + int i, n; + + _ftprintf (stderr, _T("\nOriginal size-code-val tuples:\n")); + for (i=0; i<tot_codes; i++) { + if ((i&7) == 0) _ftprintf(stderr, _T(" ")); + _ftprintf (stderr, _T("%2d-%04x-%02x "), + huffsize[i], huffcode[i], huffval[i]); + if ((i&7)==7 || i==tot_codes-1) _ftprintf(stderr, _T("\n")); + } + + _ftprintf(stderr, _T("\nIndex table: (%d entries)\n"), 1lu<<main_ix_len); + for (i=0; i<(1lu<<main_ix_len); i++) { + if ((i&7) == 0) _ftprintf(stderr, _T(" %3d: "), i); + _ftprintf (stderr, _T("%3d, "), index_p[i]); + if ((i&7) == 7) _ftprintf(stderr, _T("\n")); + } + + _ftprintf(stderr, _T("\nMain table: (%d entries)\n"), tot_codes+1); + for (i=0; i<tot_codes; i++) { + if ((i&7) == 0) _ftprintf(stderr, _T(" %3d: "), i); + _ftprintf (stderr, _T("%04x(%2d) "), main_p[i].value, main_p[i].size); + if ((i&7)==7 || i==tot_codes-1) _ftprintf(stderr, _T("\n")); + } + + if (huffsize[tot_codes-1] > main_ix_len) { + n = aux_p[0].size; + _ftprintf(stderr, _T("\nAux table: (%d entries of size-code-value)\n"),n); + for (i=0; i<n; i++) { + if ((i&3) == 0) _ftprintf(stderr, _T(" %3d: "), i); + _ftprintf (stderr, _T("%2d-%4x-%3d "), + aux_p[i].size, aux_p[i].code, aux_p[i].value); + if ((i&3)==3 || i==n-1) _ftprintf(stderr, _T("\n")); + } + } + } + #endif + + huff_tbl_p->index_p = index_p; + huff_tbl_p->main_p = main_p; + huff_tbl_p->aux_p = aux_p; + return; + + fatal_error: + assert (0); /* todo: eliminate this assert */ +} + + + +/*____________________________________________________________________________ + | | | + | huff_define_table | Defines the given Huffman table | + |___________________|________________________________________________________| + | | + | Sets the following variables: dc_tbls, ac_tbls. | + |____________________________________________________________________________| +*/ +static void huff_define_table ( + PJDEC_INST g, + BOOL ac, /* defining an AC table? (else DC) */ + UINT id, /* which table is being defined (0-3) */ + const BYTE counts[16], /* number of Huffman codes of each length 1-16 */ + const BYTE values[]) /* values associated with codes of above lengths */ +{ + huff_tbl_t *tbl_p; + + tbl_p = ac ? &(g->ac_tbls[id]) : &(g->dc_tbls[id]); + huff_free_tbl (g,tbl_p); + + DUMP (_T("\nhuff_define_table: ac=%d, id=%d\n"), ac, id, 0); + calc_table (counts, values, + ac ? AC_TBL_INDEX_LEN : DC_TBL_INDEX_LEN, + tbl_p); +} + + + +/****************************************************************************** + ****************************************************************************** + + W I N O G R A D + + + Interface into this section: + + QNORM_TO_INPUT - adjusts precision of scaled DCT value for wino-input + INPUT_PRECISION - scales result of wino_inverse_dct back to pixels + wino_scale_table - scale a quantization table into a Winograd table + wino_inverse_dct - computes inverse DCT using Winograd's algorithm + + ****************************************************************************** + ******************************************************************************/ + + + +#define SHIFT_TRUNC(mvalue,mshift) ((mvalue) >> (mshift)) + +#define QNORM_TO_INPUT(mval) SHIFT_TRUNC(mval, QNORM_PRECISION-INPUT_PRECISION) + +#define QNORM_PRECISION 16 /* prec of q-table scaled by Wino norm constants */ +#define INPUT_PRECISION 5 /* prec of input to inverse-DCT */ +#define CONST_PRECISION 9 /* prec of b1-b5 below */ + +#define b1 724L +#define b2 1338L +#define b3 724L +#define b4 554L +#define b5 392L + +#if 0 +#define CONST_PRECISION 15 + +#define b1 46341L +#define b2 85627L +#define b3 46341L +#define b4 35468L +#define b5 25080L +#endif + + + +/*____________________________________________________________________________ + | | | + | wino_scale_table | Scales a quantization table into a Winograd table | + |__________________|_________________________________________________________| + | | + | Multiplies all the components of the Quantization matrix by the fractional | + | normalization constants as required by the Winograd Transform. The | + | results are fixed-point with QNORM_PRECISION bits of fraction. | + |____________________________________________________________________________| +*/ + +static const float inv_dct_norm[] = { + 0.125000f, 0.173380f, 0.173380f, 0.163320f, + 0.240485f, 0.163320f, 0.146984f, 0.226532f, + 0.226532f, 0.146984f, 0.125000f, 0.203873f, + 0.213388f, 0.203873f, 0.125000f, 0.098212f, + 0.173380f, 0.192044f, 0.192044f, 0.173380f, + 0.098212f, 0.067650f, 0.136224f, 0.163320f, + 0.172835f, 0.163320f, 0.136224f, 0.067650f, + 0.034566f, 0.093833f, 0.128320f, 0.146984f, + 0.146984f, 0.128320f, 0.093833f, 0.034566f, + 0.047944f, 0.088388f, 0.115485f, 0.125000f, + 0.115485f, 0.088388f, 0.047944f, 0.045162f, + 0.079547f, 0.098212f, 0.098212f, 0.079547f, + 0.045162f, 0.040645f, 0.067650f, 0.077165f, + 0.067650f, 0.040645f, 0.034566f, 0.053152f, + 0.053152f, 0.034566f, 0.027158f, 0.036612f, + 0.027158f, 0.018707f, 0.018707f, 0.009558f +}; + +void wino_scale_table ( + long *tbl_p) +{ + const float *fptr; + int i; + + fptr = inv_dct_norm; + for (i=0; i<64; i++) { + *tbl_p = (long ) ((*tbl_p) * (*fptr++) * (1l<<QNORM_PRECISION) + 0.5); + tbl_p += 1; + } +} + + + +/****************************************************************************** + ****************************************************************************** + + D E C O D I N G + + + Interface into this section: + + zero_prior_DC - sets the predicted DC values to zero + decode_MCU - decodes a single Minimum Coded Unit + + ****************************************************************************** + ******************************************************************************/ + + + +static BYTE const zigzag_index_tbl[] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 0, 0, 0, 0, 0, 0, 0, 0, /* extra 16 elements in case */ + 0, 0, 0, 0, 0, 0, 0, 0 /* parse_block overruns */ +}; + + + +/*____________________________________________________________________________ + | | | + | decode_init | inits this section | + |_____________|______________________________________________________________| +*/ +static void decode_init (PJDEC_INST g) +{ + BYTE const *zig_p; + int **block_pp; + + zig_p = zigzag_index_tbl; + for (block_pp=g->block_zz; block_pp<g->block_zz+(64+16); block_pp++) + *block_pp = &(g->block[*zig_p++]); +} + + + +/*____________________________________________________________________________ + | | | + | zero_prior_DC | zeroes the predicted DC values | + |_______________|____________________________________________________________| +*/ +static void zero_prior_DC (PJDEC_INST g) +{ + g->prior_dc[0] = g->prior_dc[1] = g->prior_dc[2] = g->prior_dc[3] = 0; +} + + + +/*____________________________________________________________________________ + | | | + | parse_block | parses an 8x8 block; return data is ready for inverse DCT | + |_____________|______________________________________________________________| + | | + | Return value: TRUE = we parsed a block, | + | FALSE = hit a marker. | + | Output data is put in 'block' array. | + |____________________________________________________________________________| +*/ +static BOOL parse_block ( + PJDEC_INST g, + int comp) /* in: image component number */ +{ + /* FIX_TERM - changes a term (fetched from JPEG file) into an integer. + * The term is in ones-complement, with the sign-bit (and higher bits) + * zeroed. So the leftmost bit within the given size is the opposite of + * the desired sign bit. If that bit is 1, the number is positive, and + * needs no change. If it's 0, we set the higher bits to 1s, and add 1 + * to convert from ones-complement to twos-complement. + * Select the macro below which is fastest on your computer. + */ + + #if 1 + #define FIX_TERM(msize,mterm) { \ + if ((mterm & (1u<<((msize)-1))) == 0) \ + mterm = (mterm | (-1 << (msize))) + 1; \ + } + #endif + + #if 0 + #define FIX_TERM(msize,mterm) { \ + int mask = -1 << msize; \ + if (((mterm<<1) & mask) == 0) \ + mterm += mask + 1; \ + } + #endif + + #if 0 + #define FIX_TERM(msize,mterm) \ + mterm += ((mterm>>(msize-1)) - 1) & ~((1<<msize) - 2); + #endif + + huff_tbl_t *huff_p; + int **block_p; + long *dequant_p; + UINT siz, run, rl_byte; + int dc, ac; + + /* memset (block, 0, 64*sizeof(int)); */ + { + int *block_p, *after_p; + + for (block_p=g->block, after_p=g->block+64; block_p<after_p; ) { + *block_p++ = 0; + *block_p++ = 0; + *block_p++ = 0; + *block_p++ = 0; + *block_p++ = 0; + *block_p++ = 0; + *block_p++ = 0; + *block_p++ = 0; + } + } + + dequant_p = g->quant_tbls[g->which_quant_tbl[comp]]; + block_p = g->block_zz; + + /**************************************/ + /* Decode and dequantize the DC value */ + /**************************************/ + + DUMP (_T("\nStart of block for component %d:\n"), comp, 0, 0); + + huff_p = &(g->dc_tbls[g->which_dc_tbl[comp]]); + DECODE_HUFF (g, huff_p, DC_TBL_INDEX_LEN, siz, hit_marker) + + if (siz == 0) { + DUMP (_T("dc=0, size of dc=0\n"), 0,0,0); + dc = 0; + } else { + READ_BITS_LOAD (g, TRUE, siz, dc, syntax_err) + READ_BITS_ADVANCE (g, siz) + DUMP (_T("dc=%x, size of dc=%d\n"), dc, siz, 0); + FIX_TERM (siz, dc) + } + + dc += g->prior_dc[comp]; + g->prior_dc[comp] = dc; + *(*block_p++) = QNORM_TO_INPUT ((long )*dequant_p++ * dc); + + /*************************************************/ + /* Decode, dezigzag and dequantize the AC values */ + /*************************************************/ + + huff_p = &(g->ac_tbls[g->which_ac_tbl[comp]]); + + /* The two 'hit_marker's below should be 'syntax_error' instead. But + * Chromafax and other software erroneously uses fill 0's instead + * of fill 1's, so we parse a few of those 0's before hitting the + * marker. + */ + + while (TRUE) { + DECODE_HUFF (g, huff_p, AC_TBL_INDEX_LEN, rl_byte, hit_marker); + run = rl_byte >> 4; + siz = rl_byte & 0x0f; + DUMP (_T("rlc: run=%d, size of ac=%d"), run, siz, 0); + + if (siz == 0) { + if (run == 15) { + DUMP (_T(", run of 16\n"), 0,0,0); + block_p += 16; + dequant_p += 16; + } else if (run == 0) { + DUMP (_T(", EOB\n"), 0,0,0); + break; /* hit EOB */ + } else + goto syntax_err; + } else { + block_p += run; + dequant_p += run; + READ_BITS_LOAD (g, TRUE, siz, ac, hit_marker); + READ_BITS_ADVANCE (g, siz); + DUMP (_T(", ac=%x\n"), ac, siz, 0); + FIX_TERM (siz, ac); + *(*block_p++) = QNORM_TO_INPUT ((long )*dequant_p++ * ac); + } + + if (block_p >= g->block_zz+64) { + if (block_p > g->block_zz+64) { + PRINT(_T("parse_block: over 63 AC terms\n"), 0,0); + goto syntax_err; + } + if (! g->fDenali) + break; /* 63rd AC term was non-zero */ + } + } + + return TRUE; + + syntax_err: + PRINT(_T("parse_block: syntax error\n"), 0,0); + longjmp (g->syntax_error, BAD_HUFF_CODE); + + hit_marker: + return FALSE; + + #undef FIX_TERM +} + + + +/*____________________________________________________________________________ + | | | + | handleMarkerInStream | handles a marker in the data-stream | + |______________________|_____________________________________________________| + | | + | Parses EOI, DNL and RST; other markers cause an error (longjmp). | + | For an EOI, g->got_EOI is set to true. | + |____________________________________________________________________________| +*/ +static void handleMarkerInStream (PJDEC_INST g) +{ + BYTE marker; + + marker = mar_get(g); + + if (marker == MARKER_EOI) { + PRINT (_T("handleMarkerInStream: parsed EOI\n"), 0, 0); + g->got_EOI = TRUE; + } else if (marker == MARKER_DNL) { + PRINT (_T("handleMarkerInStream: parsing DNL\n"), 0, 0); + mar_parse_dnl (g); + } else if (g->restart_interval > 0 + && g->restart_cur_mcu == g->restart_interval + && (marker-MARKER_RST0) == g->restart_cur_marker) + { + /* it's a restart, and we expected it at this point in the data */ + PRINT (_T("handleMarkerInStream: parsed expected RST\n"), 0, 0); + g->restart_cur_marker = (g->restart_cur_marker+1) & 7; + g->restart_cur_mcu = 0; + zero_prior_DC (g); + } else { + PRINT (_T("handleMarkerInStream: illegal marker=0x%2.2X\n"), marker, 0); + longjmp (g->syntax_error, BAD_MARKER_DATA); + } +} + + + +/*____________________________________________________________________________ + | | | + | LevelShiftAndRound | Level-shifts, rounds, and outputs pixels in 0..255 | + |____________________|_______________________________________________________| +*/ +static void LevelShiftAndRound (int *inBlock_p, BYTE *outBlock_p) +{ + BYTE *outAfter; + int pixel; + + for (outAfter = outBlock_p + 64; + outBlock_p < outAfter; + outBlock_p++, inBlock_p++) + { + pixel = (*inBlock_p + + ((1<<(INPUT_PRECISION-1)) + (128<<INPUT_PRECISION))) + >> INPUT_PRECISION; + if (pixel>>8 != 0) + pixel = pixel>0 ? 255 : 0; /* clamp to 0 or 255 */ + *outBlock_p = (BYTE) pixel; + } +} + + + +/*____________________________________________________________________________ + | | | + | decode_MCU | Parses a Minimum Coded Unit, and loads pixels into out_rows_ap| + |____________|_______________________________________________________________| + | | + | The pixels are loaded starting at mcus_done (not incremented). | + | This routine handles the restart interval logic, and markers. | + | | + | Returns TRUE if an MCU was parsed, else FALSE. | + |____________________________________________________________________________| +*/ +static BOOL decode_MCU (PJDEC_INST g) +{ + BYTE baPixels[256]; + BYTE *pPixel; + int comp; + int h_block, v_block; + int ul_row, ul_col; + int row; + BYTE **row_pp; + BYTE *row_p; + + for (comp=0; comp<g->num_comps; comp++) { + for (v_block=0; v_block<g->vert_samp_facs[comp]; v_block++) { + for (h_block=0; h_block<g->horiz_samp_facs[comp]; h_block++) { + + /***** parse and inverse-dct an 8x8 block *****/ + + while (! parse_block(g,comp)) { + /* we hit a marker */ + #if 0 + /* The error-check below is commented-out to make us + * more forgiving of unexpected in-data markers, which + * we saw the Genoa color-fax test suite output at the + * end of the file. + */ + if (comp>0 || v_block>0 || h_block>0) + longjmp (g->syntax_error, UNEXPECTED_MARKER); + #endif + handleMarkerInStream(g); + if (g->got_EOI) + return FALSE; /* we're out of data; cannot proceed */ + } + + dct_inverse (g->block); + + /***** compute output pixels *****/ + + LevelShiftAndRound (g->block, baPixels); + + /***** copy block into out_rows_ap *****/ + + ul_row = v_block*8; + ul_col = (g->mcus_done*g->horiz_samp_facs[comp] + h_block) * 8; + row_pp = &(g->out_rows_ap[comp][ul_row]); + pPixel = baPixels; + + for (row=0; row<8; row++) { + row_p = *row_pp++ + ul_col; + /* copy the 8 pixel row quickly, using two dword transfers */ + *(DWORD*) row_p = *(DWORD*) pPixel; + *(DWORD*)(row_p+4) = *(DWORD*)(pPixel+4); + pPixel += 8; + } + } + } + } + + if (g->restart_interval>0 && g->restart_cur_mcu==g->restart_interval) + longjmp (g->syntax_error, NO_RESTART_MARKER); + g->restart_cur_mcu += 1; + + return TRUE; +} + + + +/*____________________________________________________________________________ + | | | + | copy_out_rows | copies a row (or rows) from out_rows_ap to output buffer | + |_______________|____________________________________________________________| + | | + | If output_subsampled is true, we output row data in the same odd order | + | which the ASIC outputs it. | + | | + | View a period as being a group of max_horiz_samp_fac by max_vert_samp_fac | + | samples. (BTW, an MCU contains exactly 8x8 periods.) Divide the entire | + | image into a grid of such periods. The ASIC outputs the periods left to | + | right, top to bottom. It outputs all the data in each period, before | + | moving to the next period. Within a period, it outputs the components in | + | succession; within a component, it outputs the samples left to right, top | + | to bottom. | + | | + | For example: | + | Three components = Y U V | + | Horiz sample factors = 2 1 1 | + | Vert sample factors = 2 1 1 | + | A period is 2x2 samples (i.e., a 2x2 square). | + | Each period contains these samples: YYYYUV. | + | Bytes output: YYYYUV YYYYUV YYYYUV .... | + | | + | In this routine, | + | | + | vert_period = index of which period we're outputting vertically within| + | an MCU (0-7). | + | | + | horiz_period = index of which period we're outputting horizontally; | + | since an MCU has 8 periods, this is 0 .. 8*mcus_per_row.| + |____________________________________________________________________________| +*/ +static void copy_out_rows ( + PJDEC_INST g, + UINT row_index, /* in: index of next row to send within MCU */ + BYTE *outbuf_p, /* out: copied-out row data */ + UINT *n_rows_p, /* out: # of rows copied out */ + UINT *n_bytes_p) /* out: # of bytes output */ +{ + BYTE *out_p; + UINT comp; + UINT vert_period, vert_mod; + UINT row, col; + BYTE *row_p; + + vert_period = row_index / g->max_vert_samp_fac; + vert_mod = row_index % g->max_vert_samp_fac; + + if (! g->output_subsampled) { + + /*******************************************/ + /* Perform duplication to undo subsampling */ + /*******************************************/ + + UINT samp_fac; + UINT horiz_mod; + + for (comp=0; comp<g->num_comps; comp++) { + samp_fac = g->vert_samp_facs[comp]; + out_p = outbuf_p + comp; + row = vert_period*samp_fac + + (vert_mod<samp_fac ? vert_mod : samp_fac-1); + row_p = g->out_rows_ap[comp][row]; + + horiz_mod = 0; + samp_fac = g->horiz_samp_facs[comp]; + + if (samp_fac == g->max_horiz_samp_fac) { + + /***** fast case: copying all pixels, with no duplication *****/ + + if (g->num_comps == 1) { + memcpy (out_p, row_p, g->traits.iPixelsPerRow); + } else { + for (col=0; col<(UINT)g->traits.iPixelsPerRow; col++) { + *out_p = *row_p++; + out_p += g->num_comps; + } + } + + } else if (samp_fac==1 && g->max_horiz_samp_fac==2) { + + /***** fast case: duplicating every other pixel *****/ + + BYTE prev_pix; + for (col=0; col<(UINT)g->traits.iPixelsPerRow; col+=2) { + *out_p = prev_pix = *row_p++; + out_p += g->num_comps; + *out_p = prev_pix; + out_p += g->num_comps; + } + + } else { + + /***** slow general case *****/ + + for (col=0; col<(UINT)g->traits.iPixelsPerRow; col++) { + *out_p = horiz_mod<samp_fac ? *row_p++ : row_p[-1]; + out_p += g->num_comps; + horiz_mod += 1; + if (horiz_mod == g->max_horiz_samp_fac) horiz_mod = 0; + } + } + } + + *n_rows_p = 1; + *n_bytes_p = g->num_comps * g->traits.iPixelsPerRow; + + } else { + + /**********************************************/ + /* Output subsampled data in ASIC's odd order */ + /**********************************************/ + + UINT horiz_period, periods_per_row; + UINT ul_row, ul_col; + BYTE *row_y1_p, *row_y2_p, *row_cb_p, *row_cr_p; + + out_p = outbuf_p; + periods_per_row = g->mcus_per_row * 8; + + if (g->num_comps==1 && g->max_horiz_samp_fac==1 && g->max_vert_samp_fac==1) { + + /***** fast case: one component; just copy the row *****/ + + memcpy (out_p, g->out_rows_ap[0][row_index], g->traits.iPixelsPerRow); + out_p += g->traits.iPixelsPerRow; + + } else if (g->num_comps==3 && + (g->horiz_samp_facs[0]==1 && + g->horiz_samp_facs[1]==1 && + g->horiz_samp_facs[2]==1) && + (g->vert_samp_facs[0]==1 && + g->vert_samp_facs[1]==1 && + g->vert_samp_facs[2]==1)) { + + /***** fast case: color with no subsampling *****/ + + row_y1_p = g->out_rows_ap[0][vert_period]; + row_cb_p = g->out_rows_ap[1][vert_period]; + row_cr_p = g->out_rows_ap[2][vert_period]; + for (col=0; col<(UINT)g->traits.iPixelsPerRow; col++) { + *out_p++ = *row_y1_p++; + *out_p++ = *row_cb_p++; + *out_p++ = *row_cr_p++; + } + + } else if (g->num_comps==3 && + (g->horiz_samp_facs[0]==2 && + g->horiz_samp_facs[1]==1 && + g->horiz_samp_facs[2]==1) && + (g->vert_samp_facs[0]==2 && + g->vert_samp_facs[1]==1 && + g->vert_samp_facs[2]==1)) { + + /***** fast case: 4-1-1 color subsampling *****/ + + row_y1_p = g->out_rows_ap[0][2*vert_period]; + row_y2_p = g->out_rows_ap[0][2*vert_period+1]; + row_cb_p = g->out_rows_ap[1][vert_period]; + row_cr_p = g->out_rows_ap[2][vert_period]; + for (horiz_period=0; horiz_period<periods_per_row; horiz_period++) { + *out_p++ = *row_y1_p++; + *out_p++ = *row_y1_p++; + *out_p++ = *row_y2_p++; + *out_p++ = *row_y2_p++; + *out_p++ = *row_cb_p++; + *out_p++ = *row_cr_p++; + } + + } else { + + /***** slow general case *****/ + + for (horiz_period=0; horiz_period<periods_per_row; horiz_period++) { + for (comp=0; comp<g->num_comps; comp++) { + ul_row = vert_period * g->vert_samp_facs[comp]; + ul_col = horiz_period * g->horiz_samp_facs[comp]; + + for (row=ul_row; row<ul_row+ g->vert_samp_facs[comp]; row++) + for (col=ul_col; col<ul_col+g->horiz_samp_facs[comp]; col++) { + *out_p++ = g->out_rows_ap[comp][row][col]; + } + } + } + } + + *n_rows_p = g->max_vert_samp_fac; + *n_bytes_p = out_p - outbuf_p; + } +} + + + + +/****************************************************************************** + ****************************************************************************** + + E X P O R T E D R O U T I N E S + + ****************************************************************************** + ******************************************************************************/ + + + +/*****************************************************************************\ + * + * jpgDecode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD jpgDecode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PJDEC_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(JDEC_INST), g); + *pXform = g; + memset (g, 0, sizeof(JDEC_INST)); + g->dwValidChk = CHECK_VALUE; + decode_init (g); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD jpgDecode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PJDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD jpgDecode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PJDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + g->output_subsampled = aXformInfo[IP_JPG_DECODE_OUTPUT_SUBSAMPLED].dword; + g->fDenali = aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD jpgDecode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + PJDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwInBufLen = MAX_HEADER_SIZE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD jpgDecode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PJDEC_INST g; + UINT comp; + UINT marker; + UINT row_len, n_rows; + UINT row; + UINT err; + BYTE *p; + + /**************/ + /* Misc. Init */ + /**************/ + + HANDLE_TO_PTR (hXform, g); + + g->rows_done = 0; + g->mcus_done = 0; + g->sending_rows = FALSE; + g->got_EOI = FALSE; + g->got_short_header = FALSE; + read_init (g); + huff_init (g); + zero_prior_DC (g); + g->restart_cur_mcu = 0; + g->restart_cur_marker = 0; + g->dwOutNextPos = 0; + + /********************/ + /* Parse the header */ + /********************/ + + if ((err=setjmp(g->syntax_error)) != 0) { + PRINT (_T("jpeg_decode_parse_header: syntax error = %d\n"), err, 0); + return IP_FATAL_ERROR | IP_INPUT_ERROR; + } + + read_buf_open (g, pbInputBuf); + + if (mar_get(g) != MARKER_SOI) + return IP_FATAL_ERROR | IP_INPUT_ERROR; + + do { + marker = mar_get (g); + mar_parse (g, marker); + if (marker == MARKER_EOI) + return IP_FATAL_ERROR | IP_INPUT_ERROR; + } while (!(marker==MARKER_SOS || g->got_short_header)); + + *pdwInputNextPos = g->dwInNextPos = *pdwInputUsed = read_buf_close (g); + + /* todo: check that all markers arrived */ + + PRINT (_T("jpeg_decode_parse_header: pixels/row=%d, num_rows=%d\n"), + g->traits.iPixelsPerRow, g->traits.lNumRows); + + g->cols_per_mcu = g->max_horiz_samp_fac * 8; + g->rows_per_mcu = g->max_vert_samp_fac * 8; + g->mcus_per_row = (g->traits.iPixelsPerRow + g->cols_per_mcu - 1) / g->cols_per_mcu; + + /*******************************************/ + /* Allocate the row-buffers in out_rows_ap */ + /*******************************************/ + + memset (g->out_rows_ap, 0, sizeof(g->out_rows_ap)); + + for (comp=0; comp<g->num_comps; comp++) { + row_len = g->horiz_samp_facs[comp] * g->mcus_per_row * 8; + n_rows = g->vert_samp_facs[comp] * 8; + + for (row=0; row<n_rows; row++) { + IP_MEM_ALLOC (row_len, p); + g->out_rows_ap[comp][row] = p; + } + } + + /***************/ + /* Return info */ + /***************/ + + *pInTraits = g->traits; + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * jpgDecode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD jpgDecode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PJDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = INBUF_NUM_MCUS * MAX_MCU_SIZE; + *pdwMinOutBufLen = g->num_comps * g->traits.iPixelsPerRow; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD jpgDecode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PJDEC_INST g; + /* The "static" keyword for ret_val and bDecoded is to prevent the + * "variable `' might be clobbered by `longjmp' or `vfork'" warning. */ + static unsigned ret_val; + UINT comp, row, row_len, n_rows, n_bytes, trash; + int iErrorCode; + static BOOL bDecoded; + const BYTE ycc_white[4] = { 255, 128, 128, 255 }; + + HANDLE_TO_PTR (hXform, g); + + *pdwInputUsed = 0; + *pdwOutputThisPos = g->dwOutNextPos; + *pdwOutputUsed = 0; + ret_val = IP_READY_FOR_DATA; + + if (setjmp(g->syntax_error) != 0) { + PRINT (_T("got a syntax error\n"), 0, 0); + *pdwInputNextPos = g->dwInNextPos; + return IP_FATAL_ERROR | IP_INPUT_ERROR; + } + + /**********************/ + /* Parsing input data */ + /**********************/ + + if (! g->sending_rows) { + if (pbInputBuf == NULL) { + + /* We are being told to flush, so we should have consumed the EOI, + * and have discarded any bytes following it. */ + if (! g->got_EOI) { + /* Unexpected end of data: we did not get an EOI */ + ret_val |= IP_INPUT_ERROR; + } + ret_val |= IP_DONE; + + } else if (g->got_EOI) { + + /* Discard bytes after the EOI. We should report an error here, but + * the ASIC+firmware of Denali/Kodiak can send stuff after the EOI. */ + *pdwInputUsed = dwInputAvail; + + } else { + + if (g->mcus_done == 0) { + /* init all row-buffers to white */ + PRINT (_T("initing all row-buffers to white\n"), 0, 0); + for (comp=0; comp<g->num_comps; comp++) { + row_len = g->horiz_samp_facs[comp] * g->mcus_per_row * 8; + n_rows = g->vert_samp_facs[comp] * 8; + + for (row=0; row<n_rows; row++) + memset (g->out_rows_ap[comp][row], ycc_white[comp], row_len); + } + } + + do { + bDecoded = FALSE; + read_buf_open (g, pbInputBuf + *pdwInputUsed); + memcpy(&(g->old_syntax_error), &(g->syntax_error), sizeof(jmp_buf)); + iErrorCode = setjmp(g->syntax_error); + if (iErrorCode == 0) { + bDecoded = decode_MCU(g); + + /* Check for (and handle) a marker (DNL, RST, EOI). + * We need to handle DNL here so the sending_rows state below + * won't output the pad rows on the bottom of the image + */ + if (! g->got_EOI) { + READ_BITS_LOAD (g, FALSE, 8, trash, at_a_marker); + goto no_marker; + at_a_marker: + handleMarkerInStream(g); + no_marker:; + } + } + memcpy(&(g->syntax_error), &(g->old_syntax_error), sizeof(jmp_buf)); + n_bytes = read_buf_close (g); + + *pdwInputUsed += n_bytes; + g->dwInNextPos += n_bytes; + + if (*pdwInputUsed > dwInputAvail) { + /* Parser read past end of input buffer */ + *pdwInputUsed = dwInputAvail; + g->dwInNextPos += dwInputAvail - n_bytes; + /* We will not make this a fatal error because that would + * immediately shut down the remaining xforms in the pipeline. + * Instead, we assume that the input data was truncated, and + * let the pipeline finish up normally so that it'll give a + * valid output file. + */ + ret_val |= IP_INPUT_ERROR; + } else if (iErrorCode != 0) { + /* An error within the data: Make it fatal */ + longjmp (g->syntax_error, iErrorCode); + } else if (g->got_EOI) { + ret_val |= IP_NEW_OUTPUT_PAGE; + /* Note: we should have just done the final MCU in a row, but + * we don't check for this because the firmware of Denali/Kodiak + * can't guarantee it. */ + } + + if (bDecoded) { + g->mcus_done += 1; + + if (g->mcus_done >= g->mcus_per_row) { + PRINT (_T("done with row of MCUs; starting row-sends\n"), 0, 0); + g->sending_rows = TRUE; + g->mcus_done = 0; + } + } + } while (!g->got_EOI && !g->sending_rows && + (dwInputAvail-*pdwInputUsed) >= MAX_MCU_SIZE); + } + } + + /***************************/ + /* Sending the output rows */ + /***************************/ + + if (g->sending_rows) { + if (g->rows_done>=g->traits.lNumRows && g->traits.lNumRows>=0) { + /* we've already output all rows, so discard these */ + g->sending_rows = FALSE; + } else { + copy_out_rows ( + g, + g->rows_done % g->rows_per_mcu, /* in: index of next row to send */ + pbOutputBuf, /* out: copied-out row data */ + &n_rows, /* out: # of rows copied out */ + &n_bytes); /* out: # of bytes output */ + PRINT (_T("copied out %d rows\n"), n_rows, 0); + *pdwOutputUsed = n_bytes; + g->dwOutNextPos += n_bytes; + g->rows_done += n_rows; + + if ((g->rows_done>=g->traits.lNumRows && g->traits.lNumRows>=0) + || (g->rows_done % g->rows_per_mcu)==0) { + g->sending_rows = FALSE; + } + /* n_rows is 1, unless the never-used output_subsampled feature is used */ + if (n_rows > 0) + ret_val |= IP_CONSUMED_ROW | IP_PRODUCED_ROW; + } + } + + *pdwInputNextPos = g->dwInNextPos; + + PRINT (_T("jpeg_decode_convert_row: Returning %04x, in_used=%d\n"), + ret_val, *pdwInputUsed); + return ret_val; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD jpgDecode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * jpgDecode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD jpgDecode_newPage ( + IP_XFORM_HANDLE hXform) +{ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ +} + + + +/*****************************************************************************\ + * + * jpgDecode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD jpgDecode_closeXform (IP_XFORM_HANDLE hXform) +{ + PJDEC_INST g; + BYTE **row_pp, **after_pp, *p; + + HANDLE_TO_PTR (hXform, g); + PRINT (_T("jpeg_decode_close\n"), 0, 0); + + row_pp = &(g->out_rows_ap[0][0]); + after_pp = row_pp + (sizeof(g->out_rows_ap)/sizeof(BYTE *)); + + for ( ; row_pp<after_pp; row_pp++) { + p = *row_pp; + if (p != NULL) { + IP_MEM_FREE (p); + *row_pp = NULL; + } + } + + huff_free_all (g); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecode_getRowCountInfo - Returns information for determining row count + * +\*****************************************************************************/ + +WORD jpgDecode_getRowCountInfo(IP_XFORM_HANDLE hXform, + int *pRcCountup,int *pRcTraits,int *pSofOffset) +{ + PJDEC_INST g; + + HANDLE_TO_PTR (hXform, g); + *pRcCountup=g->rows_done; + *pRcTraits=g->traits.lNumRows; + *pSofOffset=g->rowCountOffset; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgDecodeTbl - Jump-table for decoder + * +\*****************************************************************************/ + +IP_XFORM_TBL jpgDecodeTbl = { + jpgDecode_openXform, + jpgDecode_setDefaultInputTraits, + jpgDecode_setXformSpec, + jpgDecode_getHeaderBufSize, + jpgDecode_getActualTraits, + jpgDecode_getActualBufSizes, + jpgDecode_convert, + jpgDecode_newPage, + jpgDecode_insertedData, + jpgDecode_closeXform +}; + +/* End of File */ + + + + + + + + + + diff --git a/ip/xjpg_enc.c b/ip/xjpg_enc.c new file mode 100644 index 0000000..a65bddb --- /dev/null +++ b/ip/xjpg_enc.c @@ -0,0 +1,2184 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * xjpg_enc.c - Converts raw gray image into a valid JPEG file + * + ***************************************************************************** + * + * Name of Global Jump-Table: + * + * jpgEncodeTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_JPG_ENCODE_QUALITY_FACTORS]: + * Quality factors. Each 1..255 (best..worst), normal=20 or 0. + * If 2nd least significant byte is non-zero, then it is the + * DC factor, and lsb is the AC factor. If 2nd lsb is zero, + * then the lsb is both AC and DC factor. Q factors match PML. + * aXformInfo[IP_JPG_ENCODE_SAMPLE_FACTORS]: + * Sample factors in nibbles: HHHHVVVV, 0 means use defaults. + * aXformInfo[IP_JPG_ENCODE_ALREADY_SUBSAMPLED]: + * Is raw data already subsampled? 0=no, 1=yes. + * aXformInfo[IP_JPG_ENCODE_FOR_DENALI]: + * Output is for Denali? 0=no, 1=yes + * aXformInfo[IP_JPG_ENCODE_OUTPUT_DNL]: + * Output a DNL (Define Number of Lines) marker? 0=no, 1=yes. + * aXformInfo[IP_JPG_ENCODE_FOR_COLOR_FAX]: + * Output an APP1 per the G3 color fax standard? 0=no, 1=yes. + * aXformInfo[IP_JPG_ENCODE_DUMMY_HEADER_LEN]: + * # bytes in dummy header. 0 means output normal header. + * The firmware discards the header in the first raster data + * record. This value is the # data bytes in that record. + * This MUST be zero for JPEG files not being sent to firmware. + * + * The aXformInfo items above may all be set to 0 for typical JPEG files. + * + * For Denali, the JPEG that's output will be changed thusly: + * - An EOB will always follow every 8x8 block + * - A small change in the Huffman tables (no 15-bit codes) + * + * Capabilities and Limitations: + * + * Encodes a standard JPEG file with a JFIF 1.0 marker. + * Will *not* output a non-interleaved file; it's interleaved only. + * If the number of rows was unknown in the input traits, this encoder + * seeks back to the beginning of the file and fills in the row-count. + * A DNL is always output if the always-output-DNL item is set in + * the aXformInfo array above. + * Handles 8-bit gray, or 3-component 24-bit color. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel * must be 1 or 3 same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Jan 1998: Ported to Image Processor module of Windows software. + * Feb 1996: Written for firmware, Mark Overton; + * sections of this code were ported from HP-Labs (Hugh P. Nguyen). + * +\*****************************************************************************/ + +#include <string.h> +#include "hpip.h" +#include "ipdefs.h" +#include "xjpg_dct.h" +#include "xjpg_mrk.h" + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stdout, _T("(jpeg) ") msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + + +#define RUN_OF_16 0xf0 /* RLE code for run-of-16-zeroes */ +#define MAX_HEADER_SIZE 2000 +#define MAX_BLOCKS_IN_MCU 6 +#define MAX_MCU_SIZE (MAX_BLOCKS_IN_MCU*304) + /* max encoded MCU size, plus stuff-bytes */ +#define OUTBUF_NUM_MCUS 2 /* workbuf will be this multiple of max MCU */ + +#define Q_DEFAULT 20 /* default quality-factor */ +#define MONO_FACTORS 0x10001000u /* default mono sample factors */ +#define COLOR_FACTORS 0x21102110u /* default color sample factors */ + +#define CHECK_VALUE 0xAceC0de4U + + +/*____________________________________________________________________________ + | | + | Configuration Variables | + |____________________________________________________________________________| +*/ + +/* Encoding is centered around in_rows_ap. It is indexed by + * [color_component_number][row_number]. + * color_component_number 0 is Y (intensity); mono only has this component. + * + * Each component in in_rows_ap has a height (# rows) equal to the number of + * samples in the MCU, and a width equal to the number of samples in all the + * MCUs in a row. That is, pixels are stored in in_rows_ap with NO REPLICATION. + * So if a component has sample factors of H and V, it will have 8*V rows and + * pixels_in_row*H/max_horiz_sam_fac columns in in_rows_ap. + */ + +typedef struct { + + /**** Configuration ****/ + + BYTE lum_quant[64]; + BYTE chrom_quant[64]; + + int wino_lum_quant[64]; + int wino_lum_quant_thres[64]; + int wino_chrom_quant[64]; + int wino_chrom_quant_thres[64]; + + BOOL input_subsampled; + BOOL fDenali; + BOOL fOutputDNL; + BOOL fOutputG3APP1; + DWORD dwDummyHeaderBytes; + UINT rows_in_mcu; /* # rows & cols in each MCU */ + UINT cols_in_mcu; + UINT mcus_in_row; /* # of MCUs in each row */ + DWORD sample_factors; /* H and V sample factors, one/nibble */ + BYTE horiz_sam_facs[4]; /* horizontal sampling factors */ + BYTE vert_sam_facs [4]; /* vertical sampling factors */ + BYTE max_horiz_sam_fac; /* max sample factors */ + BYTE max_vert_sam_fac; + BYTE dc_q_factor; + BYTE ac_q_factor; + BYTE comps_in_pixel; /* # components per pixel (1 or 3) */ + BYTE whitePixel[4]; /* the value of a white pixel */ + UINT pixels_in_row; + int rows_in_page; /* negative means "unknown" */ + int xRes; /* dots per inch */ + int yRes; + + /**** Writing Bits ****/ + + DWORD wr_bit_buf; + /* Bits to be written to outbuf (accumulated left-to-right). */ + + int wr_bits_avail; + /* Number of bits not yet written in wr_bit_buf (= 32 - number written). */ + + BYTE *wr_outbuf_beg; + /* The beginning of the output buffer. */ + + BYTE *write_buf_next; + /* Next byte in outbuf to be written. */ + + /**** Encoding Blocks ****/ + + int enc_block[64]; /* scratch-pad 8x8 block */ + int *enc_block_zz[64+16]; /* zig-zag ptrs into above block */ + int prior_DC[4]; + + /**** Top Level Control ****/ + + UINT rows_received; + UINT rows_loaded; + UINT mcus_sent; + BOOL loading_rows; + + /**** Miscellaneous ****/ + + IP_IMAGE_TRAITS traits; + BYTE *in_rows_ap[4][32]; /* row-buffers [component][row] */ + BOOL fDidHeader; /* output the header yet? */ + DWORD dwInNextPos; /* next read pos in input file */ + DWORD dwOutNextPos; /* next write pos in output file */ + DWORD dwValidChk; /* struct validity check value */ + +} JENC_INST, *PJENC_INST; + + +/*____________________________________________________________________________ + | | + | Normal Quantization Tables | + |____________________________________________________________________________| +*/ + +#if 0 + +static const BYTE orig_lum_quant[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + + +static const BYTE orig_chrom_quant[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +#endif + + +/*____________________________________________________________________________ + | | + | Zigzag of Normal Quantization Tables | + |____________________________________________________________________________| +*/ + +static const BYTE orig_lum_quant[64] = { + #if 0 + /* these make color fax look better, but break gray copy + * because JPEG is sent to the device, so our tables must + * match those in the firmware + */ + 10, 10, 10, 14, 12, 10, 16, 14, + #else + 16, 11, 12, 14, 12, 10, 16, 14, + #endif + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99 +}; + + +static const BYTE orig_chrom_quant[64] = { + #if 0 + 10, 14, 14, 24, 21, 24, 47, 26, + #else + 17, 18, 18, 24, 21, 24, 47, 26, + #endif + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + + +/*____________________________________________________________________________ + | | | + | codesize_array | Array giving # of bits required to represent the index | + |________________|___________________________________________________________| +*/ + +static const BYTE codesize_array[256] = { + 0, + 1, + 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 +}; + + + +/*____________________________________________________________________________ + | | + | Huffman Tables | + |____________________________________________________________________________| +*/ + +static const BYTE lum_DC_counts[16] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const BYTE lum_DC_values[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; + +static const BYTE chrom_DC_counts[16] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const BYTE chrom_DC_values[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; + +static const BYTE lum_AC_counts[16] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d +}; + +static const BYTE lum_AC_counts_Denali[16] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x00, 0x7e + /* Above, the [01,7d] in the normal table was changed to [00,7e]. + * This alteration eliminates the sole 15-bit code, and yields + * Huffman codes as follows: + * - common codes are 12 bits wide or less, + * - uncommon codes are exactly 16 bits wide, and all those codes + * start with nine '1' bits, leaving seven bits of useful info. + * Denali uses a 4K-entry table for the common codes, and a + * quick lookup for the 7-bit leftover codes. So parsing of all + * codes is simple and fast. + */ +}; + +static const BYTE lum_AC_values[162] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const BYTE chrom_AC_counts[16] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77 +}; + +static const BYTE chrom_AC_values[162] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + + + +/*____________________________________________________________________________ + | | + | Huffman tables output by mk_jpg_huff program | + |____________________________________________________________________________| +*/ + +typedef struct { + WORD code; /* the Huff code to use */ + BYTE size; /* number of bits in above Huff code */ +} huff_elem_t; + + +static const huff_elem_t lum_DC_table[] = { + { 0x0000, 2 }, { 0x0002, 3 }, { 0x0003, 3 }, { 0x0004, 3 }, + { 0x0005, 3 }, { 0x0006, 3 }, { 0x000e, 4 }, { 0x001e, 5 }, + { 0x003e, 6 }, { 0x007e, 7 }, { 0x00fe, 8 }, { 0x01fe, 9 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, }; + + +static const huff_elem_t lum_AC_table[] = { + { 0x000a, 4 }, { 0x0000, 2 }, { 0x0001, 2 }, { 0x0004, 3 }, + { 0x000b, 4 }, { 0x001a, 5 }, { 0x0078, 7 }, { 0x00f8, 8 }, + { 0x03f6, 10 }, { 0xff82, 16 }, { 0xff83, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x000c, 4 }, { 0x001b, 5 }, { 0x0079, 7 }, + { 0x01f6, 9 }, { 0x07f6, 11 }, { 0xff84, 16 }, { 0xff85, 16 }, + { 0xff86, 16 }, { 0xff87, 16 }, { 0xff88, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x001c, 5 }, { 0x00f9, 8 }, { 0x03f7, 10 }, + { 0x0ff4, 12 }, { 0xff89, 16 }, { 0xff8a, 16 }, { 0xff8b, 16 }, + { 0xff8c, 16 }, { 0xff8d, 16 }, { 0xff8e, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003a, 6 }, { 0x01f7, 9 }, { 0x0ff5, 12 }, + { 0xff8f, 16 }, { 0xff90, 16 }, { 0xff91, 16 }, { 0xff92, 16 }, + { 0xff93, 16 }, { 0xff94, 16 }, { 0xff95, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003b, 6 }, { 0x03f8, 10 }, { 0xff96, 16 }, + { 0xff97, 16 }, { 0xff98, 16 }, { 0xff99, 16 }, { 0xff9a, 16 }, + { 0xff9b, 16 }, { 0xff9c, 16 }, { 0xff9d, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007a, 7 }, { 0x07f7, 11 }, { 0xff9e, 16 }, + { 0xff9f, 16 }, { 0xffa0, 16 }, { 0xffa1, 16 }, { 0xffa2, 16 }, + { 0xffa3, 16 }, { 0xffa4, 16 }, { 0xffa5, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007b, 7 }, { 0x0ff6, 12 }, { 0xffa6, 16 }, + { 0xffa7, 16 }, { 0xffa8, 16 }, { 0xffa9, 16 }, { 0xffaa, 16 }, + { 0xffab, 16 }, { 0xffac, 16 }, { 0xffad, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x00fa, 8 }, { 0x0ff7, 12 }, { 0xffae, 16 }, + { 0xffaf, 16 }, { 0xffb0, 16 }, { 0xffb1, 16 }, { 0xffb2, 16 }, + { 0xffb3, 16 }, { 0xffb4, 16 }, { 0xffb5, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f8, 9 }, { 0x7fc0, 15 }, { 0xffb6, 16 }, + { 0xffb7, 16 }, { 0xffb8, 16 }, { 0xffb9, 16 }, { 0xffba, 16 }, + { 0xffbb, 16 }, { 0xffbc, 16 }, { 0xffbd, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f9, 9 }, { 0xffbe, 16 }, { 0xffbf, 16 }, + { 0xffc0, 16 }, { 0xffc1, 16 }, { 0xffc2, 16 }, { 0xffc3, 16 }, + { 0xffc4, 16 }, { 0xffc5, 16 }, { 0xffc6, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01fa, 9 }, { 0xffc7, 16 }, { 0xffc8, 16 }, + { 0xffc9, 16 }, { 0xffca, 16 }, { 0xffcb, 16 }, { 0xffcc, 16 }, + { 0xffcd, 16 }, { 0xffce, 16 }, { 0xffcf, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x03f9, 10 }, { 0xffd0, 16 }, { 0xffd1, 16 }, + { 0xffd2, 16 }, { 0xffd3, 16 }, { 0xffd4, 16 }, { 0xffd5, 16 }, + { 0xffd6, 16 }, { 0xffd7, 16 }, { 0xffd8, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x03fa, 10 }, { 0xffd9, 16 }, { 0xffda, 16 }, + { 0xffdb, 16 }, { 0xffdc, 16 }, { 0xffdd, 16 }, { 0xffde, 16 }, + { 0xffdf, 16 }, { 0xffe0, 16 }, { 0xffe1, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x07f8, 11 }, { 0xffe2, 16 }, { 0xffe3, 16 }, + { 0xffe4, 16 }, { 0xffe5, 16 }, { 0xffe6, 16 }, { 0xffe7, 16 }, + { 0xffe8, 16 }, { 0xffe9, 16 }, { 0xffea, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0xffeb, 16 }, { 0xffec, 16 }, { 0xffed, 16 }, + { 0xffee, 16 }, { 0xffef, 16 }, { 0xfff0, 16 }, { 0xfff1, 16 }, + { 0xfff2, 16 }, { 0xfff3, 16 }, { 0xfff4, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x07f9, 11 }, { 0xfff5, 16 }, { 0xfff6, 16 }, { 0xfff7, 16 }, + { 0xfff8, 16 }, { 0xfff9, 16 }, { 0xfffa, 16 }, { 0xfffb, 16 }, + { 0xfffc, 16 }, { 0xfffd, 16 }, { 0xfffe, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, +}; + + +static const huff_elem_t lum_AC_table_Denali[] = { + { 0x000a, 4 }, { 0x0000, 2 }, { 0x0001, 2 }, { 0x0004, 3 }, + { 0x000b, 4 }, { 0x001a, 5 }, { 0x0078, 7 }, { 0x00f8, 8 }, + { 0x03f6, 10 }, { 0xff81, 16 }, { 0xff82, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x000c, 4 }, { 0x001b, 5 }, { 0x0079, 7 }, + { 0x01f6, 9 }, { 0x07f6, 11 }, { 0xff83, 16 }, { 0xff84, 16 }, + { 0xff85, 16 }, { 0xff86, 16 }, { 0xff87, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x001c, 5 }, { 0x00f9, 8 }, { 0x03f7, 10 }, + { 0x0ff4, 12 }, { 0xff88, 16 }, { 0xff89, 16 }, { 0xff8a, 16 }, + { 0xff8b, 16 }, { 0xff8c, 16 }, { 0xff8d, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003a, 6 }, { 0x01f7, 9 }, { 0x0ff5, 12 }, + { 0xff8e, 16 }, { 0xff8f, 16 }, { 0xff90, 16 }, { 0xff91, 16 }, + { 0xff92, 16 }, { 0xff93, 16 }, { 0xff94, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003b, 6 }, { 0x03f8, 10 }, { 0xff95, 16 }, + { 0xff96, 16 }, { 0xff97, 16 }, { 0xff98, 16 }, { 0xff99, 16 }, + { 0xff9a, 16 }, { 0xff9b, 16 }, { 0xff9c, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007a, 7 }, { 0x07f7, 11 }, { 0xff9d, 16 }, + { 0xff9e, 16 }, { 0xff9f, 16 }, { 0xffa0, 16 }, { 0xffa1, 16 }, + { 0xffa2, 16 }, { 0xffa3, 16 }, { 0xffa4, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007b, 7 }, { 0x0ff6, 12 }, { 0xffa5, 16 }, + { 0xffa6, 16 }, { 0xffa7, 16 }, { 0xffa8, 16 }, { 0xffa9, 16 }, + { 0xffaa, 16 }, { 0xffab, 16 }, { 0xffac, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x00fa, 8 }, { 0x0ff7, 12 }, { 0xffad, 16 }, + { 0xffae, 16 }, { 0xffaf, 16 }, { 0xffb0, 16 }, { 0xffb1, 16 }, + { 0xffb2, 16 }, { 0xffb3, 16 }, { 0xffb4, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f8, 9 }, { 0xff80, 16 }, { 0xffb5, 16 }, + { 0xffb6, 16 }, { 0xffb7, 16 }, { 0xffb8, 16 }, { 0xffb9, 16 }, + { 0xffba, 16 }, { 0xffbb, 16 }, { 0xffbc, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f9, 9 }, { 0xffbd, 16 }, { 0xffbe, 16 }, + { 0xffbf, 16 }, { 0xffc0, 16 }, { 0xffc1, 16 }, { 0xffc2, 16 }, + { 0xffc3, 16 }, { 0xffc4, 16 }, { 0xffc5, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01fa, 9 }, { 0xffc6, 16 }, { 0xffc7, 16 }, + { 0xffc8, 16 }, { 0xffc9, 16 }, { 0xffca, 16 }, { 0xffcb, 16 }, + { 0xffcc, 16 }, { 0xffcd, 16 }, { 0xffce, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x03f9, 10 }, { 0xffcf, 16 }, { 0xffd0, 16 }, + { 0xffd1, 16 }, { 0xffd2, 16 }, { 0xffd3, 16 }, { 0xffd4, 16 }, + { 0xffd5, 16 }, { 0xffd6, 16 }, { 0xffd7, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x03fa, 10 }, { 0xffd8, 16 }, { 0xffd9, 16 }, + { 0xffda, 16 }, { 0xffdb, 16 }, { 0xffdc, 16 }, { 0xffdd, 16 }, + { 0xffde, 16 }, { 0xffdf, 16 }, { 0xffe0, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x07f8, 11 }, { 0xffe1, 16 }, { 0xffe2, 16 }, + { 0xffe3, 16 }, { 0xffe4, 16 }, { 0xffe5, 16 }, { 0xffe6, 16 }, + { 0xffe7, 16 }, { 0xffe8, 16 }, { 0xffe9, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0xffea, 16 }, { 0xffeb, 16 }, { 0xffec, 16 }, + { 0xffed, 16 }, { 0xffee, 16 }, { 0xffef, 16 }, { 0xfff0, 16 }, + { 0xfff1, 16 }, { 0xfff2, 16 }, { 0xfff3, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x07f9, 11 }, { 0xfff4, 16 }, { 0xfff5, 16 }, { 0xfff6, 16 }, + { 0xfff7, 16 }, { 0xfff8, 16 }, { 0xfff9, 16 }, { 0xfffa, 16 }, + { 0xfffb, 16 }, { 0xfffc, 16 }, { 0xfffd, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, +}; + + +static const huff_elem_t chrom_DC_table[] = { + { 0x0000, 2 }, { 0x0001, 2 }, { 0x0002, 2 }, { 0x0006, 3 }, + { 0x000e, 4 }, { 0x001e, 5 }, { 0x003e, 6 }, { 0x007e, 7 }, + { 0x00fe, 8 }, { 0x01fe, 9 }, { 0x03fe, 10 }, { 0x07fe, 11 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, }; + + +static const huff_elem_t chrom_AC_table[] = { + { 0x0000, 2 }, { 0x0001, 2 }, { 0x0004, 3 }, { 0x000a, 4 }, + { 0x0018, 5 }, { 0x0019, 5 }, { 0x0038, 6 }, { 0x0078, 7 }, + { 0x01f4, 9 }, { 0x03f6, 10 }, { 0x0ff4, 12 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x000b, 4 }, { 0x0039, 6 }, { 0x00f6, 8 }, + { 0x01f5, 9 }, { 0x07f6, 11 }, { 0x0ff5, 12 }, { 0xff88, 16 }, + { 0xff89, 16 }, { 0xff8a, 16 }, { 0xff8b, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x001a, 5 }, { 0x00f7, 8 }, { 0x03f7, 10 }, + { 0x0ff6, 12 }, { 0x7fc2, 15 }, { 0xff8c, 16 }, { 0xff8d, 16 }, + { 0xff8e, 16 }, { 0xff8f, 16 }, { 0xff90, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x001b, 5 }, { 0x00f8, 8 }, { 0x03f8, 10 }, + { 0x0ff7, 12 }, { 0xff91, 16 }, { 0xff92, 16 }, { 0xff93, 16 }, + { 0xff94, 16 }, { 0xff95, 16 }, { 0xff96, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003a, 6 }, { 0x01f6, 9 }, { 0xff97, 16 }, + { 0xff98, 16 }, { 0xff99, 16 }, { 0xff9a, 16 }, { 0xff9b, 16 }, + { 0xff9c, 16 }, { 0xff9d, 16 }, { 0xff9e, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003b, 6 }, { 0x03f9, 10 }, { 0xff9f, 16 }, + { 0xffa0, 16 }, { 0xffa1, 16 }, { 0xffa2, 16 }, { 0xffa3, 16 }, + { 0xffa4, 16 }, { 0xffa5, 16 }, { 0xffa6, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0079, 7 }, { 0x07f7, 11 }, { 0xffa7, 16 }, + { 0xffa8, 16 }, { 0xffa9, 16 }, { 0xffaa, 16 }, { 0xffab, 16 }, + { 0xffac, 16 }, { 0xffad, 16 }, { 0xffae, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007a, 7 }, { 0x07f8, 11 }, { 0xffaf, 16 }, + { 0xffb0, 16 }, { 0xffb1, 16 }, { 0xffb2, 16 }, { 0xffb3, 16 }, + { 0xffb4, 16 }, { 0xffb5, 16 }, { 0xffb6, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x00f9, 8 }, { 0xffb7, 16 }, { 0xffb8, 16 }, + { 0xffb9, 16 }, { 0xffba, 16 }, { 0xffbb, 16 }, { 0xffbc, 16 }, + { 0xffbd, 16 }, { 0xffbe, 16 }, { 0xffbf, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f7, 9 }, { 0xffc0, 16 }, { 0xffc1, 16 }, + { 0xffc2, 16 }, { 0xffc3, 16 }, { 0xffc4, 16 }, { 0xffc5, 16 }, + { 0xffc6, 16 }, { 0xffc7, 16 }, { 0xffc8, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f8, 9 }, { 0xffc9, 16 }, { 0xffca, 16 }, + { 0xffcb, 16 }, { 0xffcc, 16 }, { 0xffcd, 16 }, { 0xffce, 16 }, + { 0xffcf, 16 }, { 0xffd0, 16 }, { 0xffd1, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f9, 9 }, { 0xffd2, 16 }, { 0xffd3, 16 }, + { 0xffd4, 16 }, { 0xffd5, 16 }, { 0xffd6, 16 }, { 0xffd7, 16 }, + { 0xffd8, 16 }, { 0xffd9, 16 }, { 0xffda, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01fa, 9 }, { 0xffdb, 16 }, { 0xffdc, 16 }, + { 0xffdd, 16 }, { 0xffde, 16 }, { 0xffdf, 16 }, { 0xffe0, 16 }, + { 0xffe1, 16 }, { 0xffe2, 16 }, { 0xffe3, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x07f9, 11 }, { 0xffe4, 16 }, { 0xffe5, 16 }, + { 0xffe6, 16 }, { 0xffe7, 16 }, { 0xffe8, 16 }, { 0xffe9, 16 }, + { 0xffea, 16 }, { 0xffeb, 16 }, { 0xffec, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x3fe0, 14 }, { 0xffed, 16 }, { 0xffee, 16 }, + { 0xffef, 16 }, { 0xfff0, 16 }, { 0xfff1, 16 }, { 0xfff2, 16 }, + { 0xfff3, 16 }, { 0xfff4, 16 }, { 0xfff5, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x03fa, 10 }, { 0x7fc3, 15 }, { 0xfff6, 16 }, { 0xfff7, 16 }, + { 0xfff8, 16 }, { 0xfff9, 16 }, { 0xfffa, 16 }, { 0xfffb, 16 }, + { 0xfffc, 16 }, { 0xfffd, 16 }, { 0xfffe, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, +}; + + + +/****************************************************************************** + ****************************************************************************** + + WRITING-BITS SECTION + + ****************************************************************************** + ****************************************************************************** + + + Interface into this section: + + write_init - inits this section + write_buf_open - we are being given a (new) output buffer + write_buf_close - done with current output buffer; return # bytes sent + write_buf_next - (a var) the next byte to write in the output buffer + WRITE_BITS_OPEN - setup code for WRITE_BITS + WRITE_BITS - outputs bits (fast) + WRITE_BITS_HIZERO - outputs bits (faster) assuming higher-order bits are 0 + WRITE_BITS_CLOSE - teardown code for WRITE_BITS + write_bits_flush - flushes the bit-cache + + ******************************************************************************/ + + + +#define INITIAL_BITS_AVAIL (8*sizeof(DWORD)) + /* Number of bits in wr_bit_buf, which is the # of bits in a DWORD . */ + + +/*____________________________________________________________________________ + | | | + | PUT_BYTE_STUFF | Puts byte into outbuf, with byte-stuffing as needed | + |________________|___________________________________________________________| + | | + | A 0xff byte within bit-data is always followed by a 0x00 byte to | + | distinguish it from markers, which are 0xff followed by a non-zero byte. | + | Therefore, PUT_BYTE_STUFF should never be used to write markers. | + |____________________________________________________________________________| +*/ +#define PUT_BYTE_STUFF(g, pbs_byte_expression) { \ + BYTE pbs_byte; \ + \ + pbs_byte = (BYTE)(pbs_byte_expression); \ + *(g->write_buf_next)++ = pbs_byte; \ + if (pbs_byte == (BYTE)0xff) \ + *(g->write_buf_next)++ = 0; \ +} + + + +/*____________________________________________________________________________ + | | | + | write_init | Inits this package, and registers the "write bytes" callback | + |____________|_______________________________________________________________| +*/ +static void write_init (PJENC_INST g) +{ + g->wr_bits_avail = INITIAL_BITS_AVAIL; + g->wr_bit_buf = 0; +} + + + +/*____________________________________________________________________________ + | | | + | write_buf_open | We are being given a (new) buffer to receive output | + |________________|___________________________________________________________| + | | + | This routine records the location of the new output buffer. | + |____________________________________________________________________________| +*/ +static void write_buf_open (PJENC_INST g, BYTE *buf_p) +{ + g->wr_outbuf_beg = g->write_buf_next = buf_p; +} + + + +/*____________________________________________________________________________ + | | | + | write_buf_close | We are done with the current output buffer | + |_________________|__________________________________________________________| + | | + | This function returns # bytes written to the output buffer | + |____________________________________________________________________________| +*/ +static int write_buf_close (PJENC_INST g) +{ + return g->write_buf_next - g->wr_outbuf_beg; +} + + + +/*____________________________________________________________________________ + | | | + | WRITE_BITS | Writes the given number of bits | + |____________|_______________________________________________________________| + | | + | WRITE_BITS_HIZERO assumes that the higher-order bits are all zero. | + | WRITE_BITS masks them out for you. | + |____________________________________________________________________________| +*/ + +#define WRITE_BITS_OPEN(g) \ + int tmp_bits_avail = g->wr_bits_avail; \ + DWORD tmp_bit_buf = g->wr_bit_buf; + + +#define WRITE_BITS_HIZERO(g,wb_value,wb_nbits) { \ + DWORD bits_loc; \ + int length_loc; \ + \ + length_loc = (int)(wb_nbits); \ + bits_loc = (DWORD)(wb_value); \ + \ + if (length_loc > tmp_bits_avail) { \ + do { \ + PUT_BYTE_STUFF (g, tmp_bit_buf >> 24) \ + tmp_bit_buf <<= 8; \ + tmp_bits_avail += 8; \ + } while (tmp_bits_avail <= 24); \ + } \ + \ + tmp_bits_avail -= length_loc; \ + tmp_bit_buf |= bits_loc << tmp_bits_avail; \ +} + + +#define WRITE_BITS(g,wb_value,wb_nbits) { \ + int len_loc; \ + \ + len_loc = (int)(wb_nbits); \ + WRITE_BITS_HIZERO (g, (DWORD )(wb_value) & ((1ul<<len_loc)-1), len_loc) \ +} + + +#define WRITE_BITS_CLOSE(g) { \ + g->wr_bits_avail = tmp_bits_avail; \ + g->wr_bit_buf = tmp_bit_buf; \ +} + + + +/*____________________________________________________________________________ + | | | + | write_bits_flush | Writes any buffered bits, with buffering via outbuf | + |__________________|_________________________________________________________| + | | + | Bits are right-padded with ones if necessary to a byte-boundary. | + |____________________________________________________________________________| +*/ +static void write_bits_flush (PJENC_INST g) +{ + int bits_used; + + if (g->wr_bits_avail != INITIAL_BITS_AVAIL) { + /* JPEG wants pad-bits to be 1's */ + g->wr_bit_buf |= ((DWORD )1 << g->wr_bits_avail) - 1; + + for (bits_used = INITIAL_BITS_AVAIL - g->wr_bits_avail; + bits_used > 0; + bits_used -= 8) { + PUT_BYTE_STUFF (g, g->wr_bit_buf >> 24) + g->wr_bit_buf <<= 8; + } + + g->wr_bits_avail = INITIAL_BITS_AVAIL; + g->wr_bit_buf = 0; + } +} + + + +/****************************************************************************** + ****************************************************************************** + + MARKERS SECTION + + ****************************************************************************** + ******************************************************************************/ + + + +/*____________________________________________________________________________ + | | + | Macros | + |____________________________________________________________________________| +*/ + + +#define PUT_MARKER(g,marker) \ +do { \ + *(g->write_buf_next)++ = 0xff; \ + *(g->write_buf_next)++ = (BYTE)marker; \ +} while (0) + + +#define PUT_INT(g,value) \ +do { \ + unsigned loc_val = (unsigned)(value); \ + *(g->write_buf_next)++ = loc_val >> 8; \ + *(g->write_buf_next)++ = loc_val & 0xff; \ +} while (0) + + + +/*____________________________________________________________________________ + | | | + | em_write_SOI | Writes Start-Of-Image marker | + |______________|_____________________________________________________________| +*/ +static void em_write_SOI (PJENC_INST g) +{ + PUT_MARKER(g, MARKER_SOI); +} + + +/*____________________________________________________________________________ + | | | + | em_write_EOI | Writes End-Of-Image marker | + |______________|_____________________________________________________________| +*/ +static void em_write_EOI (PJENC_INST g) +{ + PUT_MARKER(g, MARKER_EOI); +} + + +/*____________________________________________________________________________ + | | | + | em_write_JFIF_APP0 | Writes Application marker for JFIF 1,0 | + |____________________|_______________________________________________________| +*/ +static void em_write_JFIF_APP0 ( + PJENC_INST g, + int xRes, /* X resolution of image (number of pixels per inch) */ + int yRes) /* Y resolution of image (number of pixels per inch) */ +{ + PUT_MARKER (g, MARKER_APP+0); + PUT_INT (g, 16); /* the length */ + + /*** ID ***/ + *(g->write_buf_next)++ = 'J'; + *(g->write_buf_next)++ = 'F'; + *(g->write_buf_next)++ = 'I'; + *(g->write_buf_next)++ = 'F'; + *(g->write_buf_next)++ = '\0'; + + /*** Version 1.0 ***/ + *(g->write_buf_next)++ = 0x01; + *(g->write_buf_next)++ = 0x00; + + /*** Units (dots per inch) ***/ + *(g->write_buf_next)++ = 0x01; + + PUT_INT(g, xRes); + PUT_INT(g, yRes); + + /*** Thumbnail X and Y ***/ + *(g->write_buf_next)++ = 0x00; + *(g->write_buf_next)++ = 0x00; +} + + +/*____________________________________________________________________________ + | | | + | em_write_G3_APP1 | Writes Application marker for G3 color fax standard | + |__________________|_________________________________________________________| +*/ +static void em_write_G3_APP1 ( + PJENC_INST g, + int res) /* resolution of image (number of pixels per inch) */ +{ + res = ((res+50)/100)*100; /* round res to the nearest 100 */ + + PUT_MARKER (g, MARKER_APP+1); + PUT_INT (g, 12); /* the length */ + + /*** ID ***/ + *(g->write_buf_next)++ = 'G'; + *(g->write_buf_next)++ = '3'; + *(g->write_buf_next)++ = 'F'; + *(g->write_buf_next)++ = 'A'; + *(g->write_buf_next)++ = 'X'; + *(g->write_buf_next)++ = 0; + + PUT_INT (g, 1994); /* version is year the std was approved */ + PUT_INT (g, res); /* finally, the DPI */ +} + + +/*____________________________________________________________________________ + | | | + | em_write_SOF | Writes Start-Of-Frame marker | + |______________|_____________________________________________________________| +*/ +static void em_write_SOF ( + PJENC_INST g, + int width, /* width of image (number of pixels per row) */ + int height, /* height of image (number of rows) */ + int ncomps, /* # of color components; 1 is gray, 3 is color */ + BYTE h_sam_facs[], /* horizontal sampling factors */ + BYTE v_sam_facs[]) /* vertical sampling factors */ +{ + int i; + + PUT_MARKER(g, MARKER_SOF0); + PUT_INT(g, 8 + ncomps*3); /* the length */ + *(g->write_buf_next)++ = 8; + PUT_INT(g, height); + PUT_INT(g, width); + *(g->write_buf_next)++ = ncomps; + + for (i=0; i<ncomps; i++) { + *(g->write_buf_next)++ = i; + *(g->write_buf_next)++ = h_sam_facs[i]<<4 | v_sam_facs[i]; + *(g->write_buf_next)++ = (i==0 ? 0 : 1); + } +} + + +/*____________________________________________________________________________ + | | | + | em_write_DQT | Writes Define-Quantization-Table marker | + |______________|_____________________________________________________________| +*/ +static void em_write_DQT ( + PJENC_INST g, + int precision, /* 0 = 8-bit, 1 = 16-bit */ + int ident, /* which table, 0-3 */ + BYTE elements[64]) +{ + PUT_MARKER(g, MARKER_DQT); + PUT_INT(g, 67); /* the length */ + *(g->write_buf_next)++ = (precision << 4) + ident; + memcpy (g->write_buf_next, elements, 64); + g->write_buf_next += 64; +} + + +/*____________________________________________________________________________ + | | | + | em_write_DHTs | Writes Define-Huffman-Tables marker | + |_______________|____________________________________________________________| +*/ +static void em_write_DHTs ( + PJENC_INST g, + int ntables, /* number of tables */ + BYTE hclass[], /* 0 = DC or lossless table, 1 = AC table */ + BYTE ident[], /* 0-3 = which Huffman table this is */ + const BYTE *counts[], /* # Huffman codes of lengths 1-16 */ + const BYTE *huffval[]) /* list of associated values */ +{ + int nvals, i, j; + + nvals = 0; + for (i=0; i<ntables; i++) { + for (j=0; j<16; j++) + nvals += counts[i][j]; + } + + PUT_MARKER(g, MARKER_DHT); + PUT_INT(g, 2 + 17*ntables + nvals); /* the length */ + + for (i=0; i<ntables; i++) { + for (nvals=j=0; j<16; j++) + nvals += counts[i][j]; + + *(g->write_buf_next)++ = hclass[i]<<4 | ident[i]; + for (j=0; j<16; j++) *(g->write_buf_next)++ = counts[i][j]; + for (j=0; j<nvals; j++) *(g->write_buf_next)++ = huffval[i][j]; + } +} + + +/*____________________________________________________________________________ + | | | + | em_write_SOS | Writes Start-Of-Scan marker | + |______________|_____________________________________________________________| +*/ +static void em_write_SOS ( + PJENC_INST g, + int ncomps) /* number of color components; 1 (gray) or 3 (color) */ +{ + int i; + + PUT_MARKER(g, MARKER_SOS); + PUT_INT(g, 6 + 2*ncomps); /* the length */ + *(g->write_buf_next)++ = ncomps; + + for (i=0; i<ncomps; i++) { + *(g->write_buf_next)++ = i; + *(g->write_buf_next)++ = (i==0 ? 0x00 : 0x11); + } + + *(g->write_buf_next)++ = 0; + *(g->write_buf_next)++ = 63; + *(g->write_buf_next)++ = 0; +} + + +/*____________________________________________________________________________ + | | | + | em_write_DNL | Writes Define-Number-of-Lines marker | + |______________|_____________________________________________________________| +*/ +static void em_write_DNL ( + PJENC_INST g, + int nlines) /* number of lines (raster rows) in the JPEG file */ +{ + PUT_MARKER(g, MARKER_DNL); + PUT_INT(g, 4); /* the length */ + PUT_INT(g, nlines); +} + + + +/*____________________________________________________________________________ + | | | + | em_write_short_header | Writes a short non-standard header | + |_______________________|____________________________________________________| + | | + | This header is also output by the firmware, and is only in this | + | JPEG encoder for testing purposes. | + |____________________________________________________________________________| +*/ +static void em_write_short_header ( + PJENC_INST g, + UINT rows_in_page, + UINT pixels_in_row, + UINT xRes, + UINT yRes, + UINT dc_q_factor, + UINT ac_q_factor, + UINT comps_in_pixel, + DWORD sample_factors) +{ + PUT_MARKER (g, MARKER_SHORT_HEADER); + PUT_INT (g, 18); /* the length */ + PUT_INT (g, rows_in_page); + PUT_INT (g, pixels_in_row); + PUT_INT (g, xRes); + PUT_INT (g, yRes); + *(g->write_buf_next)++ = ac_q_factor * 5 / 2; + *(g->write_buf_next)++ = comps_in_pixel; + PUT_INT (g, sample_factors >> 16); /* horiz sample factors */ + PUT_INT (g, sample_factors & 0x0000ffffu); /* vert sample factors */ + *(g->write_buf_next)++ = dc_q_factor * 5 / 2; + *(g->write_buf_next)++ = 0; /* reserved for future use */ +} + + + +/****************************************************************************** + ****************************************************************************** + + WINOGRAD DCT SECTION + + ****************************************************************************** + ******************************************************************************/ + + + +#define BITS_IN_DCT_FRAC 15 +#define ROUND(x) (((x)+(1l<<(BITS_IN_DCT_FRAC-1))) >> BITS_IN_DCT_FRAC) + + +/*____________________________________________________________________________ + | | | + | As table | The As multiplication constants used in the Winograd transform | + |__________|_________________________________________________________________| + | | + | These have 15 bits of fraction. | + |____________________________________________________________________________| +*/ +#define a1 23170L +#define a2 17734L +#define a3 23170L +#define a4 42813L +#define a5 12540L + + +/*____________________________________________________________________________ + | | | + | wino_norm_tbl | DCT scale-factors; this table has been zigzagged | + |_______________|____________________________________________________________| +*/ +/* todo: do this in fixed-point (see image_proc.c in firmware) */ +static float const wino_norm_tbl[] = { + 0.125000f, 0.090120f, 0.090120f, 0.095671f, + 0.064973f, 0.095671f, 0.106304f, 0.068975f, + 0.068975f, 0.106304f, 0.125000f, 0.076641f, + 0.073223f, 0.076641f, 0.125000f, 0.159095f, + 0.090120f, 0.081361f, 0.081361f, 0.090120f, + 0.159095f, 0.230970f, 0.114701f, 0.095671f, + 0.090404f, 0.095671f, 0.114701f, 0.230970f, + 0.453064f, 0.166520f, 0.121766f, 0.106304f, + 0.106304f, 0.121766f, 0.166520f, 0.453064f, + 0.326641f, 0.176777f, 0.135299f, 0.125000f, + 0.135299f, 0.176777f, 0.326641f, 0.346760f, + 0.196424f, 0.159095f, 0.159095f, 0.196424f, + 0.346760f, 0.385299f, 0.230970f, 0.202489f, + 0.230970f, 0.385299f, 0.453064f, 0.293969f, + 0.293969f, 0.453064f, 0.576641f, 0.426777f, + 0.576641f, 0.837153f, 0.837153f, 1.642134f +}; + + +/*____________________________________________________________________________ + | | | + | scale_for_wino | Computes quant-table and threshold-table for Wino's DCT | + |________________|___________________________________________________________| +*/ +static void scale_for_wino ( + BYTE *in, /* in: regular quantization table */ + int *out, /* out: winograd quantization table */ + int *thresh) /* out: threshold table */ +{ + #define FIX(x) ((long) ((x)*(1l<<BITS_IN_DCT_FRAC) + 0.5)) + float const *fptr; + int i, q; + + /* Note that in order for FIX(fptr[i]/in[i]) to fit inside 16-bit signed + * int, (fptr[i]/in[i] < 1) => (in[i] > fptr[i]) (1). Since in[i] >= 1, + * and (fptr[i] < 1) for i = 0 to 62, (1) is true for i = 0 to 62. For + * i = 63, (1) is true only when in[63] >= 2 + */ + if (in[63] < 2) in[63] = 2; + + fptr = wino_norm_tbl; + for (i=0; i<64; i++) { + q = FIX (*fptr++ / (float)(*in++)); + *out++ = q; + if (q == 0) *thresh++ = 32767; + else *thresh++ = (1 << (BITS_IN_DCT_FRAC-1)) / q; + } +} + + + +/****************************************************************************** + ****************************************************************************** + + E N C O D I N G + + ****************************************************************************** + ******************************************************************************/ + + + +static const BYTE zigzag_index_table[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + + +#define WRITE_HUFF_CODE(g,val,huffman) { \ + int whc_val = (int)(val); \ + WRITE_BITS_HIZERO (g, huffman[whc_val].code, huffman[whc_val].size) \ +} + + + +static void zero_prior_DC (PJENC_INST g) +{ + g->prior_DC[0] = g->prior_DC[1] = g->prior_DC[2] = g->prior_DC[3] = 0; +} + + + +/*____________________________________________________________________________ + | | | + | encode_init | Inits this section | + |_____________|______________________________________________________________| +*/ +static void encode_init (PJENC_INST g) +{ + BYTE const *zig_p; + int **block_pp; + + zig_p = zigzag_index_table; + for (block_pp=g->enc_block_zz; block_pp<g->enc_block_zz+(64+16); block_pp++) + *block_pp = &(g->enc_block[*zig_p++]); +} + + + +/*____________________________________________________________________________ + | | | + | encode_block | Encodes an 8x8 block of pixels into Huffman data | + |______________|_____________________________________________________________| + | | + | The input data is variable 'enc_block' via 'enc_block_zz'. | + | | + | The data to be quantized is compared to thresh to determine if the | + | quantized data will be zero. In most cases, this is true, thus | + | eliminating the quantization steps (multiplication + add + shift). | + |____________________________________________________________________________| +*/ +static void encode_block ( + PJENC_INST g, + int comp, /* image component number */ + const huff_elem_t *dc_huff_p, + const huff_elem_t *ac_huff_p, + int *quant_p, + int *thresh_p) +{ + int **block_p; + int i, data, absdata, run, siz; + int diff, absdiff; + WRITE_BITS_OPEN(g) + + /************************************/ + /* Quantize and Encode DC component */ + /************************************/ + + thresh_p++; + block_p = g->enc_block_zz; + data = ROUND ((long)*(*block_p++) * (*quant_p++)); + absdiff = diff = data - g->prior_DC[comp]; + if (absdiff < 0) absdiff = -absdiff; + g->prior_DC[comp] = data; + + if (absdiff < 256) siz = codesize_array[absdiff]; + else siz = codesize_array[absdiff>>8] + 8; + + WRITE_HUFF_CODE (g, siz, dc_huff_p) + WRITE_BITS (g, diff<0 ? diff-1 : diff, siz) + + /************************************************/ + /* Quantize, Zigzag, and Encode AC coefficients */ + /************************************************/ + + run = 0; + + for (i=63; i>0; i--) /* do 63 times... */ + { + absdata = data = *(*block_p++); + if (absdata < 0) absdata = -absdata; + + if (absdata <= *thresh_p++) { + /* quantization would be zero */ + quant_p++; + run++; /* increment run-length of zeroes */ + } + else /* need to quantize */ + { + while (run >= 16) { + WRITE_HUFF_CODE (g, RUN_OF_16, ac_huff_p) + run -= 16; + } + + absdata = ROUND ((DWORD )absdata * (DWORD )(*quant_p++)); + if (absdata < 256) siz = codesize_array[absdata]; + else siz = codesize_array[absdata>>8] + 8; + + /* output the RLE code, and the AC term */ + WRITE_HUFF_CODE (g, (run<<4) + siz, ac_huff_p) + WRITE_BITS (g, data<0 ? ~absdata : absdata, siz) + run = 0; + } + } + + if (run>0 || g->fDenali) + WRITE_HUFF_CODE (g, 0, ac_huff_p) /* output EOB code */ + + WRITE_BITS_CLOSE(g) +} + + + +/*____________________________________________________________________________ + | | | + | encode_MCU | encodes the given MCU from the input rows in in_rows_ap | + |____________|_______________________________________________________________| +*/ +static void encode_MCU ( + PJENC_INST g, + UINT mcu_index) +{ + int *block_p; + UINT comp; + UINT h_block, v_block; + UINT ul_row, ul_col; + UINT row; + BYTE **row_pp; + BYTE *row_p; + + for (comp=0; comp<g->comps_in_pixel; comp++) { + for (v_block=0; v_block<g->vert_sam_facs[comp]; v_block++) { + for (h_block=0; h_block<g->horiz_sam_facs[comp]; h_block++) { + + /***** level-shift and copy the block into 'enc_block' *****/ + + ul_row = v_block*8; + ul_col = (mcu_index*g->horiz_sam_facs[comp] + h_block) * 8; + row_pp = &(g->in_rows_ap[comp][ul_row]); + block_p = g->enc_block; + + for (row=0; row<8; row++) { + row_p = *row_pp++ + ul_col; + + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p++ - 128; + *block_p++ = (int)(UINT)*row_p - 128; + } + + dct_forward (g->enc_block); + + /***** encode and output the block *****/ + + if (comp == 0) { + encode_block (g, comp, lum_DC_table, g->fDenali ? lum_AC_table_Denali : lum_AC_table, + g->wino_lum_quant, g->wino_lum_quant_thres); + } else { + encode_block (g, comp, chrom_DC_table, chrom_AC_table, + g->wino_chrom_quant, g->wino_chrom_quant_thres); + } + } + } + } +} + + + +/*____________________________________________________________________________ + | | | + | copy_in_rows | copies a row (or rows) from input buffer to in_rows_ap | + |______________|_____________________________________________________________| + | | + | If input_subsampled is true, the input row data must be in the same odd | + | order which the ASIC outputs it. | + | | + | View a period as being a group of max_horiz_sam_fac by max_vert_sam_fac | + | samples. (BTW, an MCU contains exactly 8x8 periods.) Divide the entire | + | image into a grid of such periods. The ASIC outputs the periods left to | + | right, top to bottom. It outputs all the data in each period, before | + | moving to the next period. Within a period, it outputs the components in | + | succession; within a component, it outputs the samples left to right, top | + | to bottom. | + | | + | For example: | + | Three components = Y U V | + | Horiz sample factors = 2 1 1 | + | Vert sample factors = 2 1 1 | + | A period is 2x2 samples (i.e., a 2x2 square). | + | Each period contains these samples: YYYYUV. | + | Bytes output: YYYYUV YYYYUV YYYYUV .... | + | | + | In this routine, | + | | + | vert_period = index of which period we're inputting vertically within | + | an MCU (0-7). | + | | + | horiz_period = index of which period we're inputting horizontally; | + | since an MCU has 8 periods, this is 0 .. 8*mcus_in_row. | + |____________________________________________________________________________| +*/ +static void copy_in_rows ( + PJENC_INST g, + UINT row_index, /* in: index of next row to get within MCU */ + BYTE *inbuf_p, /* in: input row data */ + UINT *n_rows_p, /* out: # of rows copied in */ + UINT *n_bytes_p) /* out: # of bytes copied in */ +{ + BYTE *in_p; + UINT comp; + UINT vert_period, vert_mod; + + vert_period = row_index / g->max_vert_sam_fac; + vert_mod = row_index % g->max_vert_sam_fac; + + if (!g->input_subsampled || (g->max_vert_sam_fac==1 && g->max_horiz_sam_fac==1)) + { + /********************************************************/ + /* Perform subsampling (or copying of non-sampled data) */ + /********************************************************/ + + BYTE *row_p; + UINT sam_fac; + UINT col, horiz_mod; + UINT inc; + + for (comp=0; comp<g->comps_in_pixel; comp++) { + sam_fac = g->vert_sam_facs[comp]; + + if (vert_mod < sam_fac) { + in_p = inbuf_p + comp; + row_p = g->in_rows_ap[comp][vert_period*sam_fac + vert_mod]; + horiz_mod = 0; + sam_fac = g->horiz_sam_facs[comp]; + + if (sam_fac == g->max_horiz_sam_fac) { + + /***** fast case: copying all pixels *****/ + + if (g->comps_in_pixel == 1) { + memcpy (row_p, in_p, g->pixels_in_row); + } else { + for (col=0; col<g->pixels_in_row; col+=4) { + *row_p++ = *in_p; in_p += g->comps_in_pixel; + *row_p++ = *in_p; in_p += g->comps_in_pixel; + *row_p++ = *in_p; in_p += g->comps_in_pixel; + *row_p++ = *in_p; in_p += g->comps_in_pixel; + } + } + + } else if (sam_fac==1 && g->max_horiz_sam_fac==2) { + + /***** fast case: copying every other pixel *****/ + + inc = 2 * g->comps_in_pixel; + for (col=0; col<g->pixels_in_row; col+=8) { + *row_p++ = *in_p; in_p += inc; + *row_p++ = *in_p; in_p += inc; + *row_p++ = *in_p; in_p += inc; + *row_p++ = *in_p; in_p += inc; + } + + } else { + + /***** slow general case *****/ + + for (col=0; col<g->pixels_in_row; col++) { + if (horiz_mod < sam_fac) *row_p++ = *in_p; + in_p += g->comps_in_pixel; + horiz_mod += 1; + if (horiz_mod == g->max_horiz_sam_fac) horiz_mod = 0; + } + } + } + } + + *n_rows_p = 1; + *n_bytes_p = g->comps_in_pixel * g->pixels_in_row; + + } else { + + /************************************************/ + /* Already subsampled (in the ASIC's odd order) */ + /************************************************/ + + UINT horiz_period, periods_in_row; + UINT ul_row, ul_col, row, col; + BYTE *row_y1_p, *row_y2_p, *row_cb_p, *row_cr_p; + + in_p = inbuf_p; + periods_in_row = g->mcus_in_row * 8; + + if (g->sample_factors == 0x21102110u) { /* fast case: 4-1-1 subsampling */ + row_y1_p = g->in_rows_ap[0][2*vert_period]; + row_y2_p = g->in_rows_ap[0][2*vert_period+1]; + row_cb_p = g->in_rows_ap[1][vert_period]; + row_cr_p = g->in_rows_ap[2][vert_period]; + for (horiz_period=0; horiz_period<periods_in_row; horiz_period++) { + *row_y1_p++ = *in_p++; + *row_y1_p++ = *in_p++; + *row_y2_p++ = *in_p++; + *row_y2_p++ = *in_p++; + *row_cb_p++ = *in_p++; + *row_cr_p++ = *in_p++; + } + } else { /* slow general case */ + + for (horiz_period=0; horiz_period<periods_in_row; horiz_period++) { + for (comp=0; comp<g->comps_in_pixel; comp++) { + ul_row = vert_period * g->vert_sam_facs[comp]; + ul_col = horiz_period * g->horiz_sam_facs[comp]; + + for (row=ul_row; row<ul_row+ g->vert_sam_facs[comp]; row++) + for (col=ul_col; col<ul_col+g->horiz_sam_facs[comp]; col++) { + g->in_rows_ap[comp][row][col] = *in_p++; + } + } + } + } + + *n_rows_p = g->max_vert_sam_fac; + *n_bytes_p = in_p - inbuf_p; + } +} + + +/****************************************************************************** + ****************************************************************************** + + EXPORTED ROUTINES + + ****************************************************************************** + ******************************************************************************/ + + + +/*____________________________________________________________________________ + | | | + | scale_q_table | scales a q-table according to the q-factors | + |_______________|____________________________________________________________| +*/ +static void scale_q_table ( + UINT dc_q_factor, + UINT ac_q_factor, + const BYTE *in, + BYTE *out) +{ + #define FINAL_DC_INDEX 9 + + UINT i, val; + UINT q; + + q = dc_q_factor; + + for (i=0; i<64; i++) { + val = ((*in++)*q + Q_DEFAULT/2) / Q_DEFAULT; + if (val < 1) val = 1; + if (val > 255) val = 255; + *out++ = (BYTE)val; + if (i == FINAL_DC_INDEX) + q = ac_q_factor; + } +} + + + +/*____________________________________________________________________________ + | | | + | output_header | outputs the JPEG header | + |_______________|____________________________________________________________| + | | + | Inputs: lum_quant, chrom_quant, xRes, yRes, pixels_in_row, comps_in_pixel,| + | horiz_sam_facs, vert_sam_facs | + |____________________________________________________________________________| +*/ +static UINT output_header ( + PJENC_INST g, + BYTE *buf_p) +{ + BYTE hclass[4]; + BYTE ident[4]; + const BYTE *counts[4]; + const BYTE *huffval[4]; + + hclass [0] = 0; + ident [0] = 0; + counts [0] = lum_DC_counts; + huffval[0] = lum_DC_values; + + hclass [1] = 1; + ident [1] = 0; + counts [1] = g->fDenali ? lum_AC_counts_Denali : lum_AC_counts; + huffval[1] = lum_AC_values; + + hclass [2] = 0; + ident [2] = 1; + counts [2] = chrom_DC_counts; + huffval[2] = chrom_DC_values; + + hclass [3] = 1; + ident [3] = 1; + counts [3] = chrom_AC_counts; + huffval[3] = chrom_AC_values; + + write_buf_open (g, buf_p); + em_write_SOI (g); + + if (0 /* todo */ ) { + em_write_short_header (g, g->rows_in_page<0 ? 0 : g->rows_in_page, + g->pixels_in_row, + g->xRes, g->yRes, + g->dc_q_factor, g->ac_q_factor, + g->comps_in_pixel, + g->sample_factors); + } else { + if (g->fOutputG3APP1) + em_write_G3_APP1 (g, g->xRes); + else + em_write_JFIF_APP0 (g, g->xRes, g->yRes); + em_write_SOF (g, g->pixels_in_row, g->rows_in_page<0 ? 0 : g->rows_in_page, + g->comps_in_pixel, g->horiz_sam_facs, g->vert_sam_facs); + em_write_DQT (g, 0, 0, g->lum_quant); + if (g->comps_in_pixel > 1) + em_write_DQT (g, 0, 1, g->chrom_quant); + em_write_DHTs (g, g->comps_in_pixel==1 ? 2 : 4, + hclass, ident, counts, huffval); + em_write_SOS (g, g->comps_in_pixel); + } + return write_buf_close (g); +} + + + +/*****************************************************************************\ + * + * jpgEncode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD jpgEncode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PJENC_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(JENC_INST), g); + *pXform = g; + memset (g, 0, sizeof(JENC_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD jpgEncode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PJENC_INST g; + + HANDLE_TO_PTR (hXform, g); + g->traits = *pTraits; /* a structure copy */ + + INSURE (g->traits.iPixelsPerRow > 0); + INSURE (g->traits.iComponentsPerPixel==1 || g->traits.iComponentsPerPixel==3); + + g->pixels_in_row = g->traits.iPixelsPerRow; + g->comps_in_pixel = g->traits.iComponentsPerPixel; + g->rows_in_page = g->traits.lNumRows; + g->xRes = g->traits.lHorizDPI >> 16; + g->yRes = g->traits.lVertDPI >> 16; + if (g->xRes < 0) g->xRes = 300; + if (g->yRes < 0) g->yRes = 300; + + g->fDidHeader = FALSE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD jpgEncode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PJENC_INST g; + UINT qfacs; + + HANDLE_TO_PTR (hXform, g); + + qfacs = aXformInfo[IP_JPG_ENCODE_QUALITY_FACTORS].dword; + g->sample_factors = aXformInfo[IP_JPG_ENCODE_SAMPLE_FACTORS].dword; + g->input_subsampled = aXformInfo[IP_JPG_ENCODE_ALREADY_SUBSAMPLED].dword; + g->fDenali = (BOOL)aXformInfo[IP_JPG_ENCODE_FOR_DENALI].dword; + g->fOutputDNL = (BOOL)aXformInfo[IP_JPG_ENCODE_OUTPUT_DNL].dword; + g->fOutputG3APP1 = (BOOL)aXformInfo[IP_JPG_ENCODE_FOR_COLOR_FAX].dword; + g->dwDummyHeaderBytes = aXformInfo[IP_JPG_ENCODE_DUMMY_HEADER_LEN].dword; + + g->dc_q_factor = (BYTE)(qfacs >> 8); + g->ac_q_factor = (BYTE)(qfacs); + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD jpgEncode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + PJENC_INST g; + + HANDLE_TO_PTR (hXform, g); + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD jpgEncode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PJENC_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Since we don't change traits, just copy out the default traits */ + *pInTraits = g->traits; + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * jpgEncode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD jpgEncode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PJENC_INST g; + WORD n; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->comps_in_pixel * g->pixels_in_row; + + n = OUTBUF_NUM_MCUS * MAX_MCU_SIZE; + if (n < MAX_HEADER_SIZE) n = MAX_HEADER_SIZE; + *pdwMinOutBufLen = n; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD jpgEncode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PJENC_INST g; + unsigned ret_val; + UINT row, n_rows, n_bytes; + int comp; + UINT row_len; + UINT n_loaded; + + HANDLE_TO_PTR (hXform, g); + + *pdwInputNextPos = g->dwInNextPos; + *pdwInputUsed = 0; + *pdwOutputThisPos = g->dwOutNextPos; + *pdwOutputUsed = 0; + ret_val = IP_READY_FOR_DATA; + + /****************************************************/ + /* Init and output the Header if we haven't already */ + /****************************************************/ + + if (! g->fDidHeader) { + UINT row_len; + BYTE *p; + DWORD factors; + UINT fac; + + /* Init */ + + g->rows_loaded = 0; + g->mcus_sent = 0; + g->loading_rows = TRUE; + write_init (g); + zero_prior_DC (g); + encode_init (g); + + if (g->ac_q_factor == 0) + g->ac_q_factor = Q_DEFAULT; + if (g->dc_q_factor == 0) + g->dc_q_factor = g->ac_q_factor; + + if (g->sample_factors == 0) + g->sample_factors = g->comps_in_pixel==1 ? MONO_FACTORS : COLOR_FACTORS; + + factors = g->sample_factors >> 16; + g->max_horiz_sam_fac = 0; + for (comp=3; comp>=0; comp--) { + fac = factors & 0x0fu; + if (fac > g->max_horiz_sam_fac) g->max_horiz_sam_fac = fac; + g->horiz_sam_facs[comp] = fac; + factors >>= 4; + } + + factors = g->sample_factors & 0x0000ffffu; + g->max_vert_sam_fac = 0; + for (comp=3; comp>=0; comp--) { + fac = factors & 0x0fu; + if (fac > g->max_vert_sam_fac) g->max_vert_sam_fac = fac; + g->vert_sam_facs[comp] = fac; + factors >>= 4; + } + + g->cols_in_mcu = g->max_horiz_sam_fac * 8; + g->rows_in_mcu = g->max_vert_sam_fac * 8; + g->mcus_in_row = (g->pixels_in_row + g->cols_in_mcu - 1) / g->cols_in_mcu; + + scale_q_table (g->dc_q_factor, g->ac_q_factor, orig_lum_quant, g->lum_quant); + scale_q_table (g->dc_q_factor, g->ac_q_factor, orig_chrom_quant, g->chrom_quant); + + /* scale lum_quant & chrom_quant for Wino DCT */ + scale_for_wino (g->lum_quant, g->wino_lum_quant, g->wino_lum_quant_thres); + scale_for_wino (g->chrom_quant, g->wino_chrom_quant, g->wino_chrom_quant_thres); + + g->whitePixel[0] = 255; /* YCC value of a white pixel (color) */ + g->whitePixel[1] = 128; /* The 255 is white in mono too */ + g->whitePixel[2] = 128; + g->whitePixel[3] = 255; + + g->dwOutNextPos = *pdwOutputUsed = (g->dwDummyHeaderBytes == 0) + ? output_header (g,pbOutputBuf) + : g->dwDummyHeaderBytes; + + *pdwOutputThisPos = 0; + + /* Allocate the row-buffers in in_rows_ap */ + + memset (g->in_rows_ap, 0, sizeof(g->in_rows_ap)); + + for (comp=0; comp<(int)g->comps_in_pixel; comp++) { + row_len = g->horiz_sam_facs[comp] * g->mcus_in_row * 8; + n_rows = g->vert_sam_facs[comp] * 8; + + for (row=0; row<n_rows; row++) { + IP_MEM_ALLOC (row_len, p); + g->in_rows_ap[comp][row] = p; + } + } + + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + g->dwInNextPos = 0; + g->fDidHeader = TRUE; + + return IP_READY_FOR_DATA; + } + + /*********************************/ + /* We are filling the input rows */ + /*********************************/ + + if (g->loading_rows) { + + /***** Init all row-buffers to white if starting new row-set *****/ + + n_loaded = g->rows_loaded % g->rows_in_mcu; + + if (n_loaded == 0) { + for (comp=0; comp<g->comps_in_pixel; comp++) { + row_len = g->horiz_sam_facs[comp] * g->mcus_in_row * 8; + n_rows = g->vert_sam_facs[comp] * 8; + + for (row=0; row<n_rows; row++) + memset (g->in_rows_ap[comp][row], g->whitePixel[comp], row_len); + } + } + + if (pbInputBuf == NULL) { + + /***** We are being told to flush *****/ + + if (n_loaded != 0) { + /* some rows were loaded; start compressing rows now */ + g->loading_rows = FALSE; + /* boost rows_loaded to next multiple of rows_in_mcu so + * n_loaded will be 0 next time around */ + g->rows_loaded += g->rows_in_mcu - n_loaded; + } else if ((g->rows_in_page<0 || (int)g->rows_received<g->rows_in_page) + && g->dwDummyHeaderBytes == 0) { + /* row-count was unknown or too big, output header w/ correct row-count */ + g->rows_in_page = g->rows_received; + *pdwOutputUsed = output_header (g, pbOutputBuf); + *pdwOutputThisPos = 0; + } else { + /* no rows were loaded, and row-count is valid, so we're done */ + PRINT (_T("jpeg_encode_convert_row: Done\n"),0,0); + write_buf_open (g, pbOutputBuf); + write_bits_flush (g); + if (g->fOutputDNL) + em_write_DNL (g, g->rows_received); + em_write_EOI (g); + *pdwOutputUsed = write_buf_close (g); + ret_val |= IP_DONE; + } + } else { + + /***** Copy the row(s) into in_rows_ap *****/ + + copy_in_rows ( + g, + n_loaded, /* in: index of next row to get */ + pbInputBuf, /* in: copied-in row data */ + &n_rows, /* out: # of rows copied in */ + &n_bytes); /* out: # of bytes fetched */ + + *pdwInputUsed = n_bytes; + *pdwInputNextPos = (g->dwInNextPos += n_bytes); + g->rows_loaded += n_rows; + g->rows_received += n_rows; + n_loaded = g->rows_loaded % g->rows_in_mcu; + + /* it's hard to set IP_PRODUCED_ROW 8 times per 8 rows, so cheat */ + if (n_rows > 0) + ret_val |= IP_CONSUMED_ROW | IP_PRODUCED_ROW; + + /***** Check if all needed rows are loaded *****/ + + if (n_loaded == 0) { + /* # rows loaded is rows_in_mcu, but the mod wrapped to zero */ + g->loading_rows = FALSE; /* start compressing rows now */ + } + } + } + + /************************************************************/ + /* Compress as many MCUs as will fit into the output buffer */ + /************************************************************/ + + while (!g->loading_rows && + dwOutputAvail-*pdwOutputUsed > MAX_MCU_SIZE) { + PRINT (_T("jpeg_encode_convert_row: Encoding MCU, mcus_sent=%d\n"), + g->mcus_sent, 0); + + write_buf_open (g, pbOutputBuf + *pdwOutputUsed); + encode_MCU (g, g->mcus_sent); + n_bytes = write_buf_close (g); + + g->dwOutNextPos += n_bytes; + *pdwOutputUsed += n_bytes; + + g->mcus_sent += 1; + if (g->mcus_sent >= g->mcus_in_row) { + g->loading_rows = TRUE; + g->mcus_sent = 0; + } + } + + PRINT (_T("jpeg_encode_convert_row: Returning %04x, out_used=%d\n"), + ret_val, *pdwOutputUsed); + return ret_val; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD jpgEncode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * jpgEncode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD jpgEncode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PJENC_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD jpgEncode_closeXform (IP_XFORM_HANDLE hXform) +{ + PJENC_INST g; + BYTE **row_pp, **after_pp, *p; + + HANDLE_TO_PTR (hXform, g); + row_pp = &(g->in_rows_ap[0][0]); + after_pp = row_pp + (sizeof(g->in_rows_ap)/sizeof(BYTE*)); + + for ( ; row_pp<after_pp; row_pp++) { + p = *row_pp; + if (p != NULL) { + IP_MEM_FREE (p); + *row_pp = NULL; + } + } + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgEncodeTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL jpgEncodeTbl = { + jpgEncode_openXform, + jpgEncode_setDefaultInputTraits, + jpgEncode_setXformSpec, + jpgEncode_getHeaderBufSize, + jpgEncode_getActualTraits, + jpgEncode_getActualBufSizes, + jpgEncode_convert, + jpgEncode_newPage, + jpgEncode_insertedData, + jpgEncode_closeXform +}; + +/* End of File */ diff --git a/ip/xjpg_fix.c b/ip/xjpg_fix.c new file mode 100644 index 0000000..709fcac --- /dev/null +++ b/ip/xjpg_fix.c @@ -0,0 +1,839 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: David Paschal (based on Mark Overton's "skel" template + * and a few bits and pieces from xjpg_{enc,dec}.c). */ + +/******************************************************************************\ + * + * xjpg_fix.c - Fixes JPEG files to have a standard JFIF APP0 header and + * a correct row-count value in the SOF header. + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * jpgFixTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * None. + * + * Capabilities and Limitations: + * + * Looks at the header, and converts an OfficeJet APP1 short header to + * a standard JFIF APP0 header. Passes the resulting header and data + * through to the JPEG decoder in order to count the number of rows in + * the image. Discards the decompressed data and passes the original + * compressed data (possibly with modified header) to the output. At + * the end of the file, seeks back to the SOF header and rewrites the + * row count field. + * + * Able to handle JPEG files with JFIF APP0 or OfficeJet APP1 headers, + * but not color-fax APP1 headers or Denali-style compression (in + * these cases, you must do the full decode, any necessary color-space + * conversion, and encode). + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- ------------------- ------------------------ + * iPixelsPerRow ignored based on header + * iBitsPerPixel ignored based on header + * iComponentsPerPixel ignored based on header + * lHorizDPI ignored based on header + * lVertDPI ignored based on header + * lNumRows ignored based on header + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include <stdio.h> + +#define PRINTF(args...) fprintf(stderr,args) + +/* TODO: Move this to a separate .h file: */ +extern IP_XFORM_TBL jpgDecodeTbl; +extern WORD jpgDecode_getRowCountInfo(IP_XFORM_HANDLE hXform, + int *pRcCountup,int *pRcTraits,int *pSofOffset); + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#define FUNC_STATUS static + +#define BEND_GET_SHORT(s) (((s)[0]<<8)|((s)[1])) +#define BEND_SET_SHORT(s,x) ((s)[0]=((x)>>8)&0xFF,(s)[1]=(x)&0xFF) + +typedef struct { + IP_XFORM_HANDLE pSlaveXform; /* JPEG-decoder slave transformer. */ + PBYTE headerBuffer; + DWORD outNextPos; + DWORD lenHeader; + DWORD lenAddedHeader; + DWORD lenHeaderBuffer; + DWORD readyForSofRewrite; + DWORD dwValidChk; /* struct validity check value */ +} JFIX_INST, *PJFIX_INST; + + +/* TODO: Move the tables to a separate common file. */ + +/* Since the firmware does not supply tables in its header, + * the tables used in the firmware are supplied below. */ + +/*____________________________________________________________________________ + | | + | Zigzag of Normal Quantization Tables | + |____________________________________________________________________________| +*/ + +static const unsigned char orig_lum_quant[64] = { + 16, 11, 12, 14, 12, 10, 16, 14, + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99 +}; + +static const unsigned char orig_chrom_quant[64] = { + 17, 18, 18, 24, 21, 24, 47, 26, + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +/*____________________________________________________________________________ + | | + | Huffman Tables | + |____________________________________________________________________________| +*/ + +static const unsigned char lum_DC_counts[16] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char lum_DC_values[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; + +static const unsigned char chrom_DC_counts[16] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char chrom_DC_values[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; + +static const unsigned char lum_AC_counts[16] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d +}; + +static const unsigned char lum_AC_values[162] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const unsigned char chrom_AC_counts[16] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77 +}; + +static const unsigned char chrom_AC_values[162] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/*____________________________________________________________________________ + | | | + | scale_q_table | scales a q-table according to the q-factors | + |_______________|____________________________________________________________| +*/ +/* TODO: Reference this from a common .h file. */ +void scale_q_table(int dc_q_factor,int ac_q_factor,int ident, + unsigned char *out) { + static const int Q_DEFAULT=20; + static const int FINAL_DC_INDEX=9; + int i,val; + int q=dc_q_factor; + const unsigned char *in=orig_lum_quant; + if (ident) in=orig_chrom_quant; + + for (i=0; i<64; i++) { + val = ((*in++)*q + Q_DEFAULT/2) / Q_DEFAULT; + if (val < 1) val = 1; + if (val > 255) val = 255; + *out++ = (unsigned char)val; + if (i == FINAL_DC_INDEX) { + q = ac_q_factor; + } + } +} + + +/*****************************************************************************\ + * + * jpgFix_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PJFIX_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(JFIX_INST), g); + *pXform = g; + memset (g, 0, sizeof(JFIX_INST)); + if (jpgDecodeTbl.openXform(&g->pSlaveXform)!=IP_DONE) { + IP_MEM_FREE(g); + goto fatal_error; + } + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFix_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PJFIX_INST g; + + HANDLE_TO_PTR (hXform, g); + + return jpgDecodeTbl.setDefaultInputTraits(g->pSlaveXform,pTraits); + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFix_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PJFIX_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Check your options in aXformInfo here, and save them. + * Use the INSURE macro like you'd use assert. INSURE jumps to + * fatal_error below if it fails. + */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFix_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + WORD r; + PJFIX_INST g; + + HANDLE_TO_PTR (hXform, g); + + r=jpgDecodeTbl.getHeaderBufSize(g->pSlaveXform,pdwInBufLen); + if (r!=IP_DONE) return r; + + g->lenHeaderBuffer=*pdwInBufLen; + IP_MEM_ALLOC(g->lenHeaderBuffer,g->headerBuffer); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFix_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + + +//#define MYLOCATE(p) (void *)(p)=(g->headerBuffer+lenAddedHeader) +#define MYLOCATE(p) (p)=(void *)(g->headerBuffer+lenAddedHeader) +#define MYWRITE(p) lenAddedHeader+=sizeof(*(p)) +#define MYWRITEBUF(data,datalen) \ + do { \ + INSURE((datalen)<=g->lenHeaderBuffer-lenAddedHeader); \ + memcpy(g->headerBuffer+lenAddedHeader,(char *)(data),(datalen)); \ + lenAddedHeader+=(datalen); \ + } while(0) + +FUNC_STATUS WORD jpgFix_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + struct shortHeader_s { + unsigned char soi[2]; /* 0xFF, 0xD8 */ + + unsigned char app1[2]; /* 0xFF, 0xE1 */ + unsigned char app1Length[2]; /* 0x00, 0x12 */ + unsigned char height[2]; + unsigned char width[2]; + unsigned char xres[2]; + unsigned char yres[2]; + unsigned char ac_q_factor; + unsigned char numComponents; /* 1=gray, 3=color */ + unsigned char xSampleFactors[2]; + unsigned char ySampleFactors[2]; + unsigned char dc_q_factor; + unsigned char reserved; /* 0x00 */ + } *pInputHeader=(struct shortHeader_s *)pbInputBuf; + struct outputSoiApp0_s { + unsigned char soi[2]; /* 0xFF, 0xD8 */ + + unsigned char app0[2]; /* 0xFF, 0xE0 */ + unsigned char app0Length[2]; /* 0x00, 0x10 */ + unsigned char jfif[5]; /* "JFIF\0" */ + unsigned char majorVersion; /* 0x01 */ + unsigned char minorVersion; /* 0x00 */ + unsigned char units; /* 0x01 = DPI */ + unsigned char xres[2]; + unsigned char yres[2]; + unsigned char xthumb; /* 0x00 */ + unsigned char ythumb; /* 0x00 */ + } *pOutputSoiApp0; + struct { + unsigned char sof0[2]; /* 0xFF, 0xC0 */ + unsigned char sof0Length[2]; /* 0x00, 0x?? */ + unsigned char eight; /* 0x08 */ + unsigned char height[2]; + unsigned char width[2]; + unsigned char numComponents; /* 1=gray, 3=color */ + } *pOutputSof0Part1; + struct { + unsigned char iComponent; + unsigned char xySampleFactors; + unsigned char isNotFirstComponent; + } *pOutputSofComponent; + struct { + unsigned char dqt[2]; /* 0xFF, 0xDB */ + unsigned char dqtLength[2]; /* 0x00, 0x43 */ + unsigned char ident; /* 0=lum., 1=chrom. */ + unsigned char elements[64]; + } *pOutputDqt; + struct { + unsigned char dht[2]; /* 0xFF, 0xC4 */ + unsigned char dhtLength[2]; /* 0x00, 0x?? */ + } *pOutputDhtPart1; + struct { + unsigned char hclass_ident; + unsigned char counts[16]; + /* Variable-length huffval table follows. */ + } *pOutputDhtPart2; + static const struct { + unsigned char hclass_ident; + const unsigned char *counts; + const unsigned char *huffval; + } dhtInfo[4]={ + {0x00,lum_DC_counts,lum_DC_values}, + {0x10,lum_AC_counts,lum_AC_values}, + {0x01,chrom_DC_counts,chrom_DC_values}, + {0x11,chrom_AC_counts,chrom_AC_values} + }; + int dhtCountCounts[4]; + struct { + unsigned char sos[2]; /* 0xFF, 0xDA */ + unsigned char sosLength[2]; /* 0x00, 0x?? */ + unsigned char numComponents; /* 1=gray, 3=color */ + } *pOutputSosPart1; + struct { + unsigned char iComponent; + unsigned char x00x11; /* (i==0 ? 0x00 : 0x11) */ + } *pOutputSosComponent; + struct { + unsigned char zero1; /* 0x00 */ + unsigned char sixtythree; /* 0x3F */ + unsigned char zero2; /* 0x00 */ + } *pOutputSosPart2; + + PJFIX_INST g; + DWORD lenRemovedHeader=0,lenAddedHeader=0; + int imax,i,j,x,y,r,len; + + HANDLE_TO_PTR (hXform, g); + + /* Validate APP1 (OfficeJet short header) record. + * If we actually get a JFIF standard APP0 record, then + * skip parsing the header. */ + if (dwInputAvail>=sizeof(*pInputHeader) && + pInputHeader->soi[0]==0xFF && pInputHeader->soi[1]==0xD8 && + pInputHeader->app1[0]==0xFF && pInputHeader->app1[1]==0xE1 && + !pInputHeader->app1Length[0] && pInputHeader->app1Length[1]==0x12) { + + /* Write standard JPEG/JFIF records... */ + lenRemovedHeader=sizeof(*pInputHeader); + + /* Start Of Image record. */ + MYLOCATE(pOutputSoiApp0); + pOutputSoiApp0->soi[0]=0xFF; + pOutputSoiApp0->soi[1]=0xD8; + /* APP0 (JFIF header) record. */ + pOutputSoiApp0->app0[0]=0xFF; + pOutputSoiApp0->app0[1]=0xE0; + pOutputSoiApp0->app0Length[0]=0x00; + pOutputSoiApp0->app0Length[1]=sizeof(*pOutputSoiApp0)-4; + strcpy((char *)pOutputSoiApp0->jfif,"JFIF"); + pOutputSoiApp0->majorVersion=0x01; + pOutputSoiApp0->minorVersion=0x00; + pOutputSoiApp0->units=0x01; + pOutputSoiApp0->xres[0]=pInputHeader->xres[0]; + pOutputSoiApp0->xres[1]=pInputHeader->xres[1]; + pOutputSoiApp0->yres[0]=pInputHeader->yres[0]; + pOutputSoiApp0->yres[1]=pInputHeader->yres[1]; + pOutputSoiApp0->xthumb=0; + pOutputSoiApp0->ythumb=0; + MYWRITE(pOutputSoiApp0); + + /* Start Of Frame record. */ + MYLOCATE(pOutputSof0Part1); + pOutputSof0Part1->sof0[0]=0xFF; + pOutputSof0Part1->sof0[1]=0xC0; + pOutputSof0Part1->sof0Length[0]=0x00; + pOutputSof0Part1->sof0Length[1]=sizeof(*pOutputSof0Part1)-2+ + pInputHeader->numComponents*sizeof(*pOutputSofComponent); + pOutputSof0Part1->eight=0x08; + pOutputSof0Part1->height[0]=pInputHeader->height[0]; + pOutputSof0Part1->height[1]=pInputHeader->height[1]; + pOutputSof0Part1->width[0]=pInputHeader->width[0]; + pOutputSof0Part1->width[1]=pInputHeader->width[1]; + pOutputSof0Part1->numComponents=pInputHeader->numComponents; + MYWRITE(pOutputSof0Part1); + x=BEND_GET_SHORT(pInputHeader->xSampleFactors); + y=BEND_GET_SHORT(pInputHeader->ySampleFactors); + for (i=0;i<pInputHeader->numComponents;i++) { + MYLOCATE(pOutputSofComponent); + pOutputSofComponent->iComponent=i; + pOutputSofComponent->xySampleFactors= + (((x>>(4*(3-i)))&0x0F)<<4) | ((y>>(4*(3-i)))&0x0F); + pOutputSofComponent->isNotFirstComponent=(!i?0:1); + MYWRITE(pOutputSofComponent); + } + + /* Define Quantization Table record. */ + imax=pInputHeader->numComponents>1?1:0; + for (i=0;i<=imax;i++) { + MYLOCATE(pOutputDqt); + pOutputDqt->dqt[0]=0xFF; + pOutputDqt->dqt[1]=0xDB; + pOutputDqt->dqtLength[0]=0x00; + pOutputDqt->dqtLength[1]=sizeof(*pOutputDqt)-2; + pOutputDqt->ident=i; /* Upper nibble=0 for 8-bit table. */ + scale_q_table(pInputHeader->dc_q_factor,pInputHeader->ac_q_factor, + i,pOutputDqt->elements); + MYWRITE(pOutputDqt); + } + imax=pInputHeader->numComponents>1?4:2; + x=sizeof(*pOutputDhtPart1)-2; + for (i=0;i<imax;i++) { + dhtCountCounts[i]=0; + x+=sizeof(*pOutputDhtPart2); + for (j=0;j<16;j++) { + y=dhtInfo[i].counts[j]; + dhtCountCounts[i]+=y; + x+=y; + } + } + MYLOCATE(pOutputDhtPart1); + pOutputDhtPart1->dht[0]=0xFF; + pOutputDhtPart1->dht[1]=0xC4; + BEND_SET_SHORT(pOutputDhtPart1->dhtLength,x); + MYWRITE(pOutputDhtPart1); + for (i=0;i<imax;i++) { + MYLOCATE(pOutputDhtPart2); + pOutputDhtPart2->hclass_ident=dhtInfo[i].hclass_ident; + memcpy(pOutputDhtPart2->counts,dhtInfo[i].counts,16); + MYWRITE(pOutputDhtPart2); + MYWRITEBUF(dhtInfo[i].huffval,dhtCountCounts[i]); + } + + /* Start Of Scan record. */ + MYLOCATE(pOutputSosPart1); + imax=pInputHeader->numComponents; + pOutputSosPart1->sos[0]=0xFF; + pOutputSosPart1->sos[1]=0xDA; + pOutputSosPart1->sosLength[0]=0; + pOutputSosPart1->sosLength[1]=sizeof(*pOutputSosPart1)-2+ + imax*sizeof(*pOutputSosComponent)+sizeof(*pOutputSosPart2); + pOutputSosPart1->numComponents=imax; + MYWRITE(pOutputSosPart1); + for (i=0;i<imax;i++) { + MYLOCATE(pOutputSosComponent); + pOutputSosComponent->iComponent=i; + pOutputSosComponent->x00x11=(!i?0x00:0x11); + MYWRITE(pOutputSosComponent); + } + MYLOCATE(pOutputSosPart2); + pOutputSosPart2->zero1=0; + pOutputSosPart2->sixtythree=63; + pOutputSosPart2->zero2=0; + MYWRITE(pOutputSosPart2); + } + + /* Save a copy of the (possibly rewritten) header (plus any residual + * data) so we can emit it later. */ + len=dwInputAvail-(lenAddedHeader-lenRemovedHeader); + if (len>g->lenHeaderBuffer-lenAddedHeader) { + len=g->lenHeaderBuffer-lenAddedHeader; + } + memcpy(g->headerBuffer+lenAddedHeader,pbInputBuf+lenRemovedHeader,len); + + /* Pass the (possibly rewritten) header to the slave transformer. */ + r=jpgDecodeTbl.getActualTraits(g->pSlaveXform, + len+lenAddedHeader,g->headerBuffer, + pdwInputUsed,pdwInputNextPos, + pInTraits,pOutTraits); + if ((r&(IP_DONE|IP_READY_FOR_DATA))!=(IP_DONE|IP_READY_FOR_DATA)) { + return (r|IP_FATAL_ERROR); + } + + g->lenHeader=*pdwInputUsed; + g->lenAddedHeader=lenAddedHeader-lenRemovedHeader; + *pdwInputUsed-=g->lenAddedHeader; + *pdwInputNextPos-=g->lenAddedHeader; + + return r; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * jpgFix_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD jpgFix_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + WORD r; + PJFIX_INST g; + + HANDLE_TO_PTR (hXform, g); + + r=jpgDecodeTbl.getActualBufSizes(g->pSlaveXform, + pdwMinInBufLen,pdwMinOutBufLen); + if (r==IP_DONE) { + INSURE(*pdwMinOutBufLen<=g->lenHeaderBuffer); + } + *pdwMinOutBufLen=g->lenHeaderBuffer; + return r; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFix_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PJFIX_INST g; + WORD r=0; + + HANDLE_TO_PTR (hXform, g); + + if (g->outNextPos<g->lenHeader) { + DWORD len=g->lenHeader-g->outNextPos; + if (len>dwOutputAvail) len=dwOutputAvail; + if (len) { + memcpy(pbOutputBuf,g->headerBuffer+g->outNextPos,len); + } + *pdwInputUsed=0; + *pdwInputNextPos=g->lenHeader-g->lenAddedHeader; + *pdwOutputUsed=len; + *pdwOutputThisPos=g->outNextPos; + g->outNextPos+=len; + r|=IP_READY_FOR_DATA; + + } else if (!g->readyForSofRewrite) { + DWORD dwOutputUsed,dwOutputThisPos; + r=jpgDecodeTbl.convert(g->pSlaveXform, + dwInputAvail,pbInputBuf,pdwInputUsed,pdwInputNextPos, + g->lenHeaderBuffer,g->headerBuffer, + &dwOutputUsed,&dwOutputThisPos); + *pdwInputNextPos-=g->lenAddedHeader; + if (r&IP_DONE) { + g->readyForSofRewrite=1; + if (!*pdwInputUsed) goto rewriteSof; + } + if (*pdwInputUsed) { + INSURE(*pdwInputUsed<=dwOutputAvail); + memcpy(pbOutputBuf,pbInputBuf,*pdwInputUsed); + } + *pdwOutputUsed=*pdwInputUsed; + *pdwOutputThisPos=g->outNextPos; + g->outNextPos+=*pdwInputUsed; + + } else { +rewriteSof: + r|=IP_DONE; + *pdwInputUsed=0; + *pdwInputNextPos=g->outNextPos-g->lenAddedHeader; + *pdwOutputUsed=0; + *pdwOutputThisPos=g->outNextPos; + if (g->readyForSofRewrite>0) { + int rcCountup,rcTraits,sofOffset; + r=jpgDecode_getRowCountInfo(g->pSlaveXform, + &rcCountup,&rcTraits,&sofOffset); + BEND_SET_SHORT(pbOutputBuf,rcCountup); + *pdwOutputUsed=2; + *pdwOutputThisPos=sofOffset; + g->readyForSofRewrite=-1; + } + } + + return r; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFix_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * jpgFix_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_newPage ( + IP_XFORM_HANDLE hXform) +{ + PJFIX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * jpgFix_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD jpgFix_closeXform (IP_XFORM_HANDLE hXform) +{ + PJFIX_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (g->pSlaveXform) jpgDecodeTbl.closeXform(g->pSlaveXform); + if (g->headerBuffer) IP_MEM_FREE(g->headerBuffer); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * jpgFixTbl - Jump-table for transform driver + * +\*****************************************************************************/ +IP_XFORM_TBL jpgFixTbl = { + jpgFix_openXform, + jpgFix_setDefaultInputTraits, + jpgFix_setXformSpec, + jpgFix_getHeaderBufSize, + jpgFix_getActualTraits, + jpgFix_getActualBufSizes, + jpgFix_convert, + jpgFix_newPage, + jpgFix_insertedData, + jpgFix_closeXform +}; + +/* End of File */ diff --git a/ip/xjpg_huf.c b/ip/xjpg_huf.c new file mode 100644 index 0000000..9373577 --- /dev/null +++ b/ip/xjpg_huf.c @@ -0,0 +1,558 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*____________________________________________________________________________ + | | | + | xjpg_huf.c | Huffman tables for JPEG encoder and decoder | + |____________|_______________________________________________________________| + | | + | Mark Overton, April 1996 | + |____________________________________________________________________________| +*/ + +#include "xjpg_huf.h" + + +/*____________________________________________________________________________ + | | + | Histograms and values for Building Huffman Tables | + |____________________________________________________________________________| +*/ + +const BYTE LuminanceDCCounts[16] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const BYTE LuminanceDCValues[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; + +#ifdef COLOR_JPEG +const BYTE ChrominanceDCCounts[16] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const BYTE ChrominanceDCValues[12] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; +#endif + +const BYTE LuminanceACCounts[16] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, /* 0x01, 0x7d */ 0x00, 0x7e + /* The alteration above yields Huffman codes as follows: + * - common codes are 12 bits wide or less, + * - uncommon codes are exactly 16 bits wide, and all those codes + * start with nine '1' bits, leaving seven bits of useful info. */ +}; + +const BYTE LuminanceACValues[162] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +#ifdef COLOR_JPEG +const BYTE ChrominanceACCounts[16] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77 +}; + +const BYTE ChrominanceACValues[162] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; +#endif + + + +/*____________________________________________________________________________ + | | + | Tables output by mk_jpg_huff program | + |____________________________________________________________________________| +*/ + + +const enc_huff_elem_t enc_DC_table[] = { + { 0x0000, 2 }, { 0x0002, 3 }, { 0x0003, 3 }, { 0x0004, 3 }, + { 0x0005, 3 }, { 0x0006, 3 }, { 0x000e, 4 }, { 0x001e, 5 }, + { 0x003e, 6 }, { 0x007e, 7 }, { 0x00fe, 8 }, { 0x01fe, 9 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, +}; + + +const dec_huff_elem_t dec_DC_table[] = { {0,0}, + { 2, 0x00 }, { 3, 0x01 }, { 3, 0x02 }, { 3, 0x03 }, + { 3, 0x04 }, { 3, 0x05 }, { 4, 0x06 }, { 5, 0x07 }, + { 6, 0x08 }, { 7, 0x09 }, { 8, 0x0a }, { 9, 0x0b }, +}; + +const BYTE dec_DC_table_index[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 0, +}; + + +const enc_huff_elem_t enc_AC_table[] = { + { 0x000a, 4 }, { 0x0000, 2 }, { 0x0001, 2 }, { 0x0004, 3 }, + { 0x000b, 4 }, { 0x001a, 5 }, { 0x0078, 7 }, { 0x00f8, 8 }, + { 0x03f6, 10 }, { 0xff81, 16 }, { 0xff82, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x000c, 4 }, { 0x001b, 5 }, { 0x0079, 7 }, + { 0x01f6, 9 }, { 0x07f6, 11 }, { 0xff83, 16 }, { 0xff84, 16 }, + { 0xff85, 16 }, { 0xff86, 16 }, { 0xff87, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x001c, 5 }, { 0x00f9, 8 }, { 0x03f7, 10 }, + { 0x0ff4, 12 }, { 0xff88, 16 }, { 0xff89, 16 }, { 0xff8a, 16 }, + { 0xff8b, 16 }, { 0xff8c, 16 }, { 0xff8d, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003a, 6 }, { 0x01f7, 9 }, { 0x0ff5, 12 }, + { 0xff8e, 16 }, { 0xff8f, 16 }, { 0xff90, 16 }, { 0xff91, 16 }, + { 0xff92, 16 }, { 0xff93, 16 }, { 0xff94, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x003b, 6 }, { 0x03f8, 10 }, { 0xff95, 16 }, + { 0xff96, 16 }, { 0xff97, 16 }, { 0xff98, 16 }, { 0xff99, 16 }, + { 0xff9a, 16 }, { 0xff9b, 16 }, { 0xff9c, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007a, 7 }, { 0x07f7, 11 }, { 0xff9d, 16 }, + { 0xff9e, 16 }, { 0xff9f, 16 }, { 0xffa0, 16 }, { 0xffa1, 16 }, + { 0xffa2, 16 }, { 0xffa3, 16 }, { 0xffa4, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x007b, 7 }, { 0x0ff6, 12 }, { 0xffa5, 16 }, + { 0xffa6, 16 }, { 0xffa7, 16 }, { 0xffa8, 16 }, { 0xffa9, 16 }, + { 0xffaa, 16 }, { 0xffab, 16 }, { 0xffac, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x00fa, 8 }, { 0x0ff7, 12 }, { 0xffad, 16 }, + { 0xffae, 16 }, { 0xffaf, 16 }, { 0xffb0, 16 }, { 0xffb1, 16 }, + { 0xffb2, 16 }, { 0xffb3, 16 }, { 0xffb4, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f8, 9 }, { 0xff80, 16 }, { 0xffb5, 16 }, + { 0xffb6, 16 }, { 0xffb7, 16 }, { 0xffb8, 16 }, { 0xffb9, 16 }, + { 0xffba, 16 }, { 0xffbb, 16 }, { 0xffbc, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01f9, 9 }, { 0xffbd, 16 }, { 0xffbe, 16 }, + { 0xffbf, 16 }, { 0xffc0, 16 }, { 0xffc1, 16 }, { 0xffc2, 16 }, + { 0xffc3, 16 }, { 0xffc4, 16 }, { 0xffc5, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x01fa, 9 }, { 0xffc6, 16 }, { 0xffc7, 16 }, + { 0xffc8, 16 }, { 0xffc9, 16 }, { 0xffca, 16 }, { 0xffcb, 16 }, + { 0xffcc, 16 }, { 0xffcd, 16 }, { 0xffce, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x03f9, 10 }, { 0xffcf, 16 }, { 0xffd0, 16 }, + { 0xffd1, 16 }, { 0xffd2, 16 }, { 0xffd3, 16 }, { 0xffd4, 16 }, + { 0xffd5, 16 }, { 0xffd6, 16 }, { 0xffd7, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x03fa, 10 }, { 0xffd8, 16 }, { 0xffd9, 16 }, + { 0xffda, 16 }, { 0xffdb, 16 }, { 0xffdc, 16 }, { 0xffdd, 16 }, + { 0xffde, 16 }, { 0xffdf, 16 }, { 0xffe0, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x07f8, 11 }, { 0xffe1, 16 }, { 0xffe2, 16 }, + { 0xffe3, 16 }, { 0xffe4, 16 }, { 0xffe5, 16 }, { 0xffe6, 16 }, + { 0xffe7, 16 }, { 0xffe8, 16 }, { 0xffe9, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0xffea, 16 }, { 0xffeb, 16 }, { 0xffec, 16 }, + { 0xffed, 16 }, { 0xffee, 16 }, { 0xffef, 16 }, { 0xfff0, 16 }, + { 0xfff1, 16 }, { 0xfff2, 16 }, { 0xfff3, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, + { 0x07f9, 11 }, { 0xfff4, 16 }, { 0xfff5, 16 }, { 0xfff6, 16 }, + { 0xfff7, 16 }, { 0xfff8, 16 }, { 0xfff9, 16 }, { 0xfffa, 16 }, + { 0xfffb, 16 }, { 0xfffc, 16 }, { 0xfffd, 16 }, { 0x0000, 0 }, + { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, { 0x0000, 0 }, +}; + + +const dec_huff_elem_t dec_AC_main_table[] = { {0,0}, + { 2, 0x01 }, { 2, 0x02 }, { 3, 0x03 }, { 4, 0x00 }, + { 4, 0x04 }, { 4, 0x11 }, { 5, 0x05 }, { 5, 0x12 }, + { 5, 0x21 }, { 6, 0x31 }, { 6, 0x41 }, { 7, 0x06 }, + { 7, 0x13 }, { 7, 0x51 }, { 7, 0x61 }, { 8, 0x07 }, + { 8, 0x22 }, { 8, 0x71 }, { 9, 0x14 }, { 9, 0x32 }, + { 9, 0x81 }, { 9, 0x91 }, { 9, 0xa1 }, { 10, 0x08 }, + { 10, 0x23 }, { 10, 0x42 }, { 10, 0xb1 }, { 10, 0xc1 }, + { 11, 0x15 }, { 11, 0x52 }, { 11, 0xd1 }, { 11, 0xf0 }, + { 12, 0x24 }, { 12, 0x33 }, { 12, 0x62 }, { 12, 0x72 }, +}; + +const BYTE dec_AC_main_table_index[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, + 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 30, 30, + 31, 31, 32, 32, 33, 34, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, +}; + + +const BYTE dec_AC_aux_table[] = { + 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, + 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x00, 0x00, +}; + +/* End of File */ diff --git a/ip/xjpg_huf.h b/ip/xjpg_huf.h new file mode 100644 index 0000000..1dd4be3 --- /dev/null +++ b/ip/xjpg_huf.h @@ -0,0 +1,83 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*____________________________________________________________________________ + | | | + | xjpg_huf.h | Huffman tables for JPEG encoder and decoder | + |____________|_______________________________________________________________| + | | + | Mark Overton, April 1996 | + |____________________________________________________________________________| +*/ + +#include "hpip.h" + +typedef struct { + WORD code; + BYTE size; +} enc_huff_elem_t; + +typedef struct { + BYTE size; + BYTE value; +} dec_huff_elem_t; + + +/* Tables used by encoder */ +extern const enc_huff_elem_t enc_DC_table[17]; +extern const enc_huff_elem_t enc_AC_table[256]; + +/* Tables used by decoder */ +extern const dec_huff_elem_t dec_DC_table[13]; +extern const BYTE dec_DC_table_index[512]; +extern const dec_huff_elem_t dec_AC_main_table[37]; +extern const BYTE dec_AC_main_table_index[4096]; +extern const BYTE dec_AC_aux_table[128]; + +/* Tables from which Huffman tables are computed */ +extern const BYTE LuminanceDCCounts[16]; +extern const BYTE LuminanceDCValues[12]; +extern const BYTE LuminanceACCounts[16]; +extern const BYTE LuminanceACValues[162]; + +#if 0 +extern const BYTE ChrominanceDCCounts[16]; +extern const BYTE ChrominanceDCValues[12]; +extern const BYTE ChrominanceACCounts[16]; +extern const BYTE ChrominanceACValues[162]; +#endif + +/* End of File */ diff --git a/ip/xjpg_mrk.h b/ip/xjpg_mrk.h new file mode 100644 index 0000000..29aecc9 --- /dev/null +++ b/ip/xjpg_mrk.h @@ -0,0 +1,91 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*____________________________________________________________________________ + | | | + | xjpg_mrk.h | Markers used in JPEG images | + |____________|_______________________________________________________________| + | | + | Mark Overton, Feb 1996 | + |____________________________________________________________________________| +*/ + +#define MARKER_NONE 0x00 +#define MARKER_END_FILE 0x100 + +#define MARKER_SOF0 0xc0 +#define MARKER_SOF1 0xc1 +#define MARKER_SOF2 0xc2 +#define MARKER_SOF3 0xc3 +#define MARKER_SOF5 0xc5 +#define MARKER_SOF6 0xc6 +#define MARKER_SOF7 0xc7 +#define MARKER_SOF8 0xc8 +#define MARKER_SOF9 0xc9 +#define MARKER_SOFA 0xca +#define MARKER_SOFB 0xcb +#define MARKER_SOFD 0xcd +#define MARKER_SOFE 0xce +#define MARKER_SOFF 0xcf + +#define MARKER_DHT 0xc4 +#define MARKER_DAC 0xcc + +#define MARKER_RST0 0xd0 +#define MARKER_RST1 0xd1 +#define MARKER_RST2 0xd2 +#define MARKER_RST3 0xd3 +#define MARKER_RST4 0xd4 +#define MARKER_RST5 0xd5 +#define MARKER_RST6 0xd6 +#define MARKER_RST7 0xd7 + +#define MARKER_SOI 0xd8 +#define MARKER_EOI 0xd9 +#define MARKER_SOS 0xda +#define MARKER_DQT 0xdb +#define MARKER_DNL 0xdc +#define MARKER_DRI 0xdd +#define MARKER_DHP 0xde +#define MARKER_EXP 0xdf + +#define MARKER_APP 0xe0 /* from 0xe0 - 0xef */ +#define MARKER_JPG 0xf0 /* from 0xf0 - 0xfd */ +#define MARKER_COM 0xfe + +#define MARKER_SHORT_HEADER (MARKER_APP+1) + +/* End of File */ diff --git a/ip/xmatrix.c b/ip/xmatrix.c new file mode 100644 index 0000000..759f5b0 --- /dev/null +++ b/ip/xmatrix.c @@ -0,0 +1,547 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xmatrix.c - runs color data thru a 3x3 matrix (24 or 48 bits/pixel) + * + * Mark Overton, June 2000 + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * matrixTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[0] = pointer to the matrix, which is an array of nine + * signed integers (type int) in 8.24 fixed-point. If we pretend that + * the array is named 'm', then pixels are computed as follows: + * + * r_out = m[0]*r_in + m[1]*g_in + m[2]*b_in + * g_out = m[3]*r_in + m[4]*g_in + m[5]*b_in + * b_out = m[6]*r_in + m[7]*g_in + m[8]*b_in + * + * Capabilities and Limitations: + * + * Passes pixels through a 3x3 matrix. 24 or 48 bits/pixel only. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 24 or 48 same as default input + * iComponentsPerPixel * must be 3 same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +/* Use the #define below if this transform will exist in a dll outside of the + * image pipeline. This will allow the functions to be exported. + * #define EXPORT_TRANFORM 1 + */ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows processed so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + int mat[9]; /* the matrix */ + DWORD dwValidChk; /* struct validity check value */ +} MAT_INST, *PMAT_INST; + + + +/*****************************************************************************\ + * + * mat_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PMAT_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(MAT_INST), g); + *pXform = g; + memset (g, 0, sizeof(MAT_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * mat_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PMAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow > 0); + INSURE (pTraits->iBitsPerPixel==24 || pTraits->iBitsPerPixel==48); + INSURE (pTraits->iComponentsPerPixel == 3); + + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * mat_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PMAT_INST g; + int *ptr; + + HANDLE_TO_PTR (hXform, g); + + ptr = (int*)aXformInfo[0].pvoid; + INSURE (ptr != NULL); + memcpy (g->mat, ptr, sizeof(g->mat)); + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * mat_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * mat_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PMAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + /* At this point, nothing will change, so compute internal variables */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + + if (g->traits.iBitsPerPixel == 24) { + int i; + for (i=0; i<9; i++) { + /* we'll use 16 bits of fraction instead of 24 */ + g->mat[i] = (g->mat[i]+(1<<7)) >> 8; + } + } + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * mat_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD mat_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PMAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * mat_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PMAT_INST g; + int nBytes; + PBYTE pIn, pOut, pOutAfter; + int rIn, gIn, bIn, rOut, gOut, bOut; + + HANDLE_TO_PTR (hXform, g); + + /**** Check for flushing ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("mat_convert: Told to flush.\n"), 0, 0); + /* we are done */ + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pbOutputBuf + nBytes; + + if (g->traits.iBitsPerPixel == 24) + { + while (pOut < pOutAfter) + { + rIn = (int)(unsigned)(*pIn++); + gIn = (int)(unsigned)(*pIn++); + bIn = (int)(unsigned)(*pIn++); + + /* for this 24-bit case, we assume that the matrix has already + * been shifted down to just 16 bits of fraction instead of 24 */ + rOut = rIn*g->mat[0] + gIn*g->mat[1] + bIn*g->mat[2]; + gOut = rIn*g->mat[3] + gIn*g->mat[4] + bIn*g->mat[5]; + bOut = rIn*g->mat[6] + gIn*g->mat[7] + bIn*g->mat[8]; + + /* we have 16 bits of fraction, so round to integer */ + rOut = (rOut+(1<<15)) >> 16; + gOut = (gOut+(1<<15)) >> 16; + bOut = (bOut+(1<<15)) >> 16; + + /* make sure the result fits in a byte */ + if (rOut > 255) rOut = 255; else if (rOut < 0) rOut = 0; + if (gOut > 255) gOut = 255; else if (gOut < 0) gOut = 0; + if (bOut > 255) bOut = 255; else if (bOut < 0) bOut = 0; + + *pOut++ = (BYTE)rOut; + *pOut++ = (BYTE)gOut; + *pOut++ = (BYTE)bOut; + } + } + else /* 48 bits per pixel */ + { + WORD *pwIn, *pwOut; + pwIn = (WORD*)pIn; + pwOut = (WORD*)pOut; + + while (pwOut < (WORD*)pOutAfter) + { + int prod0, prod1, prod2; + + /* The fixed-point calculations below are as follows: + * 17.15 = input pixel + * 8.24 = factor from matrix + * 25.39 = result of 32x32->64 multiply of above numbers + * 25.7 = result of discarding low 32 bits of 64-bit product + * Finally, we add (1<<6) and shift right 7 to round to integer. + * + * The MUL32HIHALF macro does a signed 32x32->64 multiply, and then + * discards the low 32 bits, giving you the high 32 bits. + */ + + rIn = (int)(unsigned)(*pwIn++) << 15; + gIn = (int)(unsigned)(*pwIn++) << 15; + bIn = (int)(unsigned)(*pwIn++) << 15; + + MUL32HIHALF (rIn, g->mat[0], prod0); + MUL32HIHALF (gIn, g->mat[1], prod1); + MUL32HIHALF (bIn, g->mat[2], prod2); + rOut = (prod0 + prod1 + prod2 + (1<<6)) >> 7; + if (rOut > 0x00ffff) rOut = 0x00ffff; else if (rOut < 0) rOut = 0; + *pwOut++ = rOut; + + MUL32HIHALF (rIn, g->mat[3], prod0); + MUL32HIHALF (gIn, g->mat[4], prod1); + MUL32HIHALF (bIn, g->mat[5], prod2); + gOut = (prod0 + prod1 + prod2 + (1<<6)) >> 7; + if (gOut > 0x00ffff) gOut = 0x00ffff; else if (gOut < 0) gOut = 0; + *pwOut++ = gOut; + + MUL32HIHALF (rIn, g->mat[6], prod0); + MUL32HIHALF (gIn, g->mat[7], prod1); + MUL32HIHALF (bIn, g->mat[8], prod2); + bOut = (prod0 + prod1 + prod2 + (1<<6)) >> 7; + if (bOut > 0x00ffff) bOut = 0x00ffff; else if (bOut < 0) bOut = 0; + *pwOut++ = bOut; + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * mat_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * mat_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_newPage ( + IP_XFORM_HANDLE hXform) +{ + PMAT_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * mat_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD mat_closeXform (IP_XFORM_HANDLE hXform) +{ + PMAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * matTbl - Jump-table for transform driver + * +\*****************************************************************************/ +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL matrixTbl = { + mat_openXform, + mat_setDefaultInputTraits, + mat_setXformSpec, + mat_getHeaderBufSize, + mat_getActualTraits, + mat_getActualBufSizes, + mat_convert, + mat_newPage, + mat_insertedData, + mat_closeXform +}; + +/* End of File */ + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL pXform) +{ + WORD wRet = IP_DONE; + + if (pXform) + { + *pXform = clrmapTbl; + } + else + { + wRet = IP_FATAL_ERROR; + } + + return wRet; +} +#endif diff --git a/ip/xpad.c b/ip/xpad.c new file mode 100644 index 0000000..5e32eaa --- /dev/null +++ b/ip/xpad.c @@ -0,0 +1,523 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xpad.c - Pads all four sides of the input image with extra pixels + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * padTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_PAD_LEFT ] = left: # of pixels to add to left side (or negative) + * aXformInfo[IP_PAD_RIGHT ] = right: # of pixels to add to right side + * aXformInfo[IP_PAD_TOP ] = top: # of rows to add to top (or negative) + * aXformInfo[IP_PAD_BOTTOM] = bottom: # of rows to add to bottom + * aXformInfo[IP_PAD_VALUE ] = pad value (0-255) + * aXformInfo[IP_PAD_MIN_HEIGHT] = minimum # of input rows, pad if needed + * + * The pad value above (at index 4) is the value of the added pad-pixels. + * For color data, all three bytes will be set to this value. + * For bi-level data, only the lsb is used. + * + * If 'left' is negative, then the output width will be forced to be a + * multiple of abs(left) pixels. + * If 'top' is negative, the same thing is done with the height. + * + * Capabilities and Limitations: + * + * Pads all four sides of the image. + * The image data must be fixed-length rows of uncompressed pixels. + * For bilevel data, the "left" value is changed to the nearest multiple + * of 8, and the "right" value is changed so the resulting row-width + * does not change. + * If all pad-amounts above are 0, this xform becomes merely a pass-thru. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * used input width + horiz pad + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows used if known output height, if input known + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input image */ + DWORD dwLeft, dwRight; /* # pixels to pad, left and right sides */ + DWORD dwTop; /* # rows to pad to top */ + DWORD dwBottom; /* # rows to pad to bottom */ + DWORD dwMinInRows; /* minimum # input rows, pad if necessary */ + DWORD dwInBytesPerRow; /* # bytes in each input row */ + DWORD dwOutBytesPerRow; /* # bytes in each output row */ + DWORD dwLeftPadBytes; /* # bytes to add to left side of each row */ + DWORD dwRightPadBytes; /* # bytes to add to right side of each row */ + BYTE bPadVal; /* value of pad pixels */ + DWORD dwInRows; /* number of rows input so far */ + DWORD dwOutRows; /* number of rows output so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} PAD_INST, *PPAD_INST; + + + +/*****************************************************************************\ + * + * pad_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD pad_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PPAD_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(PAD_INST), g); + *pXform = g; + memset (g, 0, sizeof(PAD_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pad_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD pad_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PPAD_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0); + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pad_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD pad_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PPAD_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwLeft = aXformInfo[IP_PAD_LEFT].dword; + g->dwRight = aXformInfo[IP_PAD_RIGHT].dword; + g->dwTop = aXformInfo[IP_PAD_TOP].dword; + g->dwBottom = aXformInfo[IP_PAD_BOTTOM].dword; + g->bPadVal = (BYTE)aXformInfo[IP_PAD_VALUE].dword; + g->dwMinInRows = aXformInfo[IP_PAD_MIN_HEIGHT].dword; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pad_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD pad_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * pad_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD pad_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PPAD_INST g; + int left, right, shift, more; + int inWidth, outWidth; + int bpp; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Compute the pad info */ + + bpp = g->traits.iBitsPerPixel; + left = g->dwLeft; + right = g->dwRight; + inWidth = g->traits.iPixelsPerRow; + + if (left < 0) { + left = -left; + // pad horizontally so that width is a multiple of left + outWidth = ((inWidth+left-1) / left) * left; + more = outWidth - inWidth; + left = more >> 1; + right = more - left; + g->dwLeft = left; + g->dwRight = right; + } + + outWidth = inWidth + left + right; + + if (bpp == 1) { + /* shift to start at nearest byte boundary */ + shift = ((left+4) & ~7) - left; + left += shift; /* this is now a multiple of 8 */ + right += shift; + + g->bPadVal = g->bPadVal & 1 ? 0xffu : 0; + } + + g->dwInBytesPerRow = (bpp*inWidth + 7) / 8; + g->dwOutBytesPerRow = (bpp*outWidth + 7) / 8; + g->dwLeftPadBytes = (bpp*left + 7) / 8; + g->dwRightPadBytes = g->dwOutBytesPerRow - g->dwInBytesPerRow - g->dwLeftPadBytes; + + /* Report the traits */ + + *pInTraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + pOutTraits->iPixelsPerRow = outWidth; + + if (pInTraits->lNumRows > 0) { + if ((int)g->dwTop < 0) { + int top, bot, inHeight, outHeight; + top = - (int)g->dwTop; + // pad vertically so that #rows is a multiple of top + inHeight = pInTraits->lNumRows; + outHeight = ((inHeight+top-1) / top) * top; + more = outHeight - inHeight; + top = more >> 1; + bot = more - top; + g->dwTop = top; + g->dwBottom = bot; + } + + pOutTraits->lNumRows = pInTraits->lNumRows + g->dwTop + g->dwBottom; + } + + INSURE ((int)g->dwLeft >= 0); + INSURE ((int)g->dwTop >= 0); + + return IP_DONE | (g->dwTop>0 ? 0 : IP_READY_FOR_DATA); + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * pad_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD pad_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PPAD_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = g->dwInBytesPerRow; + *pdwMinOutBufLen = g->dwOutBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pad_convert - Converts one row + * +\*****************************************************************************/ + +static WORD pad_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PPAD_INST g; + BOOL bWhiteRow; + + HANDLE_TO_PTR (hXform, g); + bWhiteRow = FALSE; + + /**** Decide what to do ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("pad_convert: Told to flush.\n"), 0, 0); + + if (g->dwInRows < g->dwMinInRows) { + /* We need to output another pad row on the bottom */ + bWhiteRow = TRUE; + g->dwInRows += 1; + } else if (g->dwBottom > 0) { + /* We need to output another pad row on the bottom */ + bWhiteRow = TRUE; + g->dwBottom -= 1; + } else { + /* We are done */ + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + } else if (g->dwOutRows < g->dwTop) { + /* We need to output another pad row on the top */ + bWhiteRow = TRUE; + } + + /**** Output a Row ****/ + + INSURE (bWhiteRow || (dwInputAvail >= g->dwInBytesPerRow)); + INSURE (dwOutputAvail >= g->dwOutBytesPerRow); + + if (bWhiteRow) + memset (pbOutputBuf, g->bPadVal, g->dwOutBytesPerRow); + else { + BYTE *p = pbOutputBuf; + memset (p, g->bPadVal, g->dwLeftPadBytes); + p += g->dwLeftPadBytes; + memcpy (p, pbInputBuf, g->dwInBytesPerRow); + p += g->dwInBytesPerRow; + memset (p, g->bPadVal, g->dwRightPadBytes); + + g->dwInRows += 1; + *pdwInputUsed = g->dwInBytesPerRow; + g->dwInNextPos += g->dwInBytesPerRow; + } + + g->dwOutRows += 1; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputUsed = g->dwOutBytesPerRow; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += g->dwOutBytesPerRow; + + return (bWhiteRow ? 0 : IP_CONSUMED_ROW) | + IP_PRODUCED_ROW | + (g->dwOutRows < g->dwTop ? 0 : IP_READY_FOR_DATA); + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pad_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD pad_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * pad_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD pad_newPage ( + IP_XFORM_HANDLE hXform) +{ + PPAD_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * pad_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD pad_closeXform (IP_XFORM_HANDLE hXform) +{ + PPAD_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * padTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL padTbl = { + pad_openXform, + pad_setDefaultInputTraits, + pad_setXformSpec, + pad_getHeaderBufSize, + pad_getActualTraits, + pad_getActualBufSizes, + pad_convert, + pad_newPage, + pad_insertedData, + pad_closeXform +}; + +/* End of File */ diff --git a/ip/xpcx.c b/ip/xpcx.c new file mode 100644 index 0000000..6a9327c --- /dev/null +++ b/ip/xpcx.c @@ -0,0 +1,1318 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * xpcx.c - encoder and decoder for PCX files for Image Processor + * + ***************************************************************************** + * + * Name of Global Jump-Table: + * + * pcxEncodeTbl = the encoder, + * pcxDecodeTbl = the decoder. + * + * Items in aXformInfo array passed into setXformSpec: + * + * none + * + * Capabilities and Limitations: + * + * Handles 1 or 4 bits per pixel. + * Decoder discards the palette in the PCX file (todo: keep it). + * Encoder assumes input is internal raw format, which means that + * 1 and 4 bits/pixel are assumed to be grayscales, where + * 1 bit/pixel is [0=white, 1=black], 4 bits/pixel is [0=black, 15=white]. + * Encoder outputs a palette for the preceding gray-ranges. + * If # rows is not known in input traits (negative), the encoder will + * re-write the header when # rows is known at the end of the page. So + * the output file will have a valid row-count when conversion is done. + * + * Default Input Traits, and Output Traits: + * + * For decoder: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow ignored based on header + * iBitsPerPixel ignored based on header (1 or 4) + * iComponentsPerPixel ignored 1 + * lHorizDPI ignored based on header + * lVertDPI ignored based on header + * lNumRows ignored based on header + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * For encoder: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 1 or 4 same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Mark Overton, Feb 1998 + * +\*****************************************************************************/ + +/* todo: this PCX code encodes/decodes 4-bit gray as one pixel per byte. + * it should be two pixels per byte. + */ + + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + +/* TODO: Make this work for big and little endian. */ +#define LITTLE_ENDIAN (! (defined SNAKES)) + + +/* PCX_INST - our instance variables */ + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the image */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + BOOL fDidHeader; /* already processed the header? */ + BYTE *pPlanes; /* buffer containing separated planes */ + UINT uBytesPerRawRow; /* number of bytes per unencoded row */ + UINT uBytesPerPlane; /* number of bytes per plane per row */ + UINT uRowsDone; /* number of rows converted so far */ + DWORD dwValidChk; /* struct validity check value */ +} PCX_INST, *PPCX_INST; + + + +/*____________________________________________________________________________ + | | | + | pcx_header_t | Each PCX file starts with this 128-byte header | + |______________|_____________________________________________________________| +*/ +typedef struct { + BYTE PcxId; /* must be 0Ah, which means PC Paintbrush */ + BYTE Version; /* 2=v2.8 w/ palette; 3=v2.8 w/o pal; 5=v3 w/ pal */ + BYTE EncodingMethod; /* encoding method == 1 */ + BYTE BitsPerPixel; /* = 1 for fax mode transfers */ + WORD XMin; /* X position of the upper left corner */ + WORD YMin; /* Y position of the upper left corner */ + WORD XMax; /* X position of the bottom right corner */ + WORD YMax; /* Y position of the bottom right corner */ + WORD HorizResolution; /* horizontal resolution */ + WORD VertResolution; /* vertical resolution */ + BYTE PaletteInfo[48]; /* Palette information */ + BYTE Reserved1; /* reserved, must be zero */ + BYTE ColorPlanes; /* number of color planes == 1 */ + WORD BytesPerPlane; /* bytes per plane per row */ + BYTE Reserved2[60]; /* reserved, should be zero */ +} pcx_header_t; + +#define PCX_ID 0x0A +#define PCX_VERSION 2 +#define PCX_HEADER_SIZE 128 + + +/*____________________________________________________________________________ + | | | + | swap_header_bytes | if big-endian machine, swaps bytes in WORD items | + |___________________|________________________________________________________| +*/ +static void swap_header_bytes (pcx_header_t *head_p) +{ +#if ! LITTLE_ENDIAN + #define SWAP_IT(item) \ + head_p->item = (head_p->item << 8) | (head_p->item >> 8) + + SWAP_IT (XMin); + SWAP_IT (YMin); + SWAP_IT (XMax); + SWAP_IT (YMax); + SWAP_IT (HorizResolution); + SWAP_IT (VertResolution); + SWAP_IT (BytesPerPlane); +#endif +} + + + +/*____________________________________________________________________________ + | | | + | encode_buffer | encodes inbuf (separated planes) into run-length PCX data | + |_______________|____________________________________________________________| +*/ +static UINT encode_buffer ( /* ret-val is # bytes written to outbuf */ + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to inbuf having separated planes */ + BYTE *outbuf_p) /* in: ptr to outbuf to get PCX run-lens */ +{ + BYTE *out_p; + BYTE *beg_run_p; + BYTE *aft_run_p; + BYTE *inbuf_aft_p; + BYTE byt; + UINT run_len; + + out_p = outbuf_p; + beg_run_p = inbuf_p; + inbuf_aft_p = inbuf_p + g->uBytesPerPlane * g->traits.iBitsPerPixel; + + while (beg_run_p < inbuf_aft_p) + { + byt = *beg_run_p; + + for (aft_run_p = beg_run_p+1; + aft_run_p<inbuf_aft_p && *aft_run_p==byt; + aft_run_p++) ; + + run_len = aft_run_p - beg_run_p; + if (run_len > 63) { + run_len = 63; + aft_run_p = beg_run_p + 63; + } + + if (run_len>1 || byt>=(BYTE)0xc0) + *out_p++ = (BYTE)run_len | (BYTE)0xc0; + *out_p++ = byt; + + beg_run_p = aft_run_p; + } + + return out_p - outbuf_p; +} + + + +/*____________________________________________________________________________ + | | | + | decode_buffer | decodes PCX run-length data into separated planes | + |_______________|____________________________________________________________| +*/ +static UINT decode_buffer ( /* ret-val is # bytes read from inbuf */ + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + BYTE *in_p; + BYTE *out_p; + BYTE *out_aft_p; + UINT run_len; + BYTE byt; + + in_p = inbuf_p; + out_p = outbuf_p; + out_aft_p = outbuf_p + g->uBytesPerPlane*g->traits.iBitsPerPixel; + + while (out_p < out_aft_p) + { + byt = *in_p++; + + if (byt < (BYTE)0xc0) + *out_p++ = byt; + else { + run_len = byt & (BYTE)0x3f; + if (run_len > (UINT) (out_aft_p - out_p)) + run_len = out_aft_p-out_p; /* run went past end of outbuf */ + memset (out_p, *in_p++, run_len); + out_p += run_len; + } + } + + return in_p - inbuf_p; +} + + + +/*____________________________________________________________________________ + | | + | Encoding/Decoding Bilevel | + |____________________________________________________________________________| +*/ + + + +/*____________________________________________________________________________ + | | | + | flip_pixels | in PCX, 0=black and 1=white, so this flips all the bits | + |_____________|______________________________________________________________| +*/ +static void flip_pixels ( + PCX_INST *g, /* our instance variables */ + BYTE *byte_buf_p) /* ptr to buffer to be flipped */ +{ + ULONG *buf_p; + ULONG *buf_aft_p; + + buf_p = (ULONG*)byte_buf_p; + buf_aft_p = buf_p + ((g->uBytesPerRawRow+3) >> 2); + for ( ; buf_p<buf_aft_p; buf_p++) + *buf_p = ~ *buf_p; +} + + + +static UINT encode_1 ( + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + UINT n_bytes; + + flip_pixels (g, inbuf_p); + n_bytes = encode_buffer (g, inbuf_p, outbuf_p); + flip_pixels (g, inbuf_p); + return n_bytes; +} + + + +static UINT decode_1 ( + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + UINT used; + + used = decode_buffer (g, inbuf_p, outbuf_p); + flip_pixels (g, outbuf_p); + return used; +} + + + +/*____________________________________________________________________________ + | | + | Encoding/Decoding 16-Level | + |____________________________________________________________________________| +*/ + + +#if 0 + +/*____________________________________________________________________________ + | | | + | pcxtable.c | Outputs table for PCX's 16-level decoder to stdout | + |____________|_______________________________________________________________| +*/ + +#include <stdio.h> +#define little_endian 1 + +void main (void) +{ + unsigned nib; + long unsigned outlong; + + for (nib=0; nib<=15; nib++) { + outlong = 0; + if (nib & 0x08) outlong |= 0x10000000; + if (nib & 0x04) outlong |= 0x00100000; + if (nib & 0x02) outlong |= 0x00001000; + if (nib & 0x01) outlong |= 0x00000010; + + if (little_endian) + outlong = (outlong & 0xff000000) >> 24 | + (outlong & 0x00ff0000) >> 8 | + (outlong & 0x0000ff00) << 8 | + (outlong & 0x000000ff) << 24; + + _tprintf (_T("0x%08x, "), outlong); + if ((nib%4) == 3) _tputs(_T("")); + } + _tputs (_T("")); +} + +#endif + + + +static UINT encode_4 ( + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + #if LITTLE_ENDIAN + #define NIB_0 0x000000f0 + #define NIB_1 0x0000f000 + #define NIB_2 0x00f00000 + #define NIB_3 0xf0000000 + #else + #define NIB_0 0xf0000000 + #define NIB_1 0x00f00000 + #define NIB_2 0x0000f000 + #define NIB_3 0x000000f0 + #endif + + ULONG *in_p; + ULONG *in_aft_p; + BYTE *plane_p; + ULONG quad; + ULONG mask; + BYTE byt; + + /* Separate the four planes (into pPlanes) */ + + in_aft_p = (ULONG *) (inbuf_p + g->uBytesPerRawRow); + plane_p = g->pPlanes; + mask = 0x10101010; + + while (TRUE) + { + for (in_p=(ULONG*)inbuf_p; in_p<in_aft_p; ) { + byt = 0; + quad = *in_p++ & mask; + if (quad & NIB_0) byt = 0x80; + if (quad & NIB_1) byt |= 0x40; + if (quad & NIB_2) byt |= 0x20; + if (quad & NIB_3) byt |= 0x10; + + quad = *in_p++ & mask; + if (quad & NIB_0) byt |= 0x08; + if (quad & NIB_1) byt |= 0x04; + if (quad & NIB_2) byt |= 0x02; + if (quad & NIB_3) byt |= 0x01; + *plane_p++ = byt; + } + + if (mask == 0x80808080) + break; + mask <<= 1; + } + + return encode_buffer (g, g->pPlanes, outbuf_p); +} + + + +static UINT decode_4 ( + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + #if LITTLE_ENDIAN + static const ULONG unscramble[16] = { + 0x00000000, 0x10000000, 0x00100000, 0x10100000, + 0x00001000, 0x10001000, 0x00101000, 0x10101000, + 0x00000010, 0x10000010, 0x00100010, 0x10100010, + 0x00001010, 0x10001010, 0x00101010, 0x10101010, + }; + #else + static const ULONG unscramble[16] = { + 0x00000000, 0x00000010, 0x00001000, 0x00001010, + 0x00100000, 0x00100010, 0x00101000, 0x00101010, + 0x10000000, 0x10000010, 0x10001000, 0x10001010, + 0x10100000, 0x10100010, 0x10101000, 0x10101010, + }; + #endif + + UINT used; + BYTE *plane_p; + BYTE *plane_aft_p; + ULONG *out_p; + + used = decode_buffer (g, inbuf_p, g->pPlanes); + + /* Combine the separated planes (in pPlanes) back into pixels */ + + plane_p = g->pPlanes; + + out_p = (ULONG*)outbuf_p; + plane_aft_p = plane_p + g->uBytesPerPlane; + for (; plane_p<plane_aft_p; plane_p++) { + *out_p++ = unscramble[*plane_p >> 4]; + *out_p++ = unscramble[*plane_p & 15]; + } + + out_p = (ULONG*)outbuf_p; + plane_aft_p = plane_p + g->uBytesPerPlane; + for (; plane_p<plane_aft_p; plane_p++) { + *out_p++ |= unscramble[*plane_p >> 4] << 1; + *out_p++ |= unscramble[*plane_p & 15] << 1; + } + + out_p = (ULONG*)outbuf_p; + plane_aft_p = plane_p + g->uBytesPerPlane; + for (; plane_p<plane_aft_p; plane_p++) { + *out_p++ |= unscramble[*plane_p >> 4] << 2; + *out_p++ |= unscramble[*plane_p & 15] << 2; + } + + out_p = (ULONG*)outbuf_p; + plane_aft_p = plane_p + g->uBytesPerPlane; + for (; plane_p<plane_aft_p; plane_p++) { + *out_p++ |= unscramble[*plane_p >> 4] << 3; + *out_p++ |= unscramble[*plane_p & 15] << 3; + } + + return used; +} + + + +/*____________________________________________________________________________ + | | + | Encoding/Decoding 256-Level | + |____________________________________________________________________________| +*/ + + +#if 0 + +static UINT encode_8 ( + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + assert (0); + return 0; +} + + + +static UINT decode_8 ( + PCX_INST *g, /* in: our instance variables */ + BYTE *inbuf_p, /* in: ptr to input buffer */ + BYTE *outbuf_p) /* in: ptr to output buffer */ +{ + assert (0); + return 0; +} + +#endif /* 256-level stuff */ + + + +/* +****************************************************************************** +****************************************************************************** +** +** +** E N C O D E R +** +** +****************************************************************************** +****************************************************************************** +*/ + + + +/*****************************************************************************\ + * + * pcxEncode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD pcxEncode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PPCX_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(PCX_INST), g); + *pXform = g; + memset (g, 0, sizeof(PCX_INST)); + g->dwValidChk = CHECK_VALUE; + INSURE (sizeof(pcx_header_t) == PCX_HEADER_SIZE); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxEncode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD pcxEncode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + INSURE (pTraits->iBitsPerPixel==1 || pTraits->iBitsPerPixel==4); + INSURE (pTraits->iPixelsPerRow > 0); + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxEncode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD pcxEncode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* do nothing, because we don't have any xform-specific info */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxEncode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD pcxEncode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + *pdwInBufLen = 0; /* no header on raw input data */ + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * pcxEncode_getActualTraits - Parses header, and returns input & output traits + * + ***************************************************************************** + * + * If depth is 1, a bilevel PCX will be encoded/decoded from/to a + * RASTER_BITMAP image. + * + * If depth is 4, a 16-level PCX will be encoded/decoded from/to gray data. + * The 4-bit gray value is in the upper nibble of each byte in the raw gray + * data. + * + * If depth is 8, a 256-level PCX will be encoded/decoded from/to gray data. + * + * If depth is 24, the PCX will be encoded/decoded from/to RGB data. + * +\*****************************************************************************/ + +static WORD pcxEncode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no input header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Since we don't change traits, just copy out the default traits */ + *pInTraits = g->traits; + *pOutTraits = g->traits; + + /***************************/ + /* Process the traits info */ + /***************************/ + + g->uBytesPerPlane = (g->traits.iPixelsPerRow + 7) / 8; + g->uBytesPerRawRow = g->traits.iBitsPerPixel == 1 + ? g->uBytesPerPlane + : g->traits.iPixelsPerRow; + + PRINT (_T("pcx_encode_output_header: pixels/row=%d, n_rows=%d\n"), + g->traits.iPixelsPerRow, g->traits.lNumRows); + PRINT (_T("pcx_encode_output_header: depth=%d, uBytesPerRawRow=%d\n"), + g->traits.iBitsPerPixel, g->uBytesPerRawRow); + + if (g->traits.iBitsPerPixel > 1) + IP_MEM_ALLOC (g->uBytesPerPlane*g->traits.iBitsPerPixel, g->pPlanes); + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * pcxEncode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD pcxEncode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->uBytesPerRawRow; + *pdwMinOutBufLen = g->traits.iBitsPerPixel * g->uBytesPerPlane * 2; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * outputHeader - Only called by pcxEncode_convert + * +\****************************************************************************/ + +static WORD outputHeader ( + PPCX_INST g, /* in: ptr to instance structure */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + pcx_header_t *pPCXHeader; + PIP_IMAGE_TRAITS pTr; + BYTE *pal_p; + UINT i; + + *pdwOutputThisPos = 0; + *pdwOutputUsed = PCX_HEADER_SIZE; + g->dwOutNextPos = PCX_HEADER_SIZE; + INSURE (dwOutputAvail >= PCX_HEADER_SIZE); + + pTr = &g->traits; + pPCXHeader = (pcx_header_t *)pbOutputBuf; + + pPCXHeader->PcxId = PCX_ID; + pPCXHeader->Version = PCX_VERSION; + pPCXHeader->EncodingMethod = 1; + pPCXHeader->BitsPerPixel = 1; + pPCXHeader->XMin = 0; + pPCXHeader->YMin = 0; + pPCXHeader->XMax = pTr->iPixelsPerRow - 1; + pPCXHeader->YMax = pTr->lNumRows<=0 ? 0 : pTr->lNumRows-1; + pPCXHeader->HorizResolution = (USHORT)(pTr->lHorizDPI>>16); /* todo: use width */ + pPCXHeader->VertResolution = (USHORT)(pTr->lVertDPI >>16); /* and height? */ + pPCXHeader->Reserved1 = 0; + pPCXHeader->ColorPlanes = pTr->iBitsPerPixel; + pPCXHeader->BytesPerPlane = g->uBytesPerPlane; + + memset (pPCXHeader->Reserved2, 0, 60); + + pal_p = pPCXHeader->PaletteInfo; + if (pTr->iBitsPerPixel == 1) { + memset (pal_p, 0, 48); + pal_p[3] = pal_p[4] = pal_p[5] = 255; /* 0=black, 1=white */ + } else { + /* set the palette to a black-to-white gray ramp */ + for (i=0; i<=15; i++) { + *pal_p++ = i << 4; /* red */ + *pal_p++ = i << 4; /* green */ + *pal_p++ = i << 4; /* blue */ + } + } + + swap_header_bytes (pPCXHeader); + return IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxEncode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD pcxEncode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PPCX_INST g; + UINT out_used = 0; /* init to zap stupid compiler warning */ + + HANDLE_TO_PTR (hXform, g); + + /**** Output the Header if we haven't already ****/ + + if (! g->fDidHeader) { + g->fDidHeader = TRUE; + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + return outputHeader (g, dwOutputAvail, pbOutputBuf, + pdwOutputUsed, pdwOutputThisPos); + } + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("pcx_encode_convert_row: Told to flush.\n"), 0, 0); + if (g->traits.lNumRows < 0) { + /* # rows wasn't known at first, so output header again + * now that we know the number of rows */ + g->traits.lNumRows = g->uRowsDone; + *pdwInputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + return outputHeader (g, dwOutputAvail, pbOutputBuf, + pdwOutputUsed, pdwOutputThisPos); + } + + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + switch (g->traits.iBitsPerPixel) { + case 1: out_used = encode_1 (g, pbInputBuf, pbOutputBuf); + break; + + case 4: out_used = encode_4 (g, pbInputBuf, pbOutputBuf); + break; + #if 0 + case 8: out_used = encode_8 (g, pbInputBuf, pbOutputBuf); + break; + #endif + } + + INSURE (dwInputAvail >= g->uBytesPerRawRow); + INSURE (dwOutputAvail >= out_used); + + g->dwInNextPos += g->uBytesPerRawRow; + *pdwInputNextPos = g->dwInNextPos; + *pdwInputUsed = g->uBytesPerRawRow; + *pdwOutputUsed = out_used; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += out_used; + + g->uRowsDone += 1; + + PRINT (_T("pcx_encode_convert_row: Returning, out used = %d\n"), out_used, 0); + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxEncode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD pcxEncode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * pcxEncode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD pcxEncode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * pcxEncode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD pcxEncode_closeXform (IP_XFORM_HANDLE hXform) +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + if (g->pPlanes != NULL) + IP_MEM_FREE (g->pPlanes); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxEncodeTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL pcxEncodeTbl = { + pcxEncode_openXform, + pcxEncode_setDefaultInputTraits, + pcxEncode_setXformSpec, + pcxEncode_getHeaderBufSize, + pcxEncode_getActualTraits, + pcxEncode_getActualBufSizes, + pcxEncode_convert, + pcxEncode_newPage, + pcxEncode_insertedData, + pcxEncode_closeXform +}; + + + +/* +****************************************************************************** +****************************************************************************** +** +** +** D E C O D E R +** +** +****************************************************************************** +****************************************************************************** +*/ + + + +/*****************************************************************************\ + * + * pcxDecode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD pcxDecode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + return pcxEncode_openXform(pXform); /* allocs & zeroes a new instance */ +} + + + +/*****************************************************************************\ + * + * pcxDecode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD pcxDecode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* the PCX header will overwrite most items in traits below */ + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxDecode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD pcxDecode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* do nothing, because we don't have any xform-specific info */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxDecode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD pcxDecode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + *pdwInBufLen = PCX_HEADER_SIZE; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * pcxDecode_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD pcxDecode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PPCX_INST g; + pcx_header_t *pcxhead_p; + PIP_IMAGE_TRAITS pTr; + WORD ret_val; + + ret_val = IP_DONE | IP_READY_FOR_DATA; + HANDLE_TO_PTR (hXform, g); + + INSURE (dwInputAvail >= PCX_HEADER_SIZE); + *pdwInputUsed = PCX_HEADER_SIZE; + *pdwInputNextPos = PCX_HEADER_SIZE; + g->dwInNextPos = PCX_HEADER_SIZE; + + pcxhead_p = (pcx_header_t*)pbInputBuf; + pTr = &g->traits; + + swap_header_bytes (pcxhead_p); + pTr->lNumRows = pcxhead_p->YMax - pcxhead_p->YMin + 1; + pTr->iPixelsPerRow = pcxhead_p->XMax - pcxhead_p->XMin + 1; + pTr->iBitsPerPixel = pcxhead_p->ColorPlanes; + pTr->lHorizDPI = (ULONG)pcxhead_p->HorizResolution << 16; + pTr->lVertDPI = (ULONG)pcxhead_p->VertResolution << 16; + pTr->iComponentsPerPixel = 1; + g->uBytesPerPlane = pcxhead_p->BytesPerPlane; + swap_header_bytes (pcxhead_p); + + g->uBytesPerRawRow = pTr->iBitsPerPixel == 1 + ? g->uBytesPerPlane + : g->traits.iPixelsPerRow; + + if (! (pcxhead_p->PcxId == PCX_ID + && pTr->iPixelsPerRow > 1 + && g->uBytesPerPlane == ((UINT)pTr->iPixelsPerRow+7)/8 + && (pTr->iBitsPerPixel==1 || pTr->iBitsPerPixel==4))) + ret_val |= IP_INPUT_ERROR; + + if (pTr->iBitsPerPixel > 1) + IP_MEM_ALLOC (g->uBytesPerPlane*pTr->iBitsPerPixel, g->pPlanes); + + if (pTr->lNumRows <= 1) + pTr->lNumRows = -1; /* both YMax and YMin were 0; # rows is unknown */ + + *pInTraits = *pOutTraits = g->traits; /* structure copy */ + + PRINT (_T("pcx_decode_parse_header: depth=%d, n_rows=%d\n"), + g->traits.iBitsPerPixel, g->traits.lNumRows); + + return ret_val; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * pcxDecode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD pcxDecode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->traits.iBitsPerPixel * g->uBytesPerPlane * 2; + *pdwMinOutBufLen = g->uBytesPerRawRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxDecode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD pcxDecode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PPCX_INST g; + DWORD in_used=0; + + HANDLE_TO_PTR (hXform, g); + + if (pbInputBuf == NULL) { + /* we are being told to flush */ + PRINT (_T("pcx_decode_convert_row: Told to flush; doing nothing.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + if (g->traits.lNumRows>=0 && g->uRowsDone==(UINT)g->traits.lNumRows) { + /* discard extra data after final row */ + *pdwInputUsed = dwInputAvail; + g->dwInNextPos += dwInputAvail; + *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return 0; + } + + switch (g->traits.iBitsPerPixel) { + case 1: in_used = decode_1 (g, pbInputBuf, pbOutputBuf); + break; + + case 4: in_used = decode_4 (g, pbInputBuf, pbOutputBuf); + break; + #if 0 + case 8: in_used = decode_8 (g, pbInputBuf, pbOutputBuf); + break; + #endif + } + + INSURE (dwInputAvail >= in_used); + INSURE (dwOutputAvail >= g->uBytesPerRawRow); + + g->dwInNextPos += in_used; + *pdwInputNextPos = g->dwInNextPos; + *pdwInputUsed = in_used; + *pdwOutputUsed = g->uBytesPerRawRow; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += g->uBytesPerRawRow; + + g->uRowsDone += 1; + + PRINT (_T("pcx_decode_convert_row: Returning, in used = %d\n"), in_used, 0); + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxDecode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD pcxDecode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * pcxDecode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD pcxDecode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PPCX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pcxDecode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD pcxDecode_closeXform (IP_XFORM_HANDLE hXform) +{ + return pcxEncode_closeXform (hXform); +} + + + +/*****************************************************************************\ + * + * pcxDecodeTbl - Jump-table for Decoder + * +\*****************************************************************************/ + +IP_XFORM_TBL pcxDecodeTbl = { + pcxDecode_openXform, + pcxDecode_setDefaultInputTraits, + pcxDecode_setXformSpec, + pcxDecode_getHeaderBufSize, + pcxDecode_getActualTraits, + pcxDecode_getActualBufSizes, + pcxDecode_convert, + pcxDecode_newPage, + pcxDecode_insertedData, + pcxDecode_closeXform +}; + +/* End of File */ diff --git a/ip/xpnm.c b/ip/xpnm.c new file mode 100644 index 0000000..5f814df --- /dev/null +++ b/ip/xpnm.c @@ -0,0 +1,591 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: David Paschal (based on Mark Overton's "xskel" template). */ + +/******************************************************************************\ + * + * xpnm.c - encoder and decoder for PNM (PBM, PGM, PPM) files + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * pnmEncodeTbl = the encoder. + * pnmDecodeTbl = the decoder. + * + * Items in aXformInfo array passed into setXformSpec: + * + * None. + * + * Capabilities and Limitations: + * + * Handles 1, 8, and 24 bits per pixel. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel * passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include <stdio.h> + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#define FUNC_STATUS static + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ + BOOL fIsEncode; /* false=decode, true=encode */ + BOOL fDidHeader; /* already sent/processed the header? */ +} PNM_INST, *PPNM_INST; + +#define MAX_DECODE_HEADER_SIZE 4096 +#define MAX_ENCODE_HEADER_SIZE 128 + + + +/*****************************************************************************\ + * + * pnm{De,En}code_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnmDecode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PPNM_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(PNM_INST), g); + *pXform = g; + memset (g, 0, sizeof(PNM_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + +FUNC_STATUS WORD pnmEncode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + WORD wResult=pnmDecode_openXform(pXform); + if (wResult==IP_DONE) { + PPNM_INST g; + + HANDLE_TO_PTR (*pXform, g); + + g->dwOutNextPos=MAX_ENCODE_HEADER_SIZE; + g->fIsEncode=TRUE; + } + return wResult; + + fatal_error: + return IP_FATAL_ERROR; +} + + +/*****************************************************************************\ + * + * pnm_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->traits = *pTraits; /* a structure copy */ + if (g->fIsEncode) { + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0); + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + } + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pnm_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Check your options in aXformInfo here, and save them. + * Use the INSURE macro like you'd use assert. INSURE jumps to + * fatal_error below if it fails. + */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pnm_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (!g->fIsEncode) { + *pdwInBufLen = MAX_DECODE_HEADER_SIZE; + } else { + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + } + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pnm_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +#define PEEK_CHAR(pc) \ + do { \ + if (*pdwInputUsed>=dwInputAvail) { \ + return IP_INPUT_ERROR; \ + } \ + *(pc)=pbInputBuf[*pdwInputUsed]; \ + } while (0) + +#define NEXT_CHAR ((*pdwInputUsed)++) + +#define GET_CHAR(pc) \ + do { \ + PEEK_CHAR(pc); \ + NEXT_CHAR; \ + } while (0) + +#define SKIP_WS \ + do { \ + unsigned char c; \ + PEEK_CHAR(&c); \ + if (c=='#') { \ + /* NEXT_CHAR; */ \ + do { \ + GET_CHAR(&c); \ + } while (c!='\n'); \ + PEEK_CHAR(&c); \ + } \ + if (c>' ') break; \ + NEXT_CHAR; \ + } while (42) + +#define GET_INT(pi) \ + do { \ + unsigned char c; \ + *(pi)=0; \ + SKIP_WS; \ + while (42) { \ + GET_CHAR(&c); \ + c-='0'; \ + if (c>9) { \ + break; \ + } \ + *(pi)=(*(pi)*10)+c; \ + } \ + } while (0) + +FUNC_STATUS WORD pnm_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* If there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + + /* Parse the header for decode operations. */ + if (!g->fIsEncode) { + unsigned char c; + unsigned int maxval; + + GET_CHAR(&c); + if (c!='P') { + return IP_INPUT_ERROR; + } + GET_CHAR(&c); + if (c=='4') { + /* PBM */ + g->traits.iComponentsPerPixel=1; + g->traits.iBitsPerPixel=1; + + } else if (c=='5') { + /* PGM */ + g->traits.iComponentsPerPixel=1; + g->traits.iBitsPerPixel=0; + + } else if (c=='6') { + /* PPM */ + g->traits.iComponentsPerPixel=3; + g->traits.iBitsPerPixel=0; + + } else { + /* "Plain" (all-ASCII) formats (1-3) not (yet) supported. */ + return IP_INPUT_ERROR; + } + + GET_INT(&g->traits.iPixelsPerRow); + GET_INT(&g->traits.lNumRows); + if (!g->traits.iBitsPerPixel) { + GET_INT(&maxval); + while (maxval) { + g->traits.iBitsPerPixel++; + maxval>>=1; + } + } + g->traits.iBitsPerPixel*=g->traits.iComponentsPerPixel; + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + } + + *pdwInputNextPos = *pdwInputUsed; + g->dwInNextPos = *pdwInputUsed; + + *pInTraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * pnm_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD pnm_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pnm_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PPNM_INST g; + int nBytes; + PBYTE pIn, pOut; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("pnm_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + if (g->fIsEncode && !g->fDidHeader) { + BYTE buffer[MAX_ENCODE_HEADER_SIZE]; + DWORD maxval=(2<<((g->traits.iBitsPerPixel/ + g->traits.iComponentsPerPixel)-1))-1; + int len; + + INSURE(dwOutputAvail>=MAX_ENCODE_HEADER_SIZE); + + memset(pbOutputBuf,' ',MAX_ENCODE_HEADER_SIZE); + pbOutputBuf[0]='P'; + if (g->traits.iComponentsPerPixel==1) { + if (maxval==1) { + pbOutputBuf[1]='4'; + } else { + pbOutputBuf[1]='5'; + } + } else if (g->traits.iComponentsPerPixel==3) { + pbOutputBuf[1]='6'; + } else { + goto fatal_error; + } + + snprintf((char *)buffer,MAX_ENCODE_HEADER_SIZE,"\n%d %d\n", + g->traits.iPixelsPerRow,g->dwRowsDone); + if (g->traits.iComponentsPerPixel>1 || maxval>1) { + buffer[MAX_ENCODE_HEADER_SIZE-1]=0; + len=strlen((char *)buffer); + snprintf((char *)buffer+len,MAX_ENCODE_HEADER_SIZE-len, + "%d\n",maxval); + } + + buffer[MAX_ENCODE_HEADER_SIZE-1]=0; + len=strlen((char *)buffer); + memcpy(pbOutputBuf+MAX_ENCODE_HEADER_SIZE-len,buffer,len); + + *pdwOutputUsed=MAX_ENCODE_HEADER_SIZE; + *pdwOutputThisPos=0; + g->dwOutNextPos=MAX_ENCODE_HEADER_SIZE; + g->fDidHeader=1; + } + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + + /* At this point, pIn is your input buffer, and pOut is your output buffer. + * Do whatever you are going to do here. + */ + memcpy(pOut,pIn,nBytes); + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pnm_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * pnm_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_newPage ( + IP_XFORM_HANDLE hXform) +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * pnm_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD pnm_closeXform (IP_XFORM_HANDLE hXform) +{ + PPNM_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * pnmTbl - Jump-table for transform driver + * +\*****************************************************************************/ +IP_XFORM_TBL pnmDecodeTbl = { + pnmDecode_openXform, + pnm_setDefaultInputTraits, + pnm_setXformSpec, + pnm_getHeaderBufSize, + pnm_getActualTraits, + pnm_getActualBufSizes, + pnm_convert, + pnm_newPage, + pnm_insertedData, + pnm_closeXform +}; + +IP_XFORM_TBL pnmEncodeTbl = { + pnmEncode_openXform, + pnm_setDefaultInputTraits, + pnm_setXformSpec, + pnm_getHeaderBufSize, + pnm_getActualTraits, + pnm_getActualBufSizes, + pnm_convert, + pnm_newPage, + pnm_insertedData, + pnm_closeXform +}; + +/* End of File */ diff --git a/ip/xrotate.c b/ip/xrotate.c new file mode 100644 index 0000000..130b05a --- /dev/null +++ b/ip/xrotate.c @@ -0,0 +1,819 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xrotate.c - Rotates the image + * + * Mark Overton, Feb 2000 + * + ****************************************************************************** + * + * Imagine a rotated rectangle contained somewhere within the input image, or + * even partially outside the input image. This xform outputs that rectangle + * unrotated. This xform crops out all image-area outside the rotated rectangle, + * and white-fills any part of the rectangle outside the image-area. It is + * intended for procuring a selected portion of a scan. + * + * A rotated rectangle is specified using three points in the input image. + * Depending on the orientation of these points, horizontal and/or vertical + * flipping can be performed, as well as rotation by any angle. + * + * Scaling is also performed. The interpolation for rotation also does the + * scaling in the same step, resulting in higher quality output than the usual + * use of separate rotation and scaling, as well as being faster. + * + * Rotation can be fast or slow, depending on an aXformInfo flag. If slow, + * interpolation (anti-aliasing) is done with superb results. If fast, the + * results have jaggies. + * + * Name of Global Jump-Table: + * + * rotateTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_ROTATE_UPPER_LEFT] = [xUL,yUL] = pos of upper-left corner of rotated rect + * aXformInfo[IP_ROTATE_UPPER_RIGHT] = [xUR,yUR] = pos of upper-right corner of rotated rect + * aXformInfo[IP_ROTATE_LOWER_LEFT] = [xLL,yLL] = pos of lower-left corner of rotated rect + * aXformInfo[IP_ROTATE_OUTPUT_SIZE] = [outWidth,outHeight] = size of output image (0 = no scaling) + * aXformInfo[IP_ROTATE_FAST] = rotate fast? (0=no=interpolated, 1=yes=jaggy) + * + * The coordinates (xUL,yUL) are in relation to the *input* image (Y increases + * downward), and locates what will be the upper-left corner in the output + * (before scaling). The other two points are defined similarly. If UL is to + * the right of UR, the output image is flipped horizontally. Vertical flipping + * can be done similarly. + * This rectangle is then scaled to produce outWidth and outHeight pixels. + * + * Capabilities and Limitations: + * + * Crops, white-pads, rotates by any angle, scales, mirrors, and flips vertically. + * Input data may be 1 bit/pixel, or any multiple of 8 bits/pixel. + * Internally, the miniumum amount of memory required for the rotation is + * allocated. For small angles, internal allocation will be very small. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * width of scan abs(outWidth) + * iBitsPerPixel * 1, or n*8 same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows * height of scan abs(outHeight) + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +/* Use the #define below if this transform will exist in a dll outside of the */ +/* image pipeline. This will allow the functions to be exported. */ +/* #define EXPORT_TRANFORM 1 */ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include "math.h" /* for sqrt */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + +typedef struct { + IP_IMAGE_TRAITS inTraits; /* traits of the input image */ + IP_IMAGE_TRAITS outTraits; /* traits of the output image */ + + // Items from aXformInfo: + int xUL, yUL; /* UL corner of image */ + int xUR, yUR; /* UR corner of image */ + int xLL, yLL; /* LL corner of image */ + int xLR, yLR; /* LR corner of image (computed) */ + int iOutWidth; /* width of output image */ + int iOutHeight; /* height of output image */ + BOOL bRotateFast; /* rotate fast? (produces worse jaggies) */ + + int hSlopeDx, hSlopeDy; /* input-change for moving right 1 pix in output (16.16) */ + int vSlopeDx, vSlopeDy; /* input-change for moving down 1 pix in output (16.16) */ + int xLeft, yLeft; /* current left-end of row (input coords) (16.16) */ + int xRight, yRight; /* current right-end of row (input coords) (16.16) */ + BYTE *pStrip; /* strip with a portion of input image */ + BYTE *pStripAfter; /* ptr to 1st byte after the strip-buffer */ + int stripIndexYTop; /* row-index of stripYTop in the strip */ + int stripYTop; /* Y-value for topmost row in strip */ + int stripYBottom; /* Y-value for bottommost row in strip */ + int stripRows; /* number of rows allocated in the strip */ + int stripLoaded; /* number of rows actually loaded into the strip */ + int stripSize; /* number of bytes allocated in the strip */ + int stripBytesPerRow; /* bytes/row in strip */ + int inBytesPerRow; /* bytes/row of input image */ + int outBytesPerRow; /* bytes/row of output image */ + int bytesPerPixel; /* bytes/pixel (=1 for bilevel; 1 pixel in each byte) */ + DWORD dwRowsSent; /* number of rows output so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} ROTATE_INST, *PROTATE_INST; + + + +/*****************************************************************************\ + * + * rotate_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PROTATE_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(ROTATE_INST), g); + *pXform = g; + memset (g, 0, sizeof(ROTATE_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * rotate_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PROTATE_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow > 0 && + pTraits->lNumRows > 0 && + pTraits->iBitsPerPixel > 0); + g->inTraits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * rotate_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PROTATE_INST g; + int i; + + HANDLE_TO_PTR (hXform, g); + + i = aXformInfo[IP_ROTATE_UPPER_LEFT].dword; + g->xUL = i >> 16; + g->yUL = i & 0xFFFF; + + i = aXformInfo[IP_ROTATE_UPPER_RIGHT].dword; + g->xUR = i >> 16; + g->yUR = i & 0xFFFF; + + i = aXformInfo[IP_ROTATE_LOWER_LEFT].dword; + g->xLL = i >> 16; + g->yLL = i & 0xFFFF; + + i = aXformInfo[IP_ROTATE_OUTPUT_SIZE].dword; + g->iOutWidth = i >> 16; + g->iOutHeight = i & 0xFFFF; + + g->bRotateFast = aXformInfo[IP_ROTATE_FAST].dword == 1; + + INSURE (g->iOutWidth>=0 && g->iOutHeight>=0); + g->xLR = g->xUR + (g->xLL - g->xUL); + g->yLR = g->yUR + (g->yLL - g->yUL); + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * rotate_getHeaderBufSize - Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * rotate_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PROTATE_INST g; + int dxH, dyH, dxV, dyV; + double rotWidthSq, rotHeightSq, rotWidth, rotHeight; + + HANDLE_TO_PTR (hXform, g); + + /***** Compute Output Traits (and rotation variables) *****/ + + memcpy (&g->outTraits, &g->inTraits, sizeof(IP_IMAGE_TRAITS)); + + dxH = g->xUR - g->xUL; + dyH = g->yUR - g->yUL; + dxV = g->xLL - g->xUL; + dyV = g->yLL - g->yUL; + + rotWidthSq = dxH*dxH + dyH*dyH; + rotWidth = sqrt (rotWidthSq); + rotHeightSq = dxV*dxV + dyV*dyV; + rotHeight = sqrt (rotHeightSq); + INSURE (rotWidth>0.0 && rotHeight>0.0); + + if (g->iOutWidth == 0) + g->iOutWidth = (int)(rotWidth+0.5); + if (g->iOutHeight == 0) + g->iOutHeight = (int)(rotHeight+0.5); + + g->outTraits.iPixelsPerRow = g->iOutWidth; + g->outTraits.lNumRows = g->iOutHeight; + + g->bytesPerPixel = g->inTraits.iBitsPerPixel / 8; + if (g->bytesPerPixel == 0) + g->bytesPerPixel = 1; /* bi-level is expanded to one byte/pixel */ + + g->inBytesPerRow = (g->inTraits.iPixelsPerRow*g->inTraits.iBitsPerPixel + 7) / 8; + g->outBytesPerRow = (g->iOutWidth *g->inTraits.iBitsPerPixel + 7) / 8; + + g->hSlopeDx = (dxH << 16) / g->iOutWidth; + g->hSlopeDy = (dyH << 16) / g->iOutWidth; + g->vSlopeDx = (dxV << 16) / g->iOutHeight; + g->vSlopeDy = (dyV << 16) / g->iOutHeight; + + /* we start outputting at the upper-left corner of output-image */ + /* do NOT add 0x8000 because when we're not scaling or rotating, + * it causes each pair of rows and each pair of columns to be + * averaged together, losing sharpness. */ + g->xLeft = g->xUL << 16; + g->yLeft = g->yUL << 16; + g->xRight = g->xUR << 16; + g->yRight = g->yUR << 16; + + /* set-up the strip-buffer */ + g->stripBytesPerRow = g->inTraits.iPixelsPerRow * g->bytesPerPixel; + g->stripIndexYTop = 0; + g->stripYTop = 0; + g->stripYBottom = -1; + g->stripLoaded = 0; + if (g->vSlopeDy < 0) /* we'll proceed *upward* thru the input image */ + g->stripRows = IP_MAX(g->yUL, g->yUR) - IP_MIN(g->yLL, g->yLR) + 1; + else /* normal case of proceeding *downward* */ + g->stripRows = IP_MAX(g->yUL, g->yUR) - IP_MIN(g->yUL, g->yUR) + 1; + g->stripRows += 2; /* allocate extra row on top & bottom for interpolation */ + g->stripSize = g->stripRows * g->stripBytesPerRow; + IP_MEM_ALLOC (g->stripSize, g->pStrip); + g->pStripAfter = g->pStrip + g->stripSize; + + /***** Return Values *****/ + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->inTraits; /* structure copies */ + *pOutTraits = g->outTraits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * rotate_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD rotate_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PROTATE_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = g->inBytesPerRow; + *pdwMinOutBufLen = g->outBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * rotate_convert - Converts one row + * +\*****************************************************************************/ + + + +/* ExpandBilevelRow - Expands 8 bits/pixel (input) to 1 byte/pixel (output) */ +static void ExpandBilevelRow ( + PBYTE pDest, + PBYTE pSrc, + int nPixels) +{ + BYTE mask, inbyte=0; + + mask = 0; + + while (nPixels > 0) { + if (mask == 0) { + mask = 0x80u; + inbyte = *pSrc++; + } + *pDest++ = inbyte & mask ? 0 : 255; + mask >>= 1; + nPixels -= 1; + } +} + + + +FUNC_STATUS WORD rotate_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + // We need another row if the Y of either endpoint of the current rotated row + // is below the bottom of the strip. + #define NEED_MORE \ + ((g->yLeft >>16) >= g->stripYBottom || (g->yRight>>16) >= g->stripYBottom) + + PROTATE_INST g; + PBYTE pSrc, pDest; + + HANDLE_TO_PTR (hXform, g); + + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("rotate_convert: Told to flush.\n"), 0, 0); + dwInputAvail = (DWORD)g->inBytesPerRow; + if ((long)g->dwRowsSent >= g->outTraits.lNumRows) + return IP_DONE; + } + + /**** Initial load of strip-buffer ****/ + + if (g->stripLoaded < g->stripRows) { + if (dwInputAvail == 0) + return IP_READY_FOR_DATA; + INSURE (dwInputAvail >= (DWORD)g->inBytesPerRow); + pDest = g->pStrip + g->stripBytesPerRow*g->stripLoaded; + if (g->inTraits.iBitsPerPixel == 1) + ExpandBilevelRow (pDest, pbInputBuf, g->inTraits.iPixelsPerRow); + else if (pbInputBuf == NULL) + memcpy (pDest, pDest - g->stripBytesPerRow, g->stripBytesPerRow); + else + memcpy (pDest, pbInputBuf, g->stripBytesPerRow); + g->stripLoaded += 1; + g->stripYBottom += 1; + if (g->stripLoaded == 1) { + /* first row is *above* top for interpolation; replicate it */ + memcpy (g->pStrip+g->stripBytesPerRow, g->pStrip, g->stripBytesPerRow); + g->stripLoaded += 1; + g->stripYBottom += 1; + } + if (pbInputBuf == NULL) + return 0; /* wait for next call to replicate another row and start rotation */ + *pdwInputUsed = (DWORD)g->inBytesPerRow; + g->dwInNextPos += (DWORD)g->inBytesPerRow; + *pdwInputNextPos = g->dwInNextPos; + return ((g->stripLoaded<g->stripRows || NEED_MORE) ? IP_READY_FOR_DATA : 0) + | IP_CONSUMED_ROW; + } + + /**** Load next row into strip-buffer ****/ + + if (NEED_MORE) + { + /* we need to load a row into the strip-buffer (a wrapping load) */ + if (dwInputAvail == 0) + return IP_READY_FOR_DATA; + INSURE (dwInputAvail >= (DWORD)g->inBytesPerRow); + pDest = g->pStrip + g->stripBytesPerRow*g->stripIndexYTop; + if (pbInputBuf == NULL) // then white-fill the row + memset (pDest, 255, g->stripBytesPerRow); + else if (g->inTraits.iBitsPerPixel == 1) + ExpandBilevelRow (pDest, pbInputBuf, g->inTraits.iPixelsPerRow); + else + memcpy (pDest, pbInputBuf, g->stripBytesPerRow); + g->stripIndexYTop = (g->stripIndexYTop + 1) % g->stripRows; + g->stripYTop += 1; + g->stripYBottom += 1; + if (pbInputBuf != NULL) { + *pdwInputUsed = (DWORD)g->inBytesPerRow; + g->dwInNextPos += (DWORD)g->inBytesPerRow; + *pdwInputNextPos = g->dwInNextPos; + } + } + + /**** Output a row ****/ + + if ((long)g->dwRowsSent >= g->outTraits.lNumRows) + { + /* we've outputted all rows; discard any further input */ + g->stripYBottom = -100000; /* forces strip-loading logic to keep loading */ + } + else if (! NEED_MORE) + { + int rowIndex, iPix; + int xcur,ycur, xint,yint; + unsigned xfrac,yfrac; + PBYTE pNextRow; + BYTE mask; + + INSURE (dwOutputAvail >= (DWORD)g->outBytesPerRow); + + pDest = pbOutputBuf; + xcur = g->xLeft; + ycur = g->yLeft; + + /* These two lines set-up bi-level packing: */ + *pDest = 0; + mask = 0x80u; + + for (iPix=0; iPix<g->outTraits.iPixelsPerRow; iPix++) + { + xint = xcur >> 16; + yint = (ycur >> 16) - g->stripYTop; + + /* below, unsigned makes negatives huge (therefore not in strip) */ + /* also, we check stripRows-1; the -1 allows the extra interpolation-row */ + if ((unsigned)yint < (unsigned)(g->stripRows-1) && + (unsigned)xint < (unsigned)g->inTraits.iPixelsPerRow) + { + /**** We are inside the strip ****/ + + /* Compute address of pixel in the strip */ + rowIndex = yint + g->stripIndexYTop; + if (rowIndex >= g->stripRows) + rowIndex -= g->stripRows; + pSrc = (rowIndex*g->inTraits.iPixelsPerRow + xint) * g->bytesPerPixel + + g->pStrip; + + /**** Output a pixel ****/ + + /* The byte at ptrSrcCur is the upper-left byte out of a 2x2 group. + * These four bytes are interpolated together based on xfrac and yfrac. + */ + #define INTERPOLATE_BYTE(ptrOut,ptrSrcCur,ptrSrcNex,bpp) { \ + int xtop, xbot, val; \ + xtop = (ptrSrcCur)[0]*(256-xfrac) + (ptrSrcCur)[bpp]*xfrac; \ + xbot = (ptrSrcNex)[0]*(256-xfrac) + (ptrSrcNex)[bpp]*xfrac; \ + val = xtop*(256-yfrac) + xbot*yfrac; \ + val = (val + 0x8000) >> 16; /* the add rounds result of shift */ \ + *(ptrOut) = (BYTE)val; \ + } + + #define INTERPOLATE_WORD(ptrOut,ptrSrcCur,ptrSrcNex,bpp) { \ + WORD *wptrOut = (PWORD)(ptrOut ); \ + WORD *wptrSrcCur = (PWORD)(ptrSrcCur); \ + WORD *wptrSrcNex = (PWORD)(ptrSrcNex); \ + unsigned xtop, xbot, val; \ + xtop = ((wptrSrcCur)[0]*(0x10000-xfrac)>>8) + ((wptrSrcCur)[bpp]*xfrac>>8); \ + xtop = (xtop+128) >> 8; /* the add rounds result of shift */ \ + xbot = ((wptrSrcNex)[0]*(0x10000-xfrac)>>8) + ((wptrSrcNex)[bpp]*xfrac>>8); \ + xbot = (xbot+128) >> 8; \ + val = (xtop*(0x10000-yfrac)>>8) + (xbot*yfrac>>8); \ + val = (val+128) >> 8; \ + *(wptrOut) = (WORD)val; \ + } + + pNextRow = pSrc + g->stripBytesPerRow; + if (pNextRow >= g->pStripAfter) + pNextRow -= g->stripSize; /* next row wrapped to top of strip */ + + xfrac = (xcur>>8) & 0x0000ff; + yfrac = (ycur>>8) & 0x0000ff; + + if (g->bytesPerPixel == 3) { + if (g->bRotateFast) { + pDest[0] = pSrc[0]; + pDest[1] = pSrc[1]; + pDest[2] = pSrc[2]; + } else { + /* interpolate to eliminate jaggies */ + INTERPOLATE_BYTE (pDest+0, pSrc+0, pNextRow+0, 3) + INTERPOLATE_BYTE (pDest+1, pSrc+1, pNextRow+1, 3) + INTERPOLATE_BYTE (pDest+2, pSrc+2, pNextRow+2, 3) + } + pDest += 3; + } else if (g->bytesPerPixel == 1) { + BYTE byt; + + if (g->bRotateFast) + byt = pSrc[0]; + else + INTERPOLATE_BYTE (&byt, pSrc, pNextRow, 1) + + if (g->inTraits.iBitsPerPixel == 8) + *pDest++ = byt; + else { + /* bi-level: pack 8 pixels per byte */ + if (byt < 128) + *pDest |= mask; + mask >>= 1; + if (mask == 0) { + mask = 0x80u; + pDest += 1; + *pDest = 0; + } + } + } else if (g->bytesPerPixel==2 || g->bytesPerPixel==6) { + /* 16-bit grayscale or 48-bit color */ + if (g->bRotateFast) { + memcpy (pDest, pSrc, g->bytesPerPixel); + } else { + xfrac = xcur & 0x00ffff; + yfrac = ycur & 0x00ffff; + INTERPOLATE_WORD (pDest+0, pSrc+0, pNextRow+0, g->inTraits.iComponentsPerPixel) + if (g->bytesPerPixel == 6) { + INTERPOLATE_WORD (pDest+2, pSrc+2, pNextRow+2, 3) + INTERPOLATE_WORD (pDest+4, pSrc+4, pNextRow+4, 3) + } + } + + pDest += g->bytesPerPixel; + } else { + /* unsupported bits per pixel */ + INSURE (FALSE); + } + } + else /* current pos is outside strip, so output a white pixel (padding) */ + { + if (g->inTraits.iBitsPerPixel == 1) { + mask >>= 1; + if (mask == 0) { + mask = 0x80u; + pDest += 1; + *pDest = 0; + } + } else { + memset (pDest, 255, g->bytesPerPixel); + pDest += g->bytesPerPixel; + } + } + + /* Advance to next pixel in input image */ + xcur += g->hSlopeDx; + ycur += g->hSlopeDy; + } /* end of for-each-pixel loop */ + + *pdwOutputUsed = (DWORD)g->outBytesPerRow; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += (DWORD)g->outBytesPerRow; + + g->dwRowsSent += 1; + + g->xLeft += g->vSlopeDx; + g->yLeft += g->vSlopeDy; + g->xRight += g->vSlopeDx; + g->yRight += g->vSlopeDy; + } + + return (*pdwInputUsed !=0 ? IP_CONSUMED_ROW : 0) | + (*pdwOutputUsed!=0 ? IP_PRODUCED_ROW : 0) | + (NEED_MORE ? IP_READY_FOR_DATA : 0); + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * rotate_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * rotate_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_newPage ( + IP_XFORM_HANDLE hXform) +{ + PROTATE_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * rotate_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD rotate_closeXform (IP_XFORM_HANDLE hXform) +{ + PROTATE_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (g->pStrip != NULL) + IP_MEM_FREE (g->pStrip); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * rotateTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL rotateTbl = { + rotate_openXform, + rotate_setDefaultInputTraits, + rotate_setXformSpec, + rotate_getHeaderBufSize, + rotate_getActualTraits, + rotate_getActualBufSizes, + rotate_convert, + rotate_newPage, + rotate_insertedData, + rotate_closeXform +}; + + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) rotateGetXformTable (LPIP_XFORM_TBL pXform) +{ + if (pXform == NULL) + return IP_FATAL_ERROR; + + *pXform = clrmapTbl; + return IP_DONE; +} +#endif + +/* End of File */ diff --git a/ip/xsaturation.c b/ip/xsaturation.c new file mode 100644 index 0000000..472dfda --- /dev/null +++ b/ip/xsaturation.c @@ -0,0 +1,463 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xsaturation.c - Changes the saturation of color data + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * saturationTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_SATURATION_FACTOR] = saturation factor, in 24.8 fixed point (8 bits of frac) + * + * A conceptual value of 1.0 (aXformInfo[0] = 256) means no change in + * saturation. 2.0 doubles it; 0.5 cuts it in half. 0.0 eliminates all + * color, outputting RGB grayscale. + * + * Capabilities and Limitations: + * + * The incoming data can be 24-bit color or 48-bit color. + * The pixels are assumed to be RGB. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Feb 2000 Mark Overton -- wrote original code + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwSatFac; /* desired saturation factor */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} SAT_INST, *PSAT_INST; + + + +/*****************************************************************************\ + * + * saturation_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD saturation_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PSAT_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(SAT_INST), g); + *pXform = g; + memset (g, 0, sizeof(SAT_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * saturation_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD saturation_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PSAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 + && (pTraits->iBitsPerPixel==24 || pTraits->iBitsPerPixel==48)); + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * saturation_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD saturation_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PSAT_INST g; + + HANDLE_TO_PTR (hXform, g); + g->dwSatFac = aXformInfo[IP_SATURATION_FACTOR].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * saturation_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD saturation_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * saturation_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD saturation_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PSAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * saturation_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD saturation_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PSAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * saturation_convert - Converts one row + * +\*****************************************************************************/ + +static WORD saturation_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PSAT_INST g; + int nBytes; + PBYTE pIn, pOut, pOutAfter; + int l, rv, gv, bv; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("saturation_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pOut + nBytes; + + if (g->traits.iBitsPerPixel == 24) { + /* 24-bit color in RGB */ + + while (pOut < pOutAfter) { + rv = *pIn++; + gv = *pIn++; + bv = *pIn++; + + l = NTSC_LUMINANCE (rv,gv,bv); + + rv = l + (((rv-l)*(int)g->dwSatFac + (1<<7)) >> 8); + gv = l + (((gv-l)*(int)g->dwSatFac + (1<<7)) >> 8); + bv = l + (((bv-l)*(int)g->dwSatFac + (1<<7)) >> 8); + + if (rv > 255) rv = 255; else if (rv < 0) rv = 0; + if (gv > 255) gv = 255; else if (gv < 0) gv = 0; + if (bv > 255) bv = 255; else if (bv < 0) bv = 0; + + *pOut++ = (BYTE)rv; + *pOut++ = (BYTE)gv; + *pOut++ = (BYTE)bv; + } + } else { /* 48 bits/pixel */ + PWORD src = (PWORD)pIn; + PWORD dst = (PWORD)pOut; + PWORD dstAfter = (PWORD)pOutAfter; + + while (dst < dstAfter) { + rv = (unsigned)(*src++); + gv = (unsigned)(*src++); + bv = (unsigned)(*src++); + + l = NTSC_LUMINANCE (rv,gv,bv); + + rv = l + (((rv-l)*(int)g->dwSatFac + (1<<7)) >> 8); + gv = l + (((gv-l)*(int)g->dwSatFac + (1<<7)) >> 8); + bv = l + (((bv-l)*(int)g->dwSatFac + (1<<7)) >> 8); + + if (rv > 65535) rv = 65535; else if (rv < 0) rv = 0; + if (gv > 65535) gv = 65535; else if (gv < 0) gv = 0; + if (bv > 65535) bv = 65535; else if (bv < 0) bv = 0; + + *dst++ = (WORD)rv; + *dst++ = (WORD)gv; + *dst++ = (WORD)bv; + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * saturation_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD saturation_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * saturation_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD saturation_newPage ( + IP_XFORM_HANDLE hXform) +{ + PSAT_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * saturation_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD saturation_closeXform (IP_XFORM_HANDLE hXform) +{ + PSAT_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * saturationTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL saturationTbl = { + saturation_openXform, + saturation_setDefaultInputTraits, + saturation_setXformSpec, + saturation_getHeaderBufSize, + saturation_getActualTraits, + saturation_getActualBufSizes, + saturation_convert, + saturation_newPage, + saturation_insertedData, + saturation_closeXform +}; + +/* End of File */ diff --git a/ip/xscale.c b/ip/xscale.c new file mode 100644 index 0000000..0dbd5c7 --- /dev/null +++ b/ip/xscale.c @@ -0,0 +1,1277 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux (minus bilevel scaling) by David Paschal. + */ + +/******************************************************************************\ + * + * xscale.c - Scales (and interpolates) bi-level, gray and color + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * scaleTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_SCALE_HORIZ_FACTOR] = Horizontal scale-factor, in 8.24 fixed point + * + * aXformInfo[IP_SCALE_VERT_FACTOR] = Vertical scale-factor, in 8.24 fixed point + * + * aXformInfo[IP_SCALE_FAST] = scale fast using simple pixel-replication? 0=no, 1=yes + * currently, this only affects bi-level up-scaling. + * + * Capabilities and Limitations: + * + * This driver can scale both up and down, independently. That is, one + * direction can up-scale while the other direction down-scales. Bi-level + * scaling is done using tables to maximize speed. 24-bit color, + * 8-bit gray and bi-level pixels can be scaled. + * + * Color and gray data are interpolated intellegently. + * Bi-level data are smoothed when up-scaling (several patents), and + * data-loss is avoided when down-scaling (another patent). + * + * The allowed scale-factor ranges differ based on data type (bi-level, + * gray and color). An assert will occur if range is exceeded. + * These ranges are: + * + * bi-level: horiz_fac=[1/2..4.0] vert_fac=[1/16..4.0] + * gray: horiz_fac=[1/4..6.0] vert_fac=[1/4..6.0] + * color: horiz_fac=[1/4..6.0] vert_fac=[1/4..6.0] + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output computed + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel * passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output computed + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * + * Feb 1998 Mark Overton, ported to new Windows software Image Processor + * Jun 1997 Mark Overton, added color scaling + * May 1997 Mark Overton, added gray scaling + * 1995 Mark Overton, wrote original code (bi-level only) + * +\******************************************************************************/ + +#include "string.h" /* for memset and memcpy */ +#include "assert.h" + +#include "hpip.h" +#include "ipdefs.h" + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e +#define SC_WHITE_ROW 1 /* was for tracking all-white rows; not used */ + + +/*____________________________________________________________________________ + | | + | Things common to all image-types | + |____________________________________________________________________________| +*/ + + +#define MAX_ROWS_AP 6 /* Number of entries in rows_ap */ +#define HELD_ARRAY_SIZE 7 /* # entries in apHeldOutRows array */ + +typedef enum { + IM_BILEVEL, + IM_GRAY, + IM_COLOR +} IM_TYPE; + + +/* SC_INST contains all the variables for a scaling-instance */ + +typedef struct { + IM_TYPE image_type; /* type of image (bilevel, gray, color) */ + BOOL fast; /* scale (quickly) using pixel replication? */ + BYTE nMoreFlush; /* # more flush calls reqd to send buffered rows */ + ULONG horiz_fac; /* horiz scale-factor (16.16 fixed-pt) */ + ULONG vert_fac; /* vert scale-factor (16.16 fixed-pt) */ + long vert_pos; /* current vert pos (16.16; signed, we use neg) */ + int in_row_nbytes; /* # bytes in each input row */ + int out_row_nbytes; /* # bytes in each output row */ + int out_row_pixels; /* # pixels in each output row */ + int in_nrows; /* number of rows read in so far */ + int out_nrows; /* number of rows written so far */ + BYTE *rows_ap[MAX_ROWS_AP]; /* ptrs to successive input rows */ + int nMaxOutRows; /* max # output rows resulting from one input row*/ + int nMoreHeldOutRows;/* more output rows needed to be returned */ + int iNextHeldOutRow; /* index of next output row to be returned */ + BYTE *apHeldOutRows[HELD_ARRAY_SIZE]; + /* output rows stored for subsequent returning; */ + /* index [0] is caller's output buffer */ + int top_row; /* bilevel: index of top of 3 rows in rows_ap */ + ULONG post_fac; /* bilevel: additional upscaling */ + BYTE mid_traits; /* bilevel: trait-bits of middle row */ + + ULONG inv_horiz_fac; /* gray: inverse of horiz scaling factor (16.16) */ + ULONG inv_vert_fac; /* gray: inverse of vert scaling factor (16.16) */ + long inv_vert_pos; /* cur inv vert pos (16.16; signed, we use neg) */ + BYTE n_saved_rows; /* gray: # rows saved in rows_ap for vert scaling*/ + BYTE n_alloced_rows; /* gray: # rows allocated in rows_ap */ + + IP_IMAGE_TRAITS inTraits;/* traits of the input image */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} SC_INST, *PSC_INST; + + +/****************************************************************************** + ****************************************************************************** + + DUMMY BILEVEL FUNCTIONS + + ****************************************************************************** + ******************************************************************************/ + +/* These functions were removed because they contained patented algorithms. + * Therefore, only gray and color (not bilevel) scaling are currently + * supported. If this is ever fixed, then remove the assert in + * scale_setDefaultInputTraits(). */ + +void bi_fast_open(PSC_INST g, UINT in_row_len) { + fatalBreakPoint(); +} + +void bi_scale_open(PSC_INST g, UINT in_row_len) { + fatalBreakPoint(); +} + +int bi_fast_row(PSC_INST g,PBYTE pbInputBuf,BYTE src_traits, + BYTE *apHeldOutRows[],ULONG *pdest_traits) { + fatalBreakPoint(); + return 0; +} + +int bi_scale_row(PSC_INST g,PBYTE pbInputBuf,BYTE src_traits, + BYTE *apHeldOutRows[],ULONG *pdest_traits) { + fatalBreakPoint(); + return 0; +} + +void bi_fast_close(PSC_INST g) { + fatalBreakPoint(); +} + +void bi_scale_close(PSC_INST g) { + fatalBreakPoint(); +} + + +/****************************************************************************** + ****************************************************************************** + + CONTONE (GRAY AND COLOR) + + ****************************************************************************** + ******************************************************************************/ + + +/* + * Scaling Algorithms + * + * The descriptions below are for the X axis. For the Y axis, substitute + * "input row" for "input pixel", and "output row" for "output pixel", and + * apply weights to entire rows using the same algorithms. + * + * Each axis is scaled separately using the these methods. So you can + * down-scale in one axis and up-scale in the other, if you wish. + * + * + * Down-scaling Algorithm + * + * Imagine marking the pixel-boundaries on two rulers, and laying them + * next to each other: + * + * in1 pos in2 in3 + * |______|______|______|______|______|______|______| <-- input pixels + * | | | | | <-- output pixels + * 0 out 1 + * + * An output pixel consists of a weighted sum of the input pixels that + * it overlaps. The weights are the amount of overlap. + * + * pos is the right side of the leftmost input pixel that overlaps the + * current output pixel. This position is in units of output pixels (a + * distance of 1 is the width of an output pixel), and 0 is the left side + * of the current output pixel. Since this position is in units of output + * pixels, it can be used to easily compute the weights. + * + * The weights of the overlapping input pixels are: + * + * leftmost: pos + * middle pixels (if any): scale factor (which is less than 1) + * rightmost: 1.0 - (sum of above weights) + * + * Above, the pixel 'out' is computed as: + * + * out = pos*in1 + scalefactor*in2 + (1-pos-scalefactor)*in3 + * + * + * Up-scaling Algorithm + * + * I first tried an overlap method like that used for down-scaling, but + * for large scale-factors, one output pixel would overlap two input + * pixels, and many output pixels would overlap just one input pixel. + * So the result looked almost as bad as pixel-replication, because many + * pixels were indeed just replications. + * + * So I devised an interpolating algorithm wherein a pos value is the + * weight of the next input pixel, and (1-pos) is the weight of the + * current input pixel. As pos increases along the current input pixel, + * the weight will smoothly shift from it to the next input pixel, + * eliminating jaggies. + * + * 0 in0 1 in1 + * |___________|___________|___________|___________| <-- input_pixels + * | | | out | | | | | <-- output pixels + * pos + * + * pos is the left side of the current output pixel, in units of input + * pixels. 0 is the left side of the current input pixel (in0 above). + * + * pos is advanced by adding 1/scalefactor to it. When it becomes >= 1, + * move to the next input pixel, and subtract 1 from pos. + * + * Above, the pixel 'out' is computed as: out = (1-pos)*in0 + pos*in1 + */ + +#define CONTONE_MIN_HORIZ_FAC (ULONG)0x04000 /* 0.25 */ + /* Minimum horizontal scale-factor (16.16) */ + +#define CONTONE_MAX_HORIZ_FAC ((ULONG)MAX_ROWS_AP << 16) + /* Maximum horizontal scale-factor (arbitrary) */ + +#define CONTONE_MIN_VERT_FAC (ULONG)0x04000 /* 0.25 */ + /* Minimum vertical scale-factor (16.16) */ + +#define CONTONE_MAX_VERT_FAC ((ULONG)MAX_ROWS_AP << 16) + /* Maximum vertical scale-factor (16.16) */ + + + +/*____________________________________________________________________________ + | | | + | gray_horiz_scale | Scales the given input row into the given output row | + |__________________|_________________________________________________________| + | | + | Up-scaling is done by interpolation using horiz_pos. | + | Down-scaling is done by blending (averaging two or more input pixels | + | together, forming an output pixel). | + |____________________________________________________________________________| +*/ +static void gray_horiz_scale ( + SC_INST *g, /* in: our instance variables */ + BYTE *src_p, /* in: input row to be scaled */ + BYTE *dest_p) /* out: output row that we scaled */ +{ + ULONG horiz_pos, new_pos; + BYTE *in_p, *out_p, *out_aft_p; + UINT sum, pix; + UINT w1, w2; + UINT n_pix, u; + + in_p = src_p; + out_p = dest_p; + out_aft_p = out_p + g->out_row_nbytes; + in_p[g->in_row_nbytes] = in_p[g->in_row_nbytes-1]; /* dup right pixel */ + + /**************/ + /* Up-scaling */ + /**************/ + + if (g->horiz_fac >= 0x00010000u) + { + horiz_pos = 0; + + while (out_p < out_aft_p) { + do { + w2 = (UINT) (horiz_pos >> 8); + w1 = 256 - w2; + *out_p++ = (w1*in_p[0] + w2*in_p[1]) >> 8; + horiz_pos += g->inv_horiz_fac; + } while ((horiz_pos>>16) == 0); + + horiz_pos &= 0x0000ffffu; + in_p += 1; + } + } + + /****************/ + /* Down-scaling */ + /****************/ + + else if (g->fast) + { + horiz_pos = 0; + while (out_p < out_aft_p) { + *out_p++ = *in_p; + horiz_pos += g->inv_horiz_fac; + in_p += horiz_pos >> 16; + horiz_pos &= 0x0000ffffu; + } + } + else // not fast + { + horiz_pos = g->horiz_fac; + w2 = g->horiz_fac >> 8; + + while (out_p < out_aft_p) { + new_pos = horiz_pos; + n_pix = 1; + do { + new_pos += g->horiz_fac; + n_pix += 1; + } while ((new_pos>>16) == 0); + + /* Blend n_pix pixels together using these weights: + * 1st pixel: horiz_pos + * mid pixels: horiz_fac + * final pixel: 1.0 - (sum of above weights) + */ + sum = horiz_pos >> 8; + pix = sum * (*in_p++); /* 1st pixel */ + + for (u=1; u<=n_pix-2; u++) { + pix += w2 * (*in_p++); /* middle pixels */ + sum += w2; + } + + pix += (256-sum) * (*in_p); /* final pixel */ + + *out_p++ = pix >> 8; + horiz_pos = new_pos & 0x0000ffffu; + } + } /* end if up-scaling else down-scaling */ +} + + + +/*____________________________________________________________________________ + | | | + | color_horiz_scale | Scales the given input row into the given output row | + |___________________|________________________________________________________| + | | + | Up-scaling is done by interpolation using horiz_pos. | + | Down-scaling is done by blending (averaging two or more input pixels | + | together, forming an output pixel). | + |____________________________________________________________________________| +*/ +static void color_horiz_scale ( + SC_INST *g, /* in: our instance variables */ + BYTE *src_p, /* in: input row to be scaled */ + BYTE *dest_p) /* out: output row that we scaled */ +{ + ULONG horiz_pos, new_pos; + BYTE *in_p, *out_p, *out_aft_p, *p; + UINT sum, pix_y, pix_u, pix_v; + UINT w1, w2; + UINT n_pix, u; + + in_p = src_p; + out_p = dest_p; + out_aft_p = out_p + g->out_row_nbytes; + + p = in_p + g->in_row_nbytes; + p[0] = p[-3]; /* dup right pixel */ + p[1] = p[-2]; + p[2] = p[-1]; + + /**************/ + /* Up-scaling */ + /**************/ + + if (g->horiz_fac >= 0x00010000u) + { + horiz_pos = 0; + + while (out_p < out_aft_p) { + do { + w2 = (UINT) (horiz_pos >> 8); + w1 = 256 - w2; + *out_p++ = (w1*in_p[0] + w2*in_p[3]) >> 8; /* y component */ + *out_p++ = (w1*in_p[1] + w2*in_p[4]) >> 8; /* u component */ + *out_p++ = (w1*in_p[2] + w2*in_p[5]) >> 8; /* v component */ + horiz_pos += g->inv_horiz_fac; + } while ((horiz_pos>>16) == 0); + + horiz_pos &= 0x0000ffffu; + in_p += 3; + } + } + + /****************/ + /* Down-scaling */ + /****************/ + + else if (g->fast) + { + int iStep; + horiz_pos = 0; + while (out_p < out_aft_p) { + *out_p++ = in_p[0]; + *out_p++ = in_p[1]; + *out_p++ = in_p[2]; + + horiz_pos += g->inv_horiz_fac; + iStep = horiz_pos >> 16; + in_p += iStep + iStep + iStep; + horiz_pos &= 0x0000ffffu; + } + } + else // down-scaling, not fast + { + horiz_pos = g->horiz_fac; + w2 = g->horiz_fac >> 8; + + while (out_p < out_aft_p) { + new_pos = horiz_pos; + n_pix = 1; + do { + new_pos += g->horiz_fac; + n_pix += 1; + } while ((new_pos>>16) == 0); + + /* Blend n_pix pixels together using these weights: + * 1st pixel: horiz_pos + * mid pixels: horiz_fac + * final pixel: 1.0 - (sum of above weights) + */ + sum = horiz_pos >> 8; + pix_y = sum * (*in_p++); /* 1st pixel */ + pix_u = sum * (*in_p++); + pix_v = sum * (*in_p++); + + for (u=1; u<=n_pix-2; u++) { + pix_y += w2 * (*in_p++); /* middle pixels */ + pix_u += w2 * (*in_p++); + pix_v += w2 * (*in_p++); + sum += w2; + } + + sum = 256 - sum; + pix_y += sum * in_p[0]; /* final pixel */ + pix_u += sum * in_p[1]; + pix_v += sum * in_p[2]; + + *out_p++ = pix_y >> 8; + *out_p++ = pix_u >> 8; + *out_p++ = pix_v >> 8; + horiz_pos = new_pos & 0x0000ffffu; + } + } /* end if up-scaling else down-scaling */ +} + + + +/*____________________________________________________________________________ + | | | + | weight_two_rows | Output row is a weighted-average of two rows in rows_ap | + |_________________|__________________________________________________________| + | | + | rows_ap[0] is weighted by first_weight. | + | rows_ap[1] is weighted by 1.0 - first_weight. | + | The output is written to dest_p. | + |____________________________________________________________________________| +*/ +static void weight_two_rows ( + SC_INST *g, /* in: our instance variables */ + ULONG first_weight, /* in: weight for first row (16.16) */ + BYTE *dest_p) /* out: output row */ +{ + BYTE *p1, *p2; + BYTE *out_p, *out_aft_p; + + p1 = g->rows_ap[0]; + p2 = g->rows_ap[1]; + out_p = dest_p; + out_aft_p = out_p + g->out_row_nbytes; + +#if 0 + UINT w1, w2; + w1 = first_weight >> 8; + w2 = 256 - w1; + while (out_p < out_aft_p) + *out_p++ = (w1*(*p1++) + w2*(*p2++)) >> 8; +#else + switch ((first_weight+(1u<<12)) >> 13) /* round weight to closest 8th */ + { + case 0: + memcpy (out_p, p2, g->out_row_nbytes); + break; + case 1: + while (out_p < out_aft_p) + *out_p++ = (*p1>>3) + *p2 - (*p2>>3); + p1++; p2++; + break; + case 2: + while (out_p < out_aft_p) + *out_p++ = (*p1>>2) + *p2 - (*p2>>2); + p1++; p2++; + break; + case 3: + while (out_p < out_aft_p) + *out_p++ = (*p1>>2) + (*p1>>3) + (*p2>>1) + (*p2>>3); + p1++; p2++; + break; + case 4: + while (out_p < out_aft_p) + *out_p++ = (*p1>>1) + (*p2>>1); + p1++; p2++; + break; + case 5: + while (out_p < out_aft_p) + *out_p++ = (*p1>>1) + (*p1>>3) + (*p2>>2) + (*p2>>3); + p1++; p2++; + break; + case 6: + while (out_p < out_aft_p) + *out_p++ = *p1 - (*p1>>2) + (*p2>>2); + p1++; p2++; + break; + case 7: + while (out_p < out_aft_p) + *out_p++ = *p1 - (*p1>>3) + (*p2>>3); + p1++; p2++; + break; + case 8: + memcpy (out_p, p1, g->out_row_nbytes); + break; + default: + assert (0); + } +#endif +} + + + +/*____________________________________________________________________________ + | | | + | weight_n_rows | Blends two or more rows in rows_ap into one output row | + |_______________|____________________________________________________________| + | | + | The weights are: | + | 1st row: first_weight | + | middle rows: mid_weight | + | final row: 1.0 - (sum of above weights) | + |____________________________________________________________________________| +*/ +static void weight_n_rows ( + SC_INST *g, /* in: our instance variables */ + UINT n_rows, /* in: number of rows to blend together */ + ULONG first_weight, /* in: weight of first row (16.16) */ + ULONG mid_weight, /* in: weight of middle rows (16.16) */ + BYTE *dest_p) /* out: output row */ +{ + BYTE *in_p[MAX_ROWS_AP]; + BYTE *out_p, *out_aft_p; + UINT weights[MAX_ROWS_AP]; + UINT sum; + UINT u; + + assert (n_rows>=2 && n_rows<=MAX_ROWS_AP); + + if (n_rows == 2) { + weight_two_rows (g, first_weight, dest_p); + return; + } + + out_p = dest_p; + out_aft_p = out_p + g->out_row_nbytes; + for (u=0; u<n_rows; u++) + in_p[u] = g->rows_ap[u]; + + sum = weights[0] = first_weight >> 8; + for (u=1; u<=n_rows-2; u++) + sum += (weights[u] = mid_weight >> 8); + weights[n_rows-1] = 256 - sum; + + while (out_p < out_aft_p) { + sum = 0; + for (u=0; u<n_rows; u++) + sum += weights[u] * (*in_p[u]++); + *out_p++ = sum >> 8; + } +} + + + +/*____________________________________________________________________________ + | | | + | contone_scale_open | sets up the given scaling job | + |____________________|_______________________________________________________| +*/ +static void contone_scale_open ( + SC_INST *g, /* ptr to our scaling instance */ + UINT in_row_len) /* # pixels per input row */ +{ + ULONG horiz_fac; /* scale-factors in 16.16 fixed-point */ + ULONG vert_fac; + UINT n; + + horiz_fac = g->horiz_fac; + vert_fac = g->vert_fac; + + if (! g->fast) { + assert (horiz_fac>=CONTONE_MIN_HORIZ_FAC && + horiz_fac<=CONTONE_MAX_HORIZ_FAC); + assert ( vert_fac>=CONTONE_MIN_VERT_FAC && + vert_fac<=CONTONE_MAX_VERT_FAC); + } + + g->vert_pos = 0; + g->in_row_nbytes = in_row_len; + g->out_row_pixels = g->out_row_nbytes = (horiz_fac*in_row_len) >> 16; + + if (g->image_type == IM_COLOR) { + g->in_row_nbytes *= 3; + g->out_row_nbytes *= 3; + } + + g->inv_horiz_fac = ((0x80000000u / horiz_fac) << 1) + 1u; + g->inv_vert_fac = ((0x80000000u / vert_fac) << 1) + 1u; + /* We added 1 to the inverse factors above as an unusual way of rounding */ + + if (g->fast) { + g->n_alloced_rows = 0; + } else if (vert_fac >= 0x00010000u) { /* up-scaling vertically */ + g->inv_vert_pos = g->inv_vert_fac; + g->n_alloced_rows = 2; + } else { /* down-scaling vertically */ + g->n_alloced_rows = (BYTE)((g->inv_vert_fac+0x0000ffffu) >> 16) + 1; + g->vert_pos = vert_fac; + } + + for (n=0; n<g->n_alloced_rows; n++) { + IP_MEM_ALLOC (g->out_row_nbytes, g->rows_ap[n]); + memset (g->rows_ap[n], 0xff, g->out_row_nbytes + 4); + } + + g->nMoreFlush = 0; /* no flush-calls are needed */ + return; + + fatal_error: + assert (0); +} + + + +/*____________________________________________________________________________ + | | | + | contone_scale_close | de-allocates scaling instance | + |_____________________|______________________________________________________| +*/ +static void contone_scale_close (SC_INST *g) +{ + UINT n; + + for (n=0; n<g->n_alloced_rows; n++) + IP_MEM_FREE (g->rows_ap[n]); +} + + + +/*____________________________________________________________________________ + | | | + | contone_scale_row | scales the given input row into 0 or more output rows | + |___________________|________________________________________________________| +*/ +static int contone_scale_row ( + SC_INST *g, /* in: ptr to our scaling instance */ + BYTE *src_row_p, /* in: input row */ + BYTE *dest_rows_ap[]) /* out: output rows */ +{ + UINT n_out_rows; + UINT u; + long weight; + long new_pos; + BYTE *p; + + assert (src_row_p != NULL); + + if (g->fast && g->vert_fac<=0x00010000u) + { + /* down-scaling fast */ + g->vert_pos += g->vert_fac; + n_out_rows = g->vert_pos >> 16; + g->vert_pos &= 0x0000ffffu; + + if (n_out_rows > 0) { + if (g->image_type == IM_GRAY) gray_horiz_scale (g, src_row_p, dest_rows_ap[0]); + else color_horiz_scale (g, src_row_p, dest_rows_ap[0]); + } + + return n_out_rows; + } + + p = g->rows_ap[g->n_saved_rows]; + if (g->image_type == IM_GRAY) gray_horiz_scale (g, src_row_p, p); + else color_horiz_scale (g, src_row_p, p); + + g->n_saved_rows += 1; + + if (g->n_saved_rows == 1) { + /* call again to duplicate the first row to get us started */ + return contone_scale_row (g, src_row_p, dest_rows_ap); + } + + /*************************/ + /* Up-scaling Vertically */ + /*************************/ + + if (g->vert_fac >= 0x00010000u) + { + n_out_rows = 0; + + if (g->n_saved_rows == 2) { +#if 0 + do { + weight_two_rows (g, 0x10000u-g->vert_pos, + dest_rows_ap[n_out_rows]); + n_out_rows += 1; + g->vert_pos += g->inv_vert_fac; + } while ((g->vert_pos>>16) == 0); + g->vert_pos &= 0x0000ffffu; +#else + /* We use vert_pos solely to determine the number of rows. + * We use inv_vert_pos to create the weights. + * In the commented-out code above, inv_vert_pos did both, + * but the problem was that the number of rows output can + * off by 1 compared with (num_in_rows*vert_fac), which is + * what our callers expect. + */ + g->vert_pos += g->vert_fac; + n_out_rows = (UINT)(g->vert_pos >> 16); + g->vert_pos &= 0x0000ffffu; + + for (u=0; u<n_out_rows; u++) { + weight = 0x10000 - g->inv_vert_pos; + if (weight < 0) weight = 0; + else if (weight > 0x10000) weight = 0x10000; + weight_two_rows (g, weight, dest_rows_ap[u]); + g->inv_vert_pos += g->inv_vert_fac; + } + + g->inv_vert_pos -= 0x10000; +#endif + /* discard the oldest row */ + g->n_saved_rows = 1; + p = g->rows_ap[0]; + g->rows_ap[0] = g->rows_ap[1]; + g->rows_ap[1] = p; + } + } + + /***************************/ + /* Down-scaling Vertically */ + /***************************/ + + else + { + n_out_rows = 0; + new_pos = g->vert_pos + (g->n_saved_rows-1)*g->vert_fac; + + if ((new_pos>>16) != 0) { + weight_n_rows (g, g->n_saved_rows, g->vert_pos, g->vert_fac, + dest_rows_ap[0]); + n_out_rows = 1; + g->vert_pos = new_pos & 0x0000ffffu; + + /* retain the newest row */ + p = g->rows_ap[0]; + g->rows_ap[0] = g->rows_ap[g->n_saved_rows-1]; + g->rows_ap[g->n_saved_rows-1] = p; + g->n_saved_rows = 1; + } + } + + return n_out_rows; +} + + + +/****************************************************************************** + ****************************************************************************** + + E X P O R T E D R O U T I N E S + + ****************************************************************************** + ******************************************************************************/ + + + +/*****************************************************************************\ + * + * scale_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD scale_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PSC_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(SC_INST), g); + *pXform = g; + memset (g, 0, sizeof(SC_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * scale_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD scale_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PSC_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + INSURE ( (pTraits->iBitsPerPixel==24 && pTraits->iComponentsPerPixel==3) + || (pTraits->iBitsPerPixel==8 && pTraits->iComponentsPerPixel==1) + || (pTraits->iBitsPerPixel==1 && pTraits->iComponentsPerPixel==1)); + INSURE (pTraits->iPixelsPerRow > 0); + + switch (pTraits->iBitsPerPixel) { + case 1: g->image_type = IM_BILEVEL; break; + case 8: g->image_type = IM_GRAY; break; + case 24: g->image_type = IM_COLOR; break; + } + + /* We don't actually support IM_BILEVEL currently. */ + INSURE(g->image_type != IM_BILEVEL); + + g->inTraits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * scale_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD scale_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PSC_INST g; + HANDLE_TO_PTR (hXform, g); + + g->horiz_fac = (aXformInfo[IP_SCALE_HORIZ_FACTOR].dword+0x80) >> 8; + g->vert_fac = (aXformInfo[IP_SCALE_VERT_FACTOR].dword+0x80) >> 8; + g->fast = aXformInfo[IP_SCALE_FAST].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * scale_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD scale_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * scale_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD scale_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PSC_INST g; + int i; + UINT in_row_len; + + HANDLE_TO_PTR (hXform, g); + + /**** Since there is no header, we'll report no usage of input ****/ + + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /**** Open the particular type of scaler we'll need ****/ + + in_row_len = g->inTraits.iPixelsPerRow; + + switch (g->image_type) { + case IM_BILEVEL: + if (g->vert_fac < 0x10000u) + g->fast = FALSE; /* bi-level down-scale doesn't have fast case */ + if (g->fast) bi_fast_open (g, in_row_len); + else bi_scale_open (g, in_row_len); + break; + + case IM_GRAY: + case IM_COLOR: + if (g->vert_fac > 0x10000u) + g->fast = FALSE; /* contone up-scale doesn't have fast case */ + contone_scale_open (g, in_row_len); + break; + } + + /**** Allocate the held output rows ****/ + + g->nMaxOutRows = (g->vert_fac+0x0000ffffu) >> 16; + INSURE (g->nMaxOutRows <= HELD_ARRAY_SIZE); + for (i=1; i<g->nMaxOutRows; i++) + IP_MEM_ALLOC (g->out_row_nbytes, g->apHeldOutRows[i]); + + /**** Report back input- and output-traits ****/ + + *pInTraits = g->inTraits; /* structure copies */ + *pOutTraits = g->inTraits; + pOutTraits->iPixelsPerRow = g->out_row_pixels; + if (pInTraits->lNumRows >= 0) { + /* use floating point because fixed-point product would + * overflow if # rows is over 16 bits */ + pOutTraits->lNumRows = (long) + ((float)pInTraits->lNumRows * g->vert_fac / 65536.0); + } + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * scale_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD scale_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PSC_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->in_row_nbytes; + *pdwMinOutBufLen = g->out_row_nbytes; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * scale_convert - Converts one row + * +\*****************************************************************************/ + +static WORD scale_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PSC_INST g; + BYTE src_traits; + ULONG dest_traits; + int inUsed, outUsed; + int n_rows; + WORD wResults; + + HANDLE_TO_PTR (hXform, g); + inUsed = 0; + outUsed = 0; + + /**** Return next stored output-row, if any ****/ + + if (g->nMoreHeldOutRows > 0) { + memcpy (pbOutputBuf, + g->apHeldOutRows[g->iNextHeldOutRow], + g->out_row_nbytes); + g->nMoreHeldOutRows -= 1; + g->iNextHeldOutRow += 1; + outUsed = g->out_row_nbytes; + goto finish; + } + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("scale_convert: Told to flush.\n"), 0, 0); + if (g->nMoreFlush == 0) + goto finish; + g->nMoreFlush -= 1; + /* do "scale a row" section below, with pbInputBuf equal to NULL */ + } else + inUsed = g->in_row_nbytes; + + /**** Scale a Row ****/ + + g->apHeldOutRows[0] = pbOutputBuf; /* 1st out-row is client's buffer */ + n_rows = 0; /* init to avoid compiler warning */ + src_traits = 0; + + switch (g->image_type) { + case IM_BILEVEL: + if (g->fast) + n_rows = bi_fast_row (g, pbInputBuf, src_traits, + g->apHeldOutRows, &dest_traits); + else + n_rows = bi_scale_row (g, pbInputBuf, src_traits, + g->apHeldOutRows, &dest_traits); + break; + + case IM_GRAY: + case IM_COLOR: + n_rows = contone_scale_row (g, pbInputBuf, g->apHeldOutRows); + break; + } + + INSURE (n_rows <= g->nMaxOutRows); + if (n_rows > 0) { + g->nMoreHeldOutRows = n_rows - 1; + g->iNextHeldOutRow = 1; + outUsed = g->out_row_nbytes; + } + + /**** Report results and return (inUsed and outUsed are valid here) ****/ + + finish: + + *pdwInputUsed = (DWORD)inUsed; + g->dwInNextPos += (DWORD)inUsed; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = (DWORD)outUsed; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += (DWORD)outUsed; + + wResults = ( inUsed>0 ? IP_CONSUMED_ROW : 0) + | (outUsed>0 ? IP_PRODUCED_ROW : 0) + | (g->nMoreHeldOutRows==0 ? IP_READY_FOR_DATA : 0) + | (pbInputBuf==NULL && + g->nMoreFlush==0 && + g->nMoreHeldOutRows==0 ? IP_DONE : 0); + return wResults; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * scale_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD scale_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * scale_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD scale_newPage ( + IP_XFORM_HANDLE hXform) +{ + PSC_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * scale_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD scale_closeXform (IP_XFORM_HANDLE hXform) +{ + PSC_INST g; + int i; + + HANDLE_TO_PTR (hXform, g); + + switch (g->image_type) { + case IM_BILEVEL: + if (g->fast) bi_fast_close (g); + else bi_scale_close (g); + break; + + case IM_GRAY: + case IM_COLOR: + contone_scale_close (g); + break; + } + + for (i=1; i<g->nMaxOutRows; i++) + IP_MEM_FREE (g->apHeldOutRows[i]); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * scaleTbl - Jump-table for scaler + * +\*****************************************************************************/ + +IP_XFORM_TBL scaleTbl = { + scale_openXform, + scale_setDefaultInputTraits, + scale_setXformSpec, + scale_getHeaderBufSize, + scale_getActualTraits, + scale_getActualBufSizes, + scale_convert, + scale_newPage, + scale_insertedData, + scale_closeXform +}; + +/* End of File */ diff --git a/ip/xskel.c b/ip/xskel.c new file mode 100644 index 0000000..930af94 --- /dev/null +++ b/ip/xskel.c @@ -0,0 +1,456 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xskel.c - A skeleton xform driver on which new xforms can be based + * + * You must change "skel" and "SKEL" to your actual xform name. + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * skelTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_SKEL_SPEC_1] = fully describe these options here, if any + * aXformInfo[IP_SKEL_SPEC_2] = + * + * Capabilities and Limitations: + * + * What does this xform do? What limitations of data types, ranges, etc? + * + * Default Input Traits, and Output Traits: + * + * Describe what you do with the default input traits, and how the + * output traits are determined. + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel * passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * +\******************************************************************************/ + +// Use the #define below if this transform will exist in a dll outside of the +// image pipeline. This will allow the functions to be exported. +// #define EXPORT_TRANFORM 1 + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + +#ifdef EXPORT_TRANSFORM +#define FUNC_STATUS __declspec (dllexport) +#else +#define FUNC_STATUS static +#endif + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} SKEL_INST, *PSKEL_INST; + + + +/*****************************************************************************\ + * + * skel_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PSKEL_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(SKEL_INST), g); + *pXform = g; + memset (g, 0, sizeof(SKEL_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * skel_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PSKEL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0); + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * skel_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PSKEL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Check your options in aXformInfo here, and save them. + * Use the INSURE macro like you'd use assert. INSURE jumps to + * fatal_error below if it fails. + */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * skel_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * skel_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PSKEL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + g->dwInNextPos = 0; + + *pInTraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * skel_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +FUNC_STATUS WORD skel_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PSKEL_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * skel_convert - Converts one row + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PSKEL_INST g; + int nBytes; + PBYTE pIn, pOut; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("skel_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + + /* At this point, pIn is your input buffer, and pOut is your output buffer. + * Do whatever you are going to do here. + */ + memcpy(pOut,pIn,nBytes); + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * skel_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * skel_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_newPage ( + IP_XFORM_HANDLE hXform) +{ + PSKEL_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * skel_closeXform - Destroys this instance + * +\*****************************************************************************/ + +FUNC_STATUS WORD skel_closeXform (IP_XFORM_HANDLE hXform) +{ + PSKEL_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * skelTbl - Jump-table for transform driver + * +\*****************************************************************************/ +#ifdef EXPORT_TRANSFORM +__declspec (dllexport) +#endif +IP_XFORM_TBL skelTbl = { + skel_openXform, + skel_setDefaultInputTraits, + skel_setXformSpec, + skel_getHeaderBufSize, + skel_getActualTraits, + skel_getActualBufSizes, + skel_convert, + skel_newPage, + skel_insertedData, + skel_closeXform +}; + +/* End of File */ + + +/*****************************************************************************\ + * + * ipGetXformTable - Returns pointer to the jump table + * +\*****************************************************************************/ + +#ifdef EXPORT_TRANSFORM +EXPORT(WORD) ipGetXformTable (LPIP_XFORM_TBL pXform) +{ + WORD wRet = IP_DONE; + + if (pXform) + { + *pXform = clrmapTbl; + } + else + { + wRet = IP_FATAL_ERROR; + } + + return wRet; +} +#endif diff --git a/ip/xtable.c b/ip/xtable.c new file mode 100644 index 0000000..35c85a2 --- /dev/null +++ b/ip/xtable.c @@ -0,0 +1,742 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xtable.c - Performs a 256-entry table lookup on all bytes + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * tableTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_TABLE_WHICH] = contents of table to use: + * IP_TABLE_USER = user-supplied pointer to a 256-byte table, + * IP_TABLE_USER_WORD = user-supplied pointer to a 4096-word table, + * IP_TABLE_PASS_THRU = pass-thru table that does nothing, + * IP_TABLE_GAMMA = gamma function, + * IP_TABLE_THRESHOLD = threshold (snaps each incoming byte to 0 or 255), + * IP_TABLE_MIRROR = mirror-image each incoming byte, + * IP_TABLE_USER_THREE = three user-supplied table pointers, + * IP_TABLE_USER_THREE_WORD = three user-supplied table pointers for 48-bit (see format below), + * IP_TABLE_BW_CLIP = white/black clipper (2 thresholds below) + * + * aXformInfo[IP_TABLE_OPTION] = + * parameter based on type of table to use. values: + * - (type IP_TABLE_USER) pointer to user table, + * this table is copied into this xform's instance, + * - (type IP_TABLE_GAMMA) gamma value, in 16.16 fixed-point, a value + * less than 1.0 does an inverse gamma, (for + * type IP_TABLE_GAMMA above), + * - (type IP_TABLE_THRESHOLD) threshold value. if an incoming byte is >= + * this value, it changes to 255, else it changes + * to 0 (for type IP_TABLE_THRESHOLD above), + * - (type IP_TABLE_BW_CLIP) hi word = number of 0 entries at start, + * lo word = number of 255 entries at end, + * + * For option IP_TABLE_USER_THREE above (three user-supplied table pointers), + * these three 256-byte tables are for 3-component color data. The pointers are in + * aXformInfo[IP_TABLE_COLOR_1], aXformInfo[IP_TABLE_COLOR_2], and aXformInfo[IP_TABLE_COLOR_3]. + * + * For option IP_TABLE_USER_THREE_WORD (three user tables for 48-bit color data), + * the pointers are in aXformInfo as with IP_TABLE_USER_THREE. + * Each table consists of 4096 words (8192 bytes) which are indexed by the high 12 bits + * of each color-channel. The low four bits are interpolated herein. Each table-entry + * contains a 16-bit pixel-value, even though it's indexed by just 12 bits. + * + * IP_TABLE_USER_WORD is like IP_TABLE_USER_THREE_WORD described above. + * + * For option IP_TABLE_BW_CLIP, the white/black clipper, the table starts with the given + * number of 0's, and ends with the given number of 255's, and linearly + * progresses between 1 and 254 in between them. This serves to snap + * almost-black to black, and almost-white to white. If these numbers + * are large, this table also boosts contrast. + * + * Capabilities and Limitations: + * + * The incoming data can be any kind of raw pixels of 1, 8, 16, 24 or 48 + * bits/pixel. Bi-level (1 bit/pixel) data is treated as 8 pixels per byte. + * All table-types support all these forms of input data. For 16 bits/channel + * data, the tables are interpolated. + * + * For improved precision, the following define larger (12-bit index) tables: + * IP_TABLE_USER_WORD - 16 bits per pixel (grayscale) + * IP_TABLE_USER_THREE_WORD - 48 bits per pixel (color) + * + * Also, IP_TABLE_GAMMA will create a 12-bit table when the input data + * is 16 bits/channel. + * + * 12-bit tables work with 8-bit/channel data, and 8-bit tables work with + * 16-bit/channel data. Truncation or interpolation is done as needed. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Apr 1998 Mark Overton -- wrote original code + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ +#include "math.h" /* for pow for generating gamma table */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + BYTE bWhich; /* which table to generate */ + BYTE bTables[3][256]; /* the 8-bit look-up tables */ + WORD *pwTables[3]; /* ptrs to 12-bit-index tables (for 16-bits/pixel) */ + BOOL bBigTable; /* are we using the 12-bit tables? */ + int nTables; /* # of tables defined (1 or 3) */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} TBL_INST, *PTBL_INST; + + +typedef enum { + TBL_USER, + TBL_PASS_THRU, + TBL_GAMMA, + TBL_THRESHOLD, + TBL_MIRROR, + TBL_USER_THREE, + TBL_BW_CLIP +} TABLE_TYPE; + + + +static BOOL generateTable ( + PTBL_INST g, + DWORD_OR_PVOID aXformInfo[]) +{ + IP_TABLE_TYPE which; + DWORD dwparam; + PVOID pvparam; + float flparam; + PBYTE pTable; + PWORD pwTable; + int nTable; + + g->nTables = 1; + g->bBigTable = FALSE; + pTable = g->bTables[0]; + dwparam = aXformInfo[IP_TABLE_OPTION].dword; + pvparam = aXformInfo[IP_TABLE_OPTION].pvoid; + flparam = aXformInfo[IP_TABLE_OPTION].fl; + which = (IP_TABLE_TYPE)aXformInfo[IP_TABLE_WHICH].dword; + g->bWhich = (BYTE)which; + + switch (which) + { + case IP_TABLE_USER: + if (pvparam == 0) + return FALSE; + memcpy (pTable, (PBYTE)pvparam, 256); + break; + + case IP_TABLE_USER_WORD: + if (pvparam == 0) + return FALSE; + IP_MEM_ALLOC (4097*sizeof(WORD), pwTable); /* 4097: extra entry at end */ + g->pwTables[0] = pwTable; + memcpy (pwTable, (PWORD)pvparam, 4096*sizeof(WORD)); + pwTable[4096] = pwTable[4095]; /* extra entry to help interpolation */ + g->bBigTable = TRUE; + break; + + case IP_TABLE_USER_THREE: + if (aXformInfo[IP_TABLE_COLOR_1].pvoid==0 || + aXformInfo[IP_TABLE_COLOR_2].pvoid==0 || + aXformInfo[IP_TABLE_COLOR_3].pvoid==0) + return FALSE; + memcpy (g->bTables[0], (PBYTE)aXformInfo[IP_TABLE_COLOR_1].pvoid, 256); + memcpy (g->bTables[1], (PBYTE)aXformInfo[IP_TABLE_COLOR_2].pvoid, 256); + memcpy (g->bTables[2], (PBYTE)aXformInfo[IP_TABLE_COLOR_3].pvoid, 256); + g->nTables = 3; + break; + + case IP_TABLE_USER_THREE_WORD: + if (aXformInfo[IP_TABLE_COLOR_1].pvoid==0 || + aXformInfo[IP_TABLE_COLOR_2].pvoid==0 || + aXformInfo[IP_TABLE_COLOR_3].pvoid==0) + return FALSE; + + for (nTable=0; nTable<3; nTable++) { + IP_MEM_ALLOC (4097*sizeof(WORD), pwTable); /* 4097: extra entry at end */ + g->pwTables[nTable] = pwTable; + memcpy (pwTable, (PWORD)aXformInfo[IP_TABLE_COLOR_1+nTable].pvoid, 4096*sizeof(WORD)); + pwTable[4096] = pwTable[4095]; /* extra entry to help interpolation */ + } + g->nTables = 3; + g->bBigTable = TRUE; + break; + + case IP_TABLE_PASS_THRU: + { + int i; + + for (i=0; i<=255; i++) + pTable[i] = i; + } + break; + + case IP_TABLE_GAMMA: + { + int index; + float fval; + float gamma; + float gamval; + + gamma = (float)flparam / (float)(1L<<16); + if (gamma<=0.0f || gamma>=10.0f) + return FALSE; + gamma = 1.0f / gamma; + + if (g->traits.iBitsPerPixel==16 || g->traits.iBitsPerPixel==48) { + WORD *pwTable; + IP_MEM_ALLOC (4097*sizeof(WORD), pwTable); /* 4097: extra entry at end */ + g->pwTables[0] = pwTable; + + for (index=0; index<=4095u; index++) { + fval = (float)index / 4095.0f; + gamval = 65535.0f * (float)pow(fval, gamma); + pwTable[index] = (WORD)(gamval + 0.5f); + } + + pwTable[4096] = pwTable[4095]; /* extra entry to help interpolation */ + g->bBigTable = TRUE; + } else { + for (index=0; index<=255u; index++) { + fval = (float)index / 255.0f; + gamval = 255.0f * (float)pow(fval, gamma); + pTable[index] = (BYTE)(gamval + 0.5f); + } + } + } + break; + + case IP_TABLE_THRESHOLD: + if (dwparam<1 || dwparam>255) + return FALSE; + memset (pTable, 0, dwparam); + memset (pTable+dwparam, 255, 256-dwparam); + break; + + case IP_TABLE_MIRROR: + { + UINT index, mask; + + for (index=0; index<=255; index++) { + for (mask=0x01u; mask<=0x80u; mask<<=1) { + pTable[index] <<= 1; + if (index & mask) + pTable[index] += 1; + } + } + } + break; + + case IP_TABLE_BW_CLIP: + { + DWORD nBlack, nWhite, nMid, slopeMid, posMid, index; + + nBlack = dwparam >> 16; + nWhite = dwparam & 0xFFFFu; + + if (nBlack+nWhite > 256) + return FALSE; + + for (index=0; index<nBlack; index++) + pTable[index] = 0; + + for (index=256-nWhite; index<=255; index++) + pTable[index] = 255; + + nMid = 256 - nBlack - nWhite; + slopeMid = (255ul<<16) / (nMid + 1); + posMid = 0x8000u; /* offset for rounding */ + + for (index=nBlack; index<256-nWhite; index++) { + posMid += slopeMid; + pTable[index] = (BYTE)(posMid >> 16); + } + } + break; + + default: + return FALSE; + } + + return TRUE; + +fatal_error: + return FALSE; +} + + + +/*****************************************************************************\ + * + * table_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD table_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PTBL_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(TBL_INST), g); + *pXform = g; + memset (g, 0, sizeof(TBL_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * table_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD table_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PTBL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0); + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * table_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD table_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PTBL_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (! generateTable(g,aXformInfo)) + goto fatal_error; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * table_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD table_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * table_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD table_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PTBL_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * table_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD table_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PTBL_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * table_convert - Converts one row + * +\*****************************************************************************/ + +static WORD table_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PTBL_INST g; + int nBytes, nTable; + PBYTE pIn, pOut, pOutAfter; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("table_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pOut + nBytes; + + if (g->bWhich == IP_TABLE_PASS_THRU) + { + memcpy (pOut, pIn, nBytes); + } + else if (g->traits.iBitsPerPixel==16 || g->traits.iBitsPerPixel==48) + { + /* 16 bits per channel -- interpolate between table-entries */ + int x, xHi, y1, y2; + WORD *pwTable, *pwIn, *pwOut, *pwOutAfter; + BYTE *pbTable; + + pwIn = (WORD*)pIn; + pwOut = (WORD*)pOut; + pwOutAfter = (WORD*)pOutAfter; + + if (g->bBigTable) + { + /* we're using 12-bit table(s) */ + while (pwOut < pwOutAfter) { + for (nTable=0; nTable<g->nTables; nTable++) { + pwTable = g->pwTables[nTable]; + x = (unsigned)(*pwIn++); + xHi = x >> 4; /* hi 12 bits is used for indexing into the table */ + y1 = (unsigned)pwTable[xHi ]; /* index is in 0..4095 */ + y2 = (unsigned)pwTable[xHi+1]; /* extra entry in table is for index=4096 */ + /* interpolate the lowest 4 bits */ + *pwOut++ = (WORD)(((y2-y1)*(x&0x0f)>>4) + y1); + } + } + } + else /* we're using 8-bit table(s) */ + { + while (pwOut < pwOutAfter) { + for (nTable=0; nTable<g->nTables; nTable++) { + pbTable = g->bTables[nTable]; + x = (unsigned)(*pwIn++); + xHi = x >> 8; /* hi 8 bits is used for indexing into the table */ + y1 = (unsigned)pbTable[xHi]; /* index is in 0..255 for both */ + y2 = (unsigned)pbTable[xHi==255 ? 255 : xHi+1]; + /* interpolate the lowest 8 bits */ + *pwOut++ = (WORD)((y2-y1)*(x&0x0ff) + (y1<<8)); + } + } + } + } + else /* 8 bits per channel -- the normal case */ + { + if (g->bBigTable) + { + /* using a big table for 8- or 24-bit data, for some reason */ + while (pOut < pOutAfter) + for (nTable=0; nTable<g->nTables; nTable++) + *pOut++ = (BYTE)(g->pwTables[nTable][(unsigned)(*pIn++)<<4] >> 8); + } + else if (g->nTables == 3) + { + while (pOut < pOutAfter) { + /* process two pixels at a time for improved speed */ + pOut[0] = g->bTables[0][pIn[0]]; + pOut[1] = g->bTables[1][pIn[1]]; + pOut[2] = g->bTables[2][pIn[2]]; + pOut[3] = g->bTables[0][pIn[3]]; + pOut[4] = g->bTables[1][pIn[4]]; + pOut[5] = g->bTables[2][pIn[5]]; + + pIn += 6; + pOut += 6; + } + } + else /* using a single table */ + { + while (pOut < pOutAfter) { + /* process eight pixels at a time for improved speed */ + pOut[0] = g->bTables[0][pIn[0]]; + pOut[1] = g->bTables[0][pIn[1]]; + pOut[2] = g->bTables[0][pIn[2]]; + pOut[3] = g->bTables[0][pIn[3]]; + pOut[4] = g->bTables[0][pIn[4]]; + pOut[5] = g->bTables[0][pIn[5]]; + pOut[6] = g->bTables[0][pIn[6]]; + pOut[7] = g->bTables[0][pIn[7]]; + + pIn += 8; + pOut += 8; + } + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * table_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD table_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * table_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD table_newPage ( + IP_XFORM_HANDLE hXform) +{ + PTBL_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * table_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD table_closeXform (IP_XFORM_HANDLE hXform) +{ + PTBL_INST g; + int i; + + HANDLE_TO_PTR (hXform, g); + + for (i=0; i<3; i++) + if (g->pwTables[i] != NULL) + IP_MEM_FREE (g->pwTables[i]); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tableTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL tableTbl = { + table_openXform, + table_setDefaultInputTraits, + table_setXformSpec, + table_getHeaderBufSize, + table_getActualTraits, + table_getActualBufSizes, + table_convert, + table_newPage, + table_insertedData, + table_closeXform +}; + +/* End of File */ diff --git a/ip/xthumb.c b/ip/xthumb.c new file mode 100644 index 0000000..f5fb33a --- /dev/null +++ b/ip/xthumb.c @@ -0,0 +1,558 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xthumb.c - Downscales quickly by large factor for generating thumbnail image + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * thumbTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_THUMB_SCALE_SPEC] determines scale-factor indirectly or directly, + * depending on whether it's positive or negative. + * + * positive: value is the maximum output-width in pixels. xthumb will + * select the N for the scale-factor of the form 1/N which + * results in the largest possible output width not exceeding + * the value in aXformInfo[IP_THUMB_SCALE_SPEC]. + * + * negative: absolute value is N for the scale-factor of the form 1/N. + * the scale-factor is being specified directly. + * + * Capabilities and Limitations: + * + * Downscales any type of raw data quickly by a large scale-factor + * specified by aXformInfo[IP_THUMB_SCALE_SPEC]. The pixels are averaged together. + * For bilevel input, the output is 8-bit gray, where each gray pixel + * is the average blackness of an area of the input bilevel image. + * For 8-bit gray and 24-bit color input data, the output data is + * the same type as the input. + * + * The scale-factor is of the form 1/N, where N is an integer. + * Warning: If N is 1 or almost 1, the actual output width will be + * quite smaller than that requested. + * + * If input width is smaller than output width, N will be 1 (no + * scaling). + * + * ipGetImageTraits must be called to determine the actual output width. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * used based on scale factor + * iBitsPerPixel * must be 1, 8 or 24 8 or 24 + * iComponentsPerPixel * must be 1 or 3 same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows used if valid based on scale factor + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Mar 1998 Mark Overton -- wrote original code + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS inTraits; /* traits of the input image */ + int iFactorSpec; /* factor spec in aXformInfo[0] */ + WORD wScale; /* the N in scale-factor of 1/N */ + WORD wPreShift; /* # bits to shift sum right before multiply */ + DWORD dwSumFac; /* factor to multiply sum by (16.16 fixed-pt) */ + DWORD dwOutputWidth; /* # pixels per row in output */ + DWORD dwInRowBytes; /* # bytes in each input row */ + DWORD dwOutRowBytes; /* # bytes in each output row */ + WORD wMoreRows2Sum; /* # more rows to be summed together */ + PULONG pulSums; /* pixel-sums; each pixel is a ULONG */ + ULONG ulRowsInput; /* # of rows input so far */ + ULONG ulRowsOutput; /* # of rows output so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} TN_INST, *PTN_INST; + + + +/*****************************************************************************\ + * + * thumb_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD thumb_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PTN_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(TN_INST), g); + *pXform = g; + memset (g, 0, sizeof(TN_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * thumb_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD thumb_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PTN_INST g; + + HANDLE_TO_PTR (hXform, g); + + INSURE (pTraits->iPixelsPerRow > 0); + g->inTraits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * thumb_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD thumb_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PTN_INST g; + + HANDLE_TO_PTR (hXform, g); + g->iFactorSpec = (int)aXformInfo[IP_THUMB_SCALE_SPEC].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * thumb_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD thumb_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * thumb_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD thumb_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PTN_INST g; + int N; + long lMaxSum; + long nBytes; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Compute N in our scale-factor of 1/N */ + if (g->iFactorSpec <= 0) { + N = -(g->iFactorSpec); + } else { + /* the +iFactorSpec-1 below biases N high (cieling function), + * so the output width is biased low, so that we'll never go + * larger than the requested output width. + */ + N = (g->inTraits.iPixelsPerRow + g->iFactorSpec - 1) / g->iFactorSpec; + } + + if (N < 1) N = 1; + g->wScale = N; + + /* Compute max summation of N-by-N input pixels */ + lMaxSum = (long)N*N * (g->inTraits.iBitsPerPixel==1 ? 1 : 255); + + /* Compute pre-shift so that max sum does not exceed 16 bits */ + if (lMaxSum >= (1L<<28)) g->wPreShift = 16; + else if (lMaxSum >= (1L<<24)) g->wPreShift = 12; + else if (lMaxSum >= (1L<<20)) g->wPreShift = 8; + else if (lMaxSum >= (1L<<16)) g->wPreShift = 4; + else g->wPreShift = 0; + + /* Now do the pre-shift on max sum */ + lMaxSum >>= g->wPreShift; + INSURE (lMaxSum < (1L<<16)); /* make sure it fits in 16 bits */ + + /* And compute the factor for converting a sum into a 0..255 pixel */ + g->dwSumFac = (DWORD)((255.0/(float)lMaxSum) * (float)(1L<<16)); + + *pInTraits = g->inTraits; /* structure copies */ + *pOutTraits = g->inTraits; + + if (pOutTraits->iBitsPerPixel == 1) + pOutTraits->iBitsPerPixel = 8; + pOutTraits->iPixelsPerRow = g->dwOutputWidth = pInTraits->iPixelsPerRow / N; + if (pOutTraits->lNumRows >= 0) + pOutTraits->lNumRows /= N; + + g->wMoreRows2Sum = N; + g->dwInRowBytes = + (pInTraits->iPixelsPerRow * pInTraits->iBitsPerPixel + 7) / 8; + g->dwOutRowBytes = g->dwOutputWidth * pInTraits->iComponentsPerPixel; + + nBytes = g->dwOutRowBytes * sizeof(ULONG); + IP_MEM_ALLOC (nBytes, g->pulSums); + memset (g->pulSums, 0, nBytes); + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * thumb_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD thumb_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PTN_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->dwInRowBytes; + *pdwMinOutBufLen = g->dwOutRowBytes; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * thumb_convert - Converts one row + * +\*****************************************************************************/ + +static WORD thumb_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PTN_INST g; + PBYTE pIn, pOut; + BYTE bMask, bVal=0; + ULONG ulSum, sum0, sum1, sum2; + ULONG *pulSum, *pulSumAfter; + UINT u; + BOOL fSentRow; + + HANDLE_TO_PTR (hXform, g); + pulSumAfter = g->pulSums + g->dwOutRowBytes; + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("thumb_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Sum this Input Row ****/ + + INSURE (dwInputAvail >= g->dwInRowBytes); + pIn = pbInputBuf; + + switch (g->inTraits.iBitsPerPixel) { + case 1: /* bilevel input */ + bMask = 0; + for (pulSum=g->pulSums; pulSum<pulSumAfter; pulSum++) { + ulSum = *pulSum; + for (u=g->wScale; u>0; u--) { + if (bMask == 0) { + bMask = 0x80u; + bVal = *pIn++; + } + if ((bMask & bVal) == 0) { + /* since the sum is a measure of overall whiteness, + * increment it for a *white* input pixel (0) */ + ulSum += 1; + } + bMask >>= 1; + } + *pulSum = ulSum; + } + break; + + case 8: /* 8-bit gray input */ + for (pulSum=g->pulSums; pulSum<pulSumAfter; pulSum++) { + ulSum = *pulSum; + for (u=g->wScale; u>0; u--) + ulSum += (ULONG)(*pIn++); + *pulSum = ulSum; + } + break; + + case 24: /* 3-component color input */ + for (pulSum=g->pulSums; pulSum<pulSumAfter; pulSum+=3) { + sum0 = pulSum[0]; + sum1 = pulSum[1]; + sum2 = pulSum[2]; + for (u=g->wScale; u>0; u--) { + sum0 += (ULONG)(*pIn++); + sum1 += (ULONG)(*pIn++); + sum2 += (ULONG)(*pIn++); + } + pulSum[0] = sum0; + pulSum[1] = sum1; + pulSum[2] = sum2; + } + break; + } /* switch */ + + *pdwInputUsed = g->dwInRowBytes; + g->dwInNextPos += g->dwInRowBytes; + *pdwInputNextPos = g->dwInNextPos; + g->ulRowsInput += 1; + + /**** If it's time, Compute Output Row ****/ + + *pdwOutputThisPos = g->dwOutNextPos; + g->wMoreRows2Sum -= 1; + + if (g->wMoreRows2Sum > 0) { + fSentRow = FALSE; + *pdwOutputUsed = 0; + } else { + g->wMoreRows2Sum = g->wScale; + fSentRow = TRUE; + g->ulRowsOutput += 1; + INSURE (dwOutputAvail >= g->dwOutRowBytes); + *pdwOutputUsed = g->dwOutRowBytes; + g->dwOutNextPos += g->dwOutRowBytes; + + pulSum = g->pulSums; + pOut = pbOutputBuf; + for (pulSum=g->pulSums; pulSum<pulSumAfter; pulSum++) { + *pOut++ = (BYTE)((*pulSum >> g->wPreShift) * g->dwSumFac >> 16); + } + + memset (g->pulSums, 0, g->dwOutRowBytes*sizeof(ULONG)); + } + + /**** Return ****/ + + return IP_CONSUMED_ROW | + IP_READY_FOR_DATA | + (fSentRow ? IP_PRODUCED_ROW : 0); + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * thumb_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD thumb_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * thumb_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD thumb_newPage ( + IP_XFORM_HANDLE hXform) +{ + PTN_INST g; + + HANDLE_TO_PTR (hXform, g); + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * thumb_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD thumb_closeXform (IP_XFORM_HANDLE hXform) +{ + PTN_INST g; + + HANDLE_TO_PTR (hXform, g); + + if (g->pulSums != NULL) + IP_MEM_FREE (g->pulSums); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * thumbTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL thumbTbl = { + thumb_openXform, + thumb_setDefaultInputTraits, + thumb_setXformSpec, + thumb_getHeaderBufSize, + thumb_getActualTraits, + thumb_getActualBufSizes, + thumb_convert, + thumb_newPage, + thumb_insertedData, + thumb_closeXform +}; + +/* End of File */ diff --git a/ip/xtiff.c b/ip/xtiff.c new file mode 100644 index 0000000..e1338e5 --- /dev/null +++ b/ip/xtiff.c @@ -0,0 +1,1338 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/*****************************************************************************\ + * + * xtiff.c - encoder and decoder for TIFF files for image processor + * + ***************************************************************************** + * + * Name of Global Jump-Table: + * + * tifEncodeTbl = the encoder, + * tifDecodeTbl = the decoder. + * + * Encoder: Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_TIFF_FILE_PATH] = pointer to a file-path, or NULL + * + * If NULL, a one-page TIFF is output in the normal manner. + * If not NULL, this must be a pointer to a string containing a file-path. + * If the given file does not exist, it will be created and one image will + * be put in it. If it does exist, the image will be APPENDED to this file, + * and no data will be output by this xform. So if one or more pages are + * already in the file, we will add another page to it. This xform does + * the opening and closing of the file. + * + * Decoder: Items in aXformInfo array passed into setXformSpec: + * + * None. + * + * Capabilities and Limitations: + * + * Handles 1, 8, 16, 24 and 48 bits per pixel. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Apr 2000, Mark Overton, ported header-setup from TWAIN source, and added + * multi-page capability + * Feb 1998, Mark Overton, ported to new Image Processor code + * May 1996, Mark Overton, wrote original code + * Jun 2000, Mark Overton, wrote a simple decoder that does no file-seeks + * + *****************************************************************************/ + + +#include "stdio.h" /* for FILE operations */ +#include "assert.h" +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + + + +/*____________________________________________________________________________ + | | + | Constants pertaining to tags | + |____________________________________________________________________________| +*/ + + +/* TIF_INST - our instance variables */ + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the image */ + int iBitsPerSample; /* bits per channel (1, 8 or 16) */ + BOOL bByteSwap; /* bytes are in wrong endian order, so must swap? */ + char sFilePath[200]; /* path to the file (empty string means none) */ + FILE *fileOut; /* handle of opened file */ + DWORD dwRawRowBytes; /* bytes per raw row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwValidChk; /* struct validity check value */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + BOOL fDidHeader; /* already sent the header? */ +} TIF_INST, *PTIF_INST; + + +/* Types having known sizes */ +typedef unsigned char TIFF_UBYTE; /* 8 bits */ +typedef unsigned short TIFF_USHORT; /* 16 bits */ +typedef unsigned int TIFF_ULONG; /* 32 bits */ +typedef signed char TIFF_SBYTE; /* 8 bits */ +typedef signed short TIFF_SSHORT; /* 16 bits */ +typedef signed int TIFF_SLONG; /* 32 bits */ + +/* TIFF file header defines */ +#define INTEL 0x4949 +#define TIFF_VERSION 42 + +/* TIFF field lengths */ +#define TIFFBYTE 1 +#define TIFFASCII 2 +#define TIFFSHORT 3 +#define TIFFLONG 4 +#define TIFFRATIONAL 5 +#define TIFFSBYTE 6 +#define TIFFUNDEFINED 7 +#define TIFFSSHORT 8 +#define TIFFSLONG 9 +#define TIFFSRATIONAL 10 + +/* TIFF compression type */ +#define NOCOMPRESSION 1 + +/* TIFF planar configuration */ +#define CONTIGUOUS 1 +#define PLANAR 2 + +/* TIFF photometric interpretations */ +#define ZERO_IS_WHITE 0 +#define ZERO_IS_BLACK 1 +#define RGB_COLOR 2 + +/* TIFF resolution units */ +#define DOTS_PER_INCH 2 + + +/* TIFF tags */ +#define NUMTAGS 13 +#define BITS_PER_SAMPLE 258 // 0x0102 +#define COMPRESSION 259 // 0x0103 +#define IMAGE_LENGTH 257 // 0x0101 +#define IMAGE_WIDTH 256 // 0x0100 +#define NEW_SUBFILE 254 // 0x00fe +#define PHOTO_INTERPRET 262 // 0x0106 +#define RESOLUTION_UNIT 296 // 0x0128 +#define ROWS_PER_STRIP 278 // 0x0116 +#define SAMPLES_PER_PIXEL 277 // 0x0115 +#define STRIP_COUNTS 279 // 0x0117 +#define STRIP_OFFSETS 273 // 0x0111 +#define XRESOLUTION 282 // 0x011A +#define YRESOLUTION 283 // 0x011B + +typedef struct { + TIFF_ULONG n; + TIFF_ULONG d; +} __attribute__((packed)) RATIONAL; + +typedef union { + TIFF_UBYTE b[4]; + TIFF_USHORT s[2]; + TIFF_ULONG l; + TIFF_ULONG o; +} __attribute__((packed)) TIFFVALUE; + +typedef struct { + TIFF_USHORT TagID; + TIFF_USHORT Kind; + TIFF_ULONG Length; /* number of items, NOT number of bytes */ + TIFFVALUE Value; +} __attribute__((packed)) TIFFTAG; + +typedef struct { + TIFF_USHORT NumTags; + TIFFTAG Tag[NUMTAGS]; + TIFF_ULONG OffsetNextIFD; +} __attribute__((packed)) TIFFIFD; + +typedef struct { + TIFF_UBYTE ByteOrder[2]; + TIFF_USHORT Version; + TIFF_ULONG OffsetFirstIFD; +} __attribute__((packed)) TIFFHEADER; + +#define NUMEXTBYTES (2*sizeof(RATIONAL) + 3*sizeof(TIFF_USHORT)) +#define MAX_HEADER_SIZE (sizeof(TIFFHEADER) + sizeof(TIFFIFD) + NUMEXTBYTES) + + + +/*****************************************************************************\ + * + * tifEncode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD tifEncode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PTIF_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(TIF_INST), g); + *pXform = g; + memset (g, 0, sizeof(TIF_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifEncode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD tifEncode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PTIF_INST g; + int ppr, bpp; + + HANDLE_TO_PTR (hXform, g); + ppr = pTraits->iPixelsPerRow; + bpp = pTraits->iBitsPerPixel; + + /* Insure that values we actually use are known */ + INSURE (ppr > 0); + INSURE (bpp > 0); + + g->dwRawRowBytes = (ppr*bpp + 7) / 8; + g->traits = *pTraits; /* a structure copy */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifEncode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD tifEncode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PTIF_INST g; + char *s; + + HANDLE_TO_PTR (hXform, g); + s = (char*)aXformInfo[IP_TIFF_FILE_PATH].pvoid; + if (s != NULL) + strcpy (g->sFilePath, s); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifEncode_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD tifEncode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * tifEncode_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD tifEncode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + /* Since we don't change traits, just copy out the default traits */ + *pInTraits = g->traits; + *pOutTraits = g->traits; + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * tifEncode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD tifEncode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PTIF_INST g; + UINT len; + + HANDLE_TO_PTR (hXform, g); + len = g->dwRawRowBytes; + *pdwMinInBufLen = len; + + if (len < MAX_HEADER_SIZE) + len = MAX_HEADER_SIZE; + *pdwMinOutBufLen = len; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * outputHeader - Only called by tifEncode_convert + * +\****************************************************************************/ + + + +static void SetTag (TIFFTAG *pTag, + unsigned short TagID, short Kind, long Length, int Value) +{ + pTag->TagID = (TIFF_USHORT)TagID; + pTag->Kind = (TIFF_USHORT)Kind; + pTag->Length = (TIFF_ULONG)Length; + pTag->Value.l = (TIFF_ULONG)Value; /* assumes little-endian computer */ +} + + + +static int WriteFileHeader (PBYTE pTIFF) +{ + TIFFHEADER *pHead; + + pHead = (TIFFHEADER*) pTIFF; + pHead->ByteOrder[0] = 'I'; + pHead->ByteOrder[1] = 'I'; /* assumes little-endian computer */ + pHead->Version = 42; + pHead->OffsetFirstIFD = 8; + + return 8; /* we output 8 bytes */ +} + + + +static int WriteIFD ( + PBYTE pTIFF, /* out: the IFD is written to this buffer */ + int iStartOffset, /* in: file-offset at which this IFD starts */ + int WidthBytes, /* in: row-width in bytes */ + int WidthPixels, /* in: row-width in pixels */ + int Height, /* in: number of rows */ + int BPP, /* in: bits per pixel */ + int XRes, /* in: dpi in X */ + int YRes) /* in: dpi in Y */ +{ + TIFFIFD *pIFD; + TIFFTAG *pTag; + BYTE *pMore; + TIFF_USHORT *pBPS; + int iBPSOffset, iImageOffset; + int SPP, BPS, PI; + + pIFD = (TIFFIFD*)pTIFF; + pMore = pTIFF + sizeof(TIFFIFD); /* 1st byte after the IFD */ + + /* allocate space for the 3 shorts for the 3 bps values (if needed) */ + iBPSOffset = iStartOffset + (pMore-pTIFF); + pBPS = (TIFF_USHORT*)pMore; + pMore += 3*sizeof(TIFF_USHORT); + + /* the pixels are put after the above items */ + iImageOffset = iStartOffset + (pMore-pTIFF); + + switch (BPP) { + case 1: + PI = ZERO_IS_WHITE; SPP = 1; BPS = 1; break; /* 1-bit bilevel */ + case 8: + PI = ZERO_IS_BLACK; SPP = 1; BPS = 8; break; /* 8-bit grayscale */ + case 16: + PI = ZERO_IS_BLACK; SPP = 1; BPS = 16; break; /* 16-bit grayscale */ + case 24: + PI = RGB_COLOR; SPP = 3; BPS = 8; break; /* 24-bit color */ + case 48: + PI = RGB_COLOR; SPP = 3; BPS = 16; break; /* 48-bit color */ + default: + PI = RGB_COLOR; SPP = 3; BPS = 8; /* guess 24-bit color */ + assert (0); /* crash if in debug mode */ + } + + pIFD->NumTags = NUMTAGS; + pTag = pIFD->Tag; + SetTag (pTag++, NEW_SUBFILE, TIFFSHORT, 1, 0); + SetTag (pTag++, IMAGE_WIDTH, TIFFLONG, 1, WidthPixels); + SetTag (pTag++, IMAGE_LENGTH, TIFFLONG, 1, Height); + SetTag (pTag++, BITS_PER_SAMPLE, TIFFSHORT, SPP, SPP>1 ? iBPSOffset : BPS); + SetTag (pTag++, COMPRESSION, TIFFSHORT, 1, 1); + SetTag (pTag++, PHOTO_INTERPRET, TIFFSHORT, 1, PI); + SetTag (pTag++, STRIP_OFFSETS, TIFFLONG, 1, iImageOffset); + SetTag (pTag++, SAMPLES_PER_PIXEL, TIFFSHORT, 1, SPP); + SetTag (pTag++, ROWS_PER_STRIP, TIFFLONG, 1, Height); + SetTag (pTag++, STRIP_COUNTS, TIFFLONG, 1, WidthBytes*Height); + SetTag (pTag++, XRESOLUTION, TIFFSHORT, 1, XRes); + SetTag (pTag++, YRESOLUTION, TIFFSHORT, 1, YRes); + SetTag (pTag++, RESOLUTION_UNIT, TIFFSHORT, 1, 2); + assert ((pTag - pIFD->Tag) == NUMTAGS); + pIFD->OffsetNextIFD = 0L; /* assume there is no next IFD */ + + /* Stick Samples Per Pixel here for color; Only used if pointed to by tag */ + *pBPS++ = BPS; + *pBPS++ = BPS; + *pBPS++ = BPS; + + return pMore - pTIFF; +} + + + +static WORD AppendIFDToFile ( + PTIF_INST g, /* in: ptr to instance structure */ + PBYTE pbTempBuf) /* in: temp buffer large enough to hold an IFD */ +{ + int result; + TIFF_ULONG fileEndPos, IFDPos, pointerPos; + TIFF_USHORT numTags; + int iHeaderLen, iIFDLen, iTotalLen; + + /***** If the file is empty, do usual set-up *****/ + + g->fileOut = fopen (g->sFilePath, "a+b"); + INSURE (g->fileOut != NULL); + result = fseek (g->fileOut, 0, SEEK_END); + INSURE (result == 0); + fileEndPos = ftell (g->fileOut); + INSURE (result >= 0); + + if (fileEndPos == 0) { + iHeaderLen = WriteFileHeader(pbTempBuf); + iIFDLen = WriteIFD (pbTempBuf+iHeaderLen, iHeaderLen, + g->dwRawRowBytes, g->traits.iPixelsPerRow, + g->traits.lNumRows, g->traits.iBitsPerPixel, + g->traits.lHorizDPI>>16, g->traits.lVertDPI>>16); + + iTotalLen = iHeaderLen + iIFDLen; + INSURE (iTotalLen <= MAX_HEADER_SIZE); + + result = fwrite (pbTempBuf, 1, iTotalLen, g->fileOut); + INSURE (result == iTotalLen); + + return IP_READY_FOR_DATA; + } + + /***** Find the last IFD *****/ + + result = fseek (g->fileOut, 4, SEEK_SET); + INSURE (result == 0); + result = fread (&IFDPos, 4, 1, g->fileOut); /* assumes little-endian file */ + INSURE (result == 1); + + do { /* hop thru the IFDs until we hit the last one */ + result = fseek (g->fileOut, IFDPos, SEEK_SET); + INSURE (result == 0); + result = fread (&numTags, 2, 1, g->fileOut); /* assumes little-endian file */ + INSURE (result==1 && numTags>0); + pointerPos = IFDPos + 2 + numTags*sizeof(TIFFTAG); + result = fseek (g->fileOut, pointerPos, SEEK_SET); + INSURE (result == 0); + result = fread (&IFDPos, 4, 1, g->fileOut); /* assumes little-endian file */ + INSURE (result == 1); + } while (IFDPos != 0); + + /***** PointerPos is the final IFD offset in the file; change it *****/ + + /* switch to writing from now on */ + fclose (g->fileOut); + g->fileOut = fopen(g->sFilePath, "r+b"); + INSURE (g->fileOut != NULL); + + result = fseek (g->fileOut, pointerPos, SEEK_SET); + INSURE (result == 0); + result = fwrite (&fileEndPos, 4, 1, g->fileOut); /* assumes little-endian file */ + INSURE (result == 1); + + /***** Output a new IFD for the new page *****/ + + iIFDLen = WriteIFD (pbTempBuf, fileEndPos, + g->dwRawRowBytes, g->traits.iPixelsPerRow, + g->traits.lNumRows, g->traits.iBitsPerPixel, + g->traits.lHorizDPI>>16, g->traits.lVertDPI>>16); + + result = fseek (g->fileOut, 0, SEEK_END); + INSURE (result == 0); + result = fwrite (pbTempBuf, 1, iIFDLen, g->fileOut); + INSURE (result == iIFDLen); + /* leave the file-position at the end, where image-data will go */ + + return IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +static WORD outputHeader ( + PTIF_INST g, /* in: ptr to instance structure */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + int iHeaderLen, iIFDLen, iTotalLen; + + INSURE (dwOutputAvail >= MAX_HEADER_SIZE); + *pdwOutputThisPos = 0; + + if (g->sFilePath[0] != 0) { + *pdwOutputUsed = 0; + return AppendIFDToFile (g, pbOutputBuf); + } + + iHeaderLen = WriteFileHeader(pbOutputBuf); + iIFDLen = WriteIFD (pbOutputBuf+iHeaderLen, iHeaderLen, + g->dwRawRowBytes, g->traits.iPixelsPerRow, + g->traits.lNumRows, g->traits.iBitsPerPixel, + g->traits.lHorizDPI>>16, g->traits.lVertDPI>>16); + + iTotalLen = iHeaderLen + iIFDLen; + INSURE (iTotalLen <= MAX_HEADER_SIZE); + *pdwOutputUsed = iTotalLen; + *pdwOutputThisPos = 0; + g->dwOutNextPos = iTotalLen; + + return IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifEncode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD tifEncode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PTIF_INST g; + UINT n; + + HANDLE_TO_PTR (hXform, g); + + /**** Output the Header if we haven't already ****/ + + if (! g->fDidHeader) { + g->fDidHeader = TRUE; + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + return outputHeader (g, dwOutputAvail, pbOutputBuf, + pdwOutputUsed, pdwOutputThisPos); + } + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("tif_encode_convert_row: Told to flush.\n"), 0, 0); + if (g->traits.lNumRows < 0) { + /* # rows wasn't known at first, so output header again + * now that we know the number of rows */ + INSURE (g->sFilePath[0] == 0); + g->traits.lNumRows = g->dwRowsDone; + *pdwInputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + return outputHeader (g, dwOutputAvail, pbOutputBuf, + pdwOutputUsed, pdwOutputThisPos); + } + + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + n = g->dwRawRowBytes; + INSURE (dwInputAvail >= n); + INSURE (dwOutputAvail >= n); + + if (g->sFilePath[0] == 0) { + memcpy (pbOutputBuf, pbInputBuf, n); + + *pdwOutputUsed = n; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += n; + } else { + UINT result; + INSURE (g->fileOut != NULL); + result = fwrite (pbInputBuf, 1, n, g->fileOut); + INSURE (result == n); + + *pdwOutputUsed = 0; + *pdwOutputThisPos = 0; + g->dwOutNextPos = 0; + } + + g->dwInNextPos += n; + *pdwInputNextPos = g->dwInNextPos; + *pdwInputUsed = n; + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifEncode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD tifEncode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * tifEncode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD tifEncode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * tifEncode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD tifEncode_closeXform (IP_XFORM_HANDLE hXform) +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + if (g->fileOut != NULL) + fclose (g->fileOut); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifEncodeTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL tifEncodeTbl = { + tifEncode_openXform, + tifEncode_setDefaultInputTraits, + tifEncode_setXformSpec, + tifEncode_getHeaderBufSize, + tifEncode_getActualTraits, + tifEncode_getActualBufSizes, + tifEncode_convert, + tifEncode_newPage, + tifEncode_insertedData, + tifEncode_closeXform +}; + + + + +/*****************************************************************************\ + ***************************************************************************** + * + * D E C O D E R + * + ***************************************************************************** +\*****************************************************************************/ + + + + +/*****************************************************************************\ + * + * tifDecode_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD tifDecode_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PTIF_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(TIF_INST), g); + *pXform = g; + memset (g, 0, sizeof(TIF_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifDecode_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD tifDecode_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + g->traits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifDecode_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD tifDecode_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PTIF_INST g; + char *s; + + HANDLE_TO_PTR (hXform, g); + /* file-path is not used, but might be later, so save it */ + s = (char*)aXformInfo[IP_TIFF_FILE_PATH].pvoid; + if (s != NULL) + strcpy (g->sFilePath, s); + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifDecode_getHeaderBufSize - Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD tifDecode_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + *pdwInBufLen = MAX_HEADER_SIZE + 10000; /* 10000 gives us huge margin */ + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * ByteSwap - Reverses endian-type of the given variable (1, 2, 4 or 8 bytes) + * +\*****************************************************************************/ + +static void ByteSwap ( + void *pvVar, + int nBytes) +{ + BYTE b; + BYTE *pb = (BYTE*)pvVar; + + switch (nBytes) { + case 1: + /* do nothing */ + break; + case 2: + b = pb[0]; pb[0] = pb[1]; pb[1] = b; + break; + case 4: + b = pb[1]; pb[1] = pb[2]; pb[2] = b; + b = pb[0]; pb[0] = pb[3]; pb[3] = b; + break; + case 8: + /* this is actually two longs, so fix each one */ + ByteSwap (pvVar, 4); + ByteSwap ((BYTE*)pvVar+4, 4); + break; + } +} + + + +/*****************************************************************************\ + * + * ParseTag - Parses a tag, putting value in traits or dwImageOffset + * +\*****************************************************************************/ + +static BOOL ParseTag ( + PTIF_INST g, /* in: our instance variables */ + TIFFTAG *pTag, /* in: the tag to parse */ + BYTE *pbInputBuf, /* in: input buffer containing TIFF header */ + BYTE *pbBufAfter, /* in: 1st byte after the input buffer */ + DWORD *pdwImageOffset) /* out: image start offset set by STRIP_OFFSETS */ +{ + unsigned id; + unsigned kind; + unsigned count; + void *pValue; + int value; + int nTypeBytes, nValueBytes; + int i; + + if (g->bByteSwap) { + ByteSwap (&(pTag->TagID ), 2); + ByteSwap (&(pTag->Kind ), 2); + ByteSwap (&(pTag->Length), 4); + } + + id = pTag->TagID; + kind = pTag->Kind; + count = pTag->Length; + + switch (kind) { + case TIFFUNDEFINED: nTypeBytes = 1; break; + case TIFFBYTE: nTypeBytes = 1; break; + case TIFFSBYTE: nTypeBytes = 1; break; + case TIFFSHORT: nTypeBytes = 2; break; + case TIFFSSHORT: nTypeBytes = 2; break; + case TIFFLONG: nTypeBytes = 4; break; + case TIFFSLONG: nTypeBytes = 4; break; + case TIFFRATIONAL: nTypeBytes = 8; break; + case TIFFSRATIONAL: nTypeBytes = 8; break; + default: INSURE(0); + } + + nValueBytes = count * nTypeBytes; + + if (nValueBytes <= 4) + pValue = &(pTag->Value.l); + else { + if (g->bByteSwap) + ByteSwap (&(pTag->Value.l), 4); + pValue = pbInputBuf + pTag->Value.l; + } + INSURE ((BYTE*)pValue>pbInputBuf && (BYTE*)pValue<pbBufAfter); + + if (g->bByteSwap) { + for (i=0; i<(int)count; i++) + ByteSwap ((BYTE*)pValue + i*nTypeBytes, nTypeBytes); + } + + switch (kind) { + case TIFFUNDEFINED: value = *(TIFF_UBYTE *)pValue; break; + case TIFFBYTE: value = *(TIFF_UBYTE *)pValue; break; + case TIFFSBYTE: value = *(TIFF_SBYTE *)pValue; break; + case TIFFSHORT: value = *(TIFF_USHORT*)pValue; break; + case TIFFSSHORT: value = *(TIFF_SSHORT*)pValue; break; + case TIFFLONG: value = *(TIFF_ULONG *)pValue; break; + case TIFFSLONG: value = *(TIFF_SLONG *)pValue; break; + case TIFFRATIONAL: value = ((RATIONAL*)pValue)->n / ((RATIONAL*)pValue)->d; break; + case TIFFSRATIONAL: value = ((RATIONAL*)pValue)->n / ((RATIONAL*)pValue)->d; break; + default: INSURE(0); + } + + switch (id) { + case NEW_SUBFILE: + /* do nothing */ + break; + case IMAGE_WIDTH: + g->traits.iPixelsPerRow = value; + break; + case IMAGE_LENGTH: + g->traits.lNumRows = value; + break; + case BITS_PER_SAMPLE: + g->iBitsPerSample = value; + break; + case COMPRESSION: + INSURE (value == 1); /* we only support uncompressed */ + break; + case PHOTO_INTERPRET: + /* do nothing */ + break; + case STRIP_OFFSETS: + INSURE (count == 1); /* we only support one strip */ + *pdwImageOffset = value; + break; + case SAMPLES_PER_PIXEL: + g->traits.iComponentsPerPixel = value; + break; + case ROWS_PER_STRIP: + /* do nothing -- we assume entire image is in one strip */ + break; + case STRIP_COUNTS: + /* do nothing -- this should be the # bytes in the raw data */ + break; + case XRESOLUTION: + g->traits.lHorizDPI = value << 16; + break; + case YRESOLUTION: + g->traits.lVertDPI = value << 16; + break; + case RESOLUTION_UNIT: + /* do nothing -- if it's not DPI, then reported DPI will be wrong */ + break; + default: + /* ignore the unknown tag */ + break; + } + + return TRUE; + + fatal_error: + return FALSE; +} + + + +/*****************************************************************************\ + * + * tifDecode_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD tifDecode_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PTIF_INST g; + DWORD dwImageOffset; + PBYTE pb; + TIFFHEADER *pHead; + TIFFIFD *pIFD; + int iTag, nTags; + + HANDLE_TO_PTR (hXform, g); + pb = pbInputBuf; + + /**** Parse the file-header ****/ + + pHead = (TIFFHEADER*)pb; + INSURE (pHead->ByteOrder[0]=='I' || pHead->ByteOrder[0]=='M'); + INSURE (pHead->ByteOrder[1]=='I' || pHead->ByteOrder[1]=='M'); + g->bByteSwap = pHead->ByteOrder[0] == 'M'; + if (g->bByteSwap) + ByteSwap (&(pHead->OffsetFirstIFD), 4); + INSURE (pHead->OffsetFirstIFD < dwInputAvail); + /* ignore the file-version */ + pb = pbInputBuf + pHead->OffsetFirstIFD; + + /**** Parse the IFD (i.e., the tags), setting traits ****/ + + pIFD = (TIFFIFD*)pb; + if (g->bByteSwap) + ByteSwap (&(pIFD->NumTags), 2); + nTags = pIFD->NumTags; + INSURE (nTags>0 && nTags<100); /* sanity check */ + INSURE (nTags*sizeof(TIFFTAG) < dwInputAvail); + dwImageOffset = 0; + + for (iTag=0; iTag<nTags; iTag++) { + if (! ParseTag (g, pIFD->Tag+iTag, pbInputBuf, pbInputBuf+dwInputAvail, &dwImageOffset)) + goto fatal_error; + } + + INSURE (g->iBitsPerSample==1 || g->iBitsPerSample==8 || g->iBitsPerSample==16); + g->traits.iBitsPerPixel = g->iBitsPerSample * g->traits.iComponentsPerPixel; + INSURE (dwImageOffset <= dwInputAvail); + g->dwRawRowBytes = (g->traits.iBitsPerPixel*g->traits.iPixelsPerRow + 7) / 8; + + /**** Finish up ****/ + + g->dwInNextPos = dwImageOffset; + *pdwInputUsed = dwImageOffset; + *pdwInputNextPos = dwImageOffset; + + *pInTraits = g->traits; + *pOutTraits = g->traits; + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * tifDecode_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD tifDecode_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = g->dwRawRowBytes; + *pdwMinOutBufLen = g->dwRawRowBytes; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifDecode_convert - the work-horse routine + * +\*****************************************************************************/ + +static WORD tifDecode_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PTIF_INST g; + UINT n; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("tif_decode_convert_row: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + n = g->dwRawRowBytes; + + if (dwInputAvail < n) { + /* we got a partial row at the end -- just toss it */ + g->dwInNextPos += dwInputAvail; + *pdwInputNextPos = g->dwInNextPos; + *pdwInputUsed = dwInputAvail; + *pdwOutputUsed = 0; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_READY_FOR_DATA; + } + + INSURE (dwOutputAvail >= n); + memcpy (pbOutputBuf, pbInputBuf, n); + + if (g->bByteSwap && g->iBitsPerSample==16) { + /* we need to swap bytes in the 16-bit words in the pixels */ + BYTE *pb, *pbAfter, b; + pbAfter = pbInputBuf + n; + for (pb=pbInputBuf; pb<pbAfter; pb+=4) { + /* process two words at a time for speed */ + b = pb[0]; pb[0] = pb[1]; pb[1] = b; + b = pb[2]; pb[2] = pb[3]; pb[3] = b; + } + } + + *pdwOutputUsed = n; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += n; + + g->dwInNextPos += n; + *pdwInputNextPos = g->dwInNextPos; + *pdwInputUsed = n; + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifDecode_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD tifDecode_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * tifDecode_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD tifDecode_newPage ( + IP_XFORM_HANDLE hXform) +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * tifDecode_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD tifDecode_closeXform (IP_XFORM_HANDLE hXform) +{ + PTIF_INST g; + + HANDLE_TO_PTR (hXform, g); + if (g->fileOut != NULL) + fclose (g->fileOut); + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tifDecodeTbl - Jump-table for decoder + * +\*****************************************************************************/ + +IP_XFORM_TBL tifDecodeTbl = { + tifDecode_openXform, + tifDecode_setDefaultInputTraits, + tifDecode_setXformSpec, + tifDecode_getHeaderBufSize, + tifDecode_getActualTraits, + tifDecode_getActualBufSizes, + tifDecode_convert, + tifDecode_newPage, + tifDecode_insertedData, + tifDecode_closeXform +}; + +/* End of File */ diff --git a/ip/xtonemap.c b/ip/xtonemap.c new file mode 100644 index 0000000..a92080a --- /dev/null +++ b/ip/xtonemap.c @@ -0,0 +1,496 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xtonemap.c - Performs a 256-byte tonemap to grayscale or color data + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * tonemapTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * aXformInfo[IP_TONEMAP_POINTER] = pointer to the 256-byte tonemap. + * aXformInfo[IP_TONEMAP_LUM_SPACE] = Is color-space luminance-based? + * 0: color space is RGB + * 1: color space has luminance as first byte (eg, YCC) + * + * The tonemap is indexed by luminance (0..255), and returns the new + * luminance. For color data, the luminance is computed based on r-g-b, + * and these are updated based on th new luminance. + * + * An internal copy is made of the tonemap. + * + * Capabilities and Limitations: + * + * The incoming data can be 8-bit gray or 24-bit color or 48-bit color. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * passed into output same as default input + * iComponentsPerPixel passed into output same as default input + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Jan 2000 Mark Overton -- wrote original code + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x4ba1dace + + +typedef struct { + IP_IMAGE_TRAITS traits; /* traits of the input and output image */ + BOOL bLumSpace; /* luminance-based color space? (else RGB) */ + BYTE tonemap[256]; /* the tonemap */ + DWORD dwBytesPerRow; /* # of bytes in each row */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} TMAP_INST, *PTMAP_INST; + + + +/*****************************************************************************\ + * + * tonemap_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD tonemap_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PTMAP_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(TMAP_INST), g); + *pXform = g; + memset (g, 0, sizeof(TMAP_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tonemap_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD tonemap_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PTMAP_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* insure that traits we care about are known */ + INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>1); + g->traits = *pTraits; /* a structure copy */ + g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tonemap_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD tonemap_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PTMAP_INST g; + + HANDLE_TO_PTR (hXform, g); + memcpy (g->tonemap, aXformInfo[IP_TONEMAP_POINTER].pvoid, 256); + g->bLumSpace = aXformInfo[IP_TONEMAP_LUM_SPACE].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tonemap_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD tonemap_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * tonemap_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD tonemap_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PTMAP_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pIntraits = g->traits; /* structure copies */ + *pOutTraits = g->traits; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * tonemap_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD tonemap_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PTMAP_INST g; + + HANDLE_TO_PTR (hXform, g); + + *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tonemap_convert - Converts one row + * +\*****************************************************************************/ + +static WORD tonemap_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PTMAP_INST g; + int nBytes; + PBYTE pIn, pOut, pOutAfter; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("tonemap_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + nBytes = g->dwBytesPerRow; + INSURE (dwInputAvail >= (DWORD)nBytes ); + INSURE (dwOutputAvail >= (DWORD)nBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pOut + nBytes; + + if (g->traits.iBitsPerPixel == 8) { + /* easiest case */ + while (pOut < pOutAfter) { + *pOut++ = g->tonemap[*pIn++]; + } + } else if (g->traits.iBitsPerPixel == 24) { + if (g->bLumSpace) { + /* easy case: 24-bit color in a luminance space */ + while (pOut < pOutAfter) { + *pOut = g->tonemap[*pIn]; + pIn += 3; + pOut += 3; + } + } else { + /* 24-bit color in RGB */ + int rv, gv, bv; + int l_old, l_new, dl; + + while (pOut < pOutAfter) { + rv = *pIn++; + gv = *pIn++; + bv = *pIn++; + + l_old = NTSC_LUMINANCE (rv, gv, bv); + l_new = g->tonemap[l_old]; /* new luminance */ + dl = l_new - l_old; + rv += dl; + gv += dl; + bv += dl; + + if (rv > 255) rv = 255; else if (rv < 0) rv = 0; + if (gv > 255) gv = 255; else if (gv < 0) gv = 0; + if (bv > 255) bv = 255; else if (bv < 0) bv = 0; + + *pOut++ = (BYTE)rv; + *pOut++ = (BYTE)gv; + *pOut++ = (BYTE)bv; + } + } + } else { /* 48 bits/pixel */ + PWORD src = (PWORD)pIn; + PWORD dst = (PWORD)pOut; + PWORD dstAfter = (PWORD)pOutAfter; + + int rv, gv, bv; + int l_old, l_old8, l_new1, l_new2, l_new, dl; + + while (dst < dstAfter) { + rv = *src++; + gv = *src++; + bv = *src++; + + /* use linear interpolation between tonemap entries + * to compute new luminance (l_new) */ + l_old = g->bLumSpace ? rv : NTSC_LUMINANCE (rv, gv, bv); + l_old8 = l_old >> 8; + l_new1 = g->tonemap[l_old8]; + l_new2 = l_old8<255 ? g->tonemap[l_old8+1] : l_new1; + l_new = (l_new2-l_new1)*(l_old&0x00ff) + (l_new1<<8); + + dl = l_new - l_old; + rv += dl; + if (rv > 65535) rv = 65535; else if (rv < 0) rv = 0; + + if (! g->bLumSpace) { + gv += dl; + bv += dl; + if (gv > 65535) gv = 65535; else if (gv < 0) gv = 0; + if (bv > 65535) bv = 65535; else if (bv < 0) bv = 0; + } + + *dst++ = (WORD)rv; + *dst++ = (WORD)gv; + *dst++ = (WORD)bv; + } + } + + *pdwInputUsed = nBytes; + g->dwInNextPos += nBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = nBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += nBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tonemap_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD tonemap_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * tonemap_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD tonemap_newPage ( + IP_XFORM_HANDLE hXform) +{ + PTMAP_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * tonemap_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD tonemap_closeXform (IP_XFORM_HANDLE hXform) +{ + PTMAP_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * tonemapTbl - Jump-table for transform driver + * +\*****************************************************************************/ + +IP_XFORM_TBL tonemapTbl = { + tonemap_openXform, + tonemap_setDefaultInputTraits, + tonemap_setXformSpec, + tonemap_getHeaderBufSize, + tonemap_getActualTraits, + tonemap_getActualBufSizes, + tonemap_convert, + tonemap_newPage, + tonemap_insertedData, + tonemap_closeXform +}; + +/* End of File */ diff --git a/ip/xyxtract.c b/ip/xyxtract.c new file mode 100644 index 0000000..b5156e4 --- /dev/null +++ b/ip/xyxtract.c @@ -0,0 +1,451 @@ +/* libhpojip -- HP OfficeJet image-processing library. */ + +/* Copyright (C) 1995-2002 Hewlett-Packard Company + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * In addition, as a special exception, Hewlett-Packard Company + * gives permission to link the code of this program with any + * version of the OpenSSL library which is distributed under a + * license identical to that listed in the included LICENSE.OpenSSL + * file, and distribute linked combinations including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* Original author: Mark Overton and others. + * + * Ported to Linux by David Paschal. + */ + +/******************************************************************************\ + * + * xyxtract.c - Y-extract - Extracts Y-component from YCC color data + * + ****************************************************************************** + * + * Name of Global Jump-Table: + * + * yXtractTbl + * + * Items in aXformInfo array passed into setXformSpec: + * + * xXformInfo[IP_Y_EXTRACT_COLOR_SPACE] tells us something about the color-space + * of input data: See the enum IP_Y_EXTRACT_WHICH_SPACE: + * IP_Y_EXTRACT_LUM_CHROME = luminance-chrominance, we merely fetch 1st component, + * IP_Y_EXTRACT_RGB = input is RGB, + * IP_Y_EXTRACT_BGR = input is BGR. + * + * Capabilities and Limitations: + * + * Inputs rows of 24-bit color data, and outputs rows of 8-bit gray + * consisting of the first component of the color data. + * + * Default Input Traits, and Output Traits: + * + * trait default input output + * ------------------- --------------------- ------------------------ + * iPixelsPerRow * passed into output same as default input + * iBitsPerPixel * must be 24 8 + * iComponentsPerPixel * must be 3 1 + * lHorizDPI passed into output same as default input + * lVertDPI passed into output same as default input + * lNumRows passed into output same as default input + * iNumPages passed into output same as default input + * iPageNum passed into output same as default input + * + * Above, a "*" by an item indicates it must be valid (not negative). + * + * Jan 1998 Mark Overton -- wrote code + * +\******************************************************************************/ + +#include "hpip.h" +#include "ipdefs.h" +#include "string.h" /* for memset and memcpy */ + + +#if 0 + #include "stdio.h" + #include <tchar.h> + + #define PRINT(msg,arg1,arg2) \ + _ftprintf(stderr, msg, (int)arg1, (int)arg2) +#else + #define PRINT(msg,arg1,arg2) +#endif + +#define CHECK_VALUE 0x1ce5ca7e + +typedef struct { + IP_IMAGE_TRAITS inTraits; /* traits of the input image */ + IP_Y_EXTRACT_WHICH_SPACE eInputType; /* type of input data */ + DWORD dwRowsDone; /* number of rows converted so far */ + DWORD dwInNextPos; /* file pos for subsequent input */ + DWORD dwOutNextPos; /* file pos for subsequent output */ + DWORD dwValidChk; /* struct validity check value */ +} YEX_INST, *PYEX_INST; + + + +/*****************************************************************************\ + * + * yXtract_openXform - Creates a new instance of the transformer + * + ***************************************************************************** + * + * This returns a handle for the new instance to be passed into + * all subsequent calls. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD yXtract_openXform ( + IP_XFORM_HANDLE *pXform) /* out: returned handle */ +{ + PYEX_INST g; + + INSURE (pXform != NULL); + IP_MEM_ALLOC (sizeof(YEX_INST), g); + *pXform = g; + memset (g, 0, sizeof(YEX_INST)); + g->dwValidChk = CHECK_VALUE; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * yXtract_setDefaultInputTraits - Specifies default input image traits + * + ***************************************************************************** + * + * The header of the file-type handled by the transform probably does + * not include *all* the image traits we'd like to know. Those not + * specified in the file-header are filled in from info provided by + * this routine. + * + * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error. + * +\*****************************************************************************/ + +static WORD yXtract_setDefaultInputTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PIP_IMAGE_TRAITS pTraits) /* in: default image traits */ +{ + PYEX_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Insure that values we care about are correct */ + INSURE (pTraits->iBitsPerPixel == 24); + INSURE (pTraits->iComponentsPerPixel == 3); + INSURE (pTraits->iPixelsPerRow > 0); + + g->inTraits = *pTraits; /* a structure copy */ + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * yXtract_setXformSpec - Provides xform-specific information + * +\*****************************************************************************/ + +static WORD yXtract_setXformSpec ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD_OR_PVOID aXformInfo[]) /* in: xform information */ +{ + PYEX_INST g; + HANDLE_TO_PTR (hXform, g); + g->eInputType = (IP_Y_EXTRACT_WHICH_SPACE)aXformInfo[IP_Y_EXTRACT_COLOR_SPACE].dword; + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * yXtract_getHeaderBufSize- Returns size of input buf needed to hold header + * +\*****************************************************************************/ + +static WORD yXtract_getHeaderBufSize ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD *pdwInBufLen) /* out: buf size for parsing header */ +{ + /* since input is raw pixels, there is no header, so set it to zero */ + *pdwInBufLen = 0; + return IP_DONE; +} + + + +/*****************************************************************************\ + * + * yXtract_getActualTraits - Parses header, and returns input & output traits + * +\*****************************************************************************/ + +static WORD yXtract_getActualTraits ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + DWORD dwInputAvail, /* in: # avail bytes in input buf */ + PBYTE pbInputBuf, /* in: ptr to input buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from input buf */ + PDWORD pdwInputNextPos,/* out: file-pos to read from next */ + PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */ + PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */ +{ + PYEX_INST g; + + HANDLE_TO_PTR (hXform, g); + + /* Since there is no header, we'll report no usage of input */ + *pdwInputUsed = 0; + *pdwInputNextPos = 0; + + *pInTraits = g->inTraits; + *pOutTraits = g->inTraits; + pOutTraits->iBitsPerPixel = 8; + pOutTraits->iComponentsPerPixel = 1; + + return IP_DONE | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/****************************************************************************\ + * + * yXtract_getActualBufSizes - Returns buf sizes needed for remainder of job + * +\****************************************************************************/ + +static WORD yXtract_getActualBufSizes ( + IP_XFORM_HANDLE hXform, /* in: handle for xform */ + PDWORD pdwMinInBufLen, /* out: min input buf size */ + PDWORD pdwMinOutBufLen) /* out: min output buf size */ +{ + PYEX_INST g; + + HANDLE_TO_PTR (hXform, g); + *pdwMinInBufLen = 3 * g->inTraits.iPixelsPerRow; + *pdwMinOutBufLen = g->inTraits.iPixelsPerRow; + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * yXtract_convert - Converts one row + * +\*****************************************************************************/ + +static WORD yXtract_convert ( + IP_XFORM_HANDLE hXform, + DWORD dwInputAvail, /* in: # avail bytes in in-buf */ + PBYTE pbInputBuf, /* in: ptr to in-buffer */ + PDWORD pdwInputUsed, /* out: # bytes used from in-buf */ + PDWORD pdwInputNextPos, /* out: file-pos to read from next */ + DWORD dwOutputAvail, /* in: # avail bytes in out-buf */ + PBYTE pbOutputBuf, /* in: ptr to out-buffer */ + PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */ + PDWORD pdwOutputThisPos) /* out: file-pos to write the data */ +{ + PYEX_INST g; + int inBytes, outBytes; + PBYTE pIn, pOut, pOutAfter; + UINT red, grn, blu; + + HANDLE_TO_PTR (hXform, g); + + /**** Check if we were told to flush ****/ + + if (pbInputBuf == NULL) { + PRINT (_T("yXtract_convert: Told to flush.\n"), 0, 0); + *pdwInputUsed = *pdwOutputUsed = 0; + *pdwInputNextPos = g->dwInNextPos; + *pdwOutputThisPos = g->dwOutNextPos; + return IP_DONE; + } + + /**** Output a Row ****/ + + outBytes = g->inTraits.iPixelsPerRow; + inBytes = 3 * outBytes; + INSURE (dwInputAvail >= (DWORD)inBytes ); + INSURE (dwOutputAvail >= (DWORD)outBytes); + + pIn = pbInputBuf; + pOut = pbOutputBuf; + pOutAfter = pOut + outBytes; + + switch (g->eInputType) { + case IP_Y_EXTRACT_LUM_CHROME: + while (pOut < pOutAfter) { + *pOut++ = *pIn; + pIn += 3; + } + break; + + case IP_Y_EXTRACT_RGB: + while (pOut < pOutAfter) { + red = (UINT)(*pIn++); + grn = (UINT)(*pIn++); + blu = (UINT)(*pIn++); + /* the formula below is: Y = (5*R + 9*G + 2*B) / 16 */ + *pOut++ = + (BYTE)((((red<<2)+red) + ((grn<<3)+grn) + (blu<<1) + 8) >> 4); + } + break; + + case IP_Y_EXTRACT_BGR: + while (pOut < pOutAfter) { + blu = (UINT)(*pIn++); + grn = (UINT)(*pIn++); + red = (UINT)(*pIn++); + /* the formula below is: Y = (5*R + 9*G + 2*B) / 16 */ + *pOut++ = + (BYTE)((((red<<2)+red) + ((grn<<3)+grn) + (blu<<1) + 8) >> 4); + } + break; + + default: + goto fatal_error; + } + + *pdwInputUsed = inBytes; + g->dwInNextPos += inBytes; + *pdwInputNextPos = g->dwInNextPos; + + *pdwOutputUsed = outBytes; + *pdwOutputThisPos = g->dwOutNextPos; + g->dwOutNextPos += outBytes; + + g->dwRowsDone += 1; + + return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * yXtract_insertedData - client inserted into our output stream + * +\*****************************************************************************/ + +static WORD yXtract_insertedData ( + IP_XFORM_HANDLE hXform, + DWORD dwNumBytes) +{ + fatalBreakPoint (); + return IP_FATAL_ERROR; /* must never be called (can't insert data) */ +} + + + +/*****************************************************************************\ + * + * yXtract_newPage - Tells us to flush this page, and start a new page + * +\*****************************************************************************/ + +static WORD yXtract_newPage ( + IP_XFORM_HANDLE hXform) +{ + PYEX_INST g; + + HANDLE_TO_PTR (hXform, g); + /* todo: return fatal error if convert is called again? */ + return IP_DONE; /* can't insert page-breaks, so ignore this call */ + + fatal_error: + return IP_FATAL_ERROR; + +} + + + +/*****************************************************************************\ + * + * yXtract_closeXform - Destroys this instance + * +\*****************************************************************************/ + +static WORD yXtract_closeXform (IP_XFORM_HANDLE hXform) +{ + PYEX_INST g; + + HANDLE_TO_PTR (hXform, g); + + g->dwValidChk = 0; + IP_MEM_FREE (g); /* free memory for the instance */ + + return IP_DONE; + + fatal_error: + return IP_FATAL_ERROR; +} + + + +/*****************************************************************************\ + * + * yXtractTbl - Jump-table for encoder + * +\*****************************************************************************/ + +IP_XFORM_TBL yXtractTbl = { + yXtract_openXform, + yXtract_setDefaultInputTraits, + yXtract_setXformSpec, + yXtract_getHeaderBufSize, + yXtract_getActualTraits, + yXtract_getActualBufSizes, + yXtract_convert, + yXtract_newPage, + yXtract_insertedData, + yXtract_closeXform +}; + +/* End of File */ |