1 /* libhpojip -- HP OfficeJet image-processing library. */
2
3 /* Copyright (C) 1995-2002 HP Company
4 *
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.
9 *
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.
14 *
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,
18 * MA 02111-1307, USA.
19 *
20 * In addition, as a special exception, HP 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.
30 */
31
32 /* Original author: David Paschal (based on Mark Overton's "xskel" template). */
33
34 /******************************************************************************\
35 *
36 * xpnm.c - encoder and decoder for PNM (PBM, PGM, PPM) files
37 *
38 ******************************************************************************
39 *
40 * Name of Global Jump-Table:
41 *
42 * pnmEncodeTbl = the encoder.
43 * pnmDecodeTbl = the decoder.
44 *
45 * Items in aXformInfo array passed into setXformSpec:
46 *
47 * None.
48 *
49 * Capabilities and Limitations:
50 *
51 * Handles 1, 8, and 24 bits per pixel.
52 *
53 * Default Input Traits, and Output Traits:
54 *
55 * trait default input output
56 * ------------------- --------------------- ------------------------
57 * iPixelsPerRow * passed into output same as default input
58 * iBitsPerPixel * passed into output same as default input
59 * iComponentsPerPixel * passed into output same as default input
60 * lHorizDPI passed into output same as default input
61 * lVertDPI passed into output same as default input
62 * lNumRows passed into output same as default input
63 * iNumPages passed into output same as default input
64 * iPageNum passed into output same as default input
65 *
66 * Above, a "*" by an item indicates it must be valid (not negative).
67 *
68 \******************************************************************************/
69
70 #include "hpip.h"
71 #include "ipdefs.h"
72 #include "string.h" /* for memset and memcpy */
73 #include <stdio.h>
74
75
76 #if 0
77 #include "stdio.h"
78 #include <tchar.h>
79 #define PRINT(msg,arg1,arg2) \
80 _ftprintf(stderr, msg, (int)arg1, (int)arg2)
81 #else
82 #define PRINT(msg,arg1,arg2)
83 #endif
84
85 #define CHECK_VALUE 0x4ba1dace
86
87 #define FUNC_STATUS static
88
89 typedef struct {
90 IP_IMAGE_TRAITS traits; /* traits of the input and output image */
91 DWORD dwBytesPerRow; /* # of bytes in each row */
92 DWORD dwRowsDone; /* number of rows converted so far */
93 DWORD dwInNextPos; /* file pos for subsequent input */
94 DWORD dwOutNextPos; /* file pos for subsequent output */
95 DWORD dwValidChk; /* struct validity check value */
96 BOOL fIsEncode; /* false=decode, true=encode */
97 BOOL fDidHeader; /* already sent/processed the header? */
98 } PNM_INST, *PPNM_INST;
99
100 #define MAX_DECODE_HEADER_SIZE 4096
101 #define MAX_ENCODE_HEADER_SIZE 128
102
103
104
105 /*****************************************************************************\
106 *
107 * pnm{De,En}code_openXform - Creates a new instance of the transformer
108 *
109 *****************************************************************************
110 *
111 * This returns a handle for the new instance to be passed into
112 * all subsequent calls.
113 *
114 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
115 *
116 \*****************************************************************************/
117
pnmDecode_openXform(IP_XFORM_HANDLE * pXform)118 FUNC_STATUS WORD pnmDecode_openXform (
119 IP_XFORM_HANDLE *pXform) /* out: returned handle */
120 {
121 PPNM_INST g;
122
123 INSURE (pXform != NULL);
124 IP_MEM_ALLOC (sizeof(PNM_INST), g);
125 *pXform = g;
126 memset (g, 0, sizeof(PNM_INST));
127 g->dwValidChk = CHECK_VALUE;
128 return IP_DONE;
129
130 fatal_error:
131 return IP_FATAL_ERROR;
132 }
133
pnmEncode_openXform(IP_XFORM_HANDLE * pXform)134 FUNC_STATUS WORD pnmEncode_openXform (
135 IP_XFORM_HANDLE *pXform) /* out: returned handle */
136 {
137 WORD wResult=pnmDecode_openXform(pXform);
138 if (wResult==IP_DONE) {
139 PPNM_INST g;
140
141 HANDLE_TO_PTR (*pXform, g);
142
143 g->dwOutNextPos=MAX_ENCODE_HEADER_SIZE;
144 g->fIsEncode=TRUE;
145 }
146 return wResult;
147
148 fatal_error:
149 return IP_FATAL_ERROR;
150 }
151
152
153 /*****************************************************************************\
154 *
155 * pnm_setDefaultInputTraits - Specifies default input image traits
156 *
157 *****************************************************************************
158 *
159 * The header of the file-type handled by the transform probably does
160 * not include *all* the image traits we'd like to know. Those not
161 * specified in the file-header are filled in from info provided by
162 * this routine.
163 *
164 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
165 *
166 \*****************************************************************************/
167
pnm_setDefaultInputTraits(IP_XFORM_HANDLE hXform,PIP_IMAGE_TRAITS pTraits)168 FUNC_STATUS WORD pnm_setDefaultInputTraits (
169 IP_XFORM_HANDLE hXform, /* in: handle for xform */
170 PIP_IMAGE_TRAITS pTraits) /* in: default image traits */
171 {
172 PPNM_INST g;
173
174 HANDLE_TO_PTR (hXform, g);
175
176 g->traits = *pTraits; /* a structure copy */
177 if (g->fIsEncode) {
178 /* insure that traits we care about are known */
179 INSURE (pTraits->iPixelsPerRow>0 && pTraits->iBitsPerPixel>0);
180 g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8;
181 }
182 return IP_DONE;
183
184 fatal_error:
185 return IP_FATAL_ERROR;
186 }
187
188
189
190 /*****************************************************************************\
191 *
192 * pnm_setXformSpec - Provides xform-specific information
193 *
194 \*****************************************************************************/
195
pnm_setXformSpec(IP_XFORM_HANDLE hXform,DWORD_OR_PVOID aXformInfo[])196 FUNC_STATUS WORD pnm_setXformSpec (
197 IP_XFORM_HANDLE hXform, /* in: handle for xform */
198 DWORD_OR_PVOID aXformInfo[]) /* in: xform information */
199 {
200 PPNM_INST g;
201
202 HANDLE_TO_PTR (hXform, g);
203
204 /* Check your options in aXformInfo here, and save them.
205 * Use the INSURE macro like you'd use assert. INSURE jumps to
206 * fatal_error below if it fails.
207 */
208
209 return IP_DONE;
210
211 fatal_error:
212 return IP_FATAL_ERROR;
213 }
214
215
216
217 /*****************************************************************************\
218 *
219 * pnm_getHeaderBufSize- Returns size of input buf needed to hold header
220 *
221 \*****************************************************************************/
222
pnm_getHeaderBufSize(IP_XFORM_HANDLE hXform,DWORD * pdwInBufLen)223 FUNC_STATUS WORD pnm_getHeaderBufSize (
224 IP_XFORM_HANDLE hXform, /* in: handle for xform */
225 DWORD *pdwInBufLen) /* out: buf size for parsing header */
226 {
227 PPNM_INST g;
228
229 HANDLE_TO_PTR (hXform, g);
230
231 if (!g->fIsEncode) {
232 *pdwInBufLen = MAX_DECODE_HEADER_SIZE;
233 } else {
234 /* since input is raw pixels, there is no header, so set it to zero */
235 *pdwInBufLen = 0;
236 }
237 return IP_DONE;
238
239 fatal_error:
240 return IP_FATAL_ERROR;
241 }
242
243
244
245 /*****************************************************************************\
246 *
247 * pnm_getActualTraits - Parses header, and returns input & output traits
248 *
249 \*****************************************************************************/
250
251 #define PEEK_CHAR(pc) \
252 do { \
253 if (*pdwInputUsed>=dwInputAvail) { \
254 return IP_INPUT_ERROR; \
255 } \
256 *(pc)=pbInputBuf[*pdwInputUsed]; \
257 } while (0)
258
259 #define NEXT_CHAR ((*pdwInputUsed)++)
260
261 #define GET_CHAR(pc) \
262 do { \
263 PEEK_CHAR(pc); \
264 NEXT_CHAR; \
265 } while (0)
266
267 #define SKIP_WS \
268 do { \
269 unsigned char c; \
270 PEEK_CHAR(&c); \
271 if (c=='#') { \
272 /* NEXT_CHAR; */ \
273 do { \
274 GET_CHAR(&c); \
275 } while (c!='\n'); \
276 PEEK_CHAR(&c); \
277 } \
278 if (c>' ') break; \
279 NEXT_CHAR; \
280 } while (42)
281
282 #define GET_INT(pi) \
283 do { \
284 unsigned char c; \
285 *(pi)=0; \
286 SKIP_WS; \
287 while (42) { \
288 GET_CHAR(&c); \
289 c-='0'; \
290 if (c>9) { \
291 break; \
292 } \
293 *(pi)=(*(pi)*10)+c; \
294 } \
295 } while (0)
296
pnm_getActualTraits(IP_XFORM_HANDLE hXform,DWORD dwInputAvail,PBYTE pbInputBuf,PDWORD pdwInputUsed,PDWORD pdwInputNextPos,PIP_IMAGE_TRAITS pInTraits,PIP_IMAGE_TRAITS pOutTraits)297 FUNC_STATUS WORD pnm_getActualTraits (
298 IP_XFORM_HANDLE hXform, /* in: handle for xform */
299 DWORD dwInputAvail, /* in: # avail bytes in input buf */
300 PBYTE pbInputBuf, /* in: ptr to input buffer */
301 PDWORD pdwInputUsed, /* out: # bytes used from input buf */
302 PDWORD pdwInputNextPos,/* out: file-pos to read from next */
303 PIP_IMAGE_TRAITS pInTraits, /* out: input image traits */
304 PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */
305 {
306 PPNM_INST g;
307
308 HANDLE_TO_PTR (hXform, g);
309
310 /* If there is no header, we'll report no usage of input */
311 *pdwInputUsed = 0;
312
313 /* Parse the header for decode operations. */
314 if (!g->fIsEncode) {
315 unsigned char c;
316 unsigned int maxval;
317
318 GET_CHAR(&c);
319 if (c!='P') {
320 return IP_INPUT_ERROR;
321 }
322 GET_CHAR(&c);
323 if (c=='4') {
324 /* PBM */
325 g->traits.iComponentsPerPixel=1;
326 g->traits.iBitsPerPixel=1;
327
328 } else if (c=='5') {
329 /* PGM */
330 g->traits.iComponentsPerPixel=1;
331 g->traits.iBitsPerPixel=0;
332
333 } else if (c=='6') {
334 /* PPM */
335 g->traits.iComponentsPerPixel=3;
336 g->traits.iBitsPerPixel=0;
337
338 } else {
339 /* "Plain" (all-ASCII) formats (1-3) not (yet) supported. */
340 return IP_INPUT_ERROR;
341 }
342
343 GET_INT(&g->traits.iPixelsPerRow);
344 GET_INT(&g->traits.lNumRows);
345 if (!g->traits.iBitsPerPixel) {
346 GET_INT(&maxval);
347 while (maxval) {
348 g->traits.iBitsPerPixel++;
349 maxval>>=1;
350 }
351 }
352 g->traits.iBitsPerPixel*=g->traits.iComponentsPerPixel;
353 g->dwBytesPerRow = (g->traits.iPixelsPerRow*g->traits.iBitsPerPixel + 7) / 8;
354 }
355
356 *pdwInputNextPos = *pdwInputUsed;
357 g->dwInNextPos = *pdwInputUsed;
358
359 *pInTraits = g->traits; /* structure copies */
360 *pOutTraits = g->traits;
361
362 return IP_DONE | IP_READY_FOR_DATA;
363
364 fatal_error:
365 return IP_FATAL_ERROR;
366 }
367
368
369
370 /****************************************************************************\
371 *
372 * pnm_getActualBufSizes - Returns buf sizes needed for remainder of job
373 *
374 \****************************************************************************/
375
pnm_getActualBufSizes(IP_XFORM_HANDLE hXform,PDWORD pdwMinInBufLen,PDWORD pdwMinOutBufLen)376 FUNC_STATUS WORD pnm_getActualBufSizes (
377 IP_XFORM_HANDLE hXform, /* in: handle for xform */
378 PDWORD pdwMinInBufLen, /* out: min input buf size */
379 PDWORD pdwMinOutBufLen) /* out: min output buf size */
380 {
381 PPNM_INST g;
382
383 HANDLE_TO_PTR (hXform, g);
384
385 *pdwMinInBufLen = *pdwMinOutBufLen = g->dwBytesPerRow;
386 return IP_DONE;
387
388 fatal_error:
389 return IP_FATAL_ERROR;
390 }
391
392
393
394 /*****************************************************************************\
395 *
396 * pnm_convert - Converts one row
397 *
398 \*****************************************************************************/
399
pnm_convert(IP_XFORM_HANDLE hXform,DWORD dwInputAvail,PBYTE pbInputBuf,PDWORD pdwInputUsed,PDWORD pdwInputNextPos,DWORD dwOutputAvail,PBYTE pbOutputBuf,PDWORD pdwOutputUsed,PDWORD pdwOutputThisPos)400 FUNC_STATUS WORD pnm_convert (
401 IP_XFORM_HANDLE hXform,
402 DWORD dwInputAvail, /* in: # avail bytes in in-buf */
403 PBYTE pbInputBuf, /* in: ptr to in-buffer */
404 PDWORD pdwInputUsed, /* out: # bytes used from in-buf */
405 PDWORD pdwInputNextPos, /* out: file-pos to read from next */
406 DWORD dwOutputAvail, /* in: # avail bytes in out-buf */
407 PBYTE pbOutputBuf, /* in: ptr to out-buffer */
408 PDWORD pdwOutputUsed, /* out: # bytes written in out-buf */
409 PDWORD pdwOutputThisPos) /* out: file-pos to write the data */
410 {
411 PPNM_INST g;
412 int nBytes;
413 PBYTE pIn, pOut;
414
415 HANDLE_TO_PTR (hXform, g);
416
417 /**** Check if we were told to flush ****/
418
419 if (pbInputBuf == NULL) {
420 PRINT (_T("pnm_convert: Told to flush.\n"), 0, 0);
421 *pdwInputUsed = *pdwOutputUsed = 0;
422 *pdwInputNextPos = g->dwInNextPos;
423 *pdwOutputThisPos = g->dwOutNextPos;
424 if (g->fIsEncode && !g->fDidHeader) {
425 BYTE buffer[MAX_ENCODE_HEADER_SIZE];
426 DWORD maxval=(2<<((g->traits.iBitsPerPixel/
427 g->traits.iComponentsPerPixel)-1))-1;
428 int len;
429
430 INSURE(dwOutputAvail>=MAX_ENCODE_HEADER_SIZE);
431
432 memset(pbOutputBuf,' ',MAX_ENCODE_HEADER_SIZE);
433 pbOutputBuf[0]='P';
434 if (g->traits.iComponentsPerPixel==1) {
435 if (maxval==1) {
436 pbOutputBuf[1]='4';
437 } else {
438 pbOutputBuf[1]='5';
439 }
440 } else if (g->traits.iComponentsPerPixel==3) {
441 pbOutputBuf[1]='6';
442 } else {
443 goto fatal_error;
444 }
445
446 snprintf((char *)buffer,MAX_ENCODE_HEADER_SIZE,"\n%d %d\n",
447 g->traits.iPixelsPerRow,g->dwRowsDone);
448 if (g->traits.iComponentsPerPixel>1 || maxval>1) {
449 buffer[MAX_ENCODE_HEADER_SIZE-1]=0;
450 len=strlen((char *)buffer);
451 snprintf((char *)buffer+len,MAX_ENCODE_HEADER_SIZE-len,
452 "%d\n",maxval);
453 }
454
455 buffer[MAX_ENCODE_HEADER_SIZE-1]=0;
456 len=strlen((char *)buffer);
457 memcpy(pbOutputBuf+MAX_ENCODE_HEADER_SIZE-len,buffer,len);
458
459 *pdwOutputUsed=MAX_ENCODE_HEADER_SIZE;
460 *pdwOutputThisPos=0;
461 g->dwOutNextPos=MAX_ENCODE_HEADER_SIZE;
462 g->fDidHeader=1;
463 }
464 return IP_DONE;
465 }
466
467 /**** Output a Row ****/
468
469 nBytes = g->dwBytesPerRow;
470 INSURE (dwInputAvail >= (DWORD)nBytes );
471 INSURE (dwOutputAvail >= (DWORD)nBytes);
472
473 pIn = pbInputBuf;
474 pOut = pbOutputBuf;
475
476 /* At this point, pIn is your input buffer, and pOut is your output buffer.
477 * Do whatever you are going to do here.
478 */
479 memcpy(pOut,pIn,nBytes);
480
481 *pdwInputUsed = nBytes;
482 g->dwInNextPos += nBytes;
483 *pdwInputNextPos = g->dwInNextPos;
484
485 *pdwOutputUsed = nBytes;
486 *pdwOutputThisPos = g->dwOutNextPos;
487 g->dwOutNextPos += nBytes;
488
489 g->dwRowsDone += 1;
490
491 return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA;
492
493 fatal_error:
494 return IP_FATAL_ERROR;
495 }
496
497
498
499 /*****************************************************************************\
500 *
501 * pnm_insertedData - client inserted into our output stream
502 *
503 \*****************************************************************************/
504
pnm_insertedData(IP_XFORM_HANDLE hXform,DWORD dwNumBytes)505 FUNC_STATUS WORD pnm_insertedData (
506 IP_XFORM_HANDLE hXform,
507 DWORD dwNumBytes)
508 {
509 fatalBreakPoint ();
510 return IP_FATAL_ERROR; /* must never be called (can't insert data) */
511 }
512
513
514
515 /*****************************************************************************\
516 *
517 * pnm_newPage - Tells us to flush this page, and start a new page
518 *
519 \*****************************************************************************/
520
pnm_newPage(IP_XFORM_HANDLE hXform)521 FUNC_STATUS WORD pnm_newPage (
522 IP_XFORM_HANDLE hXform)
523 {
524 PPNM_INST g;
525
526 HANDLE_TO_PTR (hXform, g);
527 /* todo: return fatal error if convert is called again? */
528 return IP_DONE; /* can't insert page-breaks, so ignore this call */
529
530 fatal_error:
531 return IP_FATAL_ERROR;
532
533 }
534
535
536
537 /*****************************************************************************\
538 *
539 * pnm_closeXform - Destroys this instance
540 *
541 \*****************************************************************************/
542
pnm_closeXform(IP_XFORM_HANDLE hXform)543 FUNC_STATUS WORD pnm_closeXform (IP_XFORM_HANDLE hXform)
544 {
545 PPNM_INST g;
546
547 HANDLE_TO_PTR (hXform, g);
548
549 g->dwValidChk = 0;
550 IP_MEM_FREE (g); /* free memory for the instance */
551
552 return IP_DONE;
553
554 fatal_error:
555 return IP_FATAL_ERROR;
556 }
557
558
559
560 /*****************************************************************************\
561 *
562 * pnmTbl - Jump-table for transform driver
563 *
564 \*****************************************************************************/
565 IP_XFORM_TBL pnmDecodeTbl = {
566 pnmDecode_openXform,
567 pnm_setDefaultInputTraits,
568 pnm_setXformSpec,
569 pnm_getHeaderBufSize,
570 pnm_getActualTraits,
571 pnm_getActualBufSizes,
572 pnm_convert,
573 pnm_newPage,
574 pnm_insertedData,
575 pnm_closeXform
576 };
577
578 IP_XFORM_TBL pnmEncodeTbl = {
579 pnmEncode_openXform,
580 pnm_setDefaultInputTraits,
581 pnm_setXformSpec,
582 pnm_getHeaderBufSize,
583 pnm_getActualTraits,
584 pnm_getActualBufSizes,
585 pnm_convert,
586 pnm_newPage,
587 pnm_insertedData,
588 pnm_closeXform
589 };
590
591 /* End of File */
592