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 * xgamma.c - Applies Gamma transform
41 ******************************************************************************
43 * Name of Global Jump-Table:
47 * Items in aXformInfo array passed into setXformSpec:
49 * aXformInfo[0] = gamma value, in 16.16 fixed point
51 * Capabilities and Limitations:
53 * Gamma values must range between 0.0 and 3.5.
54 * Only operates on 8-bit grayscale data.
55 * Uses cubic splines for speed.
57 * Default Input Traits, and Output Traits:
59 * trait default input output
60 * ------------------- --------------------- ------------------------
61 * iPixelsPerRow * passed into output same as default input
62 * iBitsPerPixel * must be 8 8
63 * iComponentsPerPixel * must be 1 1
64 * lHorizDPI passed into output same as default input
65 * lVertDPI passed into output same as default input
66 * lNumRows passed into output same as default input
67 * iNumPages passed into output same as default input
68 * iPageNum passed into output same as default input
70 * Above, a "*" by an item indicates it must be valid (not negative).
72 * Feb 1998 Mark Overton -- ported code to software
73 * early 1997 Mark Overton -- wrote original code for Kodiak firmware
75 \******************************************************************************/
79 #include "string.h" /* for memset and memcpy */
84 #define PRINT(msg,arg1,arg2) \
85 fprintf(stderr, msg, (int)arg1, (int)arg2)
87 #define PRINT(msg,arg1,arg2)
90 #define CHECK_VALUE 0x4ba1dace
94 IP_IMAGE_TRAITS traits; /* traits of the input image */
95 BYTE gammaTable[256]; /* the gamma table */
96 WORD wRowsDone; /* number of rows converted so far */
97 DWORD dwInNextPos; /* file pos for subsequent input */
98 DWORD dwOutNextPos; /* file pos for subsequent output */
99 DWORD dwValidChk; /* struct validity check value */
100 } GAM_INST, *PGAM_INST;
105 /*____________________________________________________________________________
107 | fast_sin fast_cos | fast sine and cosine functions |
108 |___________________|________________________________________________________|
110 | Input: Units of angle is fraction of circle (e.g., 1.0 is 360 deg). |
111 | Format is 0.32 fixed-point (i.e., 32 bits of fraction). |
113 | Returns: Sine or cosine expressed as 16.16 signed fixed-point. |
115 | Accuracy: Max error is 0.000054 (i.e., 14 good bits of fraction). |
116 |____________________________________________________________________________|
118 static long fast_sin (ULONG ang)
120 ULONG ang31, ang30, delta_ang, result;
121 UINT index, base, next;
123 /* 90 deg is divided into this many intervals */
125 #define N_INTERVALS (1<<INDEX_BITS)
126 #define HALF_WORST_ERR 3
128 static const USHORT sin_table[N_INTERVALS+2] = {
129 0, 1608, 3216, 4821, 6424, 8022, 9616, 11204,
130 12785, 14359, 15924, 17479, 19024, 20557, 22078, 23586,
131 25080, 26558, 28020, 29466, 30893, 32303, 33692, 35062,
132 36410, 37736, 39040, 40320, 41576, 42806, 44011, 45190,
133 46341, 47464, 48559, 49624, 50660, 51665, 52639, 53581,
134 54491, 55368, 56212, 57022, 57798, 58538, 59244, 59914,
135 60547, 61145, 61705, 62228, 62714, 63162, 63572, 63944,
136 64277, 64571, 64827, 65043, 65220, 65358, 65457, 65516,
137 65535, 65535 /* <-- these should be 65536 (won't fit in 16 bits) */
140 /* zero the msb (representing 180 deg), leaving 31 bits */
141 ang31 = (ang << 1) >> 1;
143 /* fold angle to first quadrant, leaving 30 bits */
145 if (ang30 >= 0x40000000u)
146 ang30 = 0x80000000u - ang30; /* sin(180-a) = sin(a) */
148 index = ang30 >> (30-INDEX_BITS);
149 base = sin_table [index];
150 next = sin_table [index+1];
151 delta_ang = ang30>>(14-INDEX_BITS) & 0x0000ffffu;
152 result = ((next-base)*delta_ang >> 16) + base + HALF_WORST_ERR;
154 /* negate result if 180-bit was set in original angle */
156 result = (ULONG)(-(long)result);
162 #undef HALF_WORST_ERR
166 static /* inline */ int fast_cos (UINT ang)
168 return fast_sin(0x40000000u-ang);
184 for (i=0; i<=16*65536; i++) {
185 err = sin ((float)i * (2.0*3.1415926535897932384626/(16*65536.0)))
186 - fast_sin(i<<12)/65536.0;
187 if (err < 0) err = -err;
188 if (err > max_err) max_err = err;
191 printf ("max err = %f\n", max_err);
207 for (i=0; i<=64; i++) {
208 s = sin ((float)i * (3.1415926535897932384626/(2.0*64.0)));
209 printf ("%5d, ", (int)(s*(1<<16) + 0.5));
210 if (i%8 == 7) puts("");
219 long a, b, c; /* x = at + bt^2 + ct^3 */
220 long d, e, f; /* y = dt + et^2 + ft^3 */
225 /*____________________________________________________________________________
227 | calc_cubic_coeffs | calcs coefficients given start/end angles/velocities |
228 |___________________|________________________________________________________|
230 | Units of angle is fraction of circle (e.g., 1.0 is 360 deg). |
231 | Format is 0.32 fixed-point (i.e., 32 bits of fraction). |
232 |____________________________________________________________________________|
234 static void calc_cubic_coeffs (
237 long start_vel, /* velocities are in 16.16 fixed point */
241 long start_x, start_y, final_x, final_y;
243 /* For both start and final below: vel = vel * 2.0 / (1.0 + cos(ang)),
244 * where vel is 16.16 before calc, and is 20.12 after the calc below.
246 start_vel = (start_vel<<11) / ((1lu<<14) + (fast_cos(start_ang)>>2));
247 final_vel = (final_vel<<11) / ((1lu<<14) + (fast_cos(final_ang)>>2));
249 /* Below: (vel is 20.12) * (cos>>2 is 18.14) yields a 6.26;
250 * the >>10 changes 6.26 into a 16.16.
252 start_x = start_vel*(fast_cos(start_ang)>>2) >> 10;
253 start_y = start_vel*(fast_sin(start_ang)>>2) >> 10;
254 final_x = final_vel*(fast_cos(final_ang)>>2) >> 10;
255 final_y = final_vel*(fast_sin(final_ang)>>2) >> 10;
259 p->b = (3<<16) - 2*start_x - final_x;
260 p->e = -2*start_y - final_y;
261 p->c = start_x + final_x - (2<<16);
262 p->f = start_y + final_y;
267 /*____________________________________________________________________________
269 | transform_cubic | makes cubic end at (x_end,y_end) instead of (1,0) |
270 |_________________|__________________________________________________________|
272 static void transform_cubic (
279 q.a = p->a*x_end - p->d*y_end;
280 q.b = p->b*x_end - p->e*y_end;
281 q.c = p->c*x_end - p->f*y_end;
283 q.d = p->a*y_end + p->d*x_end;
284 q.e = p->b*y_end + p->e*x_end;
285 q.f = p->c*y_end + p->f*x_end;
292 /*____________________________________________________________________________
294 | calc_gamma_from_coeffs | calcs gamma table, given the coefficients |
295 |________________________|___________________________________________________|
297 static void calc_gamma_from_coeffs (
298 cubic_t *p, /* in: coefficients of cubic curve */
299 BYTE tbl[]) /* out: array to receive the gamma table */
301 #define LG_N_INTERVALS 7 /* 7 is the max; 8 causes overflow */
302 #define N_INTERVALS (1u<<LG_N_INTERVALS)
303 #define RND_PROD(p) (((p) + ((1<<LG_N_INTERVALS)-1)) >> LG_N_INTERVALS)
305 int t, x, y, xprev=0, yprev=0, xtmp, ytmp;
307 for (t=0; t<=N_INTERVALS; t+=1) {
308 x = (((RND_PROD((RND_PROD(p->c*t) + p->b)*t) + p->a)*t
309 >> (LG_N_INTERVALS+15)) + 1) >> 1;
310 y = (((RND_PROD((RND_PROD(p->f*t) + p->e)*t) + p->d)*t
311 >> (LG_N_INTERVALS+15)) + 1) >> 1;
313 if (t==0 || x!=xprev) tbl[x] = y;
317 ytmp = (yprev+y) / 2;
322 xprev = x; yprev = y;
328 /*____________________________________________________________________________
330 | calc_gamma_table | calculates gamma table using a cubic curve |
331 |__________________|_________________________________________________________|
333 static void calc_gamma_table (
334 long gamma, /* in: gamma value as 24.8 (0.7 .. 3.5 is useful range) */
335 BYTE tbl[]) /* out: array to receive the gamma table */
337 #define FLOAT_TO_FIX(f) ((short)((f)*0x1000 + 0.5)) /* output is 8.8 */
338 #define FLOAT_TO_ANG(f) ((USHORT)(long)((f)*0x10000/360.0 + 0.5))
340 #define MIN_INDEX 0 /* corresponds to gamma=0.0 */
341 #define MAX_INDEX 6 /* corresponds to gamma=3.0 */
343 typedef struct { /* ang_base=0.16; all others are 4.12 */
344 short start_vel_base; short start_vel_slope;
345 USHORT start_ang_base; short start_ang_slope;
346 short final_vel_base; short final_vel_slope;
347 USHORT final_ang_base; short final_ang_slope;
350 static const spec_t specs[MAX_INDEX-MIN_INDEX+1] = {
351 { /* 0.0 .. 0.5 (just does a straight line) */
352 FLOAT_TO_FIX(1.0), FLOAT_TO_FIX(0.0), /* start vel */
353 FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(0.0), /* start ang */
354 FLOAT_TO_FIX(1.0), FLOAT_TO_FIX(0.0), /* final vel */
355 FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(0.0), /* final ang */
357 { /* 0.5 .. 1.0 (bogus below about 0.7) */
358 FLOAT_TO_FIX(0.05), FLOAT_TO_FIX(0.5), /* start vel */
359 FLOAT_TO_ANG(-75.0), FLOAT_TO_FIX(150.0/360.0), /* start ang */
360 FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */
361 FLOAT_TO_ANG(15.0), FLOAT_TO_FIX(-30.0/360.0), /* final ang */
364 FLOAT_TO_FIX(0.3), FLOAT_TO_FIX(0.2), /* start vel */
365 FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(60.0/360.0), /* start ang */
366 FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */
367 FLOAT_TO_ANG(0.0), FLOAT_TO_FIX(-20.0/360.0), /* final ang */
370 FLOAT_TO_FIX(0.4), FLOAT_TO_FIX(0.2), /* start vel */
371 FLOAT_TO_ANG(30.0), FLOAT_TO_FIX(20.0/360.0), /* start ang */
372 FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */
373 FLOAT_TO_ANG(-10.0), FLOAT_TO_FIX(-16.0/360.0), /* final ang */
376 FLOAT_TO_FIX(0.5), FLOAT_TO_FIX(0.3), /* start vel */
377 FLOAT_TO_ANG(40.0), FLOAT_TO_FIX(4.0/360.0), /* start ang */
378 FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */
379 FLOAT_TO_ANG(-18.0), FLOAT_TO_FIX(-8.0/360.0), /* final ang */
382 FLOAT_TO_FIX(0.65), FLOAT_TO_FIX(0.4), /* start vel */
383 FLOAT_TO_ANG(42.0), FLOAT_TO_FIX(4.0/360.0), /* start ang */
384 FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */
385 FLOAT_TO_ANG(-22.0), FLOAT_TO_FIX(-6.0/360.0), /* final ang */
388 FLOAT_TO_FIX(0.85), FLOAT_TO_FIX(0.5), /* start vel */
389 FLOAT_TO_ANG(44.0), FLOAT_TO_FIX(1.0/360.0), /* start ang */
390 FLOAT_TO_FIX(2.0), FLOAT_TO_FIX(0.0), /* final vel */
391 FLOAT_TO_ANG(-25.0), FLOAT_TO_FIX(-4.0/360.0), /* final ang */
395 #define BEGIN_BLACKS 0 /* # of 0's at beginning of gamma table */
396 #define END_WHITES 4 /* # of 255's at final of gamma table */
399 long start_vel, final_vel;
400 ULONG start_ang, final_ang;
405 if (i > MAX_INDEX) i = MAX_INDEX;
406 p = &specs[i-MIN_INDEX];
409 start_vel = ((long)p->start_vel_base << 4)
410 + (p->start_vel_slope*gamma >> 4);
411 final_vel = ((long)p->final_vel_base << 4)
412 + (p->final_vel_slope*gamma >> 4);
413 start_ang = ((long)p->start_ang_base << 16)
414 + (p->start_ang_slope*gamma << 12);
415 final_ang = ((long)p->final_ang_base << 16)
416 + (p->final_ang_slope*gamma << 12);
418 calc_cubic_coeffs (start_ang, final_ang, start_vel, final_vel, &cubic);
419 transform_cubic (255-BEGIN_BLACKS-END_WHITES, 254, &cubic);
420 calc_gamma_from_coeffs (&cubic, tbl);
422 memmove (&tbl[BEGIN_BLACKS], &tbl[0], (256-BEGIN_BLACKS)*sizeof(BYTE));
424 for (i=0; i<BEGIN_BLACKS; i++)
427 for (i=256-END_WHITES; i<=255; i++)
434 /*****************************************************************************\
436 * gamma_openXform - Creates a new instance of the transformer
438 *****************************************************************************
440 * This returns a handle for the new instance to be passed into
441 * all subsequent calls.
443 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
445 \*****************************************************************************/
447 static WORD gamma_openXform (
448 IP_XFORM_HANDLE *pXform) /* out: returned handle */
452 INSURE (pXform != NULL);
453 IP_MEM_ALLOC (sizeof(GAM_INST), g);
455 memset (g, 0, sizeof(GAM_INST));
456 g->dwValidChk = CHECK_VALUE;
460 return IP_FATAL_ERROR;
465 /*****************************************************************************\
467 * gamma_setDefaultInputTraits - Specifies default input image traits
469 *****************************************************************************
471 * The header of the file-type handled by the transform probably does
472 * not include *all* the image traits we'd like to know. Those not
473 * specified in the file-header are filled in from info provided by
476 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
478 \*****************************************************************************/
480 static WORD gamma_setDefaultInputTraits (
481 IP_XFORM_HANDLE hXform, /* in: handle for xform */
482 PIP_IMAGE_TRAITS pTraits) /* in: default image traits */
486 HANDLE_TO_PTR (hXform, g);
488 /* Insure that values we care about are correct */
489 INSURE (pTraits->iBitsPerPixel == 8);
490 INSURE (pTraits->iComponentsPerPixel == 1);
491 INSURE (pTraits->iPixelsPerRow > 0);
493 g->traits = *pTraits; /* a structure copy */
497 return IP_FATAL_ERROR;
502 /*****************************************************************************\
504 * gamma_setXformSpec - Provides xform-specific information
506 \*****************************************************************************/
508 static WORD gamma_setXformSpec (
509 IP_XFORM_HANDLE hXform, /* in: handle for xform */
510 DWORD_OR_PVOID aXformInfo[]) /* in: xform information */
515 HANDLE_TO_PTR (hXform, g);
516 gamma = aXformInfo[0].dword;
517 INSURE (gamma <= 0x38000u); /* 3.5 is our limit */
519 calc_gamma_table ((gamma+0x0080u)>>8, g->gammaTable);
520 /* todo: make calc_gamma_table accept a 16.16 gamma value */
525 return IP_FATAL_ERROR;
530 /*****************************************************************************\
532 * gamma_getHeaderBufSize- Returns size of input buf needed to hold header
534 \*****************************************************************************/
536 static WORD gamma_getHeaderBufSize (
537 IP_XFORM_HANDLE hXform, /* in: handle for xform */
538 DWORD *pwInBufLen) /* out: buf size for parsing header */
540 /* since input is raw pixels, there is no header, so set it to zero */
547 /*****************************************************************************\
549 * gamma_getActualTraits - Parses header, and returns input & output traits
551 \*****************************************************************************/
553 static WORD gamma_getActualTraits (
554 IP_XFORM_HANDLE hXform, /* in: handle for xform */
555 DWORD wInputAvail, /* in: # avail bytes in input buf */
556 PBYTE pbInputBuf, /* in: ptr to input buffer */
557 PDWORD pwInputUsed, /* out: # bytes used from input buf */
558 PDWORD pdwInputNextPos,/* out: file-pos to read from next */
559 PIP_IMAGE_TRAITS pIntraits, /* out: input image traits */
560 PIP_IMAGE_TRAITS pOutTraits) /* out: output image traits */
564 HANDLE_TO_PTR (hXform, g);
566 /* Since there is no header, we'll report no usage of input */
568 *pdwInputNextPos = 0;
570 *pIntraits = g->traits; /* structure copies */
571 *pOutTraits = g->traits;
576 return IP_FATAL_ERROR;
581 /****************************************************************************\
583 * gamma_getActualBufSizes - Returns buf sizes needed for remainder of job
585 \****************************************************************************/
587 static WORD gamma_getActualBufSizes (
588 IP_XFORM_HANDLE hXform, /* in: handle for xform */
589 PDWORD pwMinInBufLen, /* out: min input buf size */
590 PDWORD pwMinOutBufLen) /* out: min output buf size */
594 HANDLE_TO_PTR (hXform, g);
595 *pwMinInBufLen = *pwMinOutBufLen = g->traits.iPixelsPerRow;
599 return IP_FATAL_ERROR;
604 /*****************************************************************************\
606 * gamma_convert - Converts one row
608 \*****************************************************************************/
610 static WORD gamma_convert (
611 IP_XFORM_HANDLE hXform,
612 DWORD wInputAvail, /* in: # avail bytes in in-buf */
613 PBYTE pbInputBuf, /* in: ptr to in-buffer */
614 PDWORD pwInputUsed, /* out: # bytes used from in-buf */
615 PDWORD pdwInputNextPos, /* out: file-pos to read from next */
616 DWORD wOutputAvail, /* in: # avail bytes in out-buf */
617 PBYTE pbOutputBuf, /* in: ptr to out-buffer */
618 PDWORD pwOutputUsed, /* out: # bytes written in out-buf */
619 PDWORD pdwOutputThisPos) /* out: file-pos to write the data */
623 PBYTE pIn, pOut, pOutAfter;
625 HANDLE_TO_PTR (hXform, g);
627 /**** Check if we were told to flush ****/
629 if (pbInputBuf == NULL) {
630 PRINT ("gamma_convert: Told to flush.\n", 0, 0);
631 *pwInputUsed = *pwOutputUsed = 0;
632 *pdwInputNextPos = g->dwInNextPos;
633 *pdwOutputThisPos = g->dwOutNextPos;
637 /**** Output a Row ****/
639 nBytes = g->traits.iPixelsPerRow;
640 INSURE (wInputAvail >= nBytes );
641 INSURE (wOutputAvail >= nBytes);
645 pOutAfter = pOut + nBytes;
647 while (pOut < pOutAfter) {
648 pOut[0] = g->gammaTable[pIn[0]];
649 pOut[1] = g->gammaTable[pIn[1]];
650 pOut[2] = g->gammaTable[pIn[2]];
651 pOut[3] = g->gammaTable[pIn[3]];
652 pOut[4] = g->gammaTable[pIn[4]];
653 pOut[5] = g->gammaTable[pIn[5]];
654 pOut[6] = g->gammaTable[pIn[6]];
655 pOut[7] = g->gammaTable[pIn[7]];
661 *pwInputUsed = nBytes;
662 g->dwInNextPos += nBytes;
663 *pdwInputNextPos = g->dwInNextPos;
665 *pwOutputUsed = nBytes;
666 *pdwOutputThisPos = g->dwOutNextPos;
667 g->dwOutNextPos += nBytes;
671 PRINT ("gamma_convert: Returning, out used = %d\n", out_used, 0);
672 return IP_CONSUMED_ROW | IP_PRODUCED_ROW | IP_READY_FOR_DATA;
675 return IP_FATAL_ERROR;
680 /*****************************************************************************\
682 * gamma_insertedData - client inserted into our output stream
684 \*****************************************************************************/
686 static WORD gamma_insertedData (
687 IP_XFORM_HANDLE hXform,
691 return IP_FATAL_ERROR; /* must never be called (can't insert data) */
696 /*****************************************************************************\
698 * gamma_newPage - Tells us to flush this page, and start a new page
700 \*****************************************************************************/
702 static WORD gamma_newPage (
703 IP_XFORM_HANDLE hXform)
707 HANDLE_TO_PTR (hXform, g);
708 /* todo: return fatal error if convert is called again? */
709 return IP_DONE; /* can't insert page-breaks, so ignore this call */
712 return IP_FATAL_ERROR;
718 /*****************************************************************************\
720 * gamma_closeXform - Destroys this instance
722 \*****************************************************************************/
724 static WORD gamma_closeXform (IP_XFORM_HANDLE hXform)
728 HANDLE_TO_PTR (hXform, g);
731 IP_MEM_FREE (g); /* free memory for the instance */
736 return IP_FATAL_ERROR;
741 /*****************************************************************************\
743 * gammaTbl - Jump-table for transform driver
745 \*****************************************************************************/
747 IP_XFORM_TBL gammaTbl = {
749 gamma_setDefaultInputTraits,
751 gamma_getHeaderBufSize,
752 gamma_getActualTraits,
753 gamma_getActualBufSizes,