1 /* libhpojip -- HP OfficeJet image-processing library. */
3 /* Copyright (C) 1995-2002 Hewlett-Packard Company
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
13 * NON-INFRINGEMENT. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20 * In addition, as a special exception, Hewlett-Packard Company
21 * gives permission to link the code of this program with any
22 * version of the OpenSSL library which is distributed under a
23 * license identical to that listed in the included LICENSE.OpenSSL
24 * file, and distribute linked combinations including the two.
25 * You must obey the GNU General Public License in all respects
26 * for all of the code used other than OpenSSL. If you modify
27 * this file, you may extend this exception to your version of the
28 * file, but you are not obligated to do so. If you do not wish to
29 * do so, delete this exception statement from your version.
32 /* Original author: Mark Overton and others.
34 * Ported to Linux by David Paschal.
37 /******************************************************************************\
39 * xgray2bi.c - Error-diffuser and thresholder that's designed to be fast
41 ******************************************************************************
43 * Jan 1998 Mark Overton -- ported code into an xform driver
44 * June 1995 Mark Overton -- developed and benchmarked algorithm
46 * Name of Global Jump-Table:
50 * Items in aXformInfo array passed into setXformSpec:
52 * aXformInfo[IP_GRAY_2_BI_THRESHOLD] = Threshold.
53 * If threshold is zero, error-diffusion is done.
54 * If non-zero, it is the black/white threshold:
55 * A gray pixel >= to threshold becomes white, else black.
57 * Capabilities and Limitations:
59 * Inputs rows of 8-bit gray pixels, and outputs rows of bi-level pixels.
60 * The formats are the standard raw formats described in hpojip.h.
61 * The error-diffusion weights are perturbed by small internally-generated
62 * amounts to break up any pixel patterns that would otherwise occur.
64 * Default Input Traits, and Output Traits:
66 * trait default input output
67 * ------------------- --------------------- ------------------------
68 * iPixelsPerRow * passed into output same as default input
69 * iBitsPerPixel * must be 8 1
70 * iComponentsPerPixel * must be 1 1
71 * lHorizDPI passed into output same as default input
72 * lVertDPI passed into output same as default input
73 * lNumRows passed into output same as default input
74 * iNumPages passed into output same as default input
75 * iPageNum passed into output same as default input
77 * Above, a "*" by an item indicates it must be valid (not negative).
79 * Error-diffusion timings (seconds for one page):
81 * 386-33 486-33 486-66 P-90 735 image dim task
82 * ------ ------ ------ ---- ----- --------- ---------------------------------
83 * 21.6 9.0 4.4 2.3 1.4 1728x2200 200x200 fax in photo mode
84 * 43. 17.9 8.9 4.6 2.9 2400x3150 300x300 local copy, 1/4" margins
85 * 86. 36. 17.9 9.3 5.7 4800x3150 600x300 local copy, 1/4" margins
87 \******************************************************************************/
91 #include "string.h" /* for memset and memcpy */
98 #define PRINT(msg,arg1,arg2) \
99 _ftprintf(stderr, msg, (int)arg1, (int)arg2)
101 #define PRINT(msg,arg1,arg2)
104 #define CHECK_VALUE 0x1ce5ca7e
108 IP_IMAGE_TRAITS inTraits; /* traits of the input image */
109 DWORD dwRowsDone; /* number of rows converted so far */
110 BYTE bThreshold; /* white/black threshold; 0 -> diffuse */
111 DWORD dwInNextPos; /* file pos for subsequent input */
112 DWORD dwOutNextPos; /* file pos for subsequent output */
113 DWORD dwValidChk; /* struct validity check value */
114 short *pErrBuf; /* error-term buffer */
115 } G2B_INST, *PG2B_INST;
119 /****************************************************************************\
121 * thresholdRow - work-horse thresholding routine
123 \****************************************************************************/
125 static void thresholdRow (
126 int iPixelsPerRow, /* in: # of pixels in the row */
127 BYTE bThreshold, /* in: white/black threshold value */
128 BYTE baInBuf [], /* in: input pixels (0=black, 255=white) */
129 BYTE baOutBuf []) /* out: output pixels (0=white, 1=black, 8 per byte) */
138 for (iMore=iPixelsPerRow; iMore>0; iMore-=8)
142 for (bMask=0x80u; bMask!=0; bMask>>=1)
144 if (*pbIn++ < bThreshold)
154 /****************************************************************************\
156 * diffuseRow - work-horse error-diffusion routine
158 \****************************************************************************/
160 static void diffuseRow (
161 int iPixelsPerRow, /* in: # of pixels in the row */
162 short iaErrBuf [], /* in/out: error-term buffer from prior call */
163 BYTE baInBuf [], /* in: input pixels (0=black, 255=white) */
164 BYTE baOutBuf []) /* out: output pixels (0=white, 1=black, 8 per byte) */
169 int weight4, weight2, weight1;
172 BYTE *inptr, *inafter;
176 BOOL second; /* computing 2nd nibble in byte? */
178 /* The diffusion algorithm below uses the following 6 weights:
183 * These weights add to 16, so there are shifts by 4 in the code.
184 * weight4 is the 4 above. Likewise with weight2 and weight1.
185 * eptr is positioned at the 1 above.
186 * The macro reads the error at the top 4 above, and writes it at the 1.
187 * bll is positioned at the 1 above. bleft at the 3. below at the low 4.
188 * The noise perturbations add to zero, so no net noise is injected.
191 #define DIFFUSE(par_outmask) { \
192 mask = cur >> (8*sizeof(cur)-1); /* signed shift */ \
193 outvalue |= par_outmask & mask; \
194 weight4 = (cur - (~mask&0x0ff0)) >> 2; \
196 noise = (cur & (0x7f<<1)) - 0x7f; \
197 cur = ((unsigned)(*inptr++) << 4) \
198 + eptr[3] + weight2 + weight4 + noise ; \
199 weight2 = (weight4>>1); \
200 weight1 = (weight2>>1); \
201 *eptr++ = bll + weight1 - noise ; \
202 bll = bleft + weight2 + weight1 /* + noise */ ; \
203 bleft = below + weight4 - noise ; \
204 below = weight2 + noise ; \
207 cur = iaErrBuf[2] + ((unsigned)(*inptr++) << 4);
215 #define DIFFUSE(par_outmask) { \
216 /* decide if output pixel is black or white */ \
217 mask = (cur-0x800) >> (8*sizeof(cur)-1); /* all 0's or all 1's */ \
218 outvalue |= par_outmask & mask; \
220 /* compute error, and weights of 4/16, 2/16 and 1/16 */ \
221 err = cur - (~mask&0x0ff0); \
222 /* multiply error by 15/16 so it won't propagate a long distance */ \
223 err = err - (err>>4); \
224 weight4 = err >> 2; \
225 weight2 = weight4 >> 1; \
226 weight1 = weight2 >> 1; \
228 /* distribute error to neighboring pixels */ \
229 noise = err & 0x00ff; \
231 r += weight4 + noise; \
232 br = weight2 + noise; \
233 b += weight4 - noise; \
234 bl += weight2 + weight1; /* the 3 weight */ \
235 bll += weight1 - noise; \
237 /* advance right one pixel, so move values left one pixel */ \
240 rr = ((unsigned)(*inptr++) << 4) + eptr[2]; \
249 inafter = baInBuf + iPixelsPerRow;
255 cur = ((unsigned)inptr[0] << 4) + eptr[0];
256 r = ((unsigned)inptr[1] << 4) + eptr[1];
257 rr = ((unsigned)inptr[2] << 4) + eptr[2];
259 bll = bl = b = br = 0;
261 while (inptr < inafter) {
268 /* we just computed the left half of the byte */
272 /* we just computed the right half of the byte, so store it */
273 *outptr++ = outvalue;
280 *outptr = outvalue; /* store final nibble */
291 /*****************************************************************************\
293 * gray2bi_openXform - Creates a new instance of the transformer
295 *****************************************************************************
297 * This returns a handle for the new instance to be passed into
298 * all subsequent calls.
300 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
302 \*****************************************************************************/
304 static WORD gray2bi_openXform (
305 IP_XFORM_HANDLE *pXform) /* out: returned handle */
309 INSURE (pXform != NULL);
310 IP_MEM_ALLOC (sizeof(G2B_INST), g);
312 memset (g, 0, sizeof(G2B_INST));
313 g->dwValidChk = CHECK_VALUE;
317 return IP_FATAL_ERROR;
322 /*****************************************************************************\
324 * gray2bi_setDefaultInputTraits - Specifies default input image traits
326 *****************************************************************************
328 * The header of the file-type handled by the transform probably does
329 * not include *all* the image traits we'd like to know. Those not
330 * specified in the file-header are filled in from info provided by
333 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
335 \*****************************************************************************/
337 static WORD gray2bi_setDefaultInputTraits (
338 IP_XFORM_HANDLE hXform, /* in: handle for xform */
339 PIP_IMAGE_TRAITS pTraits) /* in: default image traits */
343 HANDLE_TO_PTR (hXform, g);
345 /* Insure that values we care about are correct */
346 INSURE (pTraits->iBitsPerPixel == 8);
347 INSURE (pTraits->iComponentsPerPixel == 1);
348 INSURE (pTraits->iPixelsPerRow > 0);
350 g->inTraits = *pTraits; /* a structure copy */
355 return IP_FATAL_ERROR;
360 /*****************************************************************************\
362 * gray2bi_setXformSpec - Provides xform-specific information
364 \*****************************************************************************/
366 static WORD gray2bi_setXformSpec (
367 IP_XFORM_HANDLE hXform, /* in: handle for xform */
368 DWORD_OR_PVOID aXformInfo[]) /* in: xform information */
372 HANDLE_TO_PTR (hXform, g);
373 INSURE ((DWORD)aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword <= 255);
374 g->bThreshold = (BYTE)aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword;
379 return IP_FATAL_ERROR;
384 /*****************************************************************************\
386 * gray2bi_getHeaderBufSize- Returns size of input buf needed to hold header
388 \*****************************************************************************/
390 static WORD gray2bi_getHeaderBufSize (
391 IP_XFORM_HANDLE hXform, /* in: handle for xform */
392 DWORD *pdwInBufLen) /* out: buf size for parsing header */
394 /* since input is raw pixels, there is no header, so set it to zero */
401 /*****************************************************************************\
403 * gray2bi_getActualTraits - Parses header, and returns input & output traits
405 \*****************************************************************************/
407 static WORD gray2bi_getActualTraits (
408 IP_XFORM_HANDLE hXform, /* in: handle for xform */
409 DWORD dwInputAvail, /* in: # avail bytes in input buf */
410 PBYTE pbInputBuf, /* in: ptr to input buffer */
411 PDWORD pdwInputUsed, /* out: # bytes used from input buf */
412 PDWORD pdwInputNextPos,/* out: file-pos to read from next */
413 PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */
414 PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */
419 HANDLE_TO_PTR (hXform, g);
421 /* Since there is no header, we'll report no usage of input */
423 *pdwInputNextPos = 0;
425 *pInTraits = g->inTraits;
426 *pOutTraits = g->inTraits;
427 pOutTraits->iBitsPerPixel = 1; /* this xform only changes bits/pixel */
429 if (g->bThreshold == 0) {
430 nBytes = sizeof(short) * g->inTraits.iPixelsPerRow;
431 IP_MEM_ALLOC (nBytes, g->pErrBuf);
432 memset (g->pErrBuf, 0, nBytes);
435 return IP_DONE | IP_READY_FOR_DATA;
438 return IP_FATAL_ERROR;
443 /****************************************************************************\
445 * gray2bi_getActualBufSizes - Returns buf sizes needed for remainder of job
447 \****************************************************************************/
449 static WORD gray2bi_getActualBufSizes (
450 IP_XFORM_HANDLE hXform, /* in: handle for xform */
451 PDWORD pdwMinInBufLen, /* out: min input buf size */
452 PDWORD pdwMinOutBufLen) /* out: min output buf size */
456 HANDLE_TO_PTR (hXform, g);
457 *pdwMinInBufLen = g->inTraits.iPixelsPerRow;
458 *pdwMinOutBufLen = (g->inTraits.iPixelsPerRow + 7) / 8;
463 return IP_FATAL_ERROR;
468 /*****************************************************************************\
470 * gray2bi_convert - error-diffuses one row
472 \*****************************************************************************/
474 static WORD gray2bi_convert (
475 IP_XFORM_HANDLE hXform,
476 DWORD dwInputAvail, /* in: # avail bytes in in-buf */
477 PBYTE pbInputBuf, /* in: ptr to in-buffer */
478 PDWORD pdwInputUsed, /* out: # bytes used from in-buf */
479 PDWORD pdwInputNextPos, /* out: file-pos to read from next */
480 DWORD dwOutputAvail, /* in: # avail bytes in out-buf */
481 PBYTE pbOutputBuf, /* in: ptr to out-buffer */
482 PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */
483 PDWORD pdwOutputThisPos) /* out: file-pos to write the data */
486 int inBytes, outBytes;
488 HANDLE_TO_PTR (hXform, g);
490 /**** Check if we were told to flush ****/
492 if (pbInputBuf == NULL) {
493 PRINT (_T("gray2bi_convert: Told to flush.\n"), 0, 0);
494 *pdwInputUsed = *pdwOutputUsed = 0;
495 *pdwInputNextPos = g->dwInNextPos;
496 *pdwOutputThisPos = g->dwOutNextPos;
500 /**** Convert and Output a Row ****/
502 inBytes = g->inTraits.iPixelsPerRow;
503 outBytes = (inBytes + 7) / 8;
504 INSURE (dwInputAvail >= (DWORD)inBytes );
505 INSURE (dwOutputAvail >= (DWORD)outBytes);
507 if (g->bThreshold == 0) {
508 INSURE (g->pErrBuf != NULL);
509 diffuseRow (inBytes, g->pErrBuf, pbInputBuf, pbOutputBuf);
511 thresholdRow (inBytes, g->bThreshold, pbInputBuf, pbOutputBuf);
513 *pdwInputUsed = inBytes;
514 g->dwInNextPos += inBytes;
515 *pdwInputNextPos = g->dwInNextPos;
517 *pdwOutputUsed = outBytes;
518 *pdwOutputThisPos = g->dwOutNextPos;
519 g->dwOutNextPos += outBytes;
523 return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA;
526 return IP_FATAL_ERROR;
531 /*****************************************************************************\
533 * gray2bi_insertedData - client inserted into our output stream
535 \*****************************************************************************/
537 static WORD gray2bi_insertedData (
538 IP_XFORM_HANDLE hXform,
542 return IP_FATAL_ERROR; /* must never be called (can't insert data) */
547 /*****************************************************************************\
549 * gray2bi_newPage - Tells us to flush this page, and start a new page
551 \*****************************************************************************/
553 static WORD gray2bi_newPage (
554 IP_XFORM_HANDLE hXform)
558 HANDLE_TO_PTR (hXform, g);
559 /* todo: return fatal error if convert is called again? */
560 return IP_DONE; /* can't insert page-breaks, so ignore this call */
563 return IP_FATAL_ERROR;
569 /*****************************************************************************\
571 * gray2bi_closeXform - Destroys this instance
573 \*****************************************************************************/
575 static WORD gray2bi_closeXform (IP_XFORM_HANDLE hXform)
579 HANDLE_TO_PTR (hXform, g);
581 if (g->pErrBuf != NULL)
582 IP_MEM_FREE (g->pErrBuf);
585 IP_MEM_FREE (g); /* free memory for the instance */
590 return IP_FATAL_ERROR;
595 /*****************************************************************************\
597 * gray2biTbl - Jump-table for encoder
599 \*****************************************************************************/
601 IP_XFORM_TBL gray2biTbl = {
603 gray2bi_setDefaultInputTraits,
604 gray2bi_setXformSpec,
605 gray2bi_getHeaderBufSize,
606 gray2bi_getActualTraits,
607 gray2bi_getActualBufSizes,
610 gray2bi_insertedData,