master ohmu/fastfetch-openbsd / src / 3rdparty / sixel / pixelformat.c
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 */