summaryrefslogtreecommitdiff
path: root/drivers/painter/generic/qp_surface_rgb565.c
blob: 8883ed541d826e5e7c60a67794bf16ed0e9ad205 (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
143
144
145
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#ifdef QUANTUM_PAINTER_SURFACE_ENABLE

#    include "color.h"
#    include "qp_draw.h"
#    include "qp_surface_internal.h"
#    include "qp_comms_dummy.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Surface driver impl: rgb565

static inline void setpixel_rgb565(surface_painter_device_t *surface, uint16_t x, uint16_t y, uint16_t rgb565) {
    uint16_t w = surface->base.panel_width;
    uint16_t h = surface->base.panel_height;

    // Drop out if it's off-screen
    if (x >= w || y >= h) {
        return;
    }

    // Skip messing with the dirty info if the original value already matches
    if (surface->u16buffer[y * w + x] != rgb565) {
        // Update the dirty region
        qp_surface_update_dirty(&surface->dirty, x, y);

        // Update the pixel data in the buffer
        surface->u16buffer[y * w + x] = rgb565;
    }
}

static inline void append_pixel_rgb565(surface_painter_device_t *surface, uint16_t rgb565) {
    setpixel_rgb565(surface, surface->viewport.pixdata_x, surface->viewport.pixdata_y, rgb565);
    qp_surface_increment_pixdata_location(&surface->viewport);
}

static inline void stream_pixdata_rgb565(surface_painter_device_t *surface, const uint16_t *data, uint32_t native_pixel_count) {
    for (uint32_t pixel_counter = 0; pixel_counter < native_pixel_count; ++pixel_counter) {
        append_pixel_rgb565(surface, data[pixel_counter]);
    }
}

// Stream pixel data to the current write position in GRAM
static bool qp_surface_pixdata_rgb565(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {
    painter_driver_t *        driver  = (painter_driver_t *)device;
    surface_painter_device_t *surface = (surface_painter_device_t *)driver;
    stream_pixdata_rgb565(surface, (const uint16_t *)pixel_data, native_pixel_count);
    return true;
}

// Pixel colour conversion
static bool qp_surface_palette_convert_rgb565_swapped(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
    for (int16_t i = 0; i < palette_size; ++i) {
        RGB      rgb      = hsv_to_rgb_nocie((HSV){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});
        uint16_t rgb565   = (((uint16_t)rgb.r) >> 3) << 11 | (((uint16_t)rgb.g) >> 2) << 5 | (((uint16_t)rgb.b) >> 3);
        palette[i].rgb565 = __builtin_bswap16(rgb565);
    }
    return true;
}

// Append pixels to the target location, keyed by the pixel index
static bool qp_surface_append_pixels_rgb565(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {
    uint16_t *buf = (uint16_t *)target_buffer;
    for (uint32_t i = 0; i < pixel_count; ++i) {
        buf[pixel_offset + i] = palette[palette_indices[i]].rgb565;
    }
    return true;
}

static bool rgb565_target_pixdata_transfer(painter_driver_t *surface_driver, painter_driver_t *target_driver, uint16_t x, uint16_t y, bool entire_surface) {
    surface_painter_device_t *surface_handle = (surface_painter_device_t *)surface_driver;

    uint16_t l = entire_surface ? 0 : surface_handle->dirty.l;
    uint16_t t = entire_surface ? 0 : surface_handle->dirty.t;
    uint16_t r = entire_surface ? (surface_handle->base.panel_width - 1) : surface_handle->dirty.r;
    uint16_t b = entire_surface ? (surface_handle->base.panel_height - 1) : surface_handle->dirty.b;

    // Set the target drawing area
    bool ok = qp_viewport((painter_device_t)target_driver, x + l, y + t, x + r, y + b);
    if (!ok) {
        qp_dprintf("rgb565_target_pixdata_transfer: fail (could not set target viewport)\n");
        return false;
    }

    // Housekeeping of the amount of pixels to transfer
    uint32_t  total_pixel_count = (8 * QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE) / surface_driver->native_bits_per_pixel;
    uint32_t  pixel_counter     = 0;
    uint16_t *target_buffer     = (uint16_t *)qp_internal_global_pixdata_buffer;

    // Fill the global pixdata area so that we can start transferring to the panel
    for (uint16_t y = t; y <= b; ++y) {
        for (uint16_t x = l; x <= r; ++x) {
            // Update the target buffer
            target_buffer[pixel_counter++] = surface_handle->u16buffer[y * surface_handle->base.panel_width + x];

            // If we've accumulated enough data, send it
            if (pixel_counter == total_pixel_count) {
                ok = qp_pixdata((painter_device_t)target_driver, qp_internal_global_pixdata_buffer, pixel_counter);
                if (!ok) {
                    qp_dprintf("rgb565_target_pixdata_transfer: fail (could not stream pixdata to target)\n");
                    return false;
                }
                // Reset the counter
                pixel_counter = 0;
            }
        }
    }

    // If there's any leftover data, send it
    if (pixel_counter > 0) {
        ok = qp_pixdata((painter_device_t)target_driver, qp_internal_global_pixdata_buffer, pixel_counter);
        if (!ok) {
            qp_dprintf("rgb565_target_pixdata_transfer: fail (could not stream pixdata to target)\n");
            return false;
        }
    }

    return true;
}

static bool qp_surface_append_pixdata_rgb565(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte) {
    target_buffer[pixdata_offset] = pixdata_byte;
    return true;
}

const surface_painter_driver_vtable_t rgb565_surface_driver_vtable = {
    .base =
        {
            .init            = qp_surface_init,
            .power           = qp_surface_power,
            .clear           = qp_surface_clear,
            .flush           = qp_surface_flush,
            .pixdata         = qp_surface_pixdata_rgb565,
            .viewport        = qp_surface_viewport,
            .palette_convert = qp_surface_palette_convert_rgb565_swapped,
            .append_pixels   = qp_surface_append_pixels_rgb565,
            .append_pixdata  = qp_surface_append_pixdata_rgb565,
        },
    .target_pixdata_transfer = rgb565_target_pixdata_transfer,
};

SURFACE_FACTORY_FUNCTION_IMPL(qp_make_rgb565_surface, rgb565_surface_driver_vtable, 16);

#endif // QUANTUM_PAINTER_SURFACE_ENABLE