Monorepo for Aesthetic.Computer aesthetic.computer
at main 124 lines 5.6 kB view raw
1;;; Framebuffer — foreign-memory pixel buffer for DRM/fbdev 2 3(in-package :ac-native.framebuffer) 4 5(defstruct framebuffer 6 "A 2D pixel buffer backed by CFFI foreign memory (for DRM mmap compat)." 7 (pixels (cffi:null-pointer) :type cffi:foreign-pointer) 8 (width 0 :type fixnum) 9 (height 0 :type fixnum) 10 (stride 0 :type fixnum) ; in pixels (= width for our buffers) 11 (owned t :type boolean)) ; if t, we allocated pixels and must free 12 13(defun fb-create (width height) 14 "Allocate a new framebuffer with WIDTH x HEIGHT pixels (ARGB32)." 15 (let* ((stride width) 16 (nbytes (* stride height 4)) 17 (ptr (cffi:foreign-alloc :uint32 :count (* stride height) :initial-element 0))) 18 (make-framebuffer :pixels ptr :width width :height height 19 :stride stride :owned t))) 20 21(defun fb-destroy (fb) 22 "Free the framebuffer's pixel memory (if we own it)." 23 (when (and fb (framebuffer-owned fb) 24 (not (cffi:null-pointer-p (framebuffer-pixels fb)))) 25 (cffi:foreign-free (framebuffer-pixels fb)) 26 (setf (framebuffer-pixels fb) (cffi:null-pointer)))) 27 28(declaim (inline fb-pixel-offset)) 29(defun fb-pixel-offset (fb x y) 30 (declare (optimize (speed 3) (safety 0)) 31 (type fixnum x y) 32 (type framebuffer fb)) 33 (the fixnum (+ x (* y (framebuffer-stride fb))))) 34 35(defun fb-clear (fb color-u32) 36 "Fill the entire framebuffer with a packed ARGB32 color." 37 (declare (optimize (speed 3) (safety 0)) 38 (type (unsigned-byte 32) color-u32)) 39 (let* ((n (* (framebuffer-stride fb) (framebuffer-height fb))) 40 (ptr (framebuffer-pixels fb))) 41 (dotimes (i n) 42 (setf (cffi:mem-aref ptr :uint32 i) color-u32)))) 43 44(declaim (inline fb-put-pixel)) 45(defun fb-put-pixel (fb x y color-u32) 46 "Write a pixel (no blending, no bounds check in optimized mode)." 47 (declare (optimize (speed 3) (safety 0)) 48 (type fixnum x y) 49 (type (unsigned-byte 32) color-u32) 50 (type framebuffer fb)) 51 (when (and (>= x 0) (< x (framebuffer-width fb)) 52 (>= y 0) (< y (framebuffer-height fb))) 53 (setf (cffi:mem-aref (framebuffer-pixels fb) :uint32 54 (fb-pixel-offset fb x y)) 55 color-u32))) 56 57(defun fb-blend-pixel (fb x y src-color) 58 "Alpha-blend a color onto the framebuffer at (x, y)." 59 (declare (optimize (speed 3) (safety 0)) 60 (type fixnum x y)) 61 (when (and (>= x 0) (< x (framebuffer-width fb)) 62 (>= y 0) (< y (framebuffer-height fb))) 63 (let* ((off (fb-pixel-offset fb x y)) 64 (dst-u32 (cffi:mem-aref (framebuffer-pixels fb) :uint32 off)) 65 (dst (make-color :r (ldb (byte 8 16) dst-u32) 66 :g (ldb (byte 8 8) dst-u32) 67 :b (ldb (byte 8 0) dst-u32) 68 :a 255))) 69 (setf (cffi:mem-aref (framebuffer-pixels fb) :uint32 off) 70 (color-blend src-color dst))))) 71 72;;; Convenience aliases matching exported names 73(defun fb-width (fb) (framebuffer-width fb)) 74(defun fb-height (fb) (framebuffer-height fb)) 75(defun fb-stride (fb) (framebuffer-stride fb)) 76(defun fb-pixels (fb) (framebuffer-pixels fb)) 77 78(defun fb-copy-scaled (src dst-ptr dst-w dst-h dst-stride scale) 79 "Copy SRC framebuffer to a foreign memory region at DST-PTR, scaling up by SCALE. 80 Used by DRM present to blit the small render buffer to the display. 81 Optimized: expand each source row into a temp row, then memcpy to fill scale rows." 82 (declare (optimize (speed 3) (safety 0)) 83 (type fixnum dst-w dst-h dst-stride scale) 84 (type framebuffer src)) 85 (let* ((sw (framebuffer-width src)) 86 (sh (framebuffer-height src)) 87 (sp (framebuffer-pixels src)) 88 (row-bytes (* dst-stride 4))) ; bytes per destination row 89 ;; For scale=1, use fast memcpy path 90 (if (= scale 1) 91 (let ((copy-w (min sw dst-w))) 92 (dotimes (y (min sh dst-h)) 93 (cffi:foreign-funcall "memcpy" 94 :pointer (cffi:inc-pointer dst-ptr (* y row-bytes)) 95 :pointer (cffi:inc-pointer sp (* y sw 4)) 96 :unsigned-long (* copy-w 4) 97 :pointer))) 98 ;; General case: expand each source row, then replicate 99 (let* ((expanded-w (min (* sw scale) dst-w)) 100 (expanded-bytes (* expanded-w 4))) 101 ;; Temp buffer for one expanded row 102 (cffi:with-foreign-object (tmp :uint32 expanded-w) 103 (dotimes (sy sh) 104 (let ((dst-y0 (* sy scale))) 105 (when (>= dst-y0 dst-h) (return)) 106 ;; Expand source row: replicate each pixel `scale` times 107 (let ((src-row-offset (* sy sw))) 108 (dotimes (sx sw) 109 (let ((pixel (cffi:mem-aref sp :uint32 (+ src-row-offset sx))) 110 (dx0 (* sx scale))) 111 (when (>= dx0 dst-w) (return)) 112 (dotimes (dx scale) 113 (let ((col (+ dx0 dx))) 114 (when (>= col expanded-w) (return)) 115 (setf (cffi:mem-aref tmp :uint32 col) pixel)))))) 116 ;; Copy expanded row to each of the `scale` destination rows 117 (dotimes (dy scale) 118 (let ((row (+ dst-y0 dy))) 119 (when (>= row dst-h) (return)) 120 (cffi:foreign-funcall "memcpy" 121 :pointer (cffi:inc-pointer dst-ptr (* row row-bytes)) 122 :pointer tmp 123 :unsigned-long expanded-bytes 124 :pointer))))))))))