Commit Diff


commit - 25e295bcb3df3888109fa1dae32b464bec6c9db1
commit + b4d74d2ebcfef43c0d2fcb25b342f121791e5c06
blob - b3eb2214468db5701c4530cb5474013d078a0bb4
blob + bfaced4e047208f52243069cce24a151e34cbb97
--- net.entf.graphics.asd
+++ net.entf.graphics.asd
@@ -1,5 +1,6 @@
 (defsystem "net.entf.graphics"
-  :depends-on ("net.entf.graphics/window"))
+  :depends-on ("net.entf.graphics/window"
+               "net.entf.graphics/draw"))
 
 (defsystem "net.entf.graphics/window"
   :pathname #P"window/"
@@ -24,3 +25,16 @@
                 :depends-on ("package" "protocols"))
                (:module "wayland-protocols"
                 :components ((:static-file "xdg-decoration-unstable-v1.xml")))))
+
+(defsystem "net.entf.graphics/draw"
+  :pathname #P"draw/"
+  :depends-on ("net.entf.graphics/draw/protocol")
+  :components ((:file "api")))
+
+(defsystem "net.entf.graphics/draw/protocol"
+  :pathname #P"draw/"
+  :components ((:file "package")
+               (:file "operations"
+                :depends-on ("package"))
+               (:file "protocol"
+                :depends-on ("package"))))
blob - /dev/null
blob + 1177b56dfdbe93aeb6960619bd48792925b895a6 (mode 644)
--- /dev/null
+++ draw/api.lisp
@@ -0,0 +1,46 @@
+(uiop:define-package #:net.entf.graphics/draw
+  (:use #:cl)
+  (:local-nicknames (#:p #:net.entf.graphics/draw/protocol))
+  (:export
+   #:*sheet*
+   #:with-sheet
+   #:*brush*
+   #:with-brush
+   #:fill-path
+   #:fill-rect
+   #:fill-circle))
+(in-package #:net.entf.graphics/draw)
+
+(eval-when (:load-toplevel)
+  (loop with proto-package = (find-package '#:net.entf.graphics/draw/protocol)
+        for symbol being the external-symbols of proto-package
+        for mine = (nth-value 1 (find-symbol (symbol-name symbol) *package*))
+        unless (eq mine :external)
+          do (import symbol)
+          and do (export symbol)))
+
+(defparameter *sheet* nil)
+
+(defmacro with-sheet ((sheet) &body body)
+  `(let ((*sheet* ,sheet))
+     ,@body))
+
+(defparameter *brush* nil)
+
+(defmacro with-brush ((brush) &body body)
+  `(let ((*brush* ,brush))
+     ,@body))
+
+(defun fill-path (path &key (sheet *sheet*) (brush *brush*))
+  (p:fill-path sheet brush path))
+
+(defun fill-rect (&key
+                    (sheet *sheet*)
+                    (brush *brush*)
+                    (point (p:make-point 0 0))
+                    (size (p:sheet-size sheet)))
+  (p:fill-rect sheet brush :size size :point point))
+
+(defun fill-circle (point radius
+                    &key (sheet *sheet*) (brush *brush*))
+  (p:fill-circle sheet brush point radius))
blob - /dev/null
blob + ffb6e4732ab97418805d37d8d5509dd146315366 (mode 644)
--- /dev/null
+++ draw/operations.lisp
@@ -0,0 +1 @@
+(in-package #:net.entf.graphics/draw/protocol)
blob - /dev/null
blob + 211273eb2fd1115fb9ab3548a4dac44fe1148c4a (mode 644)
--- /dev/null
+++ draw/package.lisp
@@ -0,0 +1,35 @@
+(defpackage #:net.entf.graphics/draw/protocol
+  (:use #:cl)
+  (:export
+   #:pixel-p
+   #:pixel
+   #:color
+   #:make-color
+   #:point
+   #:point-width
+   #:point-height
+   #:make-point
+   #:point-x+
+   #:point-x-
+   #:point-y+
+   #:point-y-
+   #:size
+   #:size-width
+   #:size-height
+   #:sheet
+   #:sheet-size
+   #:sheet-width
+   #:sheet-height
+   #:brush
+   #:solid-color-brush
+   #:make-solid-color-brush
+   #:path
+   #:make-path
+   #:straight-line
+   #:make-straight-line
+   #:arc
+   #:make-arc
+   #:with-path
+   #:fill-path
+   #:fill-rect
+   #:fill-circle))
blob - /dev/null
blob + beaf2638bb8a936b2cb5ddc6c47cf6ff320c748d (mode 644)
--- /dev/null
+++ draw/protocol.lisp
@@ -0,0 +1,212 @@
+(in-package #:net.entf.graphics/draw/protocol)
+
+(defparameter *printing-recursively?* nil)
+
+(defun pixel-p (value)
+  (and (>= value 0)
+       (<= value 1)))
+
+(deftype pixel ()
+  '(satisfies pixel-p))
+
+(defclass color ()
+  ((r
+    :initarg :r
+    :reader color-r)
+   (g
+    :initarg :g
+    :reader color-g)
+   (b
+    :initarg :b
+    :reader color-b)
+   (a
+    :initarg :a
+    :reader color-a)))
+
+(defun make-color (r g b &optional (a 1))
+  (check-type r pixel)
+  (check-type g pixel)
+  (check-type b pixel)
+  (check-type a pixel)
+  (make-instance 'color :r r :g g :b b :a a))
+
+(defmethod print-object ((object color) stream)
+  (with-slots (r g b a)
+      object
+    (print-unreadable-object (object stream :type t)
+      (format stream "~:[~*~;~2,'0X~]~2,'0X~2,'0X~2,'0X" (= a 1)
+              (round (* a 255))
+              (round (* r 255))
+              (round (* g 255))
+              (round (* b 255))))))
+
+(defclass point ()
+  ((x
+    :initarg :x
+    :reader point-x)
+   (y
+    :initarg :y
+    :reader point-y)))
+
+(defun make-point (x y)
+  (make-instance 'point :x x :y y))
+
+(defmethod print-object ((object point) stream)
+  (with-slots (x y)
+      object
+    (if *printing-recursively?*
+        (format stream "~A/~A" x y)
+        (print-unreadable-object (object stream :type t)
+          (format stream "~A/~A" x y)))))
+
+(defmethod point-x+ ((point point) value)
+  (make-point (+ (point-x point) value)
+              (point-y point)))
+
+(defmethod point-x- ((point point) value)
+  (make-point (- (point-x point) value)
+              (point-y point)))
+
+(defmethod point-y+ ((point point) value)
+  (make-point (point-x point)
+              (+ (point-y point) value)))
+
+(defmethod point-y- ((point point) value)
+  (make-point (point-x point)
+              (- (point-y point) value)))
+
+(defclass size ()
+  ((width
+    :initarg :width
+    :reader size-width)
+   (height
+    :initarg :height
+    :reader size-height)))
+
+(defun make-size (width height)
+  (make-instance 'size :width width :height height))
+
+(defmethod print-object ((object size) stream)
+  (with-slots (width height)
+      object
+    (if *printing-recursively?*
+        (format stream "~A/~A" width height)
+        (print-unreadable-object (object stream :type t)
+          (format stream "~A/~A" width height)))))
+
+(defclass sheet ()
+  ((size
+    :initarg size
+    :reader sheet-size)))
+
+(defmethod sheet-width ((sheet sheet))
+  (size-width (sheet-size sheet)))
+
+(defmethod sheet-height ((sheet sheet))
+  (size-height (sheet-size sheet)))
+
+(defclass brush ()
+  ())
+
+(defclass solid-color-brush (brush)
+  ((color
+    :initarg :color)))
+
+(defun make-solid-color-brush (color)
+  (make-instance 'solid-color-brush :color color))
+
+(defclass path ()
+  ((lines
+    :initarg :lines
+    :initform ())))
+
+(defun make-path (&optional lines)
+  (make-instance 'path :lines lines))
+
+(defmethod print-object ((object path) stream)
+  (print-unreadable-object (object stream :type t)
+    (with-slots (lines)
+        object
+      (let ((*printing-recursively?* t))
+        (format stream "~{~A~^ ~}" lines)))))
+
+(defclass line ()
+  ((start
+    :initarg :start
+    :reader line-start)
+   (end
+    :initarg :end
+    :reader line-end)))
+
+(defclass straight-line (line)
+  ())
+
+(defun make-straight-line (start end)
+  (make-instance 'straight-line :start start :end end))
+
+(defmethod print-object ((object straight-line) stream)
+  (with-slots (start end)
+      object
+    (if *printing-recursively?*
+        (format stream "~A⭢~A" start end)
+        (print-unreadable-object (object stream)
+          (format stream "~A⭢~A" start end)))))
+
+(defclass arc (line)
+  ())
+
+(defun make-arc (start end)
+  (make-instance 'arc :start start :end end))
+
+(defmethod print-object ((object arc) stream)
+  (with-slots (start end)
+      object
+    (if *printing-recursively?*
+        (format stream "~A⤻~A" start end)
+        (print-unreadable-object (object stream)
+          (format stream "~A⤻~A" start end)))))
+
+(defmacro with-path ((start) &body body)
+  (let ((path (gensym "PATH"))
+        (current (gensym "CURRENT"))
+        (lines (gensym "LINES")))
+    `(let ((,path (make-path))
+           (,current ,start))
+       (symbol-macrolet ((,lines (slot-value ,path ','lines)))
+         (labels ((line-to (to)
+                    (push (make-straight-line ,current to) ,lines)
+                    (setf ,current to))
+                  (arc-to (to)
+                    (push (make-arc ,current to) ,lines)
+                    (setf ,current to)))
+           ,@body)
+         (setf ,lines (nreverse ,lines)))
+       ,path)))
+
+(defgeneric fill-path (sheet brush path))
+
+(defgeneric fill-rect (sheet brush &key point size)
+  (:documentation "Fill area on SHEET denoted by POINT and SIZE with BRUSH.
+
+                   When not given, POINT will refer to the top left of the SHEET.
+                   When not given, SIZE will refer to the size of the SHEET.")
+  (:method ((sheet sheet) (brush brush) &key (point (make-point 0 0)) (size (sheet-size sheet)))
+    (declare (type point point)
+             (type size size))
+    (fill-path sheet brush (let ((right-x (+ (point-x point)
+                                             (size-width size)))
+                                 (bottom-y (+ (point-y point)
+                                              (size-height size))))
+                             (with-path (point)
+                               (line-to (make-point right-x (point-y point)))
+                               (line-to (make-point right-x bottom-y))
+                               (line-to (make-point (point-x point) bottom-y)))))))
+
+(defgeneric fill-circle (sheet brush point radius)
+  (:documentation "Full circle on SHEET with BRUSH, with it's center at POINT spanning the given RADIUS.")
+  (:method ((sheet sheet) (brush brush) point radius)
+    (fill-path sheet brush (with-path ((point-x- point radius))
+                             (arc-to (point-y- point radius))
+                             (arc-to (point-x+ point radius))
+                             (arc-to (point-y+ point radius))
+                             (arc-to (point-y- point radius))))))