xref: /haiku/src/add-ons/translators/ppm/colorspace.cpp (revision 58913f60e8de5021abf1913804364f0810b0dd2f)
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