Skip to content

Commit e5cf0bc

Browse files
version 2
1 parent 4afdd17 commit e5cf0bc

File tree

8 files changed

+535
-175
lines changed

8 files changed

+535
-175
lines changed

samples/src/main/java/io/sifr/samples/ui/CustomBlurComparisonTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ import io.sifr.shaded.modifiers.blur
4040

4141
@Composable
4242
fun CustomBlurComparisonTest() {
43-
var blurRadius by remember { mutableFloatStateOf(25f) }
44-
var edgeTreatment by remember { mutableStateOf(BlurEdgeTreatment.UNBOUNDED) }
43+
var blurRadius by remember { mutableFloatStateOf(0f) }
44+
var edgeTreatment by remember { mutableStateOf(BlurEdgeTreatment.RECTANGLE) }
4545

4646
Column(
4747
modifier = Modifier

shaded/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ publishing {
6767
register<MavenPublication>("release") {
6868
groupId = "io.sifr"
6969
artifactId = "shaded"
70-
version = "0.1.0-alpha"
70+
version = "0.2.0-alpha"
7171

7272
afterEvaluate {
7373
from(components["release"])

shaded/src/main/cpp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_library(
1010
blur_renderer
1111
SHARED
1212
blur_renderer.cpp
13+
renderer.cpp
1314
egl_helper.cpp
1415
egl_helper.h
1516
unbounded_blur.cpp

shaded/src/main/cpp/blur_renderer.cpp

Lines changed: 203 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
BlurRenderer::BlurRenderer(int width, int height)
44
: eglHelper_(width, height),
5-
quadVBO_(0), shaderProgram_(0),
5+
quadVBO_(0), horizontalShaderProgram_(0), verticalShaderProgram_(0),
66
attrPos_(-1), attrTexCoord_(-1),
7-
uniformTexture_(-1), uniformRadius_(-1), uniformTextureSize_(-1),
8-
framebuffer_(0), fboTexture_(0), initialized_(false) {}
7+
uniformTexture_(-1), uniformRadius_(-1), uniformTextureSize_(-1), uniformDirection_(-1),
8+
framebuffer1_(0), framebuffer2_(0), fboTexture1_(0), fboTexture2_(0), initialized_(false) {}
99

1010
void BlurRenderer::initialize() {
1111
if (initialized_) return;
@@ -14,7 +14,7 @@ void BlurRenderer::initialize() {
1414

1515
compileShaders();
1616
setupFullscreenQuad();
17-
setupFramebuffer();
17+
setupFramebuffers();
1818

1919
initialized_ = true;
2020
}
@@ -32,6 +32,8 @@ GLuint BlurRenderer::uploadBitmapAsTexture(unsigned char* pixels, int width, int
3232

3333
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3434
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
35+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
36+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3537

3638
return textureId;
3739
}
@@ -40,36 +42,94 @@ void BlurRenderer::render(GLuint textureId, int width, int height, float radius)
4042
initialize();
4143
eglHelper_.makeCurrent();
4244

43-
glUseProgram(shaderProgram_);
44-
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
45+
if (radius <= 0.5f) {
46+
// No blur needed, just render directly
47+
renderDirect(textureId, width, height);
48+
return;
49+
}
50+
51+
// Resize framebuffer textures if needed
52+
resizeFramebuffers(width, height);
53+
54+
// First pass: Horizontal blur
55+
renderHorizontalPass(textureId, width, height, radius);
56+
57+
// Second pass: Vertical blur
58+
renderVerticalPass(width, height, radius);
59+
}
60+
61+
void BlurRenderer::renderHorizontalPass(GLuint textureId, int width, int height, float radius) {
62+
glUseProgram(horizontalShaderProgram_);
63+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1_);
4564
glViewport(0, 0, width, height);
4665

47-
glUniform1f(uniformRadius_, radius);
48-
glUniform2f(uniformTextureSize_, static_cast<float>(width), static_cast<float>(height));
66+
// Set uniforms
67+
glUniform1f(glGetUniformLocation(horizontalShaderProgram_, "uRadius"), radius);
68+
glUniform2f(glGetUniformLocation(horizontalShaderProgram_, "uTextureSize"),
69+
static_cast<float>(width), static_cast<float>(height));
4970

71+
// Bind input texture
5072
glActiveTexture(GL_TEXTURE0);
5173
glBindTexture(GL_TEXTURE_2D, textureId);
52-
glUniform1i(uniformTexture_, 0);
74+
glUniform1i(glGetUniformLocation(horizontalShaderProgram_, "uTexture"), 0);
5375

54-
glBindBuffer(GL_ARRAY_BUFFER, quadVBO_);
76+
drawQuad(horizontalShaderProgram_);
77+
78+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
79+
}
5580

56-
glEnableVertexAttribArray(attrPos_);
57-
glVertexAttribPointer(attrPos_, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
81+
void BlurRenderer::renderVerticalPass(int width, int height, float radius) {
82+
glUseProgram(verticalShaderProgram_);
83+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2_);
84+
glViewport(0, 0, width, height);
5885

59-
glEnableVertexAttribArray(attrTexCoord_);
60-
glVertexAttribPointer(attrTexCoord_, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
86+
// Set uniforms
87+
glUniform1f(glGetUniformLocation(verticalShaderProgram_, "uRadius"), radius);
88+
glUniform2f(glGetUniformLocation(verticalShaderProgram_, "uTextureSize"),
89+
static_cast<float>(width), static_cast<float>(height));
6190

62-
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
91+
// Bind intermediate texture from horizontal pass
92+
glActiveTexture(GL_TEXTURE0);
93+
glBindTexture(GL_TEXTURE_2D, fboTexture1_);
94+
glUniform1i(glGetUniformLocation(verticalShaderProgram_, "uTexture"), 0);
6395

64-
glDisableVertexAttribArray(attrPos_);
65-
glDisableVertexAttribArray(attrTexCoord_);
96+
drawQuad(verticalShaderProgram_);
6697

6798
glBindFramebuffer(GL_FRAMEBUFFER, 0);
6899
}
69100

101+
void BlurRenderer::renderDirect(GLuint textureId, int width, int height) {
102+
// Simple pass-through for no blur
103+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2_);
104+
glViewport(0, 0, width, height);
105+
106+
// Clear and copy texture directly
107+
glClear(GL_COLOR_BUFFER_BIT);
108+
// You could implement a simple copy shader here, or just use the vertical shader with radius 0
109+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
110+
}
111+
112+
void BlurRenderer::drawQuad(GLuint shaderProgram) {
113+
glBindBuffer(GL_ARRAY_BUFFER, quadVBO_);
114+
115+
GLint posLoc = glGetAttribLocation(shaderProgram, "aPosition");
116+
GLint texLoc = glGetAttribLocation(shaderProgram, "aTexCoord");
117+
118+
glEnableVertexAttribArray(posLoc);
119+
glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
120+
121+
glEnableVertexAttribArray(texLoc);
122+
glVertexAttribPointer(texLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
123+
124+
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
125+
126+
glDisableVertexAttribArray(posLoc);
127+
glDisableVertexAttribArray(texLoc);
128+
}
129+
70130
void BlurRenderer::readFBO(unsigned char* pixels, int width, int height) {
71131
eglHelper_.makeCurrent();
72-
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
132+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2_); // Read from final output
73133
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
74134
glBindFramebuffer(GL_FRAMEBUFFER, 0);
75135
}
@@ -88,15 +148,45 @@ void BlurRenderer::setupFullscreenQuad() {
88148
glBindBuffer(GL_ARRAY_BUFFER, 0);
89149
}
90150

91-
void BlurRenderer::setupFramebuffer() {
92-
glGenFramebuffers(1, &framebuffer_);
93-
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
151+
void BlurRenderer::setupFramebuffers() {
152+
// Create two framebuffers for ping-pong rendering
153+
glGenFramebuffers(1, &framebuffer1_);
154+
glGenFramebuffers(1, &framebuffer2_);
94155

95-
glGenTextures(1, &fboTexture_);
96-
glBindTexture(GL_TEXTURE_2D, fboTexture_);
97-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4096, 4096, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
156+
glGenTextures(1, &fboTexture1_);
157+
glGenTextures(1, &fboTexture2_);
158+
159+
currentWidth_ = 0;
160+
currentHeight_ = 0;
161+
}
98162

99-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture_, 0);
163+
void BlurRenderer::resizeFramebuffers(int width, int height) {
164+
if (currentWidth_ == width && currentHeight_ == height) {
165+
return; // No resize needed
166+
}
167+
168+
currentWidth_ = width;
169+
currentHeight_ = height;
170+
171+
// Setup first framebuffer
172+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1_);
173+
glBindTexture(GL_TEXTURE_2D, fboTexture1_);
174+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
175+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
176+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
177+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
178+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
179+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture1_, 0);
180+
181+
// Setup second framebuffer
182+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2_);
183+
glBindTexture(GL_TEXTURE_2D, fboTexture2_);
184+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
185+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
186+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
187+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
188+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
189+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture2_, 0);
100190

101191
glBindFramebuffer(GL_FRAMEBUFFER, 0);
102192
}
@@ -105,6 +195,18 @@ GLuint BlurRenderer::compileShader(GLenum type, const char* source) {
105195
GLuint shader = glCreateShader(type);
106196
glShaderSource(shader, 1, &source, nullptr);
107197
glCompileShader(shader);
198+
199+
// Check compilation status
200+
GLint success;
201+
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
202+
if (!success) {
203+
GLchar infoLog[512];
204+
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
205+
// Log error here if needed
206+
glDeleteShader(shader);
207+
return 0;
208+
}
209+
108210
return shader;
109211
}
110212

@@ -119,7 +221,8 @@ void BlurRenderer::compileShaders() {
119221
}
120222
)";
121223

