Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ packages/cookbook-v2/test-results
packages/cookbook-v2/dist
*.tsbuildinfo
packages/cookbook-expo/node_modules
packages/cookbook/dist
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
1 change: 1 addition & 0 deletions packages/cookbook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 5 additions & 12 deletions packages/cookbook/src/examples/blurimgtitle.tsx
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down Expand Up @@ -51,13 +49,8 @@ const AveragePixels = ({
children: any;
quality: number;
}) => (
<Blur1D width={1} height={1} resolution={[1, 1]} direction={[0, 0.1]}>
<Blur1D
width={1}
height={quality}
resolution={[1, quality]}
direction={[0.1, 0]}
>
<Blur1D width={1} height={1} direction={[0, 0.1]}>
<Blur1D width={1} height={quality} direction={[0.1, 0]}>
{children}
</Blur1D>
</Blur1D>
Expand All @@ -75,9 +68,9 @@ const TitleBlurMap = ({
uniforms={{
threshold,
t: (
<BlurMulti factor={4} width={200} height={200}>
<Blur factor={4} passes={4} width={200} height={200}>
{title}
</BlurMulti>
</Blur>
),
}}
width={64}
Expand Down
96 changes: 3 additions & 93 deletions packages/cookbook/src/examples/blurmap.tsx
Original file line number Diff line number Diff line change
@@ -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;
}) => (
<Node
shader={shaders.blurV1D}
uniforms={{ t, map, resolution: [width, height], direction }}
/>
)
);

// 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
) : (
<BlurV1D map={map} direction={directionForPass(pass, factor, passes)}>
{rec(pass - 1)}
</BlurV1D>
);
return rec(passes);
}
);
import { BlurV } from "gl-react-blur";

const blurMapImages = [
"https://i.imgur.com/SzbbUvX.png",
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/cookbook/src/examples/blurmapdyn.tsx
Original file line number Diff line number Diff line change
@@ -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({
Expand Down
2 changes: 1 addition & 1 deletion packages/cookbook/src/examples/blurmapmouse.tsx
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
33 changes: 13 additions & 20 deletions packages/cookbook/src/examples/blurmulti.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Blur1D direction={[s, 0]}>
<Blur1D direction={[0, s]}>
<Blur1D direction={[s / Math.SQRT2, s / Math.SQRT2]}>
<Blur1D direction={[-s / Math.SQRT2, s / Math.SQRT2]}>
{children}
</Blur1D>
</Blur1D>
</Blur1D>
</Blur1D>
);
});

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 (
<Surface width={400} height={300}>
<BlurV factor={factor}>{"https://i.imgur.com/iPKTONG.jpg"}</BlurV>
<Blur factor={factor} passes={passes}>
{"https://i.imgur.com/iPKTONG.jpg"}
</Blur>
</Surface>
);
}
2 changes: 1 addition & 1 deletion packages/cookbook/src/examples/blurvideo.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand Down
6 changes: 4 additions & 2 deletions packages/cookbook/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
},
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/cookbook/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
},
Expand Down
19 changes: 19 additions & 0 deletions packages/gl-react-blur/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
The MIT License (MIT)
Copyright (c) 2016 - 2017 <renaudeau.gaetan@gmail.com>

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.
22 changes: 22 additions & 0 deletions packages/gl-react-blur/README.md
Original file line number Diff line number Diff line change
@@ -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";

<Surface width={400} height={300}>
<Blur factor={4} passes={4}>
{"https://i.imgur.com/iPKTONG.jpg"}
</Blur>
</Surface>;
```

## Components

- **`<Blur factor passes? directionForPass?>`** — 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).
- **`<BlurV map ...>`** — 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).
- **`<Blur1D direction>`** / **`<BlurV1D direction map>`** — 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`).
37 changes: 37 additions & 0 deletions packages/gl-react-blur/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "gl-react-blur",
"version": "6.0.0",
"license": "MIT",
"author": "Gaëtan Renaudeau <renaudeau.gaetan@gmail.com>",
"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:^"
}
}
Loading
Loading