From e19bade579211b18e325dbd114c588746c05f4f8 Mon Sep 17 00:00:00 2001 From: "@greweb" Date: Fri, 12 Jun 2026 23:12:47 +0200 Subject: [PATCH] feat: integrate gl-react-blur into the monorepo Imports https://github.com/gre/gl-react-blur as packages/gl-react-blur, modernized from Flow/babel-6 to TypeScript on the gl-react v6 API: Blur, Blur1D, BlurV, BlurV1D, directionForPassDefault. Components now use connectSize (inheriting size from parent) instead of requiring explicit width/height, while keeping the old multi-pass API (factor, passes, directionForPass). Version 6.0.0 like the family; published automatically on merge by changeset publish. The cookbook blur examples consumed local copies of these components: blurmulti, blurmap, blurimgtitle, blurmapdyn, blurvideo, blurmapmouse now import from gl-react-blur (blurxy keeps its educational inline implementation). blurmulti/blurmap gain a "passes" control. Adds a Blur pixel test to the shared suite (blur of a constant color is a no-op). Also gitignore packages/cookbook/dist (vite build output) which was accidentally committed. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci-tests.yml | 2 +- .gitignore | 1 + CLAUDE.md | 1 + packages/cookbook/package.json | 1 + .../cookbook/src/examples/blurimgtitle.tsx | 17 +--- packages/cookbook/src/examples/blurmap.tsx | 96 +------------------ packages/cookbook/src/examples/blurmapdyn.tsx | 2 +- .../cookbook/src/examples/blurmapmouse.tsx | 2 +- packages/cookbook/src/examples/blurmulti.tsx | 33 +++---- packages/cookbook/src/examples/blurvideo.tsx | 2 +- packages/cookbook/src/examples/index.ts | 6 +- packages/cookbook/vite.config.ts | 1 + packages/gl-react-blur/LICENSE | 19 ++++ packages/gl-react-blur/README.md | 22 +++++ packages/gl-react-blur/package.json | 37 +++++++ packages/gl-react-blur/src/Blur.tsx | 41 ++++++++ packages/gl-react-blur/src/Blur1D.tsx | 48 ++++++++++ packages/gl-react-blur/src/BlurV.tsx | 44 +++++++++ packages/gl-react-blur/src/BlurV1D.tsx | 50 ++++++++++ .../src/directionForPassDefault.ts | 24 +++++ packages/gl-react-blur/src/index.ts | 12 +++ packages/gl-react-blur/tsconfig.json | 21 ++++ packages/tests/__tests__/all.js | 31 ++++++ packages/tests/package.json | 1 + pnpm-lock.yaml | 16 ++++ tsconfig.json | 2 + 26 files changed, 401 insertions(+), 131 deletions(-) create mode 100755 packages/gl-react-blur/LICENSE create mode 100644 packages/gl-react-blur/README.md create mode 100644 packages/gl-react-blur/package.json create mode 100644 packages/gl-react-blur/src/Blur.tsx create mode 100644 packages/gl-react-blur/src/Blur1D.tsx create mode 100644 packages/gl-react-blur/src/BlurV.tsx create mode 100644 packages/gl-react-blur/src/BlurV1D.tsx create mode 100644 packages/gl-react-blur/src/directionForPassDefault.ts create mode 100644 packages/gl-react-blur/src/index.ts create mode 100644 packages/gl-react-blur/tsconfig.json diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 8f282293..e3e1cc9d 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -21,7 +21,7 @@ jobs: - run: pnpm install --frozen-lockfile - name: Build gl-react packages run: | - for d in packages/gl-react packages/gl-react-dom packages/gl-react-headless packages/gl-react-image; do + for d in packages/gl-react packages/gl-react-dom packages/gl-react-headless packages/gl-react-image packages/gl-react-blur; do pnpm exec babel --root-mode upward --source-maps --extensions '.ts,.tsx' -d "$d/lib" "$d/src" done - run: xvfb-run -s "-ac -screen 0 1280x1024x24" pnpm test diff --git a/.gitignore b/.gitignore index 3a474739..9760028d 100755 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ packages/cookbook-v2/test-results packages/cookbook-v2/dist *.tsbuildinfo packages/cookbook-expo/node_modules +packages/cookbook/dist diff --git a/CLAUDE.md b/CLAUDE.md index 3db15f2c..c4b25169 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,6 +36,7 @@ Releases are managed by [changesets](https://github.com/changesets/changesets). - **`packages/gl-react-expo/`** — React Native via Expo GLView. - **`packages/gl-react-headless/`** — Node.js implementation using headless-gl. - **`packages/gl-react-image/`** — `GLImage` component implementing `resizeMode` (cover/contain/free/stretch) in OpenGL. +- **`packages/gl-react-blur/`** — multi-pass gaussian `Blur`/`BlurV` effects (variable blur via map texture). - **`packages/tests/`** — Shared Jest test suite using `gl-react-headless` + `react-test-renderer`. - **`packages/cookbook/`** — Modern examples (Vite + TypeScript + Tailwind). diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 999e6a39..e64db06e 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -16,6 +16,7 @@ "dependencies": { "@heroicons/react": "^2.2.0", "gl-react": "workspace:^", + "gl-react-blur": "workspace:^", "gl-react-dom": "workspace:^", "gl-react-image": "workspace:^", "gl-shader": "^4.2.1", diff --git a/packages/cookbook/src/examples/blurimgtitle.tsx b/packages/cookbook/src/examples/blurimgtitle.tsx index 2a4cfdd1..e06de499 100644 --- a/packages/cookbook/src/examples/blurimgtitle.tsx +++ b/packages/cookbook/src/examples/blurimgtitle.tsx @@ -1,9 +1,7 @@ import React, { useRef, useEffect, useCallback } from "react"; import { Shaders, Node, GLSL, Bus, LinearCopy, connectSize } from "gl-react"; import { Surface } from "gl-react-dom"; -import { Blur1D } from "./blurxy"; -import { BlurV as BlurMulti } from "./blurmulti"; -import { BlurV } from "./blurmap"; +import { Blur, Blur1D, BlurV } from "gl-react-blur"; const shaders = Shaders.create({ ImageTitle: { @@ -51,13 +49,8 @@ const AveragePixels = ({ children: any; quality: number; }) => ( - - + + {children} @@ -75,9 +68,9 @@ const TitleBlurMap = ({ uniforms={{ threshold, t: ( - + {title} - + ), }} width={64} diff --git a/packages/cookbook/src/examples/blurmap.tsx b/packages/cookbook/src/examples/blurmap.tsx index e745d934..bfe1e657 100644 --- a/packages/cookbook/src/examples/blurmap.tsx +++ b/packages/cookbook/src/examples/blurmap.tsx @@ -1,98 +1,6 @@ import React from "react"; -import { Shaders, Node, GLSL, connectSize } from "gl-react"; import { Surface } from "gl-react-dom"; - -const NORM = Math.sqrt(2) / 2; - -// Cycles through H, V, and diagonal directions with increasing radius -const directionForPass = ( - p: number, - factor: number, - total: number -): [number, number] => { - const f = (factor * 2 * Math.ceil(p / 2)) / total; - switch ((p - 1) % 4) { - case 0: - return [f, 0]; - case 1: - return [0, f]; - case 2: - return [f * NORM, f * NORM]; - default: - return [f * NORM, -f * NORM]; - } -}; - -const shaders = Shaders.create({ - blurV1D: { - frag: GLSL` -precision highp float; -varying vec2 uv; -uniform sampler2D t, map; -uniform vec2 direction, resolution; -vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { - vec4 color = vec4(0.0); - vec2 off1 = vec2(1.3846153846) * direction; - vec2 off2 = vec2(3.2307692308) * direction; - color += texture2D(image, uv) * 0.2270270270; - color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; - color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; - color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; - color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; - return color; -} -void main() { - // Direction scaled by map value makes blur strength vary per-pixel - gl_FragColor = blur9(t, uv, resolution, direction * texture2D(map, uv).rg); -}`, - }, -}); - -export const BlurV1D = connectSize( - ({ - children: t, - direction, - map, - width, - height, - }: { - children: any; - direction: [number, number]; - map: any; - width: number; - height: number; - }) => ( - - ) -); - -// Recursively nests BlurV1D passes for multi-directional blur -export const BlurV = connectSize( - ({ - children, - factor, - map, - passes, - }: { - children: any; - factor: number; - map: any; - passes: number; - }) => { - const rec = (pass: number): any => - pass <= 0 ? ( - children - ) : ( - - {rec(pass - 1)} - - ); - return rec(passes); - } -); +import { BlurV } from "gl-react-blur"; const blurMapImages = [ "https://i.imgur.com/SzbbUvX.png", @@ -102,6 +10,8 @@ const blurMapImages = [ "https://i.imgur.com/wh0On3P.png", ]; +// gl-react-blur's BlurV: blur intensity varies per-pixel, +// scaled by the red/green channels of the map texture export default function BlurMapExample({ factor = 2, passes = 4, diff --git a/packages/cookbook/src/examples/blurmapdyn.tsx b/packages/cookbook/src/examples/blurmapdyn.tsx index e7634259..fcd2c57d 100644 --- a/packages/cookbook/src/examples/blurmapdyn.tsx +++ b/packages/cookbook/src/examples/blurmapdyn.tsx @@ -1,7 +1,7 @@ import React, { useRef } from "react"; import { Shaders, Node, Bus, GLSL } from "gl-react"; import { Surface } from "gl-react-dom"; -import { BlurV } from "./blurmap"; +import { BlurV } from "gl-react-blur"; import { useTimeLoop } from "../hooks/useTimeLoop"; const shaders = Shaders.create({ diff --git a/packages/cookbook/src/examples/blurmapmouse.tsx b/packages/cookbook/src/examples/blurmapmouse.tsx index c2baefea..38eca426 100644 --- a/packages/cookbook/src/examples/blurmapmouse.tsx +++ b/packages/cookbook/src/examples/blurmapmouse.tsx @@ -1,7 +1,7 @@ import React, { useState, useRef, useCallback } from "react"; import { Shaders, Node, Bus, GLSL } from "gl-react"; import { Surface } from "gl-react-dom"; -import { BlurV } from "./blurmap"; +import { BlurV } from "gl-react-blur"; const blurMapImages = [ "https://i.imgur.com/SzbbUvX.png", diff --git a/packages/cookbook/src/examples/blurmulti.tsx b/packages/cookbook/src/examples/blurmulti.tsx index c1aceb6d..31f44301 100644 --- a/packages/cookbook/src/examples/blurmulti.tsx +++ b/packages/cookbook/src/examples/blurmulti.tsx @@ -1,28 +1,21 @@ import React from "react"; -import { connectSize } from "gl-react"; import { Surface } from "gl-react-dom"; -import { Blur1D } from "./blurxy"; +import { Blur } from "gl-react-blur"; -// 4 directional blur passes for a smoother result -export const BlurV = connectSize(({ children, factor }: any) => { - const s = factor; - return ( - - - - - {children} - - - - - ); -}); - -export default function BlurMultiExample({ factor = 2 }: { factor?: number }) { +// gl-react-blur's Blur recursively chains Blur1D passes, cycling +// horizontal, vertical and diagonal directions with increasing radius +export default function BlurMultiExample({ + factor = 2, + passes = 4, +}: { + factor?: number; + passes?: number; +}) { return ( - {"https://i.imgur.com/iPKTONG.jpg"} + + {"https://i.imgur.com/iPKTONG.jpg"} + ); } diff --git a/packages/cookbook/src/examples/blurvideo.tsx b/packages/cookbook/src/examples/blurvideo.tsx index cf2c7f31..542a4093 100644 --- a/packages/cookbook/src/examples/blurvideo.tsx +++ b/packages/cookbook/src/examples/blurvideo.tsx @@ -1,7 +1,7 @@ import React, { Component, createRef } from "react"; import { Bus } from "gl-react"; import { Surface } from "gl-react-dom"; -import { BlurV } from "./blurmap"; +import { BlurV } from "gl-react-blur"; import { Saturate } from "./saturation"; import { useVideo } from "./video"; diff --git a/packages/cookbook/src/examples/index.ts b/packages/cookbook/src/examples/index.ts index 9a64089b..fe19db7a 100644 --- a/packages/cookbook/src/examples/index.ts +++ b/packages/cookbook/src/examples/index.ts @@ -200,21 +200,23 @@ export const examples: ExampleEntry[] = [ { id: "blurmulti", title: "Multi-pass Blur", - description: "4-pass blur: horizontal, vertical, and two diagonals", + description: "gl-react-blur: multi-pass gaussian Blur cycling horizontal, vertical and diagonal directions", category: "Blur", Component: lazy(() => import("./blurmulti")), controls: { factor: { type: "float", label: "Blur Factor", min: 0, max: 6, step: 0.1, default: 2 }, + passes: { type: "float", label: "Passes", min: 1, max: 8, step: 1, default: 4 }, }, }, { id: "blurmap", title: "Blur Map", - description: "Variable blur intensity controlled by a blur map texture", + description: "gl-react-blur's BlurV: variable blur intensity controlled by a blur map texture", category: "Blur", Component: lazy(() => import("./blurmap")), controls: { factor: { type: "float", label: "Blur Factor", min: 0, max: 6, step: 0.1, default: 2 }, + passes: { type: "float", label: "Passes", min: 1, max: 8, step: 1, default: 4 }, }, }, { diff --git a/packages/cookbook/vite.config.ts b/packages/cookbook/vite.config.ts index 55e441c2..291f9ba3 100644 --- a/packages/cookbook/vite.config.ts +++ b/packages/cookbook/vite.config.ts @@ -29,6 +29,7 @@ export default defineConfig({ '@': resolve(__dirname, './src'), 'gl-react': resolve(__dirname, '../gl-react/src'), 'gl-react-dom': resolve(__dirname, '../gl-react-dom/src'), + 'gl-react-blur': resolve(__dirname, '../gl-react-blur/src'), 'gl-react-image': resolve(__dirname, '../gl-react-image/src'), buffer: resolve(__dirname, './src/shims/buffer.ts'), }, diff --git a/packages/gl-react-blur/LICENSE b/packages/gl-react-blur/LICENSE new file mode 100755 index 00000000..6cdbf257 --- /dev/null +++ b/packages/gl-react-blur/LICENSE @@ -0,0 +1,19 @@ +The MIT License (MIT) +Copyright (c) 2016 - 2017 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/gl-react-blur/README.md b/packages/gl-react-blur/README.md new file mode 100644 index 00000000..3a11b381 --- /dev/null +++ b/packages/gl-react-blur/README.md @@ -0,0 +1,22 @@ +# gl-react-blur + +Universal [gl-react](https://github.com/gre/gl-react) multi-pass gaussian Blur effect with configurable intensity, also supporting variable blur with a map. + +```jsx +import { Surface } from "gl-react-dom"; // or gl-react-expo / gl-react-native / gl-react-headless +import { Blur } from "gl-react-blur"; + + + + {"https://i.imgur.com/iPKTONG.jpg"} + +; +``` + +## Components + +- **``** — multi-pass gaussian blur. `factor` is the blur intensity in pixels; `passes` (default 2) trades performance for quality; `directionForPass(pass, factor, total)` can customize the direction vector of each pass (defaults to cycling horizontal, vertical and the two diagonals with increasing radius). +- **``** — same as `Blur` but the blur intensity varies per-pixel, scaled by the red/green channels of the `map` texture (e.g. for tilt-shift or depth-of-field effects). +- **``** / **``** — a single blur pass in one direction, the building blocks of the above. + +The content to blur is given as `children` and can be any gl-react texture input (image URL, video, canvas, another Node, ...). All components accept optional `width`/`height` and otherwise inherit the size from their parent (via `connectSize`). diff --git a/packages/gl-react-blur/package.json b/packages/gl-react-blur/package.json new file mode 100644 index 00000000..c1b82c6d --- /dev/null +++ b/packages/gl-react-blur/package.json @@ -0,0 +1,37 @@ +{ + "name": "gl-react-blur", + "version": "6.0.0", + "license": "MIT", + "author": "Gaëtan Renaudeau ", + "description": "Universal gl-react multi-pass gaussian Blur effect with configurable intensity, also supporting variable blur with a map", + "keywords": [ + "gl-react", + "blur", + "gaussian", + "effect", + "gl", + "opengl", + "react", + "react-component" + ], + "repository": { + "type": "git", + "url": "https://github.com/gre/gl-react", + "directory": "packages/gl-react-blur" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "src", + "lib", + "README.md", + "LICENSE" + ], + "peerDependencies": { + "gl-react": "*", + "react": ">=18" + }, + "devDependencies": { + "gl-react": "workspace:^" + } +} diff --git a/packages/gl-react-blur/src/Blur.tsx b/packages/gl-react-blur/src/Blur.tsx new file mode 100644 index 00000000..2fa01ef5 --- /dev/null +++ b/packages/gl-react-blur/src/Blur.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { connectSize } from "gl-react"; +import Blur1D from "./Blur1D"; +import directionForPassDefault, { + DirectionForPass, +} from "./directionForPassDefault"; + +export type BlurProps = { + /** the content to blur: any gl-react texture input */ + children: any; + /** blur intensity, in pixels */ + factor: number; + /** number of Blur1D passes to run (default 2: horizontal + vertical) */ + passes?: number; + /** direction vector of a given pass; defaults to cycling H, V and diagonals */ + directionForPass?: DirectionForPass; + width?: number; + height?: number; +}; + +// Recursively nests Blur1D passes for multi-directional gaussian blur +const Blur = connectSize( + ({ + children, + factor, + passes = 2, + directionForPass = directionForPassDefault, + }: any) => { + const rec = (pass: number): any => + pass <= 0 ? ( + children + ) : ( + + {rec(pass - 1)} + + ); + return rec(passes); + } +) as React.ComponentType; + +export default Blur; diff --git a/packages/gl-react-blur/src/Blur1D.tsx b/packages/gl-react-blur/src/Blur1D.tsx new file mode 100644 index 00000000..5756cb38 --- /dev/null +++ b/packages/gl-react-blur/src/Blur1D.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { Shaders, Node, GLSL, connectSize } from "gl-react"; + +const shaders = Shaders.create({ + blur1D: { + // blur9: from https://github.com/Jam3/glsl-fast-gaussian-blur + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform vec2 direction, resolution; +vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * direction; + vec2 off2 = vec2(3.2307692308) * direction; + color += texture2D(image, uv) * 0.2270270270; + color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; + color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; + return color; +} +void main() { + gl_FragColor = blur9(t, uv, resolution, direction); +}`, + }, +}); + +export type Blur1DProps = { + /** the content to blur: any gl-react texture input */ + children: any; + /** blur direction & distance vector, in pixels */ + direction: [number, number]; + width?: number; + height?: number; +}; + +// connectSize injects width/height for correct texel offset calculation +const Blur1D = connectSize( + ({ children: t, direction, width, height }: any) => ( + + ) +) as React.ComponentType; + +export default Blur1D; diff --git a/packages/gl-react-blur/src/BlurV.tsx b/packages/gl-react-blur/src/BlurV.tsx new file mode 100644 index 00000000..d43daf60 --- /dev/null +++ b/packages/gl-react-blur/src/BlurV.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { connectSize } from "gl-react"; +import BlurV1D from "./BlurV1D"; +import directionForPassDefault, { + DirectionForPass, +} from "./directionForPassDefault"; + +export type BlurVProps = { + /** the content to blur: any gl-react texture input */ + children: any; + /** blur intensity, in pixels */ + factor: number; + /** a texture whose red/green channels scale the blur per-pixel */ + map: any; + /** number of BlurV1D passes to run (default 2: horizontal + vertical) */ + passes?: number; + /** direction vector of a given pass; defaults to cycling H, V and diagonals */ + directionForPass?: DirectionForPass; + width?: number; + height?: number; +}; + +// Recursively nests BlurV1D passes for variable multi-directional blur +const BlurV = connectSize( + ({ + children, + factor, + map, + passes = 2, + directionForPass = directionForPassDefault, + }: any) => { + const rec = (pass: number): any => + pass <= 0 ? ( + children + ) : ( + + {rec(pass - 1)} + + ); + return rec(passes); + } +) as React.ComponentType; + +export default BlurV; diff --git a/packages/gl-react-blur/src/BlurV1D.tsx b/packages/gl-react-blur/src/BlurV1D.tsx new file mode 100644 index 00000000..2a50dc22 --- /dev/null +++ b/packages/gl-react-blur/src/BlurV1D.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Shaders, Node, GLSL, connectSize } from "gl-react"; + +const shaders = Shaders.create({ + blurV1D: { + // blur9: from https://github.com/Jam3/glsl-fast-gaussian-blur + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t, map; +uniform vec2 direction, resolution; +vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * direction; + vec2 off2 = vec2(3.2307692308) * direction; + color += texture2D(image, uv) * 0.2270270270; + color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; + color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; + return color; +} +void main() { + // Direction scaled by map value makes blur strength vary per-pixel + gl_FragColor = blur9(t, uv, resolution, direction * texture2D(map, uv).rg); +}`, + }, +}); + +export type BlurV1DProps = { + /** the content to blur: any gl-react texture input */ + children: any; + /** blur direction & distance vector, in pixels */ + direction: [number, number]; + /** a texture whose red/green channels scale the blur per-pixel */ + map: any; + width?: number; + height?: number; +}; + +const BlurV1D = connectSize( + ({ children: t, direction, map, width, height }: any) => ( + + ) +) as React.ComponentType; + +export default BlurV1D; diff --git a/packages/gl-react-blur/src/directionForPassDefault.ts b/packages/gl-react-blur/src/directionForPassDefault.ts new file mode 100644 index 00000000..d13fb6a6 --- /dev/null +++ b/packages/gl-react-blur/src/directionForPassDefault.ts @@ -0,0 +1,24 @@ +const NORM = Math.sqrt(2) / 2; + +export type DirectionForPass = ( + pass: number, + factor: number, + total: number +) => [number, number]; + +// Cycles through horizontal, vertical and the 2 diagonals with increasing radius +const directionForPassDefault: DirectionForPass = (p, factor, total) => { + const f = (factor * 2 * Math.ceil(p / 2)) / total; + switch ((p - 1) % 4) { + case 0: + return [f, 0]; + case 1: + return [0, f]; + case 2: + return [f * NORM, f * NORM]; + default: + return [f * NORM, -f * NORM]; + } +}; + +export default directionForPassDefault; diff --git a/packages/gl-react-blur/src/index.ts b/packages/gl-react-blur/src/index.ts new file mode 100644 index 00000000..76a18dac --- /dev/null +++ b/packages/gl-react-blur/src/index.ts @@ -0,0 +1,12 @@ +import Blur from "./Blur"; +import Blur1D from "./Blur1D"; +import BlurV from "./BlurV"; +import BlurV1D from "./BlurV1D"; +import directionForPassDefault from "./directionForPassDefault"; + +export { Blur, Blur1D, BlurV, BlurV1D, directionForPassDefault }; +export type { BlurProps } from "./Blur"; +export type { Blur1DProps } from "./Blur1D"; +export type { BlurVProps } from "./BlurV"; +export type { BlurV1DProps } from "./BlurV1D"; +export type { DirectionForPass } from "./directionForPassDefault"; diff --git a/packages/gl-react-blur/tsconfig.json b/packages/gl-react-blur/tsconfig.json new file mode 100644 index 00000000..6593d576 --- /dev/null +++ b/packages/gl-react-blur/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "declarationDir": "lib", + "outDir": "lib", + "rootDir": "src", + "composite": true + }, + "references": [{ "path": "../gl-react" }], + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/tests/__tests__/all.js b/packages/tests/__tests__/all.js index 9c613280..cb2de6ba 100755 --- a/packages/tests/__tests__/all.js +++ b/packages/tests/__tests__/all.js @@ -2577,3 +2577,34 @@ test("gl-react-image GLImage resizeMode", async () => { }); expectToBeCloseToColorArray(free.topLeft, transparent); }); + +test("gl-react-blur Blur", () => { + const { Blur } = require("gl-react-blur"); + const shaders = Shaders.create({ + red: { + frag: GLSL` +precision highp float; +varying vec2 uv; +void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }`, + }, + }); + // blurring a constant color is a no-op: the gaussian weights sum to 1 + const inst = create( + + + + + + ); + const surface = inst.getInstance(); + surface.flush(); + expectToBeCloseToColorArray( + surface.capture(8, 8, 1, 1).data, + new Uint8Array([255, 0, 0, 255]) + ); + inst.unmount(); +}); diff --git a/packages/tests/package.json b/packages/tests/package.json index 9968d330..58949dc6 100755 --- a/packages/tests/package.json +++ b/packages/tests/package.json @@ -19,6 +19,7 @@ "version": "5.2.1", "dependencies": { "gl-react": "workspace:^", + "gl-react-blur": "workspace:^", "gl-react-headless": "workspace:^", "gl-react-image": "workspace:^", "gl-texture2d": "^2.0.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44cf274b..74ba5a24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,6 +63,9 @@ importers: gl-react: specifier: workspace:^ version: link:../gl-react + gl-react-blur: + specifier: workspace:^ + version: link:../gl-react-blur gl-react-dom: specifier: workspace:^ version: link:../gl-react-dom @@ -325,6 +328,16 @@ importers: specifier: 2.1.2 version: 2.1.2 + packages/gl-react-blur: + dependencies: + react: + specifier: '>=18' + version: 19.1.0 + devDependencies: + gl-react: + specifier: workspace:^ + version: link:../gl-react + packages/gl-react-dom: dependencies: invariant: @@ -431,6 +444,9 @@ importers: gl-react: specifier: workspace:^ version: link:../gl-react + gl-react-blur: + specifier: workspace:^ + version: link:../gl-react-blur gl-react-headless: specifier: workspace:^ version: link:../gl-react-headless diff --git a/tsconfig.json b/tsconfig.json index f52426ee..8e4443ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "paths": { "gl-react": ["./packages/gl-react/src"], "gl-react-dom": ["./packages/gl-react-dom/src"], + "gl-react-blur": ["./packages/gl-react-blur/src"], "gl-react-image": ["./packages/gl-react-image/src"], "gl-react-headless": ["./packages/gl-react-headless/src"], "gl-react-expo": ["./packages/gl-react-expo/src"], @@ -26,6 +27,7 @@ "references": [ { "path": "packages/gl-react" }, { "path": "packages/gl-react-dom" }, + { "path": "packages/gl-react-blur" }, { "path": "packages/gl-react-image" }, { "path": "packages/gl-react-headless" }, { "path": "packages/gl-react-expo" },