196 lines
6.4 KiB
C
196 lines
6.4 KiB
C
/* source/draw/sgl_draw_line.c
|
|
*
|
|
* MIT License
|
|
*
|
|
* Copyright(c) 2023-present All contributors of SGL
|
|
* Document reference link: https://sgl-docs.readthedocs.io
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <sgl_core.h>
|
|
#include <sgl_log.h>
|
|
#include <sgl_draw.h>
|
|
#include <sgl_math.h>
|
|
|
|
/**
|
|
* @brief draw a horizontal line with alpha
|
|
* @param surf surface
|
|
* @param area area that contains the line
|
|
* @param y line y position
|
|
* @param x1 line start x position
|
|
* @param x2 line end x position
|
|
* @param color line color
|
|
* @param alpha alpha of color
|
|
* @return none
|
|
*/
|
|
void sgl_draw_fill_hline(sgl_surf_t *surf, sgl_area_t *area, int16_t y, int16_t x1, int16_t x2, uint8_t width, sgl_color_t color, uint8_t alpha)
|
|
{
|
|
sgl_color_t *buf = NULL, *blend = NULL;
|
|
sgl_area_t c_rect = {.x1 = x1, .x2 = x2, .y1 = y - (width - 1) / 2, .y2 = y + width / 2}, clip = SGL_AREA_MAX;
|
|
|
|
if (c_rect.x1 > c_rect.x2) {
|
|
sgl_swap(&c_rect.x1, &c_rect.x2);
|
|
}
|
|
|
|
sgl_surf_clip_area_return(surf, area, &clip);
|
|
if (!sgl_area_selfclip(&clip, &c_rect)) {
|
|
return;
|
|
}
|
|
|
|
buf = sgl_surf_get_buf(surf, clip.x1 - surf->x1, clip.y1 - surf->y1);
|
|
for (int y = clip.y1; y <= clip.y2; y++) {
|
|
blend = buf;
|
|
for (int x = clip.x1; x <= clip.x2; x++, blend++) {
|
|
*blend = alpha == SGL_ALPHA_MAX ? color : sgl_color_mixer(color, *blend, alpha);
|
|
}
|
|
buf += surf->w;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief draw a vertical line with alpha
|
|
* @param surf surface
|
|
* @param area area that contains the line
|
|
* @param x x coordinate
|
|
* @param y1 y1 coordinate
|
|
* @param y2 y2 coordinate
|
|
* @param color line color
|
|
* @param alpha alpha of color
|
|
* @return none
|
|
*/
|
|
void sgl_draw_fill_vline(sgl_surf_t *surf, sgl_area_t *area, int16_t x, int16_t y1, int16_t y2, uint8_t width, sgl_color_t color, uint8_t alpha)
|
|
{
|
|
sgl_color_t *buf = NULL, *blend = NULL;
|
|
sgl_area_t c_rect = {.x1 = x - (width - 1) / 2, .x2 = x + width / 2, .y1 = y1,.y2 = y2}, clip = SGL_AREA_MAX;
|
|
|
|
if (c_rect.y1 > c_rect.y2) {
|
|
sgl_swap(&c_rect.y1, &c_rect.y2);
|
|
}
|
|
|
|
sgl_surf_clip_area_return(surf, area, &clip);
|
|
if (!sgl_area_selfclip(&clip, &c_rect)) {
|
|
return;
|
|
}
|
|
|
|
buf = sgl_surf_get_buf(surf, clip.x1 - surf->x1, clip.y1 - surf->y1);
|
|
for (int y = clip.y1; y <= clip.y2; y++) {
|
|
blend = buf;
|
|
for (int x = clip.x1; x <= clip.x2; x++, blend++) {
|
|
*blend = (alpha == SGL_ALPHA_MAX ? color : sgl_color_mixer(color, *blend, alpha));
|
|
}
|
|
buf += surf->w;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SDF draw anti-aliased line
|
|
* @param thickness line thickness (in pixels)
|
|
*/
|
|
static int32_t sgl_capsule_sdf_optimized(int16_t px, int16_t py, int16_t ax, int16_t ay, int16_t bx, int16_t by)
|
|
{
|
|
int64_t pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay;
|
|
int64_t b_sqd = bax * bax + bay * bay;
|
|
int64_t h = (sgl_max(sgl_min((pax * bax + pay * bay), b_sqd), 0)) << 8;
|
|
int64_t dx = (pax << 8) - bax * h / b_sqd;
|
|
int64_t dy = (pay << 8) - bay * h / b_sqd;
|
|
|
|
return sgl_sqrt(dx * dx + dy * dy);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief draw a slanted line with alpha
|
|
* @param surf surface
|
|
* @param area area that contains the line
|
|
* @param x1 line start x position
|
|
* @param y1 line start y position
|
|
* @param x2 line end x position
|
|
* @param y2 line end y position
|
|
* @param thickness line width
|
|
* @param color line color
|
|
* @param alpha alpha of color
|
|
* @return none
|
|
* @note This algorithm is SDF algorithm
|
|
*/
|
|
void draw_line_fill_slanted(sgl_surf_t *surf, sgl_area_t *area, int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t thickness, sgl_color_t color, uint8_t alpha)
|
|
{
|
|
uint8_t c;
|
|
int64_t len;
|
|
sgl_area_t clip = SGL_AREA_MAX;
|
|
sgl_color_t *buf = NULL, *blend = NULL;
|
|
int16_t thick_half = (thickness >> 1);
|
|
|
|
sgl_area_t c_rect = {
|
|
.x1 = (x1 < x2 ? x1 : x2) - thick_half,
|
|
.x2 = (x1 > x2 ? x1 : x2) + thick_half,
|
|
.y1 = (y1 < y2 ? y1 : y2) - thick_half,
|
|
.y2 = (y1 > y2 ? y1 : y2) + thick_half,
|
|
};
|
|
|
|
sgl_surf_clip_area_return(surf, area, &clip);
|
|
if (!sgl_area_selfclip(&clip, &c_rect)) {
|
|
return;
|
|
}
|
|
|
|
buf = sgl_surf_get_buf(surf, clip.x1 - surf->x1, clip.y1 - surf->y1);
|
|
for (int y = clip.y1; y <= clip.y2; y++) {
|
|
blend = buf;
|
|
|
|
for (int x = clip.x1; x <= clip.x2; x++, blend++) {
|
|
len = sgl_capsule_sdf_optimized(x, y, x1, y1, x2, y2);
|
|
|
|
if (len <= (thick_half - 1) << 8) {
|
|
*blend = (alpha == SGL_ALPHA_MAX ? color : sgl_color_mixer(color, *blend, alpha));
|
|
continue;
|
|
}
|
|
|
|
if (len > ((thick_half - 1) << 8) && len < (thick_half << 8)) {
|
|
c = len - ((thick_half - 1) << 8);
|
|
|
|
if (alpha == SGL_ALPHA_MAX)
|
|
*blend = sgl_color_mixer(*blend, color, c);
|
|
else
|
|
*blend = sgl_color_mixer(sgl_color_mixer(*blend, color, c), *blend, alpha);
|
|
}
|
|
}
|
|
buf += surf->w;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief draw a line
|
|
* @param surf surface
|
|
* @param area area that contains the line
|
|
* @param desc line description
|
|
* @return none
|
|
*/
|
|
void sgl_draw_line(sgl_surf_t *surf, sgl_area_t *area, sgl_draw_line_t *desc)
|
|
{
|
|
if (desc->x1 == desc->x2) {
|
|
sgl_draw_fill_vline(surf, area, desc->x1, desc->y1, desc->y2, desc->width / 2, desc->color, desc->alpha);
|
|
}
|
|
else if (desc->y1 == desc->y2) {
|
|
sgl_draw_fill_hline(surf, area, desc->y1, desc->x1, desc->x2, desc->width / 2, desc->color, desc->alpha);
|
|
}
|
|
else {
|
|
draw_line_fill_slanted(surf, area, desc->x1, desc->y1, desc->x2, desc->y2, desc->width, desc->color, desc->alpha);
|
|
}
|
|
}
|