separated out Pixel and Frame, getting ready for a first real frame grab
authorThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Wed, 18 Sep 2024 05:14:43 +0000 (05:14 +0000)
committerThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Wed, 18 Sep 2024 05:14:43 +0000 (05:14 +0000)
developer/cc/CircularQueue.c [new file with mode: 0644]
developer/cc/Frame.c [new file with mode: 0644]
developer/cc/Pixel.c [new file with mode: 0644]
developer/cc/echo_frames.c [new file with mode: 0644]
developer/cc/frame_2024-09-18_1221.c [new file with mode: 0644]
developer/cc/transform_frame.c [new file with mode: 0644]
developer/cc/transform_frame_stream.c
developer/developer [deleted file]
developer/ologist/emptiness_and_existence.txt [new file with mode: 0644]

diff --git a/developer/cc/CircularQueue.c b/developer/cc/CircularQueue.c
new file mode 100644 (file)
index 0000000..570182f
--- /dev/null
@@ -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 (file)
index 0000000..0a79b40
--- /dev/null
@@ -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 <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);
+}
diff --git a/developer/cc/Pixel.c b/developer/cc/Pixel.c
new file mode 100644 (file)
index 0000000..ceff03b
--- /dev/null
@@ -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 <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;
+}
diff --git a/developer/cc/echo_frames.c b/developer/cc/echo_frames.c
new file mode 100644 (file)
index 0000000..9918322
--- /dev/null
@@ -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 <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;
+}
diff --git a/developer/cc/frame_2024-09-18_1221.c b/developer/cc/frame_2024-09-18_1221.c
new file mode 100644 (file)
index 0000000..829228a
--- /dev/null
@@ -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 <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);
+}
diff --git a/developer/cc/transform_frame.c b/developer/cc/transform_frame.c
new file mode 100644 (file)
index 0000000..d895893
--- /dev/null
@@ -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
+}
index 322caf6..754db6c 100644 (file)
 // 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
diff --git a/developer/developer b/developer/developer
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/developer/ologist/emptiness_and_existence.txt b/developer/ologist/emptiness_and_existence.txt
new file mode 100644 (file)
index 0000000..0e88ffa
--- /dev/null
@@ -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?
+