]> git.ipfire.org Git - thirdparty/cups.git/blob - driver/rgb.c
Merge changes from CUPS 1.5.1-r9875.
[thirdparty/cups.git] / driver / rgb.c
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 */