--- /dev/null
+/*
+ A circular queue type for storing void pointers.
+*/
+
+// --------------------------------------------------------------------------------
+// instance state
+
+typedef struct {
+ void **buffer; // The buffer that stores void pointers
+ size_t capacity; // Maximum number of elements in the queue
+ size_t front; // Index of the front element
+ size_t rear; // Index of the rear element
+ size_t size; // Current number of elements in the queue
+} CircularQueue;
+
+// --------------------------------------------------------------------------------
+// interface
+
+// Allocates and initializes a circular queue with a given capacity
+CircularQueue *CircularQueue·alloc(size_t capacity){
+ CircularQueue *queue = malloc(sizeof(CircularQueue));
+ if(!queue) return NULL; // Allocation failed
+
+ queue->buffer = malloc(capacity * sizeof(void *));
+ if( !queue->buffer ){
+ free(queue);
+ return NULL; // Buffer allocation failed
+ }
+
+ queue->capacity = capacity;
+ queue->front = 0;
+ queue->rear = capacity - 1;
+ queue->size = 0;
+ return queue; // Success
+}
+
+// each queue `alloc` callshould have a matching dealloc call.
+void CircularQueue·dealloc(CircularQueue **queue){
+ if(!*queue) return;
+ free((*queue)->buffer);
+ free(*queue);
+ *queue = NULL;
+}
+
+int CircularQueue·is_empty(CircularQueue *queue){
+ return queue->size == 0;
+}
+
+int CircularQueue·is_full(CircularQueue *queue){
+ return queue->size == queue->capacity;
+}
+
+int CircularQueue·enqueue(CircularQueue *queue ,void *item){
+ if( CircularQueue·is_full(queue) ) return -1; // Queue is full
+
+ queue->rear = (queue->rear + 1) % queue->capacity;
+ queue->buffer[queue->rear] = item;
+ queue->size++;
+ return 0; // Success
+}
+
+void *CircularQueue·dequeue(CircularQueue *queue){
+ if( CircularQueue·is_empty(queue) ) return NULL; // Queue is empty
+
+ void *item = queue->buffer[queue->front];
+ queue->front = (queue->front + 1) % queue->capacity;
+ queue->size--;
+ return item;
+}
+
+void *CircularQueue·peek(CircularQueue *queue){
+ if( CircularQueue·is_empty(queue) ) return NULL; // Queue is empty
+
+ return queue->buffer[queue->front];
+}
--- /dev/null
+/*
+ A display frame type.
+
+ Frames to be read or written are passed by reference.
+
+ Functions that allocate a new frame typically return a reference to the
+ newly allocated frame.
+
+ Functions that accept an argument for holding an newly allocated frame, that
+ argument will be a pointer to the frame reference. E.g. f(Frame **generated),
+ *generated = NULL or *generated = malloc(...).
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+// --------------------------------------------------------------------------------
+// enum for frame types
+
+typedef enum {
+ FRAME_TYPE_LOCAL
+ ,FRAME_TYPE_DRM
+} FrameType;
+
+// --------------------------------------------------------------------------------
+// instance state
+
+typedef struct{
+ uint32_t width;
+ uint32_t height;
+ PixelRGBA *data; // Pointer to pixel data (local or DRM-mapped buffer)
+ FrameType type; // Frame type: local or DRM
+ int fd; // File descriptor for DRM (only relevant for DRM frames)
+ uint32_t drm_fb_id; // DRM framebuffer ID (only relevant for DRM frames)
+} FrameRGBA;
+
+// --------------------------------------------------------------------------------
+// interface
+
+// Allocate a local frame (in system memory)
+FrameRGBA *FrameRGBA·alloc_local(int width ,int height){
+ if(width == 0 || height == 0){
+ fprintf(stderr ,"FrameRGBA·alloc_local:: invalid dimensions\n");
+ return NULL;
+ }
+
+ FrameRGBA *frame = malloc(sizeof(FrameRGBA));
+ if(!frame){
+ fprintf(stderr ,"FrameRGBA·alloc_local:: failed to allocate frame\n");
+ return NULL;
+ }
+
+ frame->width = width;
+ frame->height = height;
+ frame->data = malloc(width * height * sizeof(PixelRGBA));
+ if(!frame->data){
+ fprintf(stderr ,"FrameRGBA·alloc_local:: failed to allocate pixel data\n");
+ free(frame);
+ return NULL;
+ }
+
+ frame->type = FRAME_TYPE_LOCAL;
+ frame->fd = -1;
+ frame->drm_fb_id = 0;
+ return frame;
+}
+
+// Allocate a frame from DRM
+FrameRGBA *FrameRGBA·alloc_capture_drm(int fd ,uint32_t width ,uint32_t height ,uint32_t buffer_id){
+ if(width == 0 || height == 0){
+ fprintf(stderr ,"FrameRGBA·alloc_capture_drm:: invalid dimensions\n");
+ return NULL;
+ }
+
+ FrameRGBA *frame = malloc(sizeof(FrameRGBA));
+ if(!frame){
+ fprintf(stderr ,"FrameRGBA·alloc_capture_drm:: failed to allocate frame\n");
+ return NULL;
+ }
+
+ frame->width = width;
+ frame->height = height;
+ frame->fd = fd;
+ frame->data = mmap(NULL, width * height * sizeof(PixelRGBA), PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer_id);
+ if(frame->data == MAP_FAILED){
+ fprintf(stderr ,"FrameRGBA·alloc_capture_drm:: failed to mmap framebuffer\n");
+ free(frame);
+ return NULL;
+ }
+
+ frame->type = FRAME_TYPE_DRM;
+ frame->drm_fb_id = buffer_id;
+ return frame;
+}
+
+// Deallocate a frame (local or DRM)
+void FrameRGBA·dealloc(FrameRGBA **frame){
+ if(!frame || !(*frame)) return;
+
+ if((*frame)->type == FRAME_TYPE_LOCAL){
+ free((*frame)->data); // Free local pixel data
+ }else if((*frame)->type == FRAME_TYPE_DRM){
+ munmap((*frame)->data, (*frame)->width * (*frame)->height * sizeof(PixelRGBA)); // Unmap DRM buffer
+ }
+
+ free(*frame);
+ *frame = NULL;
+}
+
+// Method to send a frame to the display
+int FrameRGBA·display(FrameRGBA *frame ,int fd ,uint32_t crtc_id ,uint32_t connector_id){
+ if(frame->type != FRAME_TYPE_DRM){
+ fprintf(stderr ,"FrameRGBA·display:: can only display DRM frames\n");
+ return -1;
+ }
+
+ // Use drmModeSetCrtc to display the DRM framebuffer
+ int ret = drmModeSetCrtc(fd, crtc_id, frame->drm_fb_id, 0, 0, &connector_id, 1, NULL);
+ if(ret){
+ perror("drmModeSetCrtc failed");
+ return ret;
+ }
+
+ return 0; // Success
+}
+
+// Copy one frame to another
+void FrameRGBA·copy(FrameRGBA *from_frame ,FrameRGBA **to_frame_arg){
+ if( !to_frame_arg || !(*to_frame_arg) || !from_frame ) return;
+
+ FrameRGBA *to_frame = *to_frame_arg;
+ uint32_t width ,height;
+ FrameRGBA·dimensions(from_frame ,&width ,&height);
+
+ if(to_frame->width != width || to_frame->height != height){
+ fprintf(stderr ,"FrameRGBA·copy:: dimensions mismatch\n");
+ return;
+ }
+
+ memcpy(to_frame->data, from_frame->data, width * height * sizeof(PixelRGBA));
+}
+
+// Get frame dimensions
+void FrameRGBA·dimensions(FrameRGBA *frame ,uint32_t *width ,uint32_t *height){
+ if(!frame){
+ *width = 0;
+ *height = 0;
+ return;
+ }
+ *width = frame->width;
+ *height = frame->height;
+}
+
+// if i and j walk out of frame returns NULL
+PixelRGBA *FrameRGBA·access(FrameRGBA *from_frame ,uint32_t i ,uint32_t j){
+ if(!from_frame) return NULL;
+ uint32_t width ,height;
+ FrameRGBA·dimensions(from_frame ,&width ,&height);
+ if(i >= width) return NULL;
+ if(j >= height) return NULL;
+ return &(from_frame->data[j * width + i]);
+}
+
+PixelRGBA FrameRGBA·read(FrameRGBA *from_frame ,uint32_t i ,uint32_t j){
+ if(!from_frame){
+ fprintf(stderr, "FrameRGBA·read:: warning, empty frame, so returning a neutral pixel.");
+ return PixelRGBA·neutral;
+ }
+ return *FrameRGBA·access(from_frame ,i ,j);
+}
+
+void FrameRGBA·write(FrameRGBA *to_frame ,uint32_t i ,uint32_t j ,PixelRGBA px){
+ if(!to_frame){
+ fprintf(stderr, "FrameRGBA·write:: warning, empty frame, write ignored.");
+ return;
+ }
+ *FrameRGBA·access(to_frame ,i ,j) = px;
+}
+
+// search frame for condition where f returns a non-null pointer
+// f is handed successive pointers to pixels in the frame.
+// f is given the frame in case it needs context.
+// f is given an argument is it is difficult to curry functions in C.
+void *FrameRGBA·exist
+(
+ FrameRGBA *frame
+ ,void *(*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg)
+ ,void *arg
+ ){
+
+ if(!frame) return NULL;
+
+ void *item;
+ uint32_t width ,height;
+ FrameRGBA·dimensions(frame ,&width ,&height);
+ uint32_t i = 0;
+ uint32_t j = 0;
+ do{
+ PixelRGBA *px = FrameRGBA·access(frame ,i ,j);
+ if( item=f(frame ,px ,arg) ) return item;
+
+ if( i == width - 1 ){
+ i = 0;
+ j++;
+ }else{
+ i++;
+ }
+ }while(j < height);
+ return NULL;
+}
+
+typedef struct{
+ void (*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg);
+ void *arg;
+} every_arg_t;
+void *every_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg0){
+ every_arg_t *ea = arg0;
+ ea->f(frame ,px ,ea->arg);
+ return NULL;
+}
+void FrameRGBA·every
+(
+ FrameRGBA *frame
+ ,void (*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg)
+ ,void *arg
+ ){
+ every_arg_t ea = {.f = f ,.arg = arg};
+ FrameRGBA·exist(frame ,every_f ,&ea);
+}
+
+void fill_f(FrameRGBA *to_frame ,PixelRGBA *to_px ,void *arg){
+ if(!to_frame) return;
+ PixelRGBA *px = arg;
+ *to_px = *px;
+}
+void FrameRGBA·fill(FrameRGBA *to_frame ,PixelRGBA *fill_px){
+ if(!to_frame) return;
+ FrameRGBA·every(to_frame ,fill_f ,fill_px);
+}
+
+void FrameRGBA·fill_black(FrameRGBA *to_frame){
+ if(!to_frame) return;
+ FrameRGBA·fill(to_frame ,&black);
+}
+
+void FrameRGBA·fill_white(FrameRGBA *to_frame){
+ if(!to_frame) return;
+ FrameRGBA·fill(to_frame ,&white);
+}
+
+void fill_random_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg){
+ *px = PixelRGBA·random();
+}
+void FrameRGBA·fill_random(FrameRGBA *frame){
+ if(!frame) return;
+ FrameRGBA·every(frame ,fill_random_f ,NULL);
+}
--- /dev/null
+/*
+ Pixel types
+
+ Currently implements only RGBA, which are specified by the hardware; hence
+ they must be packed as: 0xAARRGGBB.
+
+ For the sake of consistency, pixels are always passed by reference. Functions
+ that generate new pixels, return the new pixel by value.
+*/
+
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h> // for rand()
+#include <time.h> // for seeding rand()
+
+//--------------------------------------------------------------------------------
+// instance state
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ typedef union{
+ uint32_t aggregate;
+ struct __attribute__((packed)) {
+ uint8_t blue;
+ uint8_t green;
+ uint8_t red;
+ uint8_t alpha;
+ };
+ } PixelRGBA;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ typedef union{
+ uint32_t aggregate;
+ struct __attribute__((packed)) {
+ uint8_t alpha;
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ };
+ } PixelRGBA;
+#else
+ #warning "Pixel.c: unknown __BYTE_ORDER__ found when packing a PixelRGBA. Probably catastrophic, but compilation will continue so as to look for more errors."
+#endif
+
+//--------------------------------------------------------------------------------
+// interface
+
+PixelRGBA PixelRGBA·black = {.aggregate = 0xff000000};
+PixelRGBA PixelRGBA·white = {.aggregate = 0xffffffff};
+PixelRGBA PixelRGBA·neutral = {.aggregate = 0x80808080};
+
+// makes a Pixel with a random value.
+PixelRGBA PixelRGBA·random(){
+ PixelRGBA px;
+ #if RAND_MAX < 0xFFFFFFFF
+ px.aggregate = (((uint16_t)rand() & 0xFFFF) << 16) | ((uint16_t)rand() & 0xFFFF);
+ #else
+ px.aggregate = (uint32_t)rand();
+ #endif
+ #if RAND_MAX < 0xFFFF
+ #warning "Pixel.c: warning standard rand() claims to deliver less than 16-bit values."
+ #endif
+ return px;
+}
+
+// initialize a Pixel given the individual field values
+void PixelRGBA·structure
+ (
+ PixelRGBA *px
+ ,uint8_t red
+ ,uint8_t green
+ ,uint8_t blue
+ ,uint8_t alpha
+ ){
+ px->blue = blue;
+ px->green = green;
+ px->red = red;
+ px->alpha = alpha;
+}
+
+void PixelRGBA·copy_from_uint32(PixelRGBA *to_px ,uint32_t from_uint32){
+ to_px->aggregate = from_uint32;
+}
+
+void PixelRGBA·destructure
+ (
+ PixelRGBA *px
+ ,uint8_t *red
+ ,uint8_t *green
+ ,uint8_t *blue
+ ,uint8_t *alpha
+ ){
+ *blue = px->blue;
+ *green = px->green;
+ *red = px->red;
+ *alpha = px->alpha;
+}
+
+bool PixelRGBA·equals(const PixelRGBA *px0 ,const PixelRGBA *px1) {
+ return px0->aggregate == px1->aggregate;
+}
+
+PixelRGBA PixelRGBA·blend(const PixelRGBA *px0 ,const PixelRGBA *px1) {
+ PixelRGBA px2;
+ px2.alpha = (px0->alpha + px1->alpha) / 2;
+ px2.red = (px0->red + px1->red) / 2;
+ px2.green = (px0->green + px1->green) / 2;
+ px2.blue = (px0->blue + px1->blue) / 2;
+ return px2;
+}
+
+PixelRGBA PixelRGBA·invert(const PixelRGBA *px0) {
+ PixelRGBA px2;
+ px2.red = 255 - px0->red;
+ px2.green = 255 - px0->green;
+ px2.blue = 255 - px0->blue;
+ px2.alpha = px0->alpha;
+ return px2;
+}
+
+PixelRGBA PixelRGBA·map_to_grayscale(const PixelRGBA *px0) {
+ uint8_t gray_level = (uint8_t)(0.299 * px0->red + 0.587 * px0->green + 0.114 * px0->blue);
+ PixelRGBA px1 =
+ {
+ .red = gray_level
+ ,.green = gray_level
+ ,.blue = gray_level
+ ,.alpha = px0->alpha
+ };
+ return px1;
+}
--- /dev/null
+// gcc -I/usr/include/libdrm echo_frames.c -o echo_frames -ldrm
+// ./drm_info
+
+
+
+/*
+Echos frames.
+
+Proof of principles: grabs frame, does nothing to it, then displays it, in a loop.
+
+*/
+
+
+#include "Pixel.c"
+#include "Frame.c"
+#include <time.h> // For timing the loop
+
+int main() {
+ int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); // Open the DRM device
+ if(fd < 0){
+ perror("Failed to open DRM device");
+ return -1;
+ }
+
+ // Allocate a DRM captured frame
+ FrameRGBA *drm_frame = FrameRGBA·alloc_capture_drm(fd, 1920, 1080, 0); // Replace 0 with actual buffer ID
+ if(!drm_frame){
+ fprintf(stderr, "Failed to allocate DRM frame\n");
+ close(fd);
+ return -1;
+ }
+
+ // Allocate a local frame for copying
+ FrameRGBA *local_frame = FrameRGBA·alloc_local(1920, 1080);
+ if(!local_frame){
+ fprintf(stderr, "Failed to allocate local frame\n");
+ FrameRGBA·dealloc(&drm_frame);
+ close(fd);
+ return -1;
+ }
+
+ // Get current time for the 5-second loop duration
+ time_t start_time = time(NULL);
+
+ // Loop for 5 seconds
+ while(time(NULL) - start_time < 5){
+ // Capture from the DRM frame and copy to the local frame
+ FrameRGBA·copy(drm_frame, &local_frame);
+
+ // Display the local frame (for simplicity, we use the DRM frame display method)
+ if(FrameRGBA·display(local_frame, fd, 0, 0) < 0){ // Replace with proper crtc and connector IDs
+ fprintf(stderr, "Failed to display local frame\n");
+ FrameRGBA·dealloc(&drm_frame);
+ FrameRGBA·dealloc(&local_frame);
+ close(fd);
+ return -1;
+ }
+ }
+
+ // Clean up and close DRM device
+ FrameRGBA·dealloc(&drm_frame);
+ FrameRGBA·dealloc(&local_frame);
+ close(fd);
+
+ return 0;
+}
--- /dev/null
+/*
+ A display frame type.
+
+ Frames to be read or written are passed by reference.
+
+ Functions that allocate a new frame typically return a reference to the
+ newly allocated frame.
+
+ Functions that accept an argument for holding an newly allocated frame, that
+ argument will be a pointer to the frame reference. E.g. f(Frame **generated),
+ *generated = NULL or *gnerated = malloc(...).
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// --------------------------------------------------------------------------------
+// instance state
+
+typedef struct{
+ uint32_t width;
+ uint32_t height;
+ PixelRGBA data[];
+} FrameRGBA;
+
+// --------------------------------------------------------------------------------
+// interface
+
+FrameRGBA *FrameRGBA·alloc(int width ,int height){
+ bool null_frame = false;
+
+ if( width == 0 ){
+ fprintf(stderr ,"FrameRGBA·alloc:: frame width of zero, so returning a null frame pointer\n");
+ null_frame = true;
+ }
+ if( height == 0 ){
+ fprintf(stderr ,"FrameRGBA·alloc:: frame height of zero, so returning a null frame pointer\n");
+ null_frame = true;
+ }
+ if(null_frame) return NULL;
+
+ size_t size = sizeof(FrameRGBA) + width * height * sizeof(PixelRGBA);
+ FrameRGBA *frame = malloc(size);
+ if(!frame){
+ fprintf(stderr ,"FrameRGBA·alloc::failed to allocate frame\n");
+ return NULL;
+ }
+
+ frame->width = width;
+ frame->height = height;
+ return frame;
+}
+
+FrameRGBA *FrameRGBA·alloc_same(FrameRGBA *frame){
+ if(!frame) return NULL;
+ return FrameRGBA·alloc(frame->width ,frame->height);
+}
+
+void FrameRGBA·dealloc(FrameRGBA **frame){
+ free(*frame);
+ *frame = NULL;
+}
+
+void FrameRGBA·dimensions(FrameRGBA *frame ,uint32_t *width ,uint32_t *height){
+ if(!frame){
+ *width = 0;
+ *height = 0;
+ return;
+ }
+ *width = frame->width;
+ *height = frame->height;
+}
+
+// both frames have already been allocated
+void FrameRGBA·copy(FrameRGBA *from_frame ,FrameRGBA **to_frame_arg){
+ if( to_frame_arg == NULL ) return;
+ if(*to_frame_arg == NULL) return;
+ if( from_frame == NULL ){
+ *to_frame_arg = NULL;
+ return;
+ }
+ FrameRGBA *to_frame = *to_frame_arg;
+
+ uint32_t width ,height;
+ FrameRGBA·dimensions(from_frame ,&width ,&height);
+ if(to_frame->width < width){
+ width = to_frame->width;
+ fprintf(stderr ,"FrameRGBA·copy:: from_frame is wider than to_frame, there will be truncation\n");
+ }else if(to_frame->width > width){
+ fprintf(stderr ,"FrameRGBA·copy:: to_frame is wider, so stale uninitialized pixels will remain at the end of scan lines.\n");
+ }
+ if(to_frame->height < height){
+ height = to_frame->height;
+ fprintf(stderr ,"FrameRGBA·copy:: from_frame is taller than to_frame, there will be truncation\n");
+ }else if(to_frame->height > height){
+ fprintf(stderr ,"FrameRGBA·copy:: to_frame is taller, so stale or uninitialized scan lines will remain at the bottom of the frame.\n");
+ }
+
+ uint32_t j = 0;
+ uint32_t offset = 0;
+ do{
+ memcpy(
+ to_frame->data + offset,
+ from_frame->data + offset,
+ width * sizeof(PixelRGBA)
+ );
+ offset += width;
+ j++;
+ }while(j < height);
+}
+
+// if i and j walk out of frame returns NULL
+PixelRGBA *FrameRGBA·access(FrameRGBA *from_frame ,uint32_t i ,uint32_t j){
+ if(!from_frame) return NULL;
+ uint32_t width ,height;
+ FrameRGBA·dimensions(from_frame ,&width ,&height);
+ if(i >= width) return NULL;
+ if(j >= height) return NULL;
+ return &(from_frame->data[j * width + i]);
+}
+
+PixelRGBA FrameRGBA·read(FrameRGBA *from_frame ,uint32_t i ,uint32_t j){
+ if(!from_frame){
+ fprintf(stderr, "FrameRGBA·read:: warning, empty frame, so returning a neutral pixel.");
+ return PixelRGBA·neutral;
+ }
+ return *FrameRGBA·access(from_frame ,i ,j);
+}
+
+void FrameRGBA·write(FrameRGBA *to_frame ,uint32_t i ,uint32_t j ,PixelRGBA px){
+ if(!to_frame){
+ fprintf(stderr, "FrameRGBA·write:: warning, empty frame, write ignored.");
+ return;
+ }
+ *FrameRGBA·access(to_frame ,i ,j) = px;
+}
+
+// search frame for condition where f returns a non-null pointer
+// f is handed successive pointers to pixels in the frame.
+// f is given the frame in case it needs context.
+// f is given an argument is it is difficult to curry functions in C.
+void *FrameRGBA·exist
+(
+ FrameRGBA *frame
+ ,void *(*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg)
+ ,void *arg
+ ){
+
+ if(!frame) return NULL;
+
+ void *item;
+ uint32_t width ,height;
+ FrameRGBA·dimensions(frame ,&width ,&height);
+ uint32_t i = 0;
+ uint32_t j = 0;
+ do{
+ PixelRGBA *px = FrameRGBA·access(frame ,i ,j);
+ if( item=f(frame ,px ,arg) ) return item;
+
+ if( i == width - 1 ){
+ i = 0;
+ j++;
+ }else{
+ i++;
+ }
+ }while(j < height);
+ return NULL;
+}
+
+typedef struct{
+ void (*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg);
+ void *arg;
+} every_arg_t;
+void *every_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg0){
+ every_arg_t *ea = arg0;
+ ea->f(frame ,px ,ea->arg);
+ return NULL;
+}
+void FrameRGBA·every
+(
+ FrameRGBA *frame
+ ,void (*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg)
+ ,void *arg
+ ){
+ every_arg_t ea = {.f = f ,.arg = arg};
+ FrameRGBA·exist(frame ,every_f ,&ea);
+}
+
+void fill_f(FrameRGBA *to_frame ,PixelRGBA *to_px ,void *arg){
+ if(!to_frame) return;
+ PixelRGBA *px = arg;
+ *to_px = *px;
+}
+void FrameRGBA·fill(FrameRGBA *to_frame ,PixelRGBA *fill_px){
+ if(!to_frame) return;
+ FrameRGBA·every(to_frame ,fill_f ,fill_px);
+}
+
+void FrameRGBA·fill_black(FrameRGBA *to_frame){
+ if(!to_frame) return;
+ FrameRGBA·fill(to_frame ,&black);
+}
+
+void FrameRGBA·fill_white(FrameRGBA *to_frame){
+ if(!to_frame) return;
+ FrameRGBA·fill(to_frame ,&white);
+}
+
+void fill_random_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg){
+ *px = PixelRGBA·random();
+}
+void FrameRGBA·fill_random(FrameRGBA *frame){
+ if(!frame) return;
+ FrameRGBA·every(frame ,fill_random_f ,NULL);
+}
--- /dev/null
+/*
+A transform accepts a history context of past displayed frames, and the most
+recent frame, aka the curr_frame. It then creates and outputs the
+transformed_frame.
+
+All the transform function argument interface objects are allocated before
+the call, and are managed external to the transform.
+
+A programmer should not write transform code that modifies the context
+window or the curr_frame.
+
+If the identity_transform returns a non-zero value, the transform
+result will externally be discarded, and the curr_frame will be sent
+to the display instead of the transformed_frame.
+*/
+
+// curr_frame -> transformed_frame
+int identity_transform
+(
+ CircularQueue *context
+ ,FrameRGBA *curr_frame
+ ,FrameRGBA **transformed_frame
+ ,void *args
+ ){
+ FrameRGBA·copy(curr_frame ,transformed_frame);
+ return 0; // Success
+}
+
+// color curr_frame -> grayscale transformed_frame
+void grayscale_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg){
+ *px = PixelRGBA·map_to_grayscale(px);
+}
+int grayscale_transform
+(
+ CircularQueue *context
+ ,FrameRGBA *curr_frame
+ ,FrameRGBA **transformed_frame
+ ,void *args
+ ){
+ FrameRGBA·copy(curr_frame ,transformed_frame);
+ FrameRGBA·every(*transformed_frame ,grayscale_f ,NULL);
+ return 0; // Success
+}
// gcc -I/usr/include/libdrm transform_frame_stream.c -o transform_frame_stream -ldrm
// ./transform_frame_stream
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <xf86drm.h>
-#include <xf86drmMode.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <endian.h>
+/*--------------------------------------------------------------------------------
+ Transform_frame_stream.c
-// --------------------------------------------------------------------------------
-// Pixel
+ This program accepts a frame stream. It transforms each frame in the stream,
+ one by one, and then sends each transformed frame to the display.
-// These pixels have hardware support ,and thus must be packed in the order
-// expected by the hardware.
-// 0xAARRGGBB
-#if __BYTE_ORDER == __LITTLE_ENDIAN
- typedef union{
- uint32_t aggregate;
- struct __attribute__((packed)) {
- uint8_t blue;
- uint8_t green;
- uint8_t red;
- uint8_t alpha;
- };
- } PixelRGBA;
-#elif __BYTE_ORDER == __BIG_ENDIAN
- typedef union{
- uint32_t aggregate;
- struct __attribute__((packed)) {
- uint8_t alpha;
- uint8_t red;
- uint8_t green;
- uint8_t blue;
- };
- } PixelRGBA;
-#else
- #warning "Unknown __BYTE_ORDER__ found when packing a PixelRGBA."
-#endif
+ The program keeps a context window of `n` past transformed frames.
+
+ There are a number of frame transform functions to chose from.
-PixelRGBA black = {.aggregate = 0xff000000};
-PixelRGBA white = {.aggregate = 0xffffffff};
+ Each transform frame function accepts a context window of frames and the next
+ frame to be processed from the frame stream, aka the curr_frame. The
+ transform frame function optionally examines the context window, transforms
+ cur_frame, and returns the transformed_frame.
-void PixelRGBA·structure
- (
- PixelRGBA *px
- ,uint8_t red
- ,uint8_t green
- ,uint8_t blue
- ,uint8_t alpha
- ){
- px->blue = blue;
- px->green = green;
- px->red = red;
- px->alpha = alpha;
-}
-void PixelRGBA·destructure
- (
- PixelRGBA *px
- ,uint8_t *red
- ,uint8_t *green
- ,uint8_t *blue
- ,uint8_t *alpha
- ){
- *blue = px->blue;
- *green = px->green;
- *red = px->red;
- *alpha = px->alpha;
-}
-
-PixelRGBA PixelRGBA·random(){
- PixelRGBA px;
- px.aggregate = rand();
- return px;
-}
-
-
-
-// --------------------------------------------------------------------------------
-// Frame
-
-typedef struct{
- uint32_t width;
- uint32_t height;
- PixelRGBA data[];
-} FrameRGBA;
-
-FrameRGBA *FrameRGBA·alloc(int width ,int height){
- bool null_frame = false;
-
- if( width == 0 ){
- fprintf(stderr ,"FrameRGBA·alloc:: frame width of zero, so returning a null frame pointer\n");
- null_frame = true;
- }
- if( height == 0 ){
- fprintf(stderr ,"FrameRGBA·alloc:: frame height of zero, so returning a null frame pointer\n");
- null_frame = true;
- }
- if(null_frame) return NULL;
-
- size_t size = sizeof(FrameRGBA) + width * height * sizeof(PixelRGBA);
- FrameRGBA *frame = malloc(size);
- if(!frame){
- fprintf(stderr ,"FrameRGBA·alloc::failed to allocate frame\n");
- return NULL;
- }
-
- frame->width = width;
- frame->height = height;
- return frame;
-}
+ Each transformed frame is then sent to be displayed, and becomes the
+ most recent member of the context queue.
+*/
-FrameRGBA *FrameRGBA·alloc_same(FrameRGBA *frame){
- if(!frame) return NULL;
- return FrameRGBA·alloc(frame->width ,frame->height);
-}
-
-void FrameRGBA·dealloc(FrameRGBA *frame){
- free(frame);
-}
-
-void FrameRGBA·dimensions(FrameRGBA *frame ,uint32_t *width ,uint32_t *height){
- if(!frame){
- *width = 0;
- *height = 0;
- return;
- }
- *width = frame->width;
- *height = frame->height;
-}
-
-// both frames have already been allocated
-void FrameRGBA·copy(FrameRGBA *from_frame ,FrameRGBA **to_frame_arg){
- if( to_frame_arg == NULL ) return;
- if(*to_frame_arg == NULL) return;
- if( from_frame == NULL ){
- *to_frame_arg = NULL;
- return;
- }
- FrameRGBA *to_frame = *to_frame_arg;
-
- uint32_t width ,height;
- FrameRGBA·dimensions(from_frame ,&width ,&height);
- if(to_frame->width < width){
- width = to_frame->width;
- fprintf(stderr ,"FrameRGBA·copy:: from_frame is wider than to_frame, there will be truncation\n");
- }else if(to_frame->width > width){
- fprintf(stderr ,"FrameRGBA·copy:: to_frame is wider, so stale uninitialized pixels will remain at the end of scan lines.\n");
- }
- if(to_frame->height < height){
- height = to_frame->height;
- fprintf(stderr ,"FrameRGBA·copy:: from_frame is taller than to_frame, there will be truncation\n");
- }else if(to_frame->height > height){
- fprintf(stderr ,"FrameRGBA·copy:: to_frame is taller, so stale or uninitialized scan lines will remain at the bottom of the frame.\n");
- }
-
- uint32_t j = 0;
- uint32_t offset = 0;
- do{
- memcpy(
- to_frame->data + offset,
- from_frame->data + offset,
- width * sizeof(PixelRGBA)
- );
- offset += width;
- j++;
- }while(j < height);
-}
-
-// if i and j walk out of frame returns NULL
-PixelRGBA *FrameRGBA·access(FrameRGBA *from_frame ,uint32_t i ,uint32_t j){
- uint32_t width ,height;
- FrameRGBA·dimensions(from_frame ,&width ,&height);
- if(i >= width) return NULL;
- if(j >= height) return NULL;
- return &(from_frame->data[j * width + i]);
-}
-
-PixelRGBA FrameRGBA·read(FrameRGBA *from_frame ,uint32_t i ,uint32_t j){
- return *FrameRGBA·access(from_frame ,i ,j);
-}
-
-void FrameRGBA·write(FrameRGBA *to_frame ,uint32_t i ,uint32_t j ,PixelRGBA px){
- *FrameRGBA·access(to_frame ,i ,j) = px;
-}
-
-// search frame for condition where f returns a non-null pointer
-// f is handed successive pointers to pixels in the frame.
-// f is given the frame in case it needs context.
-// f is given an argument is it is difficult to curry functions in C.
-void *FrameRGBA·exist
-(
- FrameRGBA *frame
- ,void *(*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg)
- ,void *arg
- ){
-
- if(!frame) return NULL;
-
- void *item;
- uint32_t width ,height;
- FrameRGBA·dimensions(frame ,&width ,&height);
- uint32_t i = 0;
- uint32_t j = 0;
- do{
- PixelRGBA *px = FrameRGBA·access(frame ,i ,j);
- if( item=f(frame ,px ,arg) ) return item;
-
- if( i == width - 1 ){
- i = 0;
- j++;
- }else{
- i++;
- }
- }while(j < height);
- return NULL;
-}
-
-typedef struct{
- void (*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg);
- void *arg;
-} every_arg_t;
-
-void *every_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg0){
- every_arg_t *ea = arg0;
- ea->f(frame ,px ,ea->arg);
- return NULL;
-}
-void FrameRGBA·every
-(
- FrameRGBA *frame
- ,void (*f)(FrameRGBA *frame ,PixelRGBA *px ,void *arg)
- ,void *arg
- ){
- every_arg_t ea = {.f = f ,.arg = arg};
- FrameRGBA·exist(frame ,every_f ,&ea);
-}
-
-void fill_f(FrameRGBA *to_frame ,PixelRGBA *to_px ,void *arg){
- PixelRGBA *px = arg;
- *to_px = *px;
-}
-void FrameRGBA·fill(FrameRGBA *to_frame ,PixelRGBA *fill_px){
- FrameRGBA·every(to_frame ,fill_f ,fill_px);
-}
-
-void FrameRGBA·fill_black(FrameRGBA *to_frame){
- FrameRGBA·fill(to_frame ,&black);
-}
-
-void FrameRGBA·fill_white(FrameRGBA *to_frame){
- FrameRGBA·fill(to_frame ,&white);
-}
-
-void fill_random_f(FrameRGBA *frame ,PixelRGBA *px ,void *arg){
- *px = PixelRGBA·random();
-}
-void FrameRGBA·fill_random(FrameRGBA *frame){
- if(!frame) return;
- FrameRGBA·every(frame ,fill_random_f ,NULL);
-}
-
-
-// --------------------------------------------------------------------------------
-// Transform functions
-// Given the previous frame ,current frame ,and an argument; modifies the
-// current frame.
-
-int identity_transform(FrameRGBA *prev_frame ,FrameRGBA *curr_frame ,void *args){
- // no processing in the identity transform
- FrameRGBA·copy(prev_frame ,&curr_frame);
- return 0; // Success
-}
+// Pixel.c and Frame.c included directly to facilitate better optimization.
+#include "Pixel.c"
+#include "Frame.c"
+#include "transform_frame.c"
// --------------------------------------------------------------------------------
// transform frame stream
--- /dev/null
+Dedicated to My Zen Sensei, and to coffee.
+
+Chrome and Firefox enforce a model where by a container of tabs does not exist
+if it contains zero tabs. It does this by closing the application, which can
+be annoying for a user when the user was planning further operations perhaps by
+adjusting settings or opening more tabls. Git does something similar, by not storing
+empty directories.
+
+This uses the other possible interpretation. A NULL pointer is taken to mean
+that the frame exists, but has no pixels in it. So non-existence of the frame
+due to a NULL pointer and thus no allocation, along with existence of that NULL
+pointer, is taken to mean that the frame exists but has no pixels in it.
+
+In contrast, my coffee cup exists while simultaneously being empty. However, its
+existence is not independent of its emptiness. It is a consequence of the cup
+being a container, that it can be empty. If it were not a container then it
+could not be said to be empty. A non-container can be said not to hold anything,
+but it can not be empty. To say my coffee cup is empty, is to say it has the
+potential to hold coffee. It follows that the essence of a coffee cup is its
+ability to be empty, is that it has a cavity pressed into a substrate of
+clay.
+
+Unrealized potential feels unsatisfactory, so perhaps that is why we adopt the
+model of enforcing non-being on containers, or say that non-existent containers
+are empty. And for this same feeling of something being unsatisfactory about my
+empty coffee taking space on my desk .. I will go fill it.
+
+----
+
+Zen master once told me that emptiness defines being. He used the coffee cup
+as an example. The NULL pointer, is still a pointer, it represents the
+potential for a frame to exist, and thus perhaps it is the container, and it is
+empty. Though it is frame container, not a pixel container. So we have two
+levels of containers here. It is right to conflate an empty frame container
+(one that can hold a maximum of one frame) with an empty pixel container? Is
+there any hazard in doing this?
+