diff options
Diffstat (limited to 'chickadee/graphics/9-patch.scm')
-rw-r--r-- | chickadee/graphics/9-patch.scm | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/chickadee/graphics/9-patch.scm b/chickadee/graphics/9-patch.scm new file mode 100644 index 0000000..833fa42 --- /dev/null +++ b/chickadee/graphics/9-patch.scm @@ -0,0 +1,235 @@ +;;; Chickadee Game Toolkit +;;; Copyright © 2021 David Thompson <dthompson2@worcester.edu> +;;; +;;; Chickadee is free software: you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published +;;; by the Free Software Foundation, either version 3 of the License, +;;; or (at your option) any later version. +;;; +;;; Chickadee is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;; General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program. If not, see +;;; <http://www.gnu.org/licenses/>. + +(define-module (chickadee graphics 9-patch) + #:use-module (ice-9 match) + #:use-module (chickadee math matrix) + #:use-module (chickadee math rect) + #:use-module (chickadee math vector) + #:use-module (chickadee graphics blend) + #:use-module (chickadee graphics color) + #:use-module (chickadee graphics engine) + #:use-module (chickadee graphics shader) + #:use-module (chickadee graphics texture) + #:use-module (chickadee graphics buffer) + #:export (draw-9-patch* + draw-9-patch)) + +(define-geometry-type <9-patch-vertex> + 9-patch-vertex-ref + 9-patch-vertex-set! + 9-patch-vertex-append! + (position vec2) + (distance vec2)) + +(define-graphics-variable 9-patch-geometry + (make-geometry <9-patch-vertex> 4 #:index-capacity 6)) +(define-graphics-variable 9-patch-model-matrix (make-null-matrix4)) +(define-graphics-variable 9-patch-mvp-matrix (make-null-matrix4)) +(define-graphics-variable 9-patch-margins (make-null-rect)) +(define-graphics-variable 9-patch-shader + (strings->shader + " +#ifdef GLSL330 +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 distance; +#elif defined(GLSL130) +in vec2 position; +in vec2 distance; +#elif defined(GLSL120) +attribute vec2 position; +attribute vec2 distance; +#endif +#ifdef GLSL120 +varying vec2 fragDistance; +#else +out vec2 fragDistance; +#endif +uniform mat4 mvp; + +void main(void) { + fragDistance = distance; + gl_Position = mvp * vec4(position.xy, 0.0, 1.0); +} +" + " +#ifdef GLSL120 +varying vec2 fragDistance; +#else +in vec2 fragDistance; +#endif +#ifdef GLSL330 +out vec4 fragColor; +#endif +uniform vec4 subtexture; +uniform vec4 margins; +uniform float width; +uniform float height; +uniform sampler2D colorTexture; +uniform vec4 tint; +uniform int mode; + +float patch(float d, float m0, float m1, float length, float texLength) { + if(d <= m0) { // inside the left/bottom margin. + return d; + } else if(d >= length - m1) { // inside the right/top margin. + return texLength - (length - d); + } else if(mode == 0) { // in the middle, stretch mode. + return mix(m0, texLength - m1, (d - m0) / (length - m0 - m1)); + } else { // in the middle, tile mode. + return m0 + mod((d - m0), texLength - m0 - m1); + } +} + +void main (void) { + vec2 texcoord = subtexture.xy; + texcoord.x += patch(fragDistance.x, margins.x, margins.y, width, subtexture.z); + texcoord.y += patch(fragDistance.y, margins.z, margins.w, height, subtexture.w); + +#ifdef GLSL330 + fragColor = texture(colorTexture, texcoord) * tint; +#else + gl_FragColor = texture2D(colorTexture, texcoord) * tint; +#endif +} +")) + +(define* (draw-9-patch* texture + rect + matrix + #:key + (margin 0.0) + (top-margin margin) + (bottom-margin margin) + (left-margin margin) + (right-margin margin) + (mode 'stretch) + (tint white) + (blend-mode blend:alpha) + (texcoords (texture-gl-tex-rect texture))) + (let ((shader (graphics-variable-ref 9-patch-shader)) + (geometry (graphics-variable-ref 9-patch-geometry)) + (mvp (graphics-variable-ref 9-patch-mvp-matrix)) + (margins (graphics-variable-ref 9-patch-margins))) + (let* ((w (rect-width rect)) + (h (rect-height rect)) + (tex-rect (texture-gl-rect texture)) + (tw (rect-width tex-rect)) + (th (rect-height tex-rect)) + ;; Convert pixel coordinates to GL texture coordinates. + (w* (/ w tw)) + (h* (/ h th))) + (with-geometry geometry + (let* ((x1 (rect-x rect)) + (y1 (rect-y rect)) + (x2 (+ x1 w)) + (y2 (+ y1 h)) + (s1 0.0) + (t1 0.0) + (s2 w*) + (t2 h*)) + (9-patch-vertex-append! geometry + (x1 y1 s1 t1) + (x2 y1 s2 t1) + (x2 y2 s2 t2) + (x1 y2 s1 t2)) + (geometry-index-append! geometry 0 3 2 0 2 1))) + ;; Convert pixel margin values to GL texture values. + (set-rect-x! margins (/ left-margin tw)) + (set-rect-y! margins (/ right-margin tw)) + (set-rect-width! margins (/ bottom-margin th)) + (set-rect-height! margins (/ top-margin th)) + (with-graphics-state ((g:blend-mode blend-mode) + (g:texture-0 texture)) + (shader-apply shader + (geometry-vertex-array geometry) + #:width w* + #:height h* + #:subtexture texcoords + #:margins margins + #:mode (match mode + ('stretch 0) + ('tile 1)) + #:tint tint + #:mvp (if matrix + (begin + (matrix4-mult! mvp matrix + (current-projection)) + mvp) + (current-projection))))))) + +(define %null-vec2 (vec2 0.0 0.0)) +(define %default-scale (vec2 1.0 1.0)) + +(define draw-9-patch + (let ((position (vec2 0.0 0.0)) + (%rect (make-rect 0.0 0.0 0.0 0.0)) + (matrix (make-null-matrix4))) + (lambda* (texture + rect + #:key + (margin 0.0) + (top-margin margin) + (bottom-margin margin) + (left-margin margin) + (right-margin margin) + (mode 'stretch) + (origin %null-vec2) + (rotation 0.0) + (scale %default-scale) + (blend-mode blend:alpha) + (tint white)) + "Draw a 9-patch over the area RECT using TEXTURE whose +stretchable/tileable patches are defined by the given margin +measurements. The corners are never stretched/tiled, the left and +right edges will be stretched/tiled vertically, the top and bottom +edges may be stretched/tiled horizontally, and the center may be +stretched/tiled in both directions. + +MODE may be either 'stretch' (the default) or 'tile'. + +MARGIN specifies the margin size for all sides of the 9-patch. To +make margins of differing sizes, the TOP-MARGIN, BOTTOM-MARGIN, +LEFT-MARGIN, and RIGHT-MARGIN arguments may be used. + +ORIGIN, ROTATION, and SCALE allow for arbitrary transformation of the +9-patch. + +BLEND-MODE specifies the blending mode to use. Alpha blending is used +by default. + +TINT specifies the color to tint the texture. White is used by +default, which means there is no tint." + (set-rect-x! %rect 0.0) + (set-rect-y! %rect 0.0) + (set-rect-width! %rect (rect-width rect)) + (set-rect-height! %rect (rect-height rect)) + (set-vec2-x! position (rect-x rect)) + (set-vec2-y! position (rect-y rect)) + (matrix4-2d-transform! matrix + #:origin origin + #:position position + #:rotation rotation + #:scale scale) + (draw-9-patch* texture %rect matrix + #:top-margin top-margin + #:bottom-margin bottom-margin + #:left-margin left-margin + #:right-margin right-margin + #:mode mode + #:blend-mode blend-mode + #:tint tint)))) |