qp_draw_codec.c (9787B)
1 // Copyright 2021 Nick Brassel (@tzarc) 2 // Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 #include "qp_internal.h" 6 #include "qp_draw.h" 7 #include "qp_comms.h" 8 9 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 // Palette / Monochrome-format decoder 11 12 static const qp_pixel_t qp_pixel_white = {.hsv888 = {.h = 0, .s = 0, .v = 255}}; 13 static const qp_pixel_t qp_pixel_black = {.hsv888 = {.h = 0, .s = 0, .v = 0}}; 14 15 bool qp_internal_bpp_capable(uint8_t bits_per_pixel) { 16 #if !(QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS) 17 # if !(QUANTUM_PAINTER_SUPPORTS_256_PALETTE) 18 if (bits_per_pixel > 4) { 19 qp_dprintf("qp_internal_decode_palette: image bpp greater than 4\n"); 20 return false; 21 } 22 # endif 23 24 if (bits_per_pixel > 8) { 25 qp_dprintf("qp_internal_decode_palette: image bpp greater than 8\n"); 26 return false; 27 } 28 #endif 29 return true; 30 } 31 32 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) { 33 const uint8_t pixel_bitmask = (1 << bits_per_pixel) - 1; 34 const uint8_t pixels_per_byte = 8 / bits_per_pixel; 35 uint32_t remaining_pixels = pixel_count; // don't try to derive from byte_count, we may not use an entire byte 36 while (remaining_pixels > 0) { 37 int16_t byteval = input_callback(input_arg); 38 if (byteval < 0) { 39 return false; 40 } 41 uint8_t loop_pixels = remaining_pixels < pixels_per_byte ? remaining_pixels : pixels_per_byte; 42 for (uint8_t q = 0; q < loop_pixels; ++q) { 43 if (!output_callback(palette, byteval & pixel_bitmask, output_arg)) { 44 return false; 45 } 46 byteval >>= bits_per_pixel; 47 } 48 remaining_pixels -= loop_pixels; 49 } 50 return true; 51 } 52 53 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) { 54 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); 55 } 56 57 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) { 58 painter_driver_t* driver = (painter_driver_t*)device; 59 int16_t steps = 1 << bits_per_pixel; // number of items we need to interpolate 60 if (qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, steps)) { 61 if (!driver->driver_vtable->palette_convert(device, steps, qp_internal_global_pixel_lookup_table)) { 62 return false; 63 } 64 } 65 66 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); 67 } 68 69 bool qp_internal_send_bytes(painter_device_t device, uint32_t byte_count, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_byte_output_callback output_callback, void* output_arg) { 70 uint32_t remaining_bytes = byte_count; 71 while (remaining_bytes > 0) { 72 int16_t byteval = input_callback(input_arg); 73 if (byteval < 0) { 74 return false; 75 } 76 if (!output_callback(byteval, output_arg)) { 77 return false; 78 } 79 remaining_bytes -= 1; 80 } 81 return true; 82 } 83 84 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 85 // Progressive pull of bytes, push of pixels 86 87 static inline int16_t qp_drawimage_byte_uncompressed_decoder(void* cb_arg) { 88 qp_internal_byte_input_state_t* state = (qp_internal_byte_input_state_t*)cb_arg; 89 state->curr = qp_stream_get(state->src_stream); 90 return state->curr; 91 } 92 93 static inline int16_t qp_drawimage_byte_rle_decoder(void* cb_arg) { 94 qp_internal_byte_input_state_t* state = (qp_internal_byte_input_state_t*)cb_arg; 95 96 // Work out if we're parsing the initial marker byte 97 if (state->rle.mode == MARKER_BYTE) { 98 uint8_t c = qp_stream_get(state->src_stream); 99 if (c >= 128) { 100 state->rle.mode = NON_REPEATING_RUN; // non-repeated run 101 state->rle.remain = c - 127; 102 } else { 103 state->rle.mode = REPEATING_RUN; // repeated run 104 state->rle.remain = c; 105 } 106 107 state->curr = qp_stream_get(state->src_stream); 108 } 109 110 // Work out which byte we're returning 111 uint8_t c = state->curr; 112 113 // Decrement the counter of the bytes remaining 114 state->rle.remain--; 115 116 if (state->rle.remain > 0) { 117 // If we're in a non-repeating run, queue up the next byte 118 if (state->rle.mode == NON_REPEATING_RUN) { 119 state->curr = qp_stream_get(state->src_stream); 120 } 121 } else { 122 // Swap back to querying the marker byte mode 123 state->rle.mode = MARKER_BYTE; 124 } 125 126 return c; 127 } 128 129 bool qp_internal_pixel_appender(qp_pixel_t* palette, uint8_t index, void* cb_arg) { 130 qp_internal_pixel_output_state_t* state = (qp_internal_pixel_output_state_t*)cb_arg; 131 painter_driver_t* driver = (painter_driver_t*)state->device; 132 133 if (!driver->driver_vtable->append_pixels(state->device, qp_internal_global_pixdata_buffer, palette, state->pixel_write_pos++, 1, &index)) { 134 return false; 135 } 136 137 // If we've hit the transmit limit, send out the entire buffer and reset the write position 138 if (state->pixel_write_pos == state->max_pixels) { 139 if (!driver->driver_vtable->pixdata(state->device, qp_internal_global_pixdata_buffer, state->pixel_write_pos)) { 140 return false; 141 } 142 state->pixel_write_pos = 0; 143 } 144 145 return true; 146 } 147 148 bool qp_internal_byte_appender(uint8_t byteval, void* cb_arg) { 149 qp_internal_byte_output_state_t* state = (qp_internal_byte_output_state_t*)cb_arg; 150 painter_driver_t* driver = (painter_driver_t*)state->device; 151 152 if (!driver->driver_vtable->append_pixdata(state->device, qp_internal_global_pixdata_buffer, state->byte_write_pos++, byteval)) { 153 return false; 154 } 155 156 // If we've hit the transmit limit, send out the entire buffer and reset the write position 157 if (state->byte_write_pos == state->max_bytes) { 158 painter_driver_t* driver = (painter_driver_t*)state->device; 159 if (!driver->driver_vtable->pixdata(state->device, qp_internal_global_pixdata_buffer, state->byte_write_pos * 8 / driver->native_bits_per_pixel)) { 160 return false; 161 } 162 state->byte_write_pos = 0; 163 } 164 165 return true; 166 } 167 168 // Helper shared between image and font rendering -- uses either (qp_internal_decode_palette + qp_internal_pixel_appender) or (qp_internal_send_bytes) to send data data to the display based on the asset's native-ness 169 bool qp_internal_appender(painter_device_t device, uint8_t bpp, uint32_t pixel_count, qp_internal_byte_input_callback input_callback, void* input_state) { 170 painter_driver_t* driver = (painter_driver_t*)device; 171 172 bool ret = false; 173 174 // Non-native pixel format 175 if (bpp <= 8) { 176 // Set up the output state 177 qp_internal_pixel_output_state_t output_state = {.device = device, .pixel_write_pos = 0, .max_pixels = qp_internal_num_pixels_in_buffer(device)}; 178 179 // Decode the pixel data and stream to the display 180 ret = qp_internal_decode_palette(device, pixel_count, bpp, input_callback, input_state, qp_internal_global_pixel_lookup_table, qp_internal_pixel_appender, &output_state); 181 // Any leftovers need transmission as well. 182 if (ret && output_state.pixel_write_pos > 0) { 183 ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.pixel_write_pos); 184 } 185 } 186 187 // Native pixel format 188 else if (bpp != driver->native_bits_per_pixel) { 189 qp_dprintf("Asset's bpp (%d) doesn't match the target display's native_bits_per_pixel (%d)\n", bpp, driver->native_bits_per_pixel); 190 return false; 191 } else { 192 // Set up the output state 193 qp_internal_byte_output_state_t output_state = {.device = device, .byte_write_pos = 0, .max_bytes = qp_internal_num_pixels_in_buffer(device) * driver->native_bits_per_pixel / 8}; 194 195 // Stream the raw pixel data to the display 196 uint32_t byte_count = pixel_count * bpp / 8; 197 ret = qp_internal_send_bytes(device, byte_count, input_callback, input_state, qp_internal_byte_appender, &output_state); 198 // Any leftovers need transmission as well. 199 if (ret && output_state.byte_write_pos > 0) { 200 ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.byte_write_pos * 8 / driver->native_bits_per_pixel); 201 } 202 } 203 204 return ret; 205 } 206 207 qp_internal_byte_input_callback qp_internal_prepare_input_state(qp_internal_byte_input_state_t* input_state, painter_compression_t compression) { 208 switch (compression) { 209 case IMAGE_UNCOMPRESSED: 210 return qp_drawimage_byte_uncompressed_decoder; 211 case IMAGE_COMPRESSED_RLE: 212 input_state->rle.mode = MARKER_BYTE; 213 input_state->rle.remain = 0; 214 return qp_drawimage_byte_rle_decoder; 215 default: 216 return NULL; 217 } 218 }