122-
const char* fragmentShaderSrc = R"(
224+
// Optimized horizontal blur fragment shader
225+
const char* horizontalFragmentShaderSrc = R"(
123226
precision mediump float;
124227
varying vec2 vTexCoord;
125228
uniform sampler2D uTexture;
@@ -129,51 +232,96 @@ void BlurRenderer::compileShaders() {
129232
void main() {
130233
vec2 texelSize = 1.0 / uTextureSize;
131234
vec4 color = vec4(0.0);
132-
float totalWeight = 0.0;
133-
float pixelRadius = uRadius;
134-
if (pixelRadius <= 0.5) {
235+
236+
if (uRadius <= 0.5) {
135237
gl_FragColor = texture2D(uTexture, vTexCoord);
136238
return;
137239
}
138-
float sigma = pixelRadius / 2.0;
139-
int iRadius = int(ceil(pixelRadius));
140240
241+
float sigma = uRadius / 2.0;
242+
float twoSigmaSq = 2.0 * sigma * sigma;
243+
float totalWeight = 0.0;
244+
int iRadius = int(ceil(uRadius));
245+
246+
// Sample along horizontal axis only
141247
for (int x = -iRadius; x <= iRadius; ++x) {
142-
for (int y = -iRadius; y <= iRadius; ++y) {
143-
float dist = sqrt(float(x * x + y * y));
144-
if (dist <= pixelRadius) {
145-
vec2 offset = vec2(float(x), float(y)) * texelSize;
146-
vec2 sampleCoord = clamp(vTexCoord + offset, vec2(0.0), vec2(1.0));
147-
float weight = exp(-(dist * dist) / (2.0 * sigma * sigma));
148-
color += texture2D(uTexture, sampleCoord) * weight;
149-
totalWeight += weight;
150-
}
248+
float distance = abs(float(x));
249+
if (distance <= uRadius) {
250+
vec2 sampleCoord = vTexCoord + vec2(float(x) * texelSize.x, 0.0);
251+
sampleCoord = clamp(sampleCoord, vec2(0.0), vec2(1.0));
252+
253+
float weight = exp(-(distance * distance) / twoSigmaSq);
254+
color += texture2D(uTexture, sampleCoord) * weight;
255+
totalWeight += weight;
151256
}
152257
}
153258
154-
if (totalWeight > 0.0) {
155-
color /= totalWeight;
259+
gl_FragColor = color / totalWeight;
260+
}
261+
)";
262+
263+
// Optimized vertical blur fragment shader
264+
const char* verticalFragmentShaderSrc = R"(
265+
precision mediump float;
266+
varying vec2 vTexCoord;
267+
uniform sampler2D uTexture;
268+
uniform float uRadius;
269+
uniform vec2 uTextureSize;
270+
271+
void main() {
272+
vec2 texelSize = 1.0 / uTextureSize;
273+
vec4 color = vec4(0.0);
274+
275+
if (uRadius <= 0.5) {
276+
gl_FragColor = texture2D(uTexture, vTexCoord);
277+
return;
278+
}
279+
280+
float sigma = uRadius / 2.0;
281+
float twoSigmaSq = 2.0 * sigma * sigma;
282+
float totalWeight = 0.0;
283+
int iRadius = int(ceil(uRadius));
284+
285+
// Sample along vertical axis only
286+
for (int y = -iRadius; y <= iRadius; ++y) {
287+
float distance = abs(float(y));
288+
if (distance <= uRadius) {
289+
vec2 sampleCoord = vTexCoord + vec2(0.0, float(y) * texelSize.y);
290+
sampleCoord = clamp(sampleCoord, vec2(0.0), vec2(1.0));
291+
292+
float weight = exp(-(distance * distance) / twoSigmaSq);
293+
color += texture2D(uTexture, sampleCoord) * weight;
294+
totalWeight += weight;
295+
}
156296
}
157297
158-
gl_FragColor = color;
298+
gl_FragColor = color / totalWeight;
159299
}
160300
)";
161301

302+
// Compile vertex shader (shared)
162303
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
163-
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
164304

165-
shaderProgram_ = glCreateProgram();
166-
glAttachShader(shaderProgram_, vertexShader);
167-
glAttachShader(shaderProgram_, fragmentShader);
168-
glLinkProgram(shaderProgram_);
305+
// Compile horizontal fragment shader
306+
GLuint horizontalFragmentShader = compileShader(GL_FRAGMENT_SHADER, horizontalFragmentShaderSrc);
307+
308+
// Compile vertical fragment shader
309+
GLuint verticalFragmentShader = compileShader(GL_FRAGMENT_SHADER, verticalFragmentShaderSrc);
169310

170-
attrPos_ = glGetAttribLocation(shaderProgram_, "aPosition");
171-
attrTexCoord_ = glGetAttribLocation(shaderProgram_, "aTexCoord");
311+
// Create horizontal blur program
312+
horizontalShaderProgram_ = glCreateProgram();
313+
glAttachShader(horizontalShaderProgram_, vertexShader);
314+
glAttachShader(horizontalShaderProgram_, horizontalFragmentShader);
315+
glLinkProgram(horizontalShaderProgram_);
172316

173-
uniformTexture_ = glGetUniformLocation(shaderProgram_, "uTexture");
174-
uniformRadius_ = glGetUniformLocation(shaderProgram_, "uRadius");
175-
uniformTextureSize_ = glGetUniformLocation(shaderProgram_, "uTextureSize");
317+
// Create vertical blur program
318+
verticalShaderProgram_ = glCreateProgram();
319+
glAttachShader(verticalShaderProgram_, vertexShader);
320+
glAttachShader(verticalShaderProgram_, verticalFragmentShader);
321+
glLinkProgram(verticalShaderProgram_);
176322

323+
// Clean up shaders
177324
glDeleteShader(vertexShader);
178-
glDeleteShader(fragmentShader);
179-
}
325+
glDeleteShader(horizontalFragmentShader);
326+
glDeleteShader(verticalFragmentShader);
327+
}

0 commit comments

Comments
 (0)