Shopify / react-native-skia

High-performance React Native Graphics using Skia
https://shopify.github.io/react-native-skia
MIT License
6.64k stars 420 forks source link

Alpha channel not working properly in sksl shader #2478

Open sanscrid6 opened 2 weeks ago

sanscrid6 commented 2 weeks ago

Description

I have simple shader which converts image to greyscale and then apply my color. But I found issue with alpha channel, when I set value of alpha channel to value 0.1 for example it creates small opacity effect, but expected hardly seen image. Moreover if I set alpha channel to 0 my image don't disappear.

I use expo 51.0.9 and react-native 0.74.1.

Version

1.2.3

Steps to reproduce

1) Apply component to image 2) Set opacity from range [0.1, 0]

Snack, code example, screenshot, or link to a repository


import {
  ImageShader,
  Skia,
  Shader,
  Fill,
  SkImage,
  Fit,
} from '@shopify/react-native-skia';
import { useDerivedValue } from 'react-native-reanimated';

const colorShader = Skia.RuntimeEffect.Make(`
    uniform shader image;
    uniform vec4 color;

    half4 main(float2 coord) {
        vec4 baseColor = image.eval(coord);

        if(baseColor.a == 0){
          return vec4(0, 0, 0, 0);
        }

        float c = 0.2126 * baseColor.r + 0.7152 * baseColor.g + 0.0722 * baseColor.b;
        float cSrgb = 0;

        if(c < 0.0031308){
            cSrgb = c * 12.92;
        } else {
            cSrgb = 1.055 * pow(c, 1 / 2.4) - 0.055;
        }

        vec4 res = vec4(cSrgb, cSrgb, cSrgb, color.a);
        vec4 resultColor = res * color;

        return resultColor;
    }
`);

type ColorizedImageProps = {
  image: SkImage;
  width: number;
  height: number;
  color: [number, number, number, number];
  x?: number;
  y?: number;
  fit?: Fit;
};

export const ColorizedImage = ({
  image,
  width,
  height,
  color,
  x,
  y,
  fit,
}: ColorizedImageProps) => {
  const uniforms = useDerivedValue(() => ({ color }), [color]);

  if (!colorShader) return null;

  return (
    <Fill>
      <Shader source={colorShader} uniforms={uniforms}>
        <ImageShader
          image={image}
          fit={fit ?? 'cover'}
          rect={{
            x: x ?? 0,
            y: y ?? 0,
            width: width,
            height: height,
          }}
        />
      </Shader>
    </Fill>
  );
};
sanscrid6 commented 4 days ago

To apply opacity, you need to multiply the entire color vector by the alpha value, rather than simply setting the alpha value.

vec4 res = vec4(cSrgb, cSrgb, cSrgb, color.a) * myAlpha;