ohmu
·
2026-10-15
1/*
2 * Copyright (c) 2014-2019 Hayaki Saito
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the "Software"), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 * the Software, and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22#include "config.h"
23
24/* STDC_HEADERS */
25#include <stdio.h>
26#include <stdlib.h>
27
28#if HAVE_MEMORY_H
29# include <memory.h>
30#endif /* HAVE_MEMORY_H */
31
32#include <sixel.h>
33
34static void
35get_rgb(unsigned char const *data,
36 int const pixelformat,
37 int depth,
38 unsigned char *r,
39 unsigned char *g,
40 unsigned char *b)
41{
42 unsigned int pixels = 0;
43#if SWAP_BYTES
44 unsigned int low;
45 unsigned int high;
46#endif
47 int count = 0;
48
49 while (count < depth) {
50 pixels = *(data + count) | (pixels << 8);
51 count++;
52 }
53
54 /* TODO: we should swap bytes (only necessary on LSByte first hardware?) */
55#if SWAP_BYTES
56 if (depth == 2) {
57 low = pixels & 0xff;
58 high = (pixels >> 8) & 0xff;
59 pixels = (low << 8) | high;
60 }
61#endif
62
63 switch (pixelformat) {
64 case SIXEL_PIXELFORMAT_RGB555:
65 *r = ((pixels >> 10) & 0x1f) << 3;
66 *g = ((pixels >> 5) & 0x1f) << 3;
67 *b = ((pixels >> 0) & 0x1f) << 3;
68 break;
69 case SIXEL_PIXELFORMAT_RGB565:
70 *r = ((pixels >> 11) & 0x1f) << 3;
71 *g = ((pixels >> 5) & 0x3f) << 2;
72 *b = ((pixels >> 0) & 0x1f) << 3;
73 break;
74 case SIXEL_PIXELFORMAT_RGB888:
75 *r = (pixels >> 16) & 0xff;
76 *g = (pixels >> 8) & 0xff;
77 *b = (pixels >> 0) & 0xff;
78 break;
79 case SIXEL_PIXELFORMAT_BGR555:
80 *r = ((pixels >> 0) & 0x1f) << 3;
81 *g = ((pixels >> 5) & 0x1f) << 3;
82 *b = ((pixels >> 10) & 0x1f) << 3;
83 break;
84 case SIXEL_PIXELFORMAT_BGR565:
85 *r = ((pixels >> 0) & 0x1f) << 3;
86 *g = ((pixels >> 5) & 0x3f) << 2;
87 *b = ((pixels >> 11) & 0x1f) << 3;
88 break;
89 case SIXEL_PIXELFORMAT_BGR888:
90 *r = (pixels >> 0) & 0xff;
91 *g = (pixels >> 8) & 0xff;
92 *b = (pixels >> 16) & 0xff;
93 break;
94 case SIXEL_PIXELFORMAT_RGBA8888:
95 *r = (pixels >> 24) & 0xff;
96 *g = (pixels >> 16) & 0xff;
97 *b = (pixels >> 8) & 0xff;
98 break;
99 case SIXEL_PIXELFORMAT_ARGB8888:
100 *r = (pixels >> 16) & 0xff;
101 *g = (pixels >> 8) & 0xff;
102 *b = (pixels >> 0) & 0xff;
103 break;
104 case SIXEL_PIXELFORMAT_BGRA8888:
105 *r = (pixels >> 8) & 0xff;
106 *g = (pixels >> 16) & 0xff;
107 *b = (pixels >> 24) & 0xff;
108 break;
109 case SIXEL_PIXELFORMAT_ABGR8888:
110 *r = (pixels >> 0) & 0xff;
111 *g = (pixels >> 8) & 0xff;
112 *b = (pixels >> 16) & 0xff;
113 break;
114 case SIXEL_PIXELFORMAT_GA88:
115 *r = *g = *b = (pixels >> 8) & 0xff;
116 break;
117 case SIXEL_PIXELFORMAT_G8:
118 case SIXEL_PIXELFORMAT_AG88:
119 *r = *g = *b = pixels & 0xff;
120 break;
121 default:
122 *r = *g = *b = 0;
123 break;
124 }
125}
126
127
128SIXELAPI int
129sixel_helper_compute_depth(int pixelformat)
130{
131 int depth = (-1); /* unknown */
132
133 switch (pixelformat) {
134 case SIXEL_PIXELFORMAT_ARGB8888:
135 case SIXEL_PIXELFORMAT_RGBA8888:
136 case SIXEL_PIXELFORMAT_ABGR8888:
137 case SIXEL_PIXELFORMAT_BGRA8888:
138 depth = 4;
139 break;
140 case SIXEL_PIXELFORMAT_RGB888:
141 case SIXEL_PIXELFORMAT_BGR888:
142 depth = 3;
143 break;
144 case SIXEL_PIXELFORMAT_RGB555:
145 case SIXEL_PIXELFORMAT_RGB565:
146 case SIXEL_PIXELFORMAT_BGR555:
147 case SIXEL_PIXELFORMAT_BGR565:
148 case SIXEL_PIXELFORMAT_AG88:
149 case SIXEL_PIXELFORMAT_GA88:
150 depth = 2;
151 break;
152 case SIXEL_PIXELFORMAT_G1:
153 case SIXEL_PIXELFORMAT_G2:
154 case SIXEL_PIXELFORMAT_G4:
155 case SIXEL_PIXELFORMAT_G8:
156 case SIXEL_PIXELFORMAT_PAL1:
157 case SIXEL_PIXELFORMAT_PAL2:
158 case SIXEL_PIXELFORMAT_PAL4:
159 case SIXEL_PIXELFORMAT_PAL8:
160 depth = 1;
161 break;
162 default:
163 break;
164 }
165
166 return depth;
167}
168
169
170static void
171expand_rgb(unsigned char *dst,
172 unsigned char const *src,
173 int width, int height,
174 int pixelformat, int depth)
175{
176 int x;
177 int y;
178 int dst_offset;
179 int src_offset;
180 unsigned char r, g, b;
181
182 for (y = 0; y < height; y++) {
183 for (x = 0; x < width; x++) {
184 src_offset = depth * (y * width + x);
185 dst_offset = 3 * (y * width + x);
186 get_rgb(src + src_offset, pixelformat, depth, &r, &g, &b);
187
188 *(dst + dst_offset + 0) = r;
189 *(dst + dst_offset + 1) = g;
190 *(dst + dst_offset + 2) = b;
191 }
192 }
193}
194
195
196static SIXELSTATUS
197expand_palette(unsigned char *dst, unsigned char const *src,
198 int width, int height, int const pixelformat)
199{
200 SIXELSTATUS status = SIXEL_FALSE;
201 int x;
202 int y;
203 int i;
204 int bpp; /* bit per plane */
205
206 switch (pixelformat) {
207 case SIXEL_PIXELFORMAT_PAL1:
208 case SIXEL_PIXELFORMAT_G1:
209 bpp = 1;
210 break;
211 case SIXEL_PIXELFORMAT_PAL2:
212 case SIXEL_PIXELFORMAT_G2:
213 bpp = 2;
214 break;
215 case SIXEL_PIXELFORMAT_PAL4:
216 case SIXEL_PIXELFORMAT_G4:
217 bpp = 4;
218 break;
219 case SIXEL_PIXELFORMAT_PAL8:
220 case SIXEL_PIXELFORMAT_G8:
221 for (i = 0; i < width * height; ++i, ++src) {
222 *dst++ = *src;
223 }
224 status = SIXEL_OK;
225 goto end;
226 default:
227 status = SIXEL_BAD_ARGUMENT;
228 sixel_helper_set_additional_message(
229 "expand_palette: invalid pixelformat.");
230 goto end;
231 }
232
233#if HAVE_DEBUG
234 fprintf(stderr, "expanding PAL%d to PAL8...\n", bpp);
235#endif
236
237 for (y = 0; y < height; ++y) {
238 for (x = 0; x < width * bpp / 8; ++x) {
239 for (i = 0; i < 8 / bpp; ++i) {
240 *dst++ = *src >> (8 / bpp - 1 - i) * bpp & ((1 << bpp) - 1);
241 }
242 src++;
243 }
244 x = width - x * 8 / bpp;
245 if (x > 0) {
246 for (i = 0; i < x; ++i) {
247 *dst++ = *src >> (8 - (i + 1) * bpp) & ((1 << bpp) - 1);
248 }
249 src++;
250 }
251 }
252
253 status = SIXEL_OK;
254
255end:
256 return status;
257}
258
259
260SIXELAPI SIXELSTATUS
261sixel_helper_normalize_pixelformat(
262 unsigned char /* out */ *dst, /* destination buffer */
263 int /* out */ *dst_pixelformat, /* converted pixelformat */
264 unsigned char const /* in */ *src, /* source pixels */
265 int /* in */ src_pixelformat, /* format of source image */
266 int /* in */ width, /* width of source image */
267 int /* in */ height) /* height of source image */
268{
269 SIXELSTATUS status = SIXEL_FALSE;
270
271 switch (src_pixelformat) {
272 case SIXEL_PIXELFORMAT_G8:
273 expand_rgb(dst, src, width, height, src_pixelformat, 1);
274 *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
275 break;
276 case SIXEL_PIXELFORMAT_RGB565:
277 case SIXEL_PIXELFORMAT_RGB555:
278 case SIXEL_PIXELFORMAT_BGR565:
279 case SIXEL_PIXELFORMAT_BGR555:
280 case SIXEL_PIXELFORMAT_GA88:
281 case SIXEL_PIXELFORMAT_AG88:
282 expand_rgb(dst, src, width, height, src_pixelformat, 2);
283 *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
284 break;
285 case SIXEL_PIXELFORMAT_RGB888:
286 case SIXEL_PIXELFORMAT_BGR888:
287 expand_rgb(dst, src, width, height, src_pixelformat, 3);
288 *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
289 break;
290 case SIXEL_PIXELFORMAT_RGBA8888:
291 case SIXEL_PIXELFORMAT_ARGB8888:
292 case SIXEL_PIXELFORMAT_BGRA8888:
293 case SIXEL_PIXELFORMAT_ABGR8888:
294 expand_rgb(dst, src, width, height, src_pixelformat, 4);
295 *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
296 break;
297 case SIXEL_PIXELFORMAT_PAL1:
298 case SIXEL_PIXELFORMAT_PAL2:
299 case SIXEL_PIXELFORMAT_PAL4:
300 *dst_pixelformat = SIXEL_PIXELFORMAT_PAL8;
301 status = expand_palette(dst, src, width, height, src_pixelformat);
302 if (SIXEL_FAILED(status)) {
303 goto end;
304 }
305 break;
306 case SIXEL_PIXELFORMAT_G1:
307 case SIXEL_PIXELFORMAT_G2:
308 case SIXEL_PIXELFORMAT_G4:
309 *dst_pixelformat = SIXEL_PIXELFORMAT_G8;
310 status = expand_palette(dst, src, width, height, src_pixelformat);
311 if (SIXEL_FAILED(status)) {
312 goto end;
313 }
314 break;
315 case SIXEL_PIXELFORMAT_PAL8:
316 memcpy(dst, src, (size_t)(width * height));
317 *dst_pixelformat = src_pixelformat;
318 break;
319 default:
320 status = SIXEL_BAD_ARGUMENT;
321 goto end;
322 }
323
324 status = SIXEL_OK;
325
326end:
327 return status;
328}
329
330
331#if HAVE_TESTS
332static int
333test1(void)
334{
335 unsigned char dst[3];
336 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
337 int src_pixelformat = SIXEL_PIXELFORMAT_RGB888;
338 unsigned char src[] = { 0x46, 0xf3, 0xe5 };
339 int ret = 0;
340
341 int nret = EXIT_FAILURE;
342
343 ret = sixel_helper_normalize_pixelformat(dst,
344 &dst_pixelformat,
345 src,
346 src_pixelformat,
347 1,
348 1);
349 if (ret != 0) {
350 goto error;
351 }
352 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
353 goto error;
354 }
355 if ((dst[0] << 16 | dst[1] << 8 | dst[2]) != (src[0] << 16 | src[1] << 8 | src[2])) {
356 goto error;
357 }
358 return EXIT_SUCCESS;
359
360error:
361 perror("test1");
362 return nret;
363}
364
365
366static int
367test2(void)
368{
369 unsigned char dst[3];
370 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
371 int src_pixelformat = SIXEL_PIXELFORMAT_RGB555;
372 unsigned char src[] = { 0x47, 0x9c };
373 int ret = 0;
374
375 int nret = EXIT_FAILURE;
376
377 ret = sixel_helper_normalize_pixelformat(dst,
378 &dst_pixelformat,
379 src,
380 src_pixelformat,
381 1,
382 1);
383 if (ret != 0) {
384 goto error;
385 }
386 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
387 goto error;
388 }
389 if ((dst[0] >> 3 << 10 | dst[1] >> 3 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) {
390 goto error;
391 }
392 return EXIT_SUCCESS;
393
394error:
395 perror("test2");
396 return nret;
397}
398
399
400static int
401test3(void)
402{
403 unsigned char dst[3];
404 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
405 int src_pixelformat = SIXEL_PIXELFORMAT_RGB565;
406 unsigned char src[] = { 0x47, 0x9c };
407 int ret = 0;
408
409 int nret = EXIT_FAILURE;
410
411 ret = sixel_helper_normalize_pixelformat(dst,
412 &dst_pixelformat,
413 src,
414 src_pixelformat,
415 1,
416 1);
417 if (ret != 0) {
418 goto error;
419 }
420 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
421 goto error;
422 }
423 if ((dst[0] >> 3 << 11 | dst[1] >> 2 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) {
424 goto error;
425 }
426 return EXIT_SUCCESS;
427
428error:
429 perror("test3");
430 return nret;
431}
432
433
434static int
435test4(void)
436{
437 unsigned char dst[3];
438 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
439 int src_pixelformat = SIXEL_PIXELFORMAT_BGR888;
440 unsigned char src[] = { 0x46, 0xf3, 0xe5 };
441 int ret = 0;
442
443 int nret = EXIT_FAILURE;
444
445 ret = sixel_helper_normalize_pixelformat(dst,
446 &dst_pixelformat,
447 src,
448 src_pixelformat,
449 1,
450 1);
451 if (ret != 0) {
452 goto error;
453 }
454 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
455 goto error;
456 }
457 if ((dst[2] << 16 | dst[1] << 8 | dst[0]) != (src[0] << 16 | src[1] << 8 | src[2])) {
458 goto error;
459 }
460 return EXIT_SUCCESS;
461
462error:
463 perror("test4");
464 return nret;
465}
466
467
468static int
469test5(void)
470{
471 unsigned char dst[3];
472 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
473 int src_pixelformat = SIXEL_PIXELFORMAT_BGR555;
474 unsigned char src[] = { 0x23, 0xc8 };
475 int ret = 0;
476
477 int nret = EXIT_FAILURE;
478
479 ret = sixel_helper_normalize_pixelformat(dst,
480 &dst_pixelformat,
481 src,
482 src_pixelformat,
483 1,
484 1);
485 if (ret != 0) {
486 goto error;
487 }
488 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
489 goto error;
490 }
491 if ((dst[2] >> 3 << 10 | dst[1] >> 3 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
492 goto error;
493 }
494 return EXIT_SUCCESS;
495
496error:
497 perror("test5");
498 return nret;
499}
500
501
502static int
503test6(void)
504{
505 unsigned char dst[3];
506 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
507 int src_pixelformat = SIXEL_PIXELFORMAT_BGR565;
508 unsigned char src[] = { 0x47, 0x88 };
509 int ret = 0;
510
511 int nret = EXIT_FAILURE;
512
513 ret = sixel_helper_normalize_pixelformat(dst,
514 &dst_pixelformat,
515 src,
516 src_pixelformat,
517 1,
518 1);
519 if (ret != 0) {
520 goto error;
521 }
522 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
523 goto error;
524 }
525 if ((dst[2] >> 3 << 11 | dst[1] >> 2 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
526 goto error;
527 }
528 return EXIT_SUCCESS;
529
530error:
531 perror("test6");
532 return nret;
533}
534
535
536static int
537test7(void)
538{
539 unsigned char dst[3];
540 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
541 int src_pixelformat = SIXEL_PIXELFORMAT_AG88;
542 unsigned char src[] = { 0x47, 0x88 };
543 int ret = 0;
544
545 int nret = EXIT_FAILURE;
546
547 ret = sixel_helper_normalize_pixelformat(dst,
548 &dst_pixelformat,
549 src,
550 src_pixelformat,
551 1,
552 1);
553 if (ret != 0) {
554 goto error;
555 }
556 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
557 goto error;
558 }
559 if (dst[0] != src[1]) {
560 goto error;
561 }
562 return EXIT_SUCCESS;
563
564error:
565 perror("test7");
566 return nret;
567}
568
569
570static int
571test8(void)
572{
573 unsigned char dst[3];
574 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
575 int src_pixelformat = SIXEL_PIXELFORMAT_GA88;
576 unsigned char src[] = { 0x47, 0x88 };
577 int ret = 0;
578
579 int nret = EXIT_FAILURE;
580
581 ret = sixel_helper_normalize_pixelformat(dst,
582 &dst_pixelformat,
583 src,
584 src_pixelformat,
585 1,
586 1);
587 if (ret != 0) {
588 goto error;
589 }
590 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
591 goto error;
592 }
593 if (dst[0] != src[0]) {
594 goto error;
595 }
596 return EXIT_SUCCESS;
597
598error:
599 perror("test8");
600 return nret;
601}
602
603
604static int
605test9(void)
606{
607 unsigned char dst[3];
608 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
609 int src_pixelformat = SIXEL_PIXELFORMAT_RGBA8888;
610 unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
611 int ret = 0;
612
613 int nret = EXIT_FAILURE;
614
615 ret = sixel_helper_normalize_pixelformat(dst,
616 &dst_pixelformat,
617 src,
618 src_pixelformat,
619 1,
620 1);
621 if (ret != 0) {
622 goto error;
623 }
624 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
625 goto error;
626 }
627 if (dst[0] != src[0]) {
628 goto error;
629 }
630 if (dst[1] != src[1]) {
631 goto error;
632 }
633 if (dst[2] != src[2]) {
634 goto error;
635 }
636 return EXIT_SUCCESS;
637
638error:
639 perror("test8");
640 return nret;
641}
642
643
644static int
645test10(void)
646{
647 unsigned char dst[3];
648 int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
649 int src_pixelformat = SIXEL_PIXELFORMAT_ARGB8888;
650 unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
651 int ret = 0;
652
653 int nret = EXIT_FAILURE;
654
655 ret = sixel_helper_normalize_pixelformat(dst,
656 &dst_pixelformat,
657 src,
658 src_pixelformat,
659 1,
660 1);
661 if (ret != 0) {
662 goto error;
663 }
664 if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
665 goto error;
666 }
667 if (dst[0] != src[1]) {
668 goto error;
669 }
670 if (dst[1] != src[2]) {
671 goto error;
672 }
673 if (dst[2] != src[3]) {
674 goto error;
675 }
676 return EXIT_SUCCESS;
677
678error:
679 perror("test8");
680 return nret;
681}
682
683
684SIXELAPI int
685sixel_pixelformat_tests_main(void)
686{
687 int nret = EXIT_FAILURE;
688 size_t i;
689 typedef int (* testcase)(void);
690
691 static testcase const testcases[] = {
692 test1,
693 test2,
694 test3,
695 test4,
696 test5,
697 test6,
698 test7,
699 test8,
700 test9,
701 test10,
702 };
703
704 for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
705 nret = testcases[i]();
706 if (nret != EXIT_SUCCESS) {
707 goto error;
708 }
709 }
710
711 nret = EXIT_SUCCESS;
712
713error:
714 return nret;
715}
716#endif /* HAVE_TESTS */
717
718/* emacs Local Variables: */
719/* emacs mode: c */
720/* emacs tab-width: 4 */
721/* emacs indent-tabs-mode: nil */
722/* emacs c-basic-offset: 4 */
723/* emacs End: */
724/* vim: set expandtab ts=4 sts=4 sw=4 : */
725/* EOF */