1 /*
2 * RGB color separation code for CUPS.
3 *
4 * Copyright 2007 by Apple Inc.
5 * Copyright 1993-2005 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "COPYING"
10 * which should have been included with this file.
11 *
12 * Contents:
13 *
14 * cupsRGBDelete() - Delete a color separation.
15 * cupsRGBDoGray() - Do a grayscale separation...
16 * cupsRGBDoRGB() - Do a RGB separation...
17 * cupsRGBLoad() - Load a RGB color profile from a PPD file.
18 * cupsRGBNew() - Create a new RGB color separation.
19 */
20
21 /*
22 * Include necessary headers.
23 */
24
25 #include "driver.h"
26
27
28 /*
29 * 'cupsRGBDelete()' - Delete a color separation.
30 */
31
32 void
cupsRGBDelete(cups_rgb_t * rgbptr)33 cupsRGBDelete(cups_rgb_t *rgbptr) /* I - Color separation */
34 {
35 if (rgbptr == NULL)
36 return;
37
38 free(rgbptr->colors[0][0][0]);
39 free(rgbptr->colors[0][0]);
40 free(rgbptr->colors[0]);
41 free(rgbptr->colors);
42 free(rgbptr);
43 }
44
45
46 /*
47 * 'cupsRGBDoGray()' - Do a grayscale separation...
48 */
49
50 void
cupsRGBDoGray(cups_rgb_t * rgbptr,const unsigned char * input,unsigned char * output,int num_pixels)51 cupsRGBDoGray(cups_rgb_t *rgbptr,
52 /* I - Color separation */
53 const unsigned char *input,
54 /* I - Input grayscale pixels */
55 unsigned char *output,
56 /* O - Output Device-N pixels */
57 int num_pixels)
58 /* I - Number of pixels */
59 {
60 int i; /* Looping var */
61 int lastgray; /* Previous grayscale */
62 int xs, ys, zs, /* Current RGB row offsets */
63 g, gi, gm0, gm1;/* Current gray index and multipliers ... */
64 const unsigned char *color; /* Current color data */
65 int tempg; /* Current separation color */
66 int rgbsize; /* Separation data size */
67
68
69 /*
70 * Range check input...
71 */
72
73 if (!rgbptr || !input || !output || num_pixels <= 0)
74 return;
75
76 /*
77 * Initialize variables used for the duration of the separation...
78 */
79
80 lastgray = -1;
81 rgbsize = rgbptr->num_channels;
82 xs = rgbptr->cube_size * rgbptr->cube_size * rgbptr->num_channels;
83 ys = rgbptr->cube_size * rgbptr->num_channels;
84 zs = rgbptr->num_channels;
85
86 /*
87 * Loop through it all...
88 */
89
90 while (num_pixels > 0)
91 {
92 /*
93 * See if the next pixel is a cached value...
94 */
95
96 num_pixels --;
97
98 g = cups_srgb_lut[*input++];
99
100 if (g == lastgray)
101 {
102 /*
103 * Copy previous color and continue...
104 */
105
106 memcpy(output, output - rgbptr->num_channels, rgbsize);
107
108 output += rgbptr->num_channels;
109 continue;
110 }
111 else if (g == 0x00 && rgbptr->cache_init)
112 {
113 /*
114 * Copy black color and continue...
115 */
116
117 memcpy(output, rgbptr->black, rgbsize);
118
119 output += rgbptr->num_channels;
120 continue;
121 }
122 else if (g == 0xff && rgbptr->cache_init)
123 {
124 /*
125 * Copy white color and continue...
126 */
127
128 memcpy(output, rgbptr->white, rgbsize);
129
130 output += rgbptr->num_channels;
131 continue;
132 }
133
134 /*
135 * Nope, figure this one out on our own...
136 */
137
138 gi = rgbptr->cube_index[g];
139 gm0 = rgbptr->cube_mult[g];
140 gm1 = 256 - gm0;
141
142 color = rgbptr->colors[gi][gi][gi];
143
144 for (i = 0; i < rgbptr->num_channels; i ++, color ++)
145 {
146 tempg = (color[0] * gm0 + color[xs + ys + zs] * gm1) / 256;
147
148 if (tempg > 255)
149 *output++ = 255;
150 else if (tempg < 0)
151 *output++ = 0;
152 else
153 *output++ = tempg;
154 }
155 }
156 }
157
158
159 /*
160 * 'cupsRGBDoRGB()' - Do a RGB separation...
161 */
162
163 void
cupsRGBDoRGB(cups_rgb_t * rgbptr,const unsigned char * input,unsigned char * output,int num_pixels)164 cupsRGBDoRGB(cups_rgb_t *rgbptr,
165 /* I - Color separation */
166 const unsigned char *input,
167 /* I - Input RGB pixels */
168 unsigned char *output,
169 /* O - Output Device-N pixels */
170 int num_pixels)
171 /* I - Number of pixels */
172 {
173 int i; /* Looping var */
174 int rgb, /* Current RGB color */
175 lastrgb; /* Previous RGB color */
176 int r, ri, rm0, rm1, rs,
177 /* Current red index, multipliexs, and row offset */
178 g, gi, gm0, gm1, gs,
179 /* Current green ... */
180 b, bi, bm0, bm1, bs;
181 /* Current blue ... */
182 const unsigned char *color; /* Current color data */
183 int tempr, /* Current separation colors */
184 tempg, /* ... */
185 tempb ; /* ... */
186 int rgbsize; /* Separation data size */
187
188
189 /*
190 * Range check input...
191 */
192
193 if (!rgbptr || !input || !output || num_pixels <= 0)
194 return;
195
196 /*
197 * Initialize variables used for the duration of the separation...
198 */
199
200 lastrgb = -1;
201 rgbsize = rgbptr->num_channels;
202 rs = rgbptr->cube_size * rgbptr->cube_size * rgbptr->num_channels;
203 gs = rgbptr->cube_size * rgbptr->num_channels;
204 bs = rgbptr->num_channels;
205
206 /*
207 * Loop through it all...
208 */
209
210 while (num_pixels > 0)
211 {
212 /*
213 * See if the next pixel is a cached value...
214 */
215
216 num_pixels --;
217
218 r = cups_srgb_lut[*input++];
219 g = cups_srgb_lut[*input++];
220 b = cups_srgb_lut[*input++];
221 rgb = (((r << 8) | g) << 8) | b;
222
223 if (rgb == lastrgb)
224 {
225 /*
226 * Copy previous color and continue...
227 */
228
229 memcpy(output, output - rgbptr->num_channels, rgbsize);
230
231 output += rgbptr->num_channels;
232 continue;
233 }
234 else if (rgb == 0x000000 && rgbptr->cache_init)
235 {
236 /*
237 * Copy black color and continue...
238 */
239
240 memcpy(output, rgbptr->black, rgbsize);
241
242 output += rgbptr->num_channels;
243 continue;
244 }
245 else if (rgb == 0xffffff && rgbptr->cache_init)
246 {
247 /*
248 * Copy white color and continue...
249 */
250
251 memcpy(output, rgbptr->white, rgbsize);
252
253 output += rgbptr->num_channels;
254 continue;
255 }
256
257 /*
258 * Nope, figure this one out on our own...
259 */
260
261 ri = rgbptr->cube_index[r];
262 rm0 = rgbptr->cube_mult[r];
263 rm1 = 256 - rm0;
264
265 gi = rgbptr->cube_index[g];
266 gm0 = rgbptr->cube_mult[g];
267 gm1 = 256 - gm0;
268
269 bi = rgbptr->cube_index[b];
270 bm0 = rgbptr->cube_mult[b];
271 bm1 = 256 - bm0;
272
273 color = rgbptr->colors[ri][gi][bi];
274
275 for (i = rgbptr->num_channels; i > 0; i --, color ++)
276 {
277 tempb = (color[0] * bm0 + color[bs] * bm1) / 256;
278 tempg = tempb * gm0;
279 tempb = (color[gs] * gm0 + color[gs + bs] * bm1) / 256;
280 tempg = (tempg + tempb * gm1) / 256;
281
282 tempr = tempg * rm0;
283
284 tempb = (color[rs] * bm0 + color[rs + bs] * bm1) / 256;
285 tempg = tempb * gm0;
286 tempb = (color[rs + gs] * bm0 + color[rs + gs + bs] * bm1) / 256;
287 tempg = (tempg + tempb * gm1) / 256;
288
289 tempr = (tempr + tempg * rm1) / 256;
290
291 if (tempr > 255)
292 *output++ = 255;
293 else if (tempr < 0)
294 *output++ = 0;
295 else
296 *output++ = tempr;
297 }
298 }
299 }
300
301
302 /*
303 * 'cupsRGBLoad()' - Load a RGB color profile from a PPD file.
304 */
305
306 cups_rgb_t * /* O - New color profile */
cupsRGBLoad(ppd_file_t * ppd,const char * colormodel,const char * media,const char * resolution)307 cupsRGBLoad(ppd_file_t *ppd, /* I - PPD file */
308 const char *colormodel, /* I - Color model */
309 const char *media, /* I - Media type */
310 const char *resolution) /* I - Resolution */
311 {
312 int i, /* Looping var */
313 cube_size, /* Size of color lookup cube */
314 num_channels, /* Number of color channels */
315 num_samples; /* Number of color samples */
316 cups_sample_t *samples; /* Color samples */
317 float values[7]; /* Color sample values */
318 char spec[PPD_MAX_NAME]; /* Profile name */
319 ppd_attr_t *attr; /* Attribute from PPD file */
320 cups_rgb_t *rgbptr; /* RGB color profile */
321
322
323 /*
324 * Find the following attributes:
325 *
326 * cupsRGBProfile - Specifies the cube size, number of channels, and
327 * number of samples
328 * cupsRGBSample - Specifies an RGB to CMYK color sample
329 */
330
331 if ((attr = cupsFindAttr(ppd, "cupsRGBProfile", colormodel, media,
332 resolution, spec, sizeof(spec))) == NULL)
333 {
334 fputs("DEBUG2: No cupsRGBProfile attribute found for the current settings!\n", stderr);
335 return (NULL);
336 }
337
338 if (!attr->value || sscanf(attr->value, "%d%d%d", &cube_size, &num_channels,
339 &num_samples) != 3)
340 {
341 fprintf(stderr, "ERROR: Bad cupsRGBProfile attribute \'%s\'!\n",
342 attr->value ? attr->value : "(null)");
343 return (NULL);
344 }
345
346 if (cube_size < 2 || cube_size > 16 ||
347 num_channels < 1 || num_channels > CUPS_MAX_RGB ||
348 num_samples != (cube_size * cube_size * cube_size))
349 {
350 fprintf(stderr, "ERROR: Bad cupsRGBProfile attribute \'%s\'!\n",
351 attr->value);
352 return (NULL);
353 }
354
355 /*
356 * Allocate memory for the samples and read them...
357 */
358
359 if ((samples = calloc(num_samples, sizeof(cups_sample_t))) == NULL)
360 {
361 fputs("ERROR: Unable to allocate memory for RGB profile!\n", stderr);
362 return (NULL);
363 }
364
365 /*
366 * Read all of the samples...
367 */
368
369 for (i = 0; i < num_samples; i ++)
370 if ((attr = ppdFindNextAttr(ppd, "cupsRGBSample", spec)) == NULL)
371 break;
372 else if (!attr->value)
373 {
374 fputs("ERROR: Bad cupsRGBSample value!\n", stderr);
375 break;
376 }
377 else if (sscanf(attr->value, "%f%f%f%f%f%f%f", values + 0,
378 values + 1, values + 2, values + 3, values + 4, values + 5,
379 values + 6) != (3 + num_channels))
380 {
381 fputs("ERROR: Bad cupsRGBSample value!\n", stderr);
382 break;
383 }
384 else
385 {
386 samples[i].rgb[0] = (int)(255.0 * values[0] + 0.5);
387 samples[i].rgb[1] = (int)(255.0 * values[1] + 0.5);
388 samples[i].rgb[2] = (int)(255.0 * values[2] + 0.5);
389 samples[i].colors[0] = (int)(255.0 * values[3] + 0.5);
390 if (num_channels > 1)
391 samples[i].colors[1] = (int)(255.0 * values[4] + 0.5);
392 if (num_channels > 2)
393 samples[i].colors[2] = (int)(255.0 * values[5] + 0.5);
394 if (num_channels > 3)
395 samples[i].colors[3] = (int)(255.0 * values[6] + 0.5);
396 }
397
398 /*
399 * If everything went OK, create the color profile...
400 */
401
402 if (i == num_samples)
403 rgbptr = cupsRGBNew(num_samples, samples, cube_size, num_channels);
404 else
405 rgbptr = NULL;
406
407 /*
408 * Free the temporary sample array and return...
409 */
410
411 free(samples);
412
413 return (rgbptr);
414 }
415
416
417 /*
418 * 'cupsRGBNew()' - Create a new RGB color separation.
419 */
420
421 cups_rgb_t * /* O - New color separation or NULL */
cupsRGBNew(int num_samples,cups_sample_t * samples,int cube_size,int num_channels)422 cupsRGBNew(int num_samples, /* I - Number of samples */
423 cups_sample_t *samples, /* I - Samples */
424 int cube_size, /* I - Size of LUT cube */
425 int num_channels) /* I - Number of color components */
426 {
427 cups_rgb_t *rgbptr; /* New color separation */
428 int i; /* Looping var */
429 int r, g, b; /* Current RGB */
430 int tempsize; /* Sibe of main arrays */
431 unsigned char *tempc; /* Pointer for C arrays */
432 unsigned char **tempb ; /* Pointer for Z arrays */
433 unsigned char ***tempg; /* Pointer for Y arrays */
434 unsigned char ****tempr; /* Pointer for X array */
435 unsigned char rgb[3]; /* Temporary RGB value */
436
437
438 /*
439 * Range-check the input...
440 */
441
442 if (!samples || num_samples != (cube_size * cube_size * cube_size) ||
443 num_channels <= 0 || num_channels > CUPS_MAX_RGB)
444 return (NULL);
445
446 /*
447 * Allocate memory for the separation...
448 */
449
450 if ((rgbptr = calloc(1, sizeof(cups_rgb_t))) == NULL)
451 return (NULL);
452
453 /*
454 * Allocate memory for the samples and the LUT cube...
455 */
456
457 tempsize = cube_size * cube_size * cube_size; /* FUTURE: num_samples < cs^3 */
458
459 tempc = calloc(tempsize, num_channels);
460 tempb = calloc(tempsize, sizeof(unsigned char *));
461 tempg = calloc(cube_size * cube_size, sizeof(unsigned char **));
462 tempr = calloc(cube_size, sizeof(unsigned char ***));
463
464 if (tempc == NULL || tempb == NULL || tempg == NULL || tempr == NULL)
465 {
466 free(rgbptr);
467
468 if (tempc)
469 free(tempc);
470
471 if (tempb)
472 free(tempb);
473
474 if (tempg)
475 free(tempg);
476
477 if (tempr)
478 free(tempr);
479
480 return (NULL);
481 }
482
483 /*
484 * Fill in the arrays...
485 */
486
487 for (i = 0, r = 0; r < cube_size; r ++)
488 {
489 tempr[r] = tempg + r * cube_size;
490
491 for (g = 0; g < cube_size; g ++)
492 {
493 tempr[r][g] = tempb + i;
494
495 for (b = 0; b < cube_size; b ++, i ++)
496 tempr[r][g][b] = tempc + i * num_channels;
497 }
498 }
499
500 for (i = 0; i < num_samples; i ++)
501 {
502 r = samples[i].rgb[0] * (cube_size - 1) / 255;
503 g = samples[i].rgb[1] * (cube_size - 1) / 255;
504 b = samples[i].rgb[2] * (cube_size - 1) / 255;
505
506 memcpy(tempr[r][g][b], samples[i].colors, num_channels);
507 }
508
509 rgbptr->cube_size = cube_size;
510 rgbptr->num_channels = num_channels;
511 rgbptr->colors = tempr;
512
513 /*
514 * Generate the lookup tables for the cube indices and multipliers...
515 */
516
517 for (i = 0; i < 256; i ++)
518 {
519 rgbptr->cube_index[i] = i * (cube_size - 1) / 256;
520
521 if (i == 0)
522 rgbptr->cube_mult[i] = 256;
523 else
524 rgbptr->cube_mult[i] = 255 - ((i * (cube_size - 1)) & 255);
525 }
526
527 /*
528 * Generate the black and white cache values for the separation...
529 */
530
531 rgb[0] = 0;
532 rgb[1] = 0;
533 rgb[2] = 0;
534
535 cupsRGBDoRGB(rgbptr, rgb, rgbptr->black, 1);
536
537 rgb[0] = 255;
538 rgb[1] = 255;
539 rgb[2] = 255;
540
541 cupsRGBDoRGB(rgbptr, rgb, rgbptr->white, 1);
542
543 rgbptr->cache_init = 1;
544
545 /*
546 * Return the separation...
547 */
548
549 return (rgbptr);
550 }
551
552