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 */
20int
21expand_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
262int
263collapse_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
500static void
501print_data(unsigned char* ptr, int n)
502{
503	while (n-- > 0) {
504		printf("%02x ", *(ptr++));
505	}
506	printf("\n");
507}
508#endif
509
510
511status_t
512convert_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.	*/
586int
587calc_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