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