summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2021-09-07 20:12:35 -0400
committerDavid Thompson <dthompson2@worcester.edu>2021-09-08 07:24:43 -0400
commitb63ce7f900ee6a4631eacd1db07b2c9c9be12780 (patch)
tree853f02e690a84de7de46382cf68f0788a84d2691
parentde3be995ed26f9ea78f7462095919ba08df2cd25 (diff)
graphics: path: Add support for linear/radial gradient fills.
-rw-r--r--chickadee/graphics/path.scm109
-rw-r--r--data/shaders/path-fill-frag.glsl28
-rw-r--r--data/shaders/path-fill-vert.glsl9
3 files changed, 136 insertions, 10 deletions
diff --git a/chickadee/graphics/path.scm b/chickadee/graphics/path.scm
index 24796a7..4978fbb 100644
--- a/chickadee/graphics/path.scm
+++ b/chickadee/graphics/path.scm
@@ -62,6 +62,15 @@
regular-polygon
ellipse
circle
+ gradient?
+ gradient-type
+ gradient-matrix
+ gradient-start-color
+ gradient-end-color
+ gradient-range
+ gradient-radial-ratio
+ linear-gradient
+ radial-gradient
stroke
fill
fill-and-stroke
@@ -469,6 +478,67 @@
;;;
+;;; Gradients
+;;;
+
+(define-record-type <gradient>
+ (make-gradient type matrix start-color end-color range radial-ratio)
+ gradient?
+ (type gradient-type)
+ (matrix gradient-matrix)
+ (start-color gradient-start-color)
+ (end-color gradient-end-color)
+ (range gradient-range)
+ ;; This x:y ratio is used to squash/stretch radial gradients to give
+ ;; an elliptical appearance.
+ (radial-ratio gradient-radial-ratio))
+
+(define (angle->vec2 theta)
+ (vec2 (cos theta) (sin theta)))
+
+(define (make-range offset length)
+ (vec2 offset (+ offset length)))
+
+(define (make-gradient-matrix origin rotation)
+ (matrix3* (matrix3-translate (vec2* origin -1.0))
+ (matrix3-rotate rotation)))
+
+(define (transform-gradient gradient matrix)
+ (make-gradient (gradient-type gradient)
+ ;; The matrix needs to be inverted in order to
+ ;; convert world space coordinates back into local
+ ;; coordinates within the fragment shader. We need
+ ;; the local coordinates for the gradient math to
+ ;; produce the correct result.
+ (matrix3* (matrix3-inverse matrix) (gradient-matrix gradient))
+ (gradient-start-color gradient)
+ (gradient-end-color gradient)
+ (gradient-range gradient)
+ (gradient-radial-ratio gradient)))
+
+(define* (linear-gradient #:key (origin %origin) (start-color white)
+ (end-color black) (rotation 0.0) (offset 0.0)
+ (length 100.0))
+ (make-gradient 'linear
+ (make-gradient-matrix origin rotation)
+ start-color
+ end-color
+ (make-range offset length)
+ 0.0))
+
+(define* (radial-gradient #:key (origin %origin) (start-color white)
+ (end-color black) (radius 50.0)
+ (radius-x radius) (radius-y radius)
+ (rotation 0.0) (offset 0.0))
+ (make-gradient 'radial
+ (make-gradient-matrix origin rotation)
+ start-color
+ end-color
+ (make-range offset (- radius-x offset))
+ (/ radius-x radius-y)))
+
+
+;;;
;;; Path Tesselation
;;;
@@ -1106,7 +1176,6 @@
(define *debug?* #f)
-;; TODO: gradients
(define* (draw-filled-path filled-path matrix)
(let ((shader (graphics-variable-ref fill-shader))
(mvp (graphics-variable-ref mvp-matrix))
@@ -1151,10 +1220,26 @@
;; we only draw fragments that are part of the filled path.
(with-graphics-state ((g:stencil-test stencil-cover-and-clear)
(g:blend-mode (filled-path-blend-mode filled-path)))
- (shader-apply shader
- (geometry-vertex-array quad-geometry)
- #:mvp mvp
- #:color (filled-path-color filled-path))))))
+ (let ((color (filled-path-color filled-path)))
+ (if (gradient? color)
+ ;; Linear/radial gradient fill.
+ (shader-apply shader
+ (geometry-vertex-array quad-geometry)
+ #:mvp mvp
+ #:color (gradient-start-color color)
+ #:end-color (gradient-end-color color)
+ #:gradient-matrix (gradient-matrix color)
+ #:gradient-range (gradient-range color)
+ #:radial-gradient-ratio (gradient-radial-ratio color)
+ #:mode (case (gradient-type color)
+ ((linear) 1)
+ ((radial) 2)))
+ ;; Solid fill.
+ (shader-apply shader
+ (geometry-vertex-array quad-geometry)
+ #:mvp mvp
+ #:color (filled-path-color filled-path)
+ #:mode 0)))))))
;; TODO: dashed stroke
;; TODO: miter styles and miter limit
@@ -1222,7 +1307,9 @@
(array-list-pop! filled-paths))))
(fill-path filled-path compiled-path
#:blend-mode blend-mode
- #:color fill-color)
+ #:color (if (gradient? fill-color)
+ (transform-gradient fill-color matrix)
+ fill-color))
(array-list-push! result filled-path)
(loop rest matrix blend-mode fill-color stroke-color stroke-width
stroke-feather stroke-cap)))
@@ -1250,8 +1337,14 @@
stroke-feather stroke-cap)))
;; Apply transformation matrix.
(('transform transform)
- (loop rest (matrix3* matrix transform) blend-mode fill-color
- stroke-color stroke-width stroke-feather stroke-cap))
+ (loop rest
+ (matrix3* transform matrix)
+ blend-mode
+ fill-color
+ stroke-color
+ stroke-width
+ stroke-feather
+ stroke-cap))
;; Set style properties.
((or ('set-style 'blend-mode blend-mode)
('set-style 'fill-color fill-color)
diff --git a/data/shaders/path-fill-frag.glsl b/data/shaders/path-fill-frag.glsl
index 3af191f..e336e76 100644
--- a/data/shaders/path-fill-frag.glsl
+++ b/data/shaders/path-fill-frag.glsl
@@ -6,12 +6,38 @@ out vec4 fragColor;
#define fragColor gl_FragColor
#endif
+#ifdef GLSL120
+attribute vec2 fragPosition;
+#else
+in vec2 fragPosition;
+#endif
+
+uniform int mode;
uniform vec4 color;
+uniform vec4 endColor;
+uniform mat3 gradientMatrix;
+uniform vec2 gradientRange;
+uniform float radialGradientRatio;
+
+vec4 gradientMix(float x) {
+ float start = gradientRange.x;
+ float end = gradientRange.y;
+ float t = clamp((x - start) / (end - start), 0.0, 1.0);
+ return mix(color, endColor, t);
+}
void main(void) {
if (color.a <= 0.0) {
discard;
}
- fragColor = color;
+ if(mode == 0) { // solid color
+ fragColor = color;
+ } else if(mode == 1) { // linear gradient
+ float x = (gradientMatrix * vec3(fragPosition, 1.0)).x;
+ fragColor = gradientMix(x);
+ } else if(mode == 2) { // radial gradient
+ vec2 p = (gradientMatrix * vec3(fragPosition, 1.0)).xy;
+ fragColor = gradientMix(length(p * vec2(1.0, radialGradientRatio)));
+ }
}
diff --git a/data/shaders/path-fill-vert.glsl b/data/shaders/path-fill-vert.glsl
index d95b71b..1752978 100644
--- a/data/shaders/path-fill-vert.glsl
+++ b/data/shaders/path-fill-vert.glsl
@@ -8,6 +8,12 @@ in vec2 position;
attribute vec2 position;
#endif
+#ifdef GLSL120
+varying vec2 fragPosition;
+#else
+out vec2 fragPosition;
+#endif
+
uniform mat4 mvp;
uniform vec4 color;
@@ -17,6 +23,7 @@ void main(void) {
if (color.a <= 0.0) {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
} else {
- gl_Position = mvp * vec4(position.xy, 0.0, 1.0);
+ fragPosition = position;
+ gl_Position = mvp * vec4(position, 0.0, 1.0);
}
}