From: Thomas Walker Lynch Date: Wed, 18 Sep 2024 05:14:43 +0000 (+0000) Subject: separated out Pixel and Frame, getting ready for a first real frame grab X-Git-Url: https://git.reasoningtechnology.com/style/rt_dark_doc.css?a=commitdiff_plain;h=88a93127d45d58611e77e6f93a7e1a9f0283f698;p=noflash separated out Pixel and Frame, getting ready for a first real frame grab --- diff --git a/developer/cc/CircularQueue.c b/developer/cc/CircularQueue.c new file mode 100644 index 0000000..570182f --- /dev/null +++ b/developer/cc/CircularQueue.c @@ -0,0 +1,75 @@ +/* + 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]; +} diff --git a/developer/cc/Frame.c b/developer/cc/Frame.c new file mode 100644 index 0000000..0a79b40 --- /dev/null +++ b/developer/cc/Frame.c @@ -0,0 +1,261 @@ +/* + 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 +#include +#include +#include +#include + +// -------------------------------------------------------------------------------- +// 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); +} diff --git a/developer/cc/Pixel.c b/developer/cc/Pixel.c new file mode 100644 index 0000000..ceff03b --- /dev/null +++ b/developer/cc/Pixel.c @@ -0,0 +1,129 @@ +/* + 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 +#include +#include // for rand() +#include // 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; +} diff --git a/developer/cc/echo_frames.c b/developer/cc/echo_frames.c new file mode 100644 index 0000000..9918322 --- /dev/null +++ b/developer/cc/echo_frames.c @@ -0,0 +1,66 @@ +// 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 // 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; +} diff --git a/developer/cc/frame_2024-09-18_1221.c b/developer/cc/frame_2024-09-18_1221.c new file mode 100644 index 0000000..829228a --- /dev/null +++ b/developer/cc/frame_2024-09-18_1221.c @@ -0,0 +1,217 @@ +/* + 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 +#include +#include + +// -------------------------------------------------------------------------------- +// 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); +} diff --git a/developer/cc/transform_frame.c b/developer/cc/transform_frame.c new file mode 100644 index 0000000..d895893 --- /dev/null +++ b/developer/cc/transform_frame.c @@ -0,0 +1,43 @@ +/* +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 +} diff --git a/developer/cc/transform_frame_stream.c b/developer/cc/transform_frame_stream.c index 322caf6..754db6c 100644 --- a/developer/cc/transform_frame_stream.c +++ b/developer/cc/transform_frame_stream.c @@ -2,280 +2,29 @@ // gcc -I/usr/include/libdrm transform_frame_stream.c -o transform_frame_stream -ldrm // ./transform_frame_stream -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/*-------------------------------------------------------------------------------- + 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 diff --git a/developer/developer b/developer/developer deleted file mode 100644 index e69de29..0000000 diff --git a/developer/ologist/emptiness_and_existence.txt b/developer/ologist/emptiness_and_existence.txt new file mode 100644 index 0000000..0e88ffa --- /dev/null +++ b/developer/ologist/emptiness_and_existence.txt @@ -0,0 +1,37 @@ +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? +