You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
5.4 KiB

  1. // Copyright 2021 Nick Brassel (@tzarc)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "color.h"
  4. #include "qp_internal.h"
  5. #include "qp_comms.h"
  6. #include "qp_draw.h"
  7. #include "qp_tft_panel.h"
  8. #define BYTE_SWAP(x) (((((uint16_t)(x)) >> 8) & 0x00FF) | ((((uint16_t)(x)) << 8) & 0xFF00))
  9. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  10. // Native pixel format conversion
  11. uint16_t qp_rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b) {
  12. uint16_t rgb565 = (((uint16_t)r) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)b) >> 3);
  13. return rgb565;
  14. }
  15. uint16_t qp_rgb888_to_rgb565_swapped(uint8_t r, uint8_t g, uint8_t b) {
  16. uint16_t rgb565 = (((uint16_t)r) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)b) >> 3);
  17. return BYTE_SWAP(rgb565);
  18. }
  19. uint16_t qp_rgb888_to_bgr565(uint8_t r, uint8_t g, uint8_t b) {
  20. uint16_t bgr565 = (((uint16_t)b) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)r) >> 3);
  21. return bgr565;
  22. }
  23. uint16_t qp_rgb888_to_bgr565_swapped(uint8_t r, uint8_t g, uint8_t b) {
  24. uint16_t bgr565 = (((uint16_t)b) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)r) >> 3);
  25. return BYTE_SWAP(bgr565);
  26. }
  27. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  28. // Quantum Painter API implementations
  29. // Power control
  30. bool qp_tft_panel_power(painter_device_t device, bool power_on) {
  31. struct painter_driver_t * driver = (struct painter_driver_t *)device;
  32. struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
  33. qp_comms_command(device, power_on ? vtable->opcodes.display_on : vtable->opcodes.display_off);
  34. return true;
  35. }
  36. // Screen clear
  37. bool qp_tft_panel_clear(painter_device_t device) {
  38. struct painter_driver_t *driver = (struct painter_driver_t *)device;
  39. driver->driver_vtable->init(device, driver->rotation); // Re-init the LCD
  40. return true;
  41. }
  42. // Screen flush
  43. bool qp_tft_panel_flush(painter_device_t device) {
  44. // No-op, as there's no framebuffer in RAM for this device.
  45. return true;
  46. }
  47. // Viewport to draw to
  48. bool qp_tft_panel_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
  49. struct painter_driver_t * driver = (struct painter_driver_t *)device;
  50. struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
  51. // Fix up the drawing location if required
  52. left += driver->offset_x;
  53. right += driver->offset_x;
  54. top += driver->offset_y;
  55. bottom += driver->offset_y;
  56. // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation
  57. if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) {
  58. uint16_t temp;
  59. temp = left;
  60. left = top;
  61. top = temp;
  62. temp = right;
  63. right = bottom;
  64. bottom = temp;
  65. }
  66. if (vtable->num_window_bytes == 1) {
  67. // Set up the x-window
  68. uint8_t xbuf[2] = {left & 0xFF, right & 0xFF};
  69. qp_comms_command_databuf(device, vtable->opcodes.set_column_address, xbuf, sizeof(xbuf));
  70. // Set up the y-window
  71. uint8_t ybuf[2] = {top & 0xFF, bottom & 0xFF};
  72. qp_comms_command_databuf(device, vtable->opcodes.set_row_address, ybuf, sizeof(ybuf));
  73. } else if (vtable->num_window_bytes == 2) {
  74. // Set up the x-window
  75. uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF};
  76. qp_comms_command_databuf(device, vtable->opcodes.set_column_address, xbuf, sizeof(xbuf));
  77. // Set up the y-window
  78. uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF};
  79. qp_comms_command_databuf(device, vtable->opcodes.set_row_address, ybuf, sizeof(ybuf));
  80. }
  81. // Lock in the window
  82. qp_comms_command(device, vtable->opcodes.enable_writes);
  83. return true;
  84. }
  85. // Stream pixel data to the current write position in GRAM
  86. bool qp_tft_panel_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {
  87. qp_comms_send(device, pixel_data, native_pixel_count * sizeof(uint16_t));
  88. return true;
  89. }
  90. // Convert supplied palette entries into their native equivalents
  91. bool qp_tft_panel_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
  92. struct painter_driver_t * driver = (struct painter_driver_t *)device;
  93. struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
  94. for (int16_t i = 0; i < palette_size; ++i) {
  95. RGB rgb = hsv_to_rgb_nocie((HSV){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});
  96. palette[i].rgb565 = vtable->rgb888_to_native16bit(rgb.r, rgb.g, rgb.b);
  97. }
  98. return true;
  99. }
  100. // Append pixels to the target location, keyed by the pixel index
  101. bool qp_tft_panel_append_pixels(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {
  102. uint16_t *buf = (uint16_t *)target_buffer;
  103. for (uint32_t i = 0; i < pixel_count; ++i) {
  104. buf[pixel_offset + i] = palette[palette_indices[i]].rgb565;
  105. }
  106. return true;
  107. }