summaryrefslogtreecommitdiff
path: root/quantum/painter/qp_draw_codec.c
blob: 438dce399460d7e9646e8d68ed42b5d3a6cf02a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"
#include "qp_draw.h"
#include "qp_comms.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Palette / Monochrome-format decoder

static const qp_pixel_t qp_pixel_white = {.hsv888 = {.h = 0, .s = 0, .v = 255}};
static const qp_pixel_t qp_pixel_black = {.hsv888 = {.h = 0, .s = 0, .v = 0}};

bool qp_internal_bpp_capable(uint8_t bits_per_pixel) {
#if !(QUANTUM_PAINTER_SUPPORTS_256_PALETTE)
    if (bits_per_pixel > 4) {
        qp_dprintf("qp_internal_decode_palette: image bpp greater than 4\n");
        return false;
    }
#endif

    if (bits_per_pixel > 8) {
        qp_dprintf("qp_internal_decode_palette: image bpp greater than 8\n");
        return false;
    }

    return true;
}

bool qp_internal_decode_palette(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t* palette, qp_internal_pixel_output_callback output_callback, void* output_arg) {
    const uint8_t pixel_bitmask    = (1 << bits_per_pixel) - 1;
    const uint8_t pixels_per_byte  = 8 / bits_per_pixel;
    uint32_t      remaining_pixels = pixel_count; // don't try to derive from byte_count, we may not use an entire byte
    while (remaining_pixels > 0) {
        uint8_t byteval = input_callback(input_arg);
        if (byteval < 0) {
            return false;
        }
        uint8_t loop_pixels = remaining_pixels < pixels_per_byte ? remaining_pixels : pixels_per_byte;
        for (uint8_t q = 0; q < loop_pixels; ++q) {
            if (!output_callback(palette, byteval & pixel_bitmask, output_arg)) {
                return false;
            }
            byteval >>= bits_per_pixel;
        }
        remaining_pixels -= loop_pixels;
    }
    return true;
}

bool qp_internal_decode_grayscale(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_pixel_output_callback output_callback, void* output_arg) {
    return qp_internal_decode_recolor(device, pixel_count, bits_per_pixel, input_callback, input_arg, qp_pixel_white, qp_pixel_black, output_callback, output_arg);
}

bool qp_internal_decode_recolor(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, qp_internal_pixel_output_callback output_callback, void* output_arg) {
    struct painter_driver_t* driver = (struct painter_driver_t*)device;
    int16_t                  steps  = 1 << bits_per_pixel; // number of items we need to interpolate
    if (qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, steps)) {
        if (!driver->driver_vtable->palette_convert(device, steps, qp_internal_global_pixel_lookup_table)) {
            return false;
        }
    }

    return qp_internal_decode_palette(device, pixel_count, bits_per_pixel, input_callback, input_arg, qp_internal_global_pixel_lookup_table, output_callback, output_arg);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Progressive pull of bytes, push of pixels

static inline int16_t qp_drawimage_byte_uncompressed_decoder(void* cb_arg) {
    struct qp_internal_byte_input_state* state = (struct qp_internal_byte_input_state*)cb_arg;
    state->curr                                = qp_stream_get(state->src_stream);
    return state->curr;
}

static inline int16_t qp_drawimage_byte_rle_decoder(void* cb_arg) {
    struct qp_internal_byte_input_state* state = (struct qp_internal_byte_input_state*)cb_arg;

    // Work out if we're parsing the initial marker byte
    if (state->rle.mode == MARKER_BYTE) {
        uint8_t c = qp_stream_get(state->src_stream);
        if (c >= 128) {
            state->rle.mode   = NON_REPEATING_RUN; // non-repeated run
            state->rle.remain = c - 127;
        } else {
            state->rle.mode   = REPEATING_RUN; // repeated run
            state->rle.remain = c;
        }

        state->curr = qp_stream_get(state->src_stream);
    }

    // Work out which byte we're returning
    uint8_t c = state->curr;

    // Decrement the counter of the bytes remaining
    state->rle.remain--;

    if (state->rle.remain > 0) {
        // If we're in a non-repeating run, queue up the next byte
        if (state->rle.mode == NON_REPEATING_RUN) {
            state->curr = qp_stream_get(state->src_stream);
        }
    } else {
        // Swap back to querying the marker byte mode
        state->rle.mode = MARKER_BYTE;
    }

    return c;
}

bool qp_internal_pixel_appender(qp_pixel_t* palette, uint8_t index, void* cb_arg) {
    struct qp_internal_pixel_output_state* state  = (struct qp_internal_pixel_output_state*)cb_arg;
    struct painter_driver_t*               driver = (struct painter_driver_t*)state->device;

    if (!driver->driver_vtable->append_pixels(state->device, qp_internal_global_pixdata_buffer, palette, state->pixel_write_pos++, 1, &index)) {
        return false;
    }

    // If we've hit the transmit limit, send out the entire buffer and reset the write position
    if (state->pixel_write_pos == state->max_pixels) {
        if (!driver->driver_vtable->pixdata(state->device, qp_internal_global_pixdata_buffer, state->pixel_write_pos)) {
            return false;
        }
        state->pixel_write_pos = 0;
    }

    return true;
}

qp_internal_byte_input_callback qp_internal_prepare_input_state(struct qp_internal_byte_input_state* input_state, painter_compression_t compression) {
    switch (compression) {
        case IMAGE_UNCOMPRESSED:
            return qp_drawimage_byte_uncompressed_decoder;
        case IMAGE_COMPRESSED_RLE:
            input_state->rle.mode   = MARKER_BYTE;
            input_state->rle.remain = 0;
            return qp_drawimage_byte_rle_decoder;
        default:
            return NULL;
    }
}