Files
waveform-draw/draw_parra/dmx2img.c
2025-12-17 02:24:38 +01:00

244 lines
8.7 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*=====================================================================
draw_12_tilted_rects.c
---------------------------------------------------------------
Creates an 800×600 ARGB framebuffer, draws 12 white rectangles
whose top and bottom edges are slanted (as in the Python example
you supplied) and streams the buffer to a TCP server on
localhost:12345.
Compile (Linux / macOS):
gcc -Wall -O2 draw_12_tilted_rects.c -lm -o draw_12_tilted_rects
Run:
./draw_12_tilted_rects
-------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "artnet_receiver.h"
#define FB_W 800 /* framebuffer width */
#define FB_H 600 /* framebuffer height */
#define BPP 4 /* bytes per pixel (A,R,G,B) */
#define FRAMEBUF_SIZE (FB_W * FB_H * BPP)
#define MARGIN_TOP 100
#define MARGIN_BOTTOM 150
#define SPACING 10 /* space between rectangles */
#define LINE_THICKNESS 4 /* thickness of outline (pixels) */
#define NUM_RECTS 12
///*---------------------------------------------------------------*/
///* 1. Perrectangle tilt angles (in degrees). Edit as you wish. */
int16_t rotAnglesDeg[NUM_RECTS] = {
0, 5, 10, 15, 20, 25,
30, 35, 40, 45, 50, 55
};
/*---------------------------------------------------------------*/
/* 2. ARGB colour helpers */
static const uint32_t COL_BLACK = 0xFF000000; /* opaque black */
static const uint32_t COL_WHITE = 0xFFFFFFFF; /* opaque white */
/*---------------------------------------------------------------*/
/* 3. Simple pixel write (boundschecked) */
static inline void set_pixel(uint8_t *fb, int x, int y, uint32_t col)
{
if (x < 0 || x >= FB_W || y < 0 || y >= FB_H)
return; /* ignore outofbounds writes */
uint32_t *dst = (uint32_t *)(fb + (y * FB_W + x) * BPP);
*dst = col;
}
/*---------------------------------------------------------------*/
/* 4. Thick line Bresenham + square brush */
static void draw_thick_line(uint8_t *fb,
int x0, int y0,
int x1, int y1,
uint32_t col,
int thickness)
{
int dx = abs(x1 - x0);
int dy = -abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx + dy; /* error term */
while (1) {
/* paint a square centred on the current pixel */
for (int ty = -thickness/2; ty <= thickness/2; ++ty) {
for (int tx = -thickness/2; tx <= thickness/2; ++tx) {
set_pixel(fb, x0 + tx, y0 + ty, col);
}
}
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err;
if (e2 >= dy) { err += dy; x0 += sx; }
if (e2 <= dx) { err += dx; y0 += sy; }
}
}
/*---------------------------------------------------------------*/
/* 5. Build the whole framebuffer */
static void build_framebuffer(uint8_t *fb)
{
/* 5.1 background = opaque black */
for (size_t i = 0; i < FRAMEBUF_SIZE; ++i) fb[i] = 0;
uint32_t *pix = (uint32_t *)fb;
for (size_t i = 0; i < FB_W * FB_H; ++i) pix[i] = COL_BLACK;
/* 5.2 geometry that does NOT depend on the angle */
const int usable_h = FB_H - MARGIN_TOP - MARGIN_BOTTOM; /* 400 */
const int rect_h = usable_h; /* 400 */
const int rect_w = rect_h / 8; /* ≈33 */
const int half_w = rect_w / 2;
const int total_rect_w = NUM_RECTS * rect_w + (NUM_RECTS - 1) * SPACING;
const int x0_start = (FB_W - total_rect_w) / 2; /* centre strip */
const int y_top = MARGIN_TOP;
const int y_bottom = FB_H - MARGIN_BOTTOM - 1; /* inclusive */
/* 5.3 draw each rectangle */
for (int i = 0; i < NUM_RECTS; ++i) {
/* ---- centre X (kept fixed while tilting) ---- */
int orig_x0 = x0_start + i * (rect_w + SPACING);
int cx = orig_x0 + half_w; /* centre X coordinate */
/* ---- angle in radians, sin and cos ---- */
double angle_rad = rotAnglesDeg[i] * M_PI / 180.0;
double sin_a = sin(angle_rad);
double cos_a = cos(angle_rad);
/* ---- vertical offset applied to the endpoints of the top/bottom lines ---- */
double offset = sin_a * half_w; /* sin(angle) * (RECT_W/2) */
/* ---- X offsets for the slanted top/bottom edges ---- */
double x_offset = cos_a * half_w; /* cos(angle) * (RECT_W/2) */
/* ---- compute the four endpoints (rounded to nearest integer) ---- */
int x0_top = (int)round(cx - x_offset);
int y0_top = (int)round(y_top + offset);
int x1_top = (int)round(cx + x_offset);
int y1_top = (int)round(y_top - offset);
int x0_bot = (int)round(cx - x_offset);
int y0_bot = (int)round(y_bottom + offset);
int x1_bot = (int)round(cx + x_offset);
int y1_bot = (int)round(y_bottom - offset);
/* ---- draw the four sides (thick) ---- */
draw_thick_line(fb, x0_top, y0_top, x1_top, y1_top,
COL_WHITE, LINE_THICKNESS); /* top */
draw_thick_line(fb, x0_bot, y0_bot, x1_bot, y1_bot,
COL_WHITE, LINE_THICKNESS); /* bottom*/
/* vertical sides use the X coordinates of the *top* points;
the Ycoordinates are taken from the corresponding top/bottom
endpoint, therefore they are automatically different when the
angle ≠ 0 or 180. */
draw_thick_line(fb, x0_top, y0_top, x0_bot, y0_bot,
COL_WHITE, LINE_THICKNESS); /* left */
draw_thick_line(fb, x1_top, y1_top, x1_bot, y1_bot,
COL_WHITE, LINE_THICKNESS); /* right */
}
}
/*---------------------------------------------------------------*/
/* 6. Send the framebuffer over TCP (lengthprefixed) */
static int send_framebuffer(const uint8_t *fb, size_t len,
const char *host, uint16_t port)
{
struct sockaddr_in srv;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
return -1;
}
memset(&srv, 0, sizeof(srv));
srv.sin_family = AF_INET;
srv.sin_port = htons(port);
if (inet_pton(AF_INET, host, &srv.sin_addr) <= 0) {
perror("inet_pton");
close(sock);
return -1;
}
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
perror("connect");
close(sock);
return -1;
}
/* ---- optional 4byte length prefix (bigendian) ---- */
uint32_t be_len = htonl((uint32_t)len);
if (write(sock, &be_len, sizeof(be_len)) != sizeof(be_len)) {
perror("write length prefix");
close(sock);
return -1;
}
/* ---- send the raw buffer ---- */
size_t sent = 0;
while (sent < len) {
ssize_t n = write(sock, fb + sent, len - sent);
if (n <= 0) {
perror("write framebuffer");
close(sock);
return -1;
}
sent += n;
}
close(sock);
return 0;
}
/*---------------------------------------------------------------*/
int main(void)
{
/* ---------------------------------------------------------------
* 1⃣ Start ArtNet receiver it will fill the globals
* --------------------------------------------------------------- */
if (!artnet_init()) {
fprintf(stderr, "Failed to start ArtNet receiver\n");
return EXIT_FAILURE;
}
uint8_t *framebuf = malloc(FRAMEBUF_SIZE);
if (!framebuf) {
fprintf(stderr, "Failed to allocate framebuffer (%zu bytes)\n",
(size_t)FRAMEBUF_SIZE);
return EXIT_FAILURE;
}
while(1){
//for(uint8_t i=0; i<sizeof(rotAnglesDeg)/sizeof(rotAnglesDeg[0]); i++)
// rotAnglesDeg[i]=(rotAnglesDeg[i]+3)%180;
build_framebuffer(framebuf);
if (send_framebuffer(framebuf, FRAMEBUF_SIZE, "127.0.0.1", 12345) != 0) {
fprintf(stderr, "Failed to send framebuffer\n");
free(framebuf);
return EXIT_FAILURE;
}
usleep(100000);
}
printf("Framebuffer (%zu bytes) sent successfully.\n", (size_t)FRAMEBUF_SIZE);
free(framebuf);
return EXIT_SUCCESS;
}