I’m trying to create an image filter app using FragmentShader with Flutter.
I used OpenGL code to apply a lookup table filter and created and applied my own shader code in the filter.frag file.
This lookup table works fine when I build with MacOS, but when I build with an IOS emulator, the shader breaks and looks weird.
The code seems to be fine, but does anyone know why this difference is happening?
I have tried using a library called flutter_image_filters to test this, but the results are the same. I attached my filter.frag, painter.dart code.
- filter.frag
vec4 lookupFrom2DTexture(vec3 textureColor) {
float blueColor = textureColor.b * 63.0;
vec2 quad1 = vec2(0.0, 0.0);
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
vec2 quad2 = vec2(0.0, 0.0);
quad2.y = floor(ceil(blueColor) / 8.0);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
vec2 texPos1 = vec2(0.0, 0.0);
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
vec2 texPos2 = vec2(0.0, 0.0);
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
vec4 newColor1 = texture(uLookUp, texPos1);
vec4 newColor2 = texture(uLookUp, texPos2);
return mix(newColor1, newColor2, fract(blueColor));
}
vec4 applyColorLookup(vec4 sourceColor){
vec4 newColor = lookupFrom2DTexture(clamp(sourceColor.rgb, 0.0, 1.0));
return mix(sourceColor, vec4(newColor.rgb, sourceColor.w), 1.0);
}
void main() {
vec2 uv = FlutterFragCoord().xy / canvasSize;
// Calculate the aspect ratio of the canvas and the texture
float canvasAspect = canvasSize.x / canvasSize.y;
float textureAspect = textureSize(uTexture, 0).x / textureSize(uTexture, 0).y;
// Calculate the scale factor to fit the image within the canvas
float scaleFactor = min(canvasAspect / textureAspect, 1.0);
// Calculate the position to center the image
vec2 imagePos = (canvasSize - imgSize) * 0.5;
// Map the UV coordinates to the centered image
vec2 centeredUV = (FlutterFragCoord().xy - imagePos) / imgSize;
// Sample the texture using the centered UV coordinates
vec4 color = texture(uTexture, centeredUV);
color = applyColorLookup(color);
fragColor = color;
}
- painter.dart
import "dart:ui" as ui;
import "package:film_n/controllers/main_controller.dart";
import "package:flutter/cupertino.dart";
import "package:flutter/material.dart";
import "package:flutter/widgets.dart";
import "package:get/get.dart";
// make an image shader painter
class ImageShaderPainter extends CustomPainter {
final ui.Image? image;
final ui.Image? lut;
final ui.FragmentShader? shader;
final controller = Get.find<MainController>();
ImageShaderPainter({
this.image,
this.lut,
this.shader,
});
@override
void paint(Canvas canvas, Size size) {
if (image == null || shader == null || lut == null) {
return;
}
late double scaledWidth;
late double scaledHeight;
// Get the original width and height of the image
final double imageWidth = image!.width.toDouble();
final double imageHeight = image!.height.toDouble();
// Get the width and height of the canvas or the available space
final double canvasWidth = size.width;
final double canvasHeight = size.height;
// Calculate the aspect ratios of the image and the canvas
final double imageAspectRatio = imageWidth / imageHeight;
final double canvasAspectRatio = canvasWidth / canvasHeight;
if (imageAspectRatio canvasAspectRatio) {
// Image is wider than the canvas
scaledWidth = canvasWidth;
scaledHeight = scaledWidth / imageAspectRatio;
} else {
// Image is taller than the canvas or has the same aspect ratio
scaledHeight = canvasHeight;
scaledWidth = scaledHeight * imageAspectRatio;
}
// set the image position arguments
shader!.setFloat(0, size.width);
shader!.setFloat(1, size.height);
shader!.setFloat(2, scaledWidth);
shader!.setFloat(3, scaledHeight);
shader!.setImageSampler(0, image!);
shader!.setImageSampler(1, lut!);
final paint = Paint()..shader = shader;
canvas.drawRect(
Rect.fromCenter(
center: Offset(canvasWidth / 2, canvasHeight / 2),
width: scaledWidth,
height: scaledHeight),
paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
if (oldDelegate is ImageShaderPainter &&
oldDelegate.shader == shader &&
oldDelegate.lut == lut &&) {
return false;
}
return true;
}
}
I tried changing the precision to highp, but it didn’t make a difference.