1 /* colorspace.cpp */
2 /*
3 Copyright 1999, Be Incorporated. All Rights Reserved.
4 This file may be used under the terms of the Be Sample Code License.
5 */
6
7
8 #include <Debug.h>
9 #include <GraphicsDefs.h>
10 #include <InterfaceDefs.h>
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "colorspace.h"
17
18
19 /* expands to BGRA in the output buffer from <whatever> in the input buffer */
20 int
expand_data(color_space from_space,unsigned char * in_data,int rowbytes,unsigned char * out_buf)21 expand_data(color_space from_space, unsigned char* in_data, int rowbytes,
22 unsigned char* out_buf)
23 {
24 ASSERT(in_data != out_buf);
25
26 /* We don't do YUV and friends yet. */
27 /* It's important to replicate the most significant component bits to LSB
28 * when going 15->24 */
29 unsigned char* in_out = out_buf;
30 switch (from_space) {
31 case B_RGB32:
32 while (rowbytes > 3) {
33 out_buf[0] = in_data[0];
34 out_buf[1] = in_data[1];
35 out_buf[2] = in_data[2];
36 out_buf[3] = 255;
37 out_buf += 4;
38 in_data += 4;
39 rowbytes -= 4;
40 }
41 break;
42 case B_RGBA32:
43 memcpy(out_buf, in_data, rowbytes);
44 break;
45 case B_RGB24:
46 while (rowbytes > 2) {
47 out_buf[0] = in_data[0];
48 out_buf[1] = in_data[1];
49 out_buf[2] = in_data[2];
50 out_buf[3] = 255;
51 out_buf += 4;
52 in_data += 3;
53 rowbytes -= 3;
54 }
55 break;
56 case B_RGB15:
57 while (rowbytes > 1) {
58 uint16 val = in_data[0] + (in_data[1] << 8);
59 out_buf[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
60 out_buf[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
61 out_buf[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
62 out_buf[3] = 255;
63 out_buf += 4;
64 in_data += 2;
65 rowbytes -= 2;
66 }
67 break;
68 case B_RGBA15:
69 while (rowbytes > 1) {
70 uint16 val = in_data[0] + (in_data[1] << 8);
71 out_buf[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
72 out_buf[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
73 out_buf[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
74 out_buf[3] = (val & 0x8000) ? 255 : 0;
75 out_buf += 4;
76 in_data += 2;
77 rowbytes -= 2;
78 }
79 break;
80 case B_RGB16:
81 while (rowbytes > 1) {
82 uint16 val = in_data[0] + (in_data[1] << 8);
83 out_buf[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
84 out_buf[1] = ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
85 out_buf[2] = ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
86 out_buf[3] = 255;
87 out_buf += 4;
88 in_data += 2;
89 rowbytes -= 2;
90 }
91 break;
92 case B_RGB32_BIG:
93 while (rowbytes > 3) {
94 out_buf[0] = in_data[3];
95 out_buf[1] = in_data[2];
96 out_buf[2] = in_data[1];
97 out_buf[3] = 255;
98 out_buf += 4;
99 in_data += 4;
100 rowbytes -= 4;
101 }
102 break;
103 case B_RGBA32_BIG:
104 while (rowbytes > 3) {
105 out_buf[0] = in_data[3];
106 out_buf[1] = in_data[2];
107 out_buf[2] = in_data[1];
108 out_buf[3] = in_data[0];
109 out_buf += 4;
110 in_data += 4;
111 rowbytes -= 4;
112 }
113 break;
114 case B_RGB24_BIG:
115 while (rowbytes > 2) {
116 out_buf[0] = in_data[2];
117 out_buf[1] = in_data[1];
118 out_buf[2] = in_data[0];
119 out_buf[3] = 255;
120 out_buf += 4;
121 in_data += 3;
122 rowbytes -= 3;
123 }
124 break;
125 case B_RGB15_BIG:
126 while (rowbytes > 1) {
127 uint16 val = in_data[1] + (in_data[0] << 8);
128 out_buf[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
129 out_buf[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
130 out_buf[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
131 out_buf[3] = 255;
132 out_buf += 4;
133 in_data += 2;
134 rowbytes -= 2;
135 }
136 break;
137 case B_RGBA15_BIG:
138 while (rowbytes > 1) {
139 uint16 val = in_data[1] + (in_data[0] << 8);
140 out_buf[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
141 out_buf[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
142 out_buf[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
143 out_buf[3] = (val & 0x8000) ? 255 : 0;
144 out_buf += 4;
145 in_data += 2;
146 rowbytes -= 2;
147 }
148 break;
149 case B_RGB16_BIG:
150 while (rowbytes > 1) {
151 uint16 val = in_data[1] + (in_data[0] << 8);
152 out_buf[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
153 out_buf[1] = ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
154 out_buf[2] = ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
155 out_buf[3] = 255;
156 out_buf += 4;
157 in_data += 2;
158 rowbytes -= 2;
159 }
160 break;
161 case B_CMAP8: {
162 const color_map* map = system_colors();
163 while (rowbytes > 0) {
164 rgb_color c = map->color_list[in_data[0]];
165 out_buf[0] = c.blue;
166 out_buf[1] = c.green;
167 out_buf[2] = c.red;
168 out_buf[3] = c.alpha;
169 out_buf += 4;
170 in_data += 1;
171 rowbytes -= 1;
172 }
173 } break;
174 case B_GRAY8:
175 while (rowbytes > 0) {
176 unsigned char ch = *in_data;
177 out_buf[0] = ch;
178 out_buf[1] = ch;
179 out_buf[2] = ch;
180 out_buf[3] = 255;
181 out_buf += 4;
182 in_data += 1;
183 rowbytes -= 1;
184 }
185 break;
186 case B_GRAY1:
187 while (rowbytes > 0) { /* expansion 1->32 is pretty good :-) */
188 unsigned char c1 = *in_data;
189 for (int b = 128; b; b = b >> 1) {
190 unsigned char ch;
191 if (c1 & b) {
192 ch = 0;
193 } else {
194 ch = 255;
195 }
196 out_buf[0] = ch;
197 out_buf[1] = ch;
198 out_buf[2] = ch;
199 out_buf[3] = 255;
200 out_buf += 4;
201 }
202 in_data += 1;
203 rowbytes -= 1;
204 }
205 break;
206 case B_CMY24: /* We do the "clean" inversion which doesn't correct
207 for printing ink deficiencies. */
208 while (rowbytes > 2) {
209 out_buf[0] = 255 - in_data[2];
210 out_buf[1] = 255 - in_data[1];
211 out_buf[2] = 255 - in_data[0];
212 out_buf[3] = 255;
213 out_buf += 4;
214 in_data += 3;
215 rowbytes -= 3;
216 }
217 break;
218 case B_CMY32:
219 while (rowbytes > 3) {
220 out_buf[0] = 255 - in_data[2];
221 out_buf[1] = 255 - in_data[1];
222 out_buf[2] = 255 - in_data[0];
223 out_buf[3] = 255;
224 out_buf += 4;
225 in_data += 4;
226 rowbytes -= 4;
227 }
228 break;
229 case B_CMYA32:
230 while (rowbytes > 3) {
231 out_buf[0] = 255 - in_data[2];
232 out_buf[1] = 255 - in_data[1];
233 out_buf[2] = 255 - in_data[0];
234 out_buf[3] = in_data[3];
235 out_buf += 4;
236 in_data += 4;
237 rowbytes -= 4;
238 }
239 break;
240 case B_CMYK32: /* We assume uniform gray removal, and no
241 under-color-removal. */
242 while (rowbytes > 3) {
243 int comp = 255 - in_data[2] - in_data[3];
244 out_buf[0] = comp < 0 ? 0 : comp;
245 comp = 255 - in_data[1] - in_data[3];
246 out_buf[1] = comp < 0 ? 0 : comp;
247 comp = 255 - in_data[0] - in_data[3];
248 out_buf[2] = comp < 0 ? 0 : comp;
249 out_buf[3] = 255;
250 out_buf += 4;
251 in_data += 4;
252 rowbytes -= 4;
253 }
254 break;
255 default:
256 break;
257 }
258 return out_buf - in_out;
259 }
260
261
262 int
collapse_data(unsigned char * in_buf,int num_bytes,color_space out_space,unsigned char * out_buf)263 collapse_data(unsigned char* in_buf, int num_bytes, color_space out_space,
264 unsigned char* out_buf)
265 {
266 ASSERT(in_buf != out_buf);
267
268 unsigned char* in_out = out_buf;
269 /* We could increase perceived image quality of down conversions by
270 * implementing */
271 /* dithering. However, someone might want to operate on the images after
272 */
273 /* conversion, in which case dithering would be un-good. Besides, good
274 * error */
275 /* diffusion requires more than one scan line to propagate errors to. */
276 switch (out_space) {
277 case B_RGB32:
278 memcpy(out_buf, in_buf, num_bytes);
279 break;
280 case B_RGBA32:
281 memcpy(out_buf, in_buf, num_bytes);
282 break;
283 case B_RGB24:
284 while (num_bytes > 3) {
285 out_buf[0] = in_buf[0];
286 out_buf[1] = in_buf[1];
287 out_buf[2] = in_buf[2];
288 out_buf += 3;
289 in_buf += 4;
290 num_bytes -= 4;
291 }
292 break;
293 case B_RGB16:
294 while (num_bytes > 3) {
295 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 3) & 0x7e0)
296 | ((in_buf[2] << 8) & 0xf800);
297 out_buf[0] = val;
298 out_buf[1] = val >> 8;
299 out_buf += 2;
300 in_buf += 4;
301 num_bytes -= 4;
302 }
303 break;
304 case B_RGB15:
305 while (num_bytes > 3) {
306 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 2) & 0x3e0)
307 | ((in_buf[2] << 7) & 0x7c00) | 0x8000;
308 out_buf[0] = val;
309 out_buf[1] = val >> 8;
310 out_buf += 2;
311 in_buf += 4;
312 num_bytes -= 4;
313 }
314 break;
315 case B_RGBA15:
316 while (num_bytes > 3) {
317 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 2) & 0x3e0)
318 | ((in_buf[2] << 7) & 0x7c00);
319 if (in_buf[3] > 127) {
320 val = val | 0x8000;
321 }
322 out_buf[0] = val;
323 out_buf[1] = val >> 8;
324 out_buf += 2;
325 in_buf += 4;
326 num_bytes -= 4;
327 }
328 break;
329 case B_CMAP8: {
330 const color_map* map = system_colors();
331 while (num_bytes > 3) {
332 if (in_buf[3] < 128) {
333 out_buf[0] = B_TRANSPARENT_8_BIT;
334 } else {
335 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 2) & 0x3e0)
336 | ((in_buf[2] << 7) & 0x7c00);
337 out_buf[0] = map->index_map[val];
338 }
339 out_buf += 1;
340 in_buf += 4;
341 num_bytes -= 4;
342 }
343 } break;
344 case B_GRAY8:
345 while (num_bytes > 3) { /* There are better algorithms than Y =
346 .25B+.50G+.25R */
347 /* but hardly faster -- and it's still better than (B+G+R)/3 !
348 */
349 out_buf[0] = (in_buf[0] + in_buf[1] * 2 + in_buf[2]) >> 2;
350 out_buf += 1;
351 in_buf += 4;
352 num_bytes -= 4;
353 }
354 break;
355 case B_GRAY1: {
356 uchar ob = 0;
357 int cnt = 0;
358 uchar c = 0;
359 while (num_bytes > 3) {
360 if (cnt == 8) {
361 out_buf[0] = ob;
362 out_buf += 1;
363 cnt = 0;
364 ob = 0;
365 }
366 c = ((in_buf[0] + in_buf[1] * 2 + in_buf[2]) & 0x200)
367 >> (2 + cnt);
368 ob = ob | c;
369 cnt++;
370 in_buf += 4;
371 num_bytes -= 4;
372 }
373 if (cnt > 0) {
374 out_buf[0] = ob;
375 out_buf += 1;
376 }
377 } break;
378 /* big endian version, when the encoding is not endianess independant */
379 case B_RGB32_BIG:
380 while (num_bytes > 3) {
381 out_buf[3] = in_buf[0];
382 out_buf[2] = in_buf[1];
383 out_buf[1] = in_buf[2];
384 out_buf += 4;
385 in_buf += 4;
386 num_bytes -= 4;
387 }
388 break;
389 case B_RGBA32_BIG:
390 while (num_bytes > 3) {
391 out_buf[3] = in_buf[0];
392 out_buf[2] = in_buf[1];
393 out_buf[1] = in_buf[2];
394 out_buf[0] = in_buf[3];
395 out_buf += 4;
396 in_buf += 4;
397 num_bytes -= 4;
398 }
399 break;
400 case B_RGB24_BIG:
401 while (num_bytes > 3) {
402 out_buf[2] = in_buf[0];
403 out_buf[1] = in_buf[1];
404 out_buf[0] = in_buf[2];
405 out_buf += 3;
406 in_buf += 4;
407 num_bytes -= 4;
408 }
409 break;
410 case B_RGB16_BIG:
411 while (num_bytes > 3) {
412 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 3) & 0x7e0)
413 | ((in_buf[2] << 8) & 0xf800);
414 out_buf[0] = val >> 8;
415 out_buf[1] = val;
416 out_buf += 2;
417 in_buf += 4;
418 num_bytes -= 4;
419 }
420 break;
421 case B_RGB15_BIG:
422 while (num_bytes > 3) {
423 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 2) & 0x3e0)
424 | ((in_buf[2] << 7) & 0x7c00) | 0x8000;
425 out_buf[0] = val >> 8;
426 out_buf[1] = val;
427 out_buf += 2;
428 in_buf += 4;
429 num_bytes -= 4;
430 }
431 break;
432 case B_RGBA15_BIG:
433 while (num_bytes > 3) {
434 uint16 val = (in_buf[0] >> 3) | ((in_buf[1] << 2) & 0x3e0)
435 | ((in_buf[2] << 7) & 0x7c00);
436 if (in_buf[3] > 127) {
437 val = val | 0x8000;
438 }
439 out_buf[0] = val >> 8;
440 out_buf[1] = val;
441 out_buf += 2;
442 in_buf += 4;
443 num_bytes -= 4;
444 }
445 break;
446 case B_CMY24:
447 while (num_bytes > 3) {
448 out_buf[0] = 255 - in_buf[2];
449 out_buf[1] = 255 - in_buf[1];
450 out_buf[2] = 255 - in_buf[0];
451 out_buf += 3;
452 in_buf += 4;
453 num_bytes -= 4;
454 }
455 break;
456 case B_CMY32:
457 while (num_bytes > 3) {
458 out_buf[0] = 255 - in_buf[2];
459 out_buf[1] = 255 - in_buf[1];
460 out_buf[2] = 255 - in_buf[0];
461 out_buf += 4;
462 in_buf += 4;
463 num_bytes -= 4;
464 }
465 break;
466 case B_CMYA32:
467 while (num_bytes > 3) {
468 out_buf[0] = 255 - in_buf[2];
469 out_buf[1] = 255 - in_buf[1];
470 out_buf[2] = 255 - in_buf[0];
471 out_buf[3] = in_buf[3];
472 out_buf += 4;
473 in_buf += 4;
474 num_bytes -= 4;
475 }
476 break;
477 case B_CMYK32:
478 while (num_bytes > 3) { /* We do direct gray removal */
479 int c = 255 - in_buf[2];
480 int m = 255 - in_buf[1];
481 int y = 255 - in_buf[0];
482 int k = (c > m) ? ((y > c) ? y : c) : ((y > m) ? y : m);
483 out_buf[0] = c - k;
484 out_buf[1] = m - k;
485 out_buf[2] = y - k;
486 out_buf[3] = k;
487 out_buf += 4;
488 in_buf += 4;
489 num_bytes -= 4;
490 }
491 break;
492 default:
493 break;
494 }
495 return out_buf - in_out;
496 }
497
498
499 #if DEBUG_DATA
500 static void
print_data(unsigned char * ptr,int n)501 print_data(unsigned char* ptr, int n)
502 {
503 while (n-- > 0) {
504 printf("%02x ", *(ptr++));
505 }
506 printf("\n");
507 }
508 #endif
509
510
511 status_t
convert_space(color_space in_space,color_space out_space,unsigned char * in_data,int rowbytes,unsigned char * out_data)512 convert_space(color_space in_space, color_space out_space,
513 unsigned char* in_data, int rowbytes, unsigned char* out_data)
514 {
515 ASSERT(in_data != out_data);
516
517 /* Instead of coding each transformation separately, which would create
518 */
519 /* a very large number of conversion functions, we write one function to
520 */
521 /* convert to RGBA32, and another function to convert from RGBA32, and */
522 /* put them together to get a manageable program, at a slight expense in
523 */
524 /* conversion speed. */
525
526 int n;
527 #if DEBUG_DATA
528 printf("convert_space(%x, %x, %x)\n", in_space, out_space, rowbytes);
529 printf("raw data: ");
530 print_data(in_data, rowbytes);
531 #endif
532 /* If we convert from a format to itself, well... */
533 if (in_space == out_space) {
534 memcpy(out_data, in_data, rowbytes);
535 return B_OK;
536 }
537 /* When the input format is RGBA32, we don't need the first conversion.
538 */
539 if (in_space == B_RGBA32) {
540 n = collapse_data(in_data, rowbytes, out_space, out_data);
541 #if DEBUG_DATA
542 printf("collapsed data: ");
543 print_data(out_data, n);
544 #endif
545 return B_OK;
546 }
547 /* When the output format is RGBA32, we don't need any second conversion.
548 */
549 if (out_space == B_RGB32 || out_space == B_RGBA32) {
550 n = expand_data(in_space, in_data, rowbytes, out_data);
551 #if DEBUG_DATA
552 printf("expanded data: ");
553 print_data(out_data, n);
554 #endif
555 return B_OK;
556 }
557 /* Figure out byte expansion rate -- usually isn't more than 4 */
558 int mul = 4;
559 if (in_space == B_GRAY1) {
560 mul = 32;
561 }
562 unsigned char* buf = (unsigned char*) malloc(rowbytes * mul);
563 if (buf == NULL) {
564 /* oops! */
565 return B_NO_MEMORY;
566 }
567 n = expand_data(in_space, in_data, rowbytes, buf);
568 #if DEBUG_DATA
569 printf("expanded data: ");
570 print_data(out_data, n);
571 #endif
572 n = collapse_data(buf, n, out_space, out_data);
573 #if DEBUG_DATA
574 printf("collapsed data: ");
575 print_data(out_data, n);
576 #endif
577 free(buf);
578 return B_OK;
579 }
580
581
582 /* Figure out what the rowbytes is for a given width in a given color space.
583 */
584 /* Rowbytes is bytes per pixel times width, rounded up to nearest multiple
585 * of 4. */
586 int
calc_rowbytes(color_space space,int width)587 calc_rowbytes(color_space space, int width)
588 {
589 int v = width * 4;
590 switch (space) {
591 default:
592 /* 4 is fine */
593 break;
594 case B_RGB24:
595 case B_CMY24:
596 case B_RGB24_BIG:
597 v = width * 3;
598 break;
599 case B_RGB15:
600 case B_RGBA15:
601 case B_RGB16:
602 case B_RGB15_BIG:
603 case B_RGBA15_BIG:
604 case B_RGB16_BIG:
605 v = width * 2;
606 break;
607 case B_CMAP8:
608 case B_GRAY8:
609 v = width;
610 break;
611 case B_GRAY1:
612 v = (width + 7) / 8; /* whole bytes only, please */
613 break;
614 }
615 v = (v + 3) & ~3;
616 return v;
617 }
618