如何在Flutter中调整图像或小部件的色相、饱和度和亮度?

8
在我的Flutter应用中,我有一张图片和三个滑块,一个用于色相,一个用于饱和度,另一个用于亮度,我正在尝试使用ColorFiltered小部件进行这些调整,但我不知道在ColorFilter.matrix中放置什么。
我的代码大致如下:
ColorFiltered(
  colorFilter: ColorFilter.matrix(
    // What goes here?
  ),
  child: Container(
    decoration: BoxDecoration(
      image: DecorationImage(
        fit: BoxFit.cover,
        image: NetworkImage(myImageUrl),
      )
    )
  )
)

有人知道如何基于HSV值生成颜色过滤矩阵吗?

4个回答

22
为了解决这个问题,我建立了一个ImageFilter小部件,可以像这样使用:
ImageFilter(
  hue: 0.1,
  brightness: -0.6,
  saturation: 0.8,
  child: Container(
    decoration: BoxDecoration(
      image: DecorationImage(
        fit: BoxFit.cover,
        image: NetworkImage(imageUrl),
      ),
    )
  )
)

输入必须为介于-1和1之间的十进制小数形式的百分比。

使用3个ColorFiltered小部件层:

Widget ImageFilter({brightness, saturation, hue, child}) {
  return ColorFiltered(
    colorFilter: ColorFilter.matrix(
      ColorFilterGenerator.brightnessAdjustMatrix(
        value: brightness,
      )
    ),
    child: ColorFiltered(
      colorFilter: ColorFilter.matrix(
        ColorFilterGenerator.saturationAdjustMatrix(
          value: saturation,
        )
      ),
      child: ColorFiltered(
        colorFilter: ColorFilter.matrix(
          ColorFilterGenerator.hueAdjustMatrix(
            value: hue,
          )
        ),
        child: child,
      )
    )
  );
}

为了生成滤镜矩阵,我参考了类似于安卓的这个问题的答案:https://dev59.com/PW855IYBdhLWcg3wbjnO#7917978,并创建了在Flutter中可用的ColorFilterGenerator

import 'dart:math';

class ColorFilterGenerator {
    static List<double> hueAdjustMatrix({double value}) {
      value = value * pi;

      if (value == 0)
        return [
          1,0,0,0,0,
          0,1,0,0,0,
          0,0,1,0,0,
          0,0,0,1,0,
        ];

      double cosVal = cos(value);
      double sinVal = sin(value);
      double lumR = 0.213;
      double lumG = 0.715;
      double lumB = 0.072;

      return List<double>.from(<double>[
        (lumR + (cosVal * (1 - lumR))) + (sinVal * (-lumR)), (lumG + (cosVal * (-lumG))) + (sinVal * (-lumG)), (lumB + (cosVal * (-lumB))) + (sinVal * (1 - lumB)), 0, 0, (lumR + (cosVal * (-lumR))) + (sinVal * 0.143), (lumG + (cosVal * (1 - lumG))) + (sinVal * 0.14), (lumB + (cosVal * (-lumB))) + (sinVal * (-0.283)), 0, 0, (lumR + (cosVal * (-lumR))) + (sinVal * (-(1 - lumR))), (lumG + (cosVal * (-lumG))) + (sinVal * lumG), (lumB + (cosVal * (1 - lumB))) + (sinVal * lumB), 0, 0, 0, 0, 0, 1, 0,
      ]).map((i) => i.toDouble()).toList();
    }

    static List<double> brightnessAdjustMatrix({double value}}) {
      if (value <= 0)
        value = value * 255;
      else value = value * 100

      if (value == 0)
        return [
          1,0,0,0,0,
          0,1,0,0,0,
          0,0,1,0,0,
          0,0,0,1,0,
        ];

      return List<double>.from(<double>[
        1, 0, 0, 0, value, 0, 1, 0, 0, value, 0, 0, 1, 0, value, 0, 0, 0, 1, 0
      ]).map((i) => i.toDouble()).toList();
    }

    static List<double> saturationAdjustMatrix({double value}) {
      value = value * 100;

      if (value == 0)
        return [
          1,0,0,0,0,
          0,1,0,0,0,
          0,0,1,0,0,
          0,0,0,1,0,
        ];

      double x = ((1 + ((value > 0) ? ((3 * value) / 100) : (value / 100)))).toDouble();
      double lumR = 0.3086;
      double lumG = 0.6094;
      double lumB = 0.082;

      return List<double>.from(<double>[
        (lumR * (1 - x)) + x, lumG * (1 - x), lumB * (1 - x),
        0, 0,
        lumR * (1 - x),
        (lumG * (1 - x)) + x,
        lumB * (1 - x),
        0, 0,
        lumR * (1 - x),
        lumG * (1 - x),
        (lumB * (1 - x)) + x,
        0, 0, 0, 0, 0, 1, 0,
      ]).map((i) => i.toDouble()).toList();
    }
}

