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 * xchangeBPP.c - Changes bits per pixel
41 ******************************************************************************
43 * Name of Global Jump-Table:
47 * Items in aXformInfo array passed into setXformSpec:
49 * aXformInfo[IP_CHANGE_BPP_OUTPUT_BPP] = bits/pixel to output
51 * Capabilities and Limitations:
53 * 1 bpp is assumed to be bilevel (0=white, 1=black).
54 * 8 and 16 bpp are assumed to be grayscale.
55 * 24 and 48 bpp are assumed to be 3-component color (rgb is assumed when
56 * we convert from color into grayscale).
57 * Among the above supported bpp values, any bpp can be changed into any
58 * other bpp. Changing into bi-level (bpp=1) performs simple thresholding.
59 * Use xgray2bi if you want error-diffusion.
60 * If the input and output bpp values are the same, this xform merely passes
61 * the pixels through unexamined.
63 * Default Input Traits, and Output Traits:
65 * trait default input output
66 * ------------------- --------------------- ------------------------
67 * iPixelsPerRow * passed into output same as default input
68 * iBitsPerPixel * anything changed as specified
69 * iComponentsPerPixel * 1 or 3 can be changed to 3
70 * lHorizDPI passed into output same as default input
71 * lVertDPI passed into output same as default input
72 * lNumRows passed into output same as default input
73 * iNumPages passed into output same as default input
74 * iPageNum passed into output same as default input
76 * Above, a "*" by an item indicates it must be valid (not negative).
78 * Mar 1998 Mark Overton -- wrote code (for 1 -> 8/24 only)
79 * Apr 2000 Mark Overton -- generalized as a change-BPP xform
81 \******************************************************************************/
85 #include "string.h" /* for memset and memcpy */
93 #define PRINT(msg,arg1,arg2) \
94 _ftprintf(stderr, msg, (int)arg1, (int)arg2)
96 #define PRINT(msg,arg1,arg2)
99 #define CHECK_VALUE 0x1ce5ca7e
102 IP_IMAGE_TRAITS inTraits; /* traits of the input image */
103 UINT uRowsDone; /* number of rows converted so far */
104 WORD wOutBitsPerPixel; /* bits/pixel to output */
105 DWORD dwInRowBytes; /* # bytes per input row */
106 DWORD dwOutRowBytes; /* # bytes per output row */
107 DWORD dwInNextPos; /* file pos for subsequent input */
108 DWORD dwOutNextPos; /* file pos for subsequent output */
109 DWORD dwValidChk; /* struct validity check value */
110 } CBPP_INST, *PCBPP_INST;
114 /*****************************************************************************\
116 * changeBPP_openXform - Creates a new instance of the transformer
118 *****************************************************************************
120 * This returns a handle for the new instance to be passed into
121 * all subsequent calls.
123 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
125 \*****************************************************************************/
127 static WORD changeBPP_openXform (
128 IP_XFORM_HANDLE *pXform) /* out: returned handle */
132 INSURE (pXform != NULL);
133 IP_MEM_ALLOC (sizeof(CBPP_INST), g);
135 memset (g, 0, sizeof(CBPP_INST));
136 g->dwValidChk = CHECK_VALUE;
140 return IP_FATAL_ERROR;
145 /*****************************************************************************\
147 * changeBPP_setDefaultInputTraits - Specifies default input image traits
149 *****************************************************************************
151 * The header of the file-type handled by the transform probably does
152 * not include *all* the image traits we'd like to know. Those not
153 * specified in the file-header are filled in from info provided by
156 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
158 \*****************************************************************************/
160 static WORD changeBPP_setDefaultInputTraits (
161 IP_XFORM_HANDLE hXform, /* in: handle for xform */
162 PIP_IMAGE_TRAITS pTraits) /* in: default image traits */
167 HANDLE_TO_PTR (hXform, g);
169 /* Insure that values we care about are correct */
170 bpp = pTraits->iBitsPerPixel;
171 INSURE (bpp==1 || bpp==8 || bpp==16 || bpp==24 || bpp==48);
172 INSURE (pTraits->iComponentsPerPixel==1 || pTraits->iComponentsPerPixel==3);
173 INSURE (pTraits->iPixelsPerRow > 0);
175 g->inTraits = *pTraits; /* a structure copy */
179 return IP_FATAL_ERROR;
184 /*****************************************************************************\
186 * changeBPP_setXformSpec - Provides xform-specific information
188 \*****************************************************************************/
190 static WORD changeBPP_setXformSpec (
191 IP_XFORM_HANDLE hXform, /* in: handle for xform */
192 DWORD_OR_PVOID aXformInfo[]) /* in: xform information */
197 HANDLE_TO_PTR (hXform, g);
198 nBits = aXformInfo[IP_CHANGE_BPP_OUTPUT_BPP].dword;
199 g->wOutBitsPerPixel = (WORD)nBits;
203 return IP_FATAL_ERROR;
208 /*****************************************************************************\
210 * changeBPP_getHeaderBufSize- Returns size of input buf needed to hold header
212 \*****************************************************************************/
214 static WORD changeBPP_getHeaderBufSize (
215 IP_XFORM_HANDLE hXform, /* in: handle for xform */
216 DWORD *pdwInBufLen) /* out: buf size for parsing header */
218 /* since input is raw pixels, there is no header, so set it to zero */
225 /*****************************************************************************\
227 * changeBPP_getActualTraits - Parses header, and returns input & output traits
229 \*****************************************************************************/
231 static WORD changeBPP_getActualTraits (
232 IP_XFORM_HANDLE hXform, /* in: handle for xform */
233 DWORD dwInputAvail, /* in: # avail bytes in input buf */
234 PBYTE pbInputBuf, /* in: ptr to input buffer */
235 PDWORD pdwInputUsed, /* out: # bytes used from input buf */
236 PDWORD pdwInputNextPos,/* out: file-pos to read from next */
237 PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */
238 PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */
242 HANDLE_TO_PTR (hXform, g);
244 /* Since there is no header, we'll report no usage of input */
246 *pdwInputNextPos = 0;
248 *pInTraits = g->inTraits; /* structure copies */
249 *pOutTraits = g->inTraits;
250 pOutTraits->iBitsPerPixel = g->wOutBitsPerPixel;
251 pOutTraits->iComponentsPerPixel = g->wOutBitsPerPixel<24 ? 1 : 3;
253 g->dwInRowBytes = (g->inTraits.iPixelsPerRow*g->inTraits.iBitsPerPixel + 7) / 8;
254 g->dwOutRowBytes = (g->inTraits.iPixelsPerRow*g->wOutBitsPerPixel + 7) / 8;
256 return IP_DONE | IP_READY_FOR_DATA;
259 return IP_FATAL_ERROR;
264 /****************************************************************************\
266 * changeBPP_getActualBufSizes - Returns buf sizes needed for remainder of job
268 \****************************************************************************/
270 static WORD changeBPP_getActualBufSizes (
271 IP_XFORM_HANDLE hXform, /* in: handle for xform */
272 PDWORD pdwMinInBufLen, /* out: min input buf size */
273 PDWORD pdwMinOutBufLen) /* out: min output buf size */
277 HANDLE_TO_PTR (hXform, g);
278 *pdwMinInBufLen = g->dwInRowBytes;
279 *pdwMinOutBufLen = g->dwOutRowBytes;
284 return IP_FATAL_ERROR;
289 /*****************************************************************************\
291 * changeBPP_convert - Converts one row
293 \*****************************************************************************/
295 static WORD changeBPP_convert (
296 IP_XFORM_HANDLE hXform,
297 DWORD dwInputAvail, /* in: # avail bytes in in-buf */
298 PBYTE pbInputBuf, /* in: ptr to in-buffer */
299 PDWORD pdwInputUsed, /* out: # bytes used from in-buf */
300 PDWORD pdwInputNextPos, /* out: file-pos to read from next */
301 DWORD dwOutputAvail, /* in: # avail bytes in out-buf */
302 PBYTE pbOutputBuf, /* in: ptr to out-buffer */
303 PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */
304 PDWORD pdwOutputThisPos) /* out: file-pos to write the data */
307 PBYTE pIn, pOut, pInAfter;
309 BYTE bMask, bBilevel;
314 #define OUTPUT_THRESHOLDED_BIT(grayParam) { \
315 int gray = grayParam; \
316 if (gray < 128) bBilevel |= bMask; \
319 *pOut++ = (BYTE)bBilevel; \
321 bMask = (BYTE)0x80; \
325 HANDLE_TO_PTR (hXform, g);
327 /**** Check if we were told to flush ****/
329 if (pbInputBuf == NULL) {
330 PRINT (_T("changeBPP_convert: Told to flush.\n"), 0, 0);
331 *pdwInputUsed = *pdwOutputUsed = 0;
332 *pdwInputNextPos = g->dwInNextPos;
333 *pdwOutputThisPos = g->dwOutNextPos;
337 /**** Output a Row ****/
339 INSURE (dwInputAvail >= g->dwInRowBytes );
340 INSURE (dwOutputAvail >= g->dwOutRowBytes);
347 pInAfter = pIn + g->dwInRowBytes;
349 if (g->inTraits.iBitsPerPixel == g->wOutBitsPerPixel) {
350 /* no change in bpp; just copy the buffer */
351 memcpy (pOut, pIn, g->dwInRowBytes);
352 } else if (g->inTraits.iBitsPerPixel == 1) {
353 while (pIn < pInAfter) {
356 if (g->wOutBitsPerPixel == 48) {
357 for (bMask=0x80u; bMask!=0; bMask>>=1) {
358 dwPixels = (bMask & bBilevel)
360 : 0xffffffff; /* white */
361 *(DWORD*)pOut = dwPixels;
362 *(WORD*)(pOut+4) = (WORD)dwPixels;
365 } else if (g->wOutBitsPerPixel == 24) {
366 for (bMask=0x80u; bMask!=0; bMask>>=1) {
367 *(DWORD*)pOut = (bMask & bBilevel)
369 : 0xffffffff; /* white */
372 } else if (g->wOutBitsPerPixel == 16) {
373 for (bMask=0x80u; bMask!=0; bMask>>=1) {
374 *(WORD*)pOut = (bMask & bBilevel)
376 : 0xffff; /* white */
379 } else if (g->wOutBitsPerPixel == 8) {
380 for (bMask=0x80u; bMask!=0; bMask>>=1) {
381 *pOut++ = (bMask & bBilevel)
389 else if (g->inTraits.iBitsPerPixel == 8) {
390 if (g->wOutBitsPerPixel == 48) {
391 while (pIn < pInAfter) {
392 wPixels = (*pIn++ << 8);
393 *(WORD*)(pOut+0) = wPixels;
394 *(WORD*)(pOut+2) = wPixels;
395 *(WORD*)(pOut+4) = wPixels;
398 } else if (g->wOutBitsPerPixel == 24) {
399 while (pIn < pInAfter) {
405 } else if (g->wOutBitsPerPixel == 16) {
406 while (pIn < pInAfter) {
407 wPixels = (*pIn++ << 8);
408 *(WORD*)pOut = wPixels;
411 } else if (g->wOutBitsPerPixel == 1) {
412 while (pIn < pInAfter) {
413 OUTPUT_THRESHOLDED_BIT (*pIn);
419 else if (g->inTraits.iBitsPerPixel == 16) {
420 if (g->wOutBitsPerPixel == 48) {
421 while (pIn < pInAfter) {
422 wPixels = *(WORD*)pIn;
423 *(WORD*)(pOut+0) = wPixels;
424 *(WORD*)(pOut+2) = wPixels;
425 *(WORD*)(pOut+4) = wPixels;
429 } else if (g->wOutBitsPerPixel == 24) {
430 while (pIn < pInAfter) {
431 bPixels = (*(WORD*)pIn) >> 8;
437 } else if (g->wOutBitsPerPixel == 8) {
438 while (pIn < pInAfter) {
439 *pOut++ = (*(WORD*)pIn) >> 8;
442 } else if (g->wOutBitsPerPixel == 1) {
443 while (pIn < pInAfter) {
444 OUTPUT_THRESHOLDED_BIT ((*(WORD*)pIn) >> 8);
450 else if (g->inTraits.iBitsPerPixel == 24) {
451 if (g->wOutBitsPerPixel == 48) {
452 while (pIn < pInAfter) {
453 *(WORD*)(pOut+0) = (WORD)(*pIn++) << 8;
454 *(WORD*)(pOut+2) = (WORD)(*pIn++) << 8;
455 *(WORD*)(pOut+4) = (WORD)(*pIn++) << 8;
458 } else if (g->wOutBitsPerPixel == 16) {
459 /* converting rgb color (24) to grayscale (16) */
460 while (pIn < pInAfter) {
464 *(WORD*)pOut = NTSC_LUMINANCE (rv, gv, bv);
467 } else if (g->wOutBitsPerPixel == 8) {
468 /* converting rgb color (24) to grayscale (8) */
469 while (pIn < pInAfter) {
473 *pOut++ = NTSC_LUMINANCE (rv, gv, bv);
475 } else if (g->wOutBitsPerPixel == 1) {
476 /* converting rgb color (24) to bi-level */
477 while (pIn < pInAfter) {
481 OUTPUT_THRESHOLDED_BIT (NTSC_LUMINANCE(rv,gv,bv));
486 else if (g->inTraits.iBitsPerPixel == 48) {
487 if (g->wOutBitsPerPixel == 24) {
488 while (pIn < pInAfter) {
489 *pOut++ = ((WORD*)pIn)[0] >> 8;
490 *pOut++ = ((WORD*)pIn)[1] >> 8;
491 *pOut++ = ((WORD*)pIn)[2] >> 8;
494 } else if (g->wOutBitsPerPixel == 16) {
495 /* converting rgb color (48) to grayscale (16) */
496 while (pIn < pInAfter) {
497 rv = ((WORD*)pIn)[0];
498 gv = ((WORD*)pIn)[1];
499 bv = ((WORD*)pIn)[2];
500 *(WORD*)pOut = NTSC_LUMINANCE (rv, gv, bv);
504 } else if (g->wOutBitsPerPixel == 8) {
505 /* converting rgb color (48) to grayscale (8) */
506 while (pIn < pInAfter) {
507 rv = ((WORD*)pIn)[0];
508 gv = ((WORD*)pIn)[1];
509 bv = ((WORD*)pIn)[2];
510 *pOut++ = NTSC_LUMINANCE (rv, gv, bv) >> 8;
513 } else if (g->wOutBitsPerPixel == 1) {
514 /* converting rgb color (48) to bi-level */
515 while (pIn < pInAfter) {
516 rv = ((WORD*)pIn)[0];
517 gv = ((WORD*)pIn)[1];
518 bv = ((WORD*)pIn)[2];
519 OUTPUT_THRESHOLDED_BIT (NTSC_LUMINANCE(rv,gv,bv) >> 8);
526 if (g->inTraits.iBitsPerPixel>1 && g->wOutBitsPerPixel==1 && bMask!=(BYTE)0x80)
527 *pOut = bBilevel; /* output any partially-filled byte */
529 *pdwInputUsed = g->dwInRowBytes;
530 g->dwInNextPos += g->dwInRowBytes;
531 *pdwInputNextPos = g->dwInNextPos;
533 *pdwOutputUsed = g->dwOutRowBytes;
534 *pdwOutputThisPos = g->dwOutNextPos;
535 g->dwOutNextPos += g->dwOutRowBytes;
539 return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA;
542 return IP_FATAL_ERROR;
547 /*****************************************************************************\
549 * changeBPP_insertedData - client inserted into our output stream
551 \*****************************************************************************/
553 static WORD changeBPP_insertedData (
554 IP_XFORM_HANDLE hXform,
558 return IP_FATAL_ERROR; /* must never be called (can't insert data) */
563 /*****************************************************************************\
565 * changeBPP_newPage - Tells us to flush this page, and start a new page
567 \*****************************************************************************/
569 static WORD changeBPP_newPage (
570 IP_XFORM_HANDLE hXform)
574 HANDLE_TO_PTR (hXform, g);
575 /* todo: return fatal error if convert is called again? */
576 return IP_DONE; /* can't insert page-breaks, so ignore this call */
579 return IP_FATAL_ERROR;
585 /*****************************************************************************\
587 * changeBPP_closeXform - Destroys this instance
589 \*****************************************************************************/
591 static WORD changeBPP_closeXform (IP_XFORM_HANDLE hXform)
595 HANDLE_TO_PTR (hXform, g);
598 IP_MEM_FREE (g); /* free memory for the instance */
603 return IP_FATAL_ERROR;
608 /*****************************************************************************\
610 * changeBPPTbl - Jump-table for xform
612 \*****************************************************************************/
614 IP_XFORM_TBL changeBPPTbl = {
616 changeBPP_setDefaultInputTraits,
617 changeBPP_setXformSpec,
618 changeBPP_getHeaderBufSize,
619 changeBPP_getActualTraits,
620 changeBPP_getActualBufSizes,
623 changeBPP_insertedData,