Newer
Older
avrtft / paint.c
/* 
 * File:   paint.c
 * Author: torsten.roemer@luniks.net
 *
 * Created on 29. Dezember 2023, 18:01
 */

#include "paint.h"

#define THICKNESS   2 * fmax(1, DISPLAY_WIDTH / 320.0)
#define THICK_OFF   (THICKNESS + 1) / 2;

static uint16_t colors[] = {
    0xf800, 0x07e0, 0x001f, 
    0xffe0, 0xf81f, 0x07ff, 
    0x0000, 0xe71c, 0xffff};

static uint8_t tool = TOOL_FREE;
static Point prev = {0};
static uint16_t color = 0x0;
static uint8_t thick = THICKNESS; // line thickness
static uint8_t thoff = THICK_OFF; // offset to "center" point relative to thickness

/**
 * Paints a rectangle with the given origin and dimensions, line thickness
 * and color.
 * 
 * @param x
 * @param y
 * @param width
 * @param height
 * @param thickness
 * @param color
 */
static void paintRectangle(x_t x, y_t y, width_t width, height_t height, 
                    uint8_t thickness, uint16_t color) {
    width -= thickness;
    height -= thickness;
    
    fillArea(x, y, width, thickness, color);
    fillArea(x + width, y, thickness, height, color);
    fillArea(x, y + height, width + thickness, thickness, color);
    fillArea(x, y, thickness, height, color);
}

/**
 * Paints the color selection.
 */
static void paintColors(void) {
    for (uint8_t i = 0; i < CTRL_COUNT; i++) {
       fillArea(0, CTRL_WIDTH * i, CTRL_WIDTH, CTRL_WIDTH, colors[i]);
    }    
}

/**
 * Paints the tool selection.
 */
static void paintTools(void) {
    fillArea(DISPLAY_WIDTH - CTRL_WIDTH, 0, 
             CTRL_WIDTH, CTRL_WIDTH * TOOL_COUNT, 0xffff);
    for (uint8_t i = 0; i < TOOL_COUNT; i++) {
        paintRectangle(DISPLAY_WIDTH - CTRL_WIDTH, CTRL_WIDTH * i, 
                       CTRL_WIDTH, CTRL_WIDTH + 1, 1, 0x0);
        writeBitmap(DISPLAY_WIDTH - CTRL_WIDTH + BITMAP_PADDING, 
                    CTRL_WIDTH * i + BITMAP_PADDING, i);
    }
}

void initPaint() {
    setFrame(0xffff);

    // color selection
    paintColors();

    // tool selection
    paintTools();
    
    // highlight default tool
    paintRectangle(DISPLAY_WIDTH - CTRL_WIDTH, CTRL_WIDTH * tool, 
                   CTRL_WIDTH, CTRL_WIDTH + 1, 2, 0x0);
}

void paintEvent(uint8_t event, Point *point) {
    if (point->x < CTRL_WIDTH + thoff) {
        if (event == EVENT_PRESS_DOWN) {
            // color selected
            uint8_t i = point->y / (CTRL_WIDTH + 1);
            // repaint colors, highlight and select color
            paintColors();
            paintRectangle(0, CTRL_WIDTH * i,  
                           CTRL_WIDTH, CTRL_WIDTH, 2, 0x0);
            color = colors[i];
        }
    } else if (point->x > DISPLAY_WIDTH - CTRL_WIDTH - thoff - 1) {
        if (event == EVENT_PRESS_DOWN) {
            uint8_t i = point->y / (CTRL_WIDTH + 1);
            // tool selected
            if (i == TOOL_CLEAR) {
                // clear canvas
                fillArea(CTRL_WIDTH, 0, DISPLAY_WIDTH - 2 * CTRL_WIDTH, 
                         DISPLAY_HEIGHT, 0xffff);
            } else if (i == TOOL_THICK) {
                // increment line thickness
                thick += THICKNESS;
                if (thick > THICKNESS * 4) thick = THICKNESS;
                thoff = (thick + 1) / 2;
            } else if (i < TOOL_COUNT) {
                // repaint tools, highlight and select tool
                paintTools();
                paintRectangle(DISPLAY_WIDTH - CTRL_WIDTH, CTRL_WIDTH * i, 
                               CTRL_WIDTH, CTRL_WIDTH + 1, 2, 0x0);
                tool = i;
            }
            prev = (Point){-1};
        }
    } else {
        paintPoint(event, point);
    }
}

void paintPoint(uint8_t event, Point *point) {
    if (tool == TOOL_ERASER) {
        fillArea(point->x - thoff, point->y - thoff, thick, thick, 0xffff);
    } else {
        fillArea(point->x - thoff, point->y - thoff, thick, thick, color);
    }

    if (prev.x != -1) {
        // connect previous and current point with a straight line if distance > 1
        if ((tool == TOOL_LINE) || 
            (tool == TOOL_FREE && event == EVENT_CONTACT)) {
            float d = sqrt(pow(point->x - prev.x, 2) + pow(point->y - prev.y, 2));
            if (d > 1) {
                float xd = (point->x - prev.x) / d;
                float yd = (point->y - prev.y) / d;

                for (int i = 1; i < d; i++) {
                    x_t xi = prev.x + xd * i;
                    y_t yi = prev.y + yd * i;
                    fillArea(xi - thoff, yi - thoff, thick, thick, color);
                }
            }
        }
        
        // draw a rectangle spanning the previous and current point
        if (tool == TOOL_RECT && event == EVENT_PRESS_DOWN) {
            y_t x1 = fmin(prev.x, point->x);
            y_t y1 = fmin(prev.y, point->y);
            x_t x2 = fmax(prev.x, point->x);
            x_t y2 = fmax(prev.y, point->y);
            
            paintRectangle(x1 - thoff, y1 - thoff, 
                           x2 - x1 + thick, y2 - y1 + thick, thick, color);
            // unset previous point and leave (for now)
            prev = (Point){-1};
            return;
        }
    }

    prev = *point;
}