我敢打赌,有一种方法可以连接色调/饱和度/亮度矩阵(就像我上面提到的 Android 问题中所做的那样),并且只使用1个颜色过滤矩阵(这可能更有效),但这对我的情况起作用。


1
感谢您分享您的工作,它运行良好。 是否可以分享对比度调整? 在Android问题中,我看到有人解释了HSBC调整,我希望您能帮助我完成对比度部分的代码。 无论如何,谢谢。 - Prooshani
1
@Prooshani - 如果您在stackoverflow上发布了一个新的问题,并在评论中标记了我的名字,我会帮助您解决问题! - BananaNeil
1
谢谢@BananaNeil,我已经自己解决了。如果需要的话,我可以分享结果。 - Prooshani
1
这对社区来说可能是一件好事!我建议将其放入新的问答中。 - BananaNeil
@Prooshani,您是如何添加对比度的?可以分享一下吗? - AgentRed
显示剩余2条评论

1
我已将BananaNeil的答案添加到Themed包中。
使用提供的ChangeColors小部件可以更改任何小部件的亮度、饱和度和色调,包括图像,如下所示:
ChangeColors(
   hue: 0.55,
   brightness: 0.2,
   saturation: 0.1,
   child: Image.asset('myImage.png'),
);

参数如下:

亮度

  • 负值会使其变暗(-1 最暗)。
  • 正值会使其变亮(1 最亮,但可超过此范围)。
  • 0.0 不变。

饱和度

  • 负值会使其变得不鲜艳(-1 为灰度)。
  • 正值会使其更加鲜艳(1 最鲜艳,但可超过此范围)。
  • 0.0 不变。

色调

  • -1.01.0(注意:1.0 对应 -1.0,如1.2 等同于 -0.8)。
  • 0.0 不变。添加或减去多个 2.0 同样保持不变。

请注意:与 BananaNeil 的答案相比,ChangeColors 小部件不同之处在于它是一个适当的 StatelessWidget,代码是空安全的,它修复了饱和度的限制,使您不会出现奇怪的效果,并添加了参数说明文档。如果您不想添加包,则只需从 GitHub 复制代码即可。


1
太酷了!非常感谢! - BananaNeil
1
我没有仔细检查过,但我会将其标记为被接受的答案,因为它似乎做了我所做的事情,但实现起来更容易。 - BananaNeil

1
我们需要在其中包含一个颜色矩阵。 一些示例如下:
static const ColorFilter sepia = ColorFilter.matrix(<double>[
 0.393, 0.769, 0.189, 0, 0,
 0.349, 0.686, 0.168, 0, 0,
 0.272, 0.534, 0.131, 0, 0,
 0,     0,     0,     1, 0]);

static const ColorFilter greyscale = ColorFilter.matrix(<double>[
 0.2126, 0.7152, 0.0722, 0, 0,
 0.2126, 0.7152, 0.0722, 0, 0,
 0.2126, 0.7152, 0.0722, 0, 0,
 0,      0,      0,      1, 0]);

只需复制上面的矩阵即可获得棕褐色或灰度滤镜。


0
我进一步调整了BananaNeil和Marcelo Glasberg的答案,将这三个矩阵合并为一个,并增加了对调整孩子对比度的支持,从而提高了性能。
ColorFiltered(
  colorFilter: ColorFilters.matrix(brightness: -0.5, saturation: 0.5),
  child: Image.network('https://upload.wikimedia.org/wikipedia/commons/c/c6/500_x_500_SMPTE_Color_Bars.png'),
);

它作为Stevia包的一部分提供。

或者,如果您不想添加该包,可以复制源代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接