Flutter Color 大调整,需适配迁移,颜色不再是 0-255,而是 0-1.0,支持更大色域
在之前的 3.10 里, Flutter 的 Impeller 在 iOS 上支持了 P3 广色域图像渲染,但是当时也仅仅是当具有广色域图像或渐变时,Impeller 才会在 iOS 上显示 P3 的广色域的颜色,而如果你使用的是 Color API,会发现使用的还是 sRGB 色域的绘制。
⚠️ 如果对原因和调整不感兴趣的,可以直接看后面的适配。
那么实现 sRGB 和 P3 有什么区别?简单来说,最直观的感受就是如下图所示的区别,在具备 P3 色域的设备屏幕上, P3 拥有更广的色域,所以可以看到更鲜艳的颜色,而在如今 2024 来看,其实大部分移动设备都已经具备了 P3 色域的硬件支持。

简单看下如下图所示 sRGB(白色三角形)与 Display P3(橙色三角形)的色域对比,从比例上说,P3 广色域可以比 sRGB 多出至少 25% 的颜色支持。

当然, 3.10 中 Flutter 只是为 Impeller 开启了图像渲染是的广色域支持,对于使用 Color 类 API 的情况,依然还停留在 sRGB 下,它没有对在 Flutter Widget 中渲染广色域提供 API 的支持。
所以 Flutter Team 在 2023 年开启了重构 Color 对象的提案,让 Color 也调整为支持 P3 广色域的能力,最直观的就是,原本参数的有效范围 [0, 255] 的整形,而现在颜色分量调整为 [0,1.0] 的浮点。
你是不是奇怪,为什么 [0, 255] 变成 [0,1.0] ,但是色域居然还变大了?这其实是因为整形变成了浮点之后,精度带来的可用位数增加:
在此之前,整形的 [0, 255] 颜色也就是每个颜色只有 8 位,从 Dart 层传递到引擎的方式是:ARGB(4x8) 被打包成一个 32 位 alpha-red-blue-green 的 Dart 整数 ,每个 color 有 8 位(255),之后 color 会被转化为 Skia 的
SkColor,然后被处理为引擎中绘制需要的DlColor,都是整形数据。在 Impeller 中,
DlColor会进一步被转换为impeller::Color,impeller::Color是 128 位,每个颜色都具有 32 位标准化浮点,也就是精度让位数变高了。
那为什么提高位数对支持色域很重要?因为 P3 覆盖的颜色较多,8bit/channel 已经不能表达所有 P3 颜色了,所以至少需要用 10bit/channel 来表示。
而很明显,在此之前 Flutter 的 sRGB [0, 255] 的整形表示,每个颜色只有 8 位,合起来一个整体 x4 也只有 32 位,而使用浮点 [0-1.0] 表示,则改变了这一格局:
从 3.27 beta 版本开始,
dart:ui中 Color 从最大 64 位变为最大 320 位。

为什么说是从 64 位变为 320 位?因为原本只有一个整型 int ,int 在 dart 里是 64 位,所以原本 Color 最大只有 64 位,而现在增加了 a、r、g、b 四个 double ,也就是多了四个 64 位精度的参数,所以 Color 现一共有 5 个参数,变成了最大 320 位,不过未来 value 会被弃用:
也就是每个 a、r、g、b 每个理论可用为 64 位之多,如果不考虑 value ,Color 就是从原本的 64 位整数更改为 256 位浮点数。

之后,组件将颜色的 TypedData 发送到 C++,而前面说过,在 Engine 层的 DlColor 也调整到 128 位适配 impeller::Color , 也就是 DlColor 是 4 个 32 位的 float 组成,同时往后兼容 sRGB 色彩空间。
不要在意 Dart/64 => C++/32 ,只是语言特性导致的差异。


另外,ColorSpace 也新增了 displayP3 选项,至此整个 Color 广色域支持初步完成,但是真实的 P3 渲染效果目前只在 iOS 上可以看到,后续 Android 需要等 Vulkan Impeller 支持的成熟才能在底层进一步适配支持。

代码兼容调整
随着 Color 底层支持的变化,Color 上层 API 也发生了不少调整,其实本次调整算是比较大的 break ,因为色域变化后其实上层 API 调整还是很明显的, 例如最明显的就是不再推荐用 fromARGB ,而是用 Color.from 。
// Before
final magenta = Color.fromARGB(0xff, 0xff, 0x0, 0xff);
// After
final magenta = Color.from(alpha: 1.0, red: 1.0, green: 0.0, blue: 1.0)
另外,在以前 Color 具有 “opacity” 的概念,所有会有 opacity 和 withOpacity() 的存在,以前引入 Opacity 是为了让 Color 具有浮点的 alpha 通道,而现在 alpha 是一个浮点值,所以 opacity 显得多余,未来opacity 和 withOpacity 已被弃用并计划删除:
// Before
final x = color.opacity;
// After
final x = color.a;// Before
final x = color.withOpacity(0.0);
// After
final x = color.withValues(alpha: 0.0);

另外,如果有对于 Color 的继承最好暂时换成 implements ,也就是不需要再实现 super 相关部分逻辑,另外为什么说是暂时,因为目前 Flutter 团队计划未来 Color 会锁定并通过 sealed 封闭,所以如果通过 sealed 封闭 ,那以后基本不会有 Color 的外部实现了:
class Foo implements Color {int _red;double get r => _red * 255.0;
}
其次,现在使用 Color 并对颜色执行任何类型的计算,都需要首先检查颜色的 ColorSpace ,然后再执行计算逻辑,这里推荐使用新的 Color.withValues 方法来执行颜色空间转换:
// Before
double redRatio(Color x, Color y) => x.red / y.red;// After
double redRatio(Color x, Color y) {final xPrime = x.withValues(colorSpace: ColorSpace.extendedSRGB);final yPrime = y.withValues(colorSpace: ColorSpace.extendedSRGB);return xPrime.r / yPrime.r;
}
使用颜色进行计算,如果不对齐色彩空间可能会导致细微的意外结果,例如在上面的示例中,如果使用不同的颜色空间与对齐的颜色空间进行计算时,redRatio 的差异为 0.09。
最后
事实上,本次调整虽然看起来就像是一个简单的 toDouble 过程,但是它对于 Flutter 的 UI 影响确实不小,不管是颜色 API 的适配,还是计算方式的变化,甚至是色域的支持等场景,如果单从影响效果看,这个合并可以说是影响广泛,整出边界渲染问题其实并非不可能,所以这个合并也是憋了这么久才被合并到 beta ,也许不久之后,我们就可以在正式版本中看到它了。

参考资料:
https://github.com/flutter/flutter/issues/55092
https://github.com/dart-lang/sdk/issues/56363
https://github.com/flutter/flutter/issues/127852
https://github.com/flutter/flutter/issues/127855
https://docs.flutter.dev/release/breaking-changes/wide-gamut-framework
相关文章:
Flutter Color 大调整,需适配迁移,颜色不再是 0-255,而是 0-1.0,支持更大色域
在之前的 3.10 里, Flutter 的 Impeller 在 iOS 上支持了 P3 广色域图像渲染,但是当时也仅仅是当具有广色域图像或渐变时,Impeller 才会在 iOS 上显示 P3 的广色域的颜色,而如果你使用的是 Color API,会发现使用的还是…...
如何使用VBA识别Excel中的“单元格中的图片”(2/2)
Excel 365升级了新功能,支持两种不同的插入图片方式: 放置在单元格中(Place in cell),新功能,此操作插入的图片下文中简称为单元格中的图片。放置在单元格上(Place over cell)&…...
2024系统架构师---下午题目常考概念
1.管道-过滤器的概念:管道-过滤器风格具备高内聚、低耦合、支持软件重用、扩展性好、支持并发等优点,但它有编写复杂、不适合处理交互应用等缺点。 2.隐式调用的概念:隐式调用基于事件触发的思想,具备支持软件重用,改…...
【Linux】从零开始认识五种IO模型 --- 理解五种IO模型,开始使用非阻塞IO
恐惧让你沦为囚犯, 希望让你重获自由。 --- 《肖申克的救赎》--- 五种IO模型与阻塞IO 1 前言2 五种IO模型3 非阻塞IO 1 前言 通过网络通信的学习,我们能够理解网络通信的本质是进程间通信,而进程间通信的本质就是IO。 IO就是input与outp…...
Spring Boot 集成阿里云直播点播
在当今数字化时代,视频直播和点播服务已经成为许多应用的核心功能。阿里云提供了强大的直播和点播服务,能够满足各种规模的应用需求。而 Spring Boot 作为一种流行的 Java 开发框架,能够快速构建高效的应用程序。本文将详细介绍如何在 Spring…...
舍伍德业务安全架构(Sherwood Applied Business Security Architecture, SABSA)
舍伍德业务安全架构(Sherwood Applied Business Security Architecture, SABSA)是一个企业级的安全架构框架,它提供了一个全面的方法来设计和实现信息安全策略。SABSA模型将业务需求与安全控制相结合,确保企业的信息安全措施能够支…...
论可以对抗ai编程的软件开发平台(直接把软件需求描述变成软件的抗ai开发平台)的设计
论可以对抗ai编程的软件开发平台(直接把软件需求描述变成软件的抗ai开发平台)的设计 大家知道,传统的数学密码,都可以被量子计算机破解,但是这些年发展出很多数学密码,量子计算机也破解不了,叫…...
饿了么数据库表设计
有商家表、商品表、商品规格表、购物车表,不难分析出表是不够全面的。 (1)首先分析需要补充的表 1.对于购物车而言肯定有对应的用户,因此要添加一个用户表。 2.商品规格是冷,热,半分糖、全糖,对于冷热和半分糖是可以分…...
Flink处理乱序的数据的最佳实践
目录 网络延迟和分布式系统 事件时间与处理时间的差异 事件时间和水位线(Watermark) 时间窗口(TimeWindow) 滚动窗口(Tumbling Window) 滑动窗口(Sliding Window) 会话窗口(Session Window) 自定义Watermark生成策略 设置允许延迟和侧输出 设置允许的最大延迟时间 使…...
Android OpenGL ES详解——模板Stencil
目录 一、概念 1、模板测试 2、模板缓冲 二、模板测试如何使用 1、开启和关闭模板测试 2、开启/禁止模板缓冲区写入 3、模板测试策略函数 4、更新模板缓冲 5、模板测试应用——物体轮廓 三、模板缓冲如何使用 1、创建模板缓冲 2、使用模板缓冲 3、模板缓冲应用——…...
vscode在cmake config中不知道怎么选一个工具包?select a kit
vscode在cmake config中不知道怎么选一个工具包,或者发现一直在用VS的工具包想换成自己的工具包。select a kit vscode在cmake config中不知道怎么选一个工具包,或者发现一直在用VS的工具包想换成自己的工具包。select a kit 1.在VSCode中 按ctrlshift…...
无人机之无线电监测设备技术篇
一、技术原理 无人机的无线电监测设备主要通过捕捉和分析无人机发出的无线电信号来实现对无人机的监测和定位。这些信号包括无人机的上行遥控信号、下行数据图传信号等。设备采用多种技术手段,如频谱分析、信号解调、定位算法等,对接收到的信号进行处理和…...
【系统架构设计师】预测试卷一:案例分析
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 试题一(共25分)【问题 1】(12分)【问题 2】(13分)试题二(共 25分)【问题 1】(12分)【问题 2】(7分)【问题 3】(6分)试题三(共25分)【问题 1】(9分)【问题 2】(16分)试题四(共25分)【问题 1】…...
一篇文章教会你I2C通信(软件I2C和硬件I2C)以读取MPU6050为例,附STM32代码示例
目录 一、I2C通信介绍: (1)基本概念: (2)特点: (3)工作原理: 二、I2C通信原理: (1)I2C 物理层: &…...
Python实现SPFA算法
目录 Python实现SPFA算法引言一、SPFA算法的理论基础1.1 最短路径问题1.2 SPFA算法的基本原理1.3 SPFA算法的复杂度 二、SPFA算法的Python实现2.1 基本实现2.2 案例一:使用SPFA算法进行城市交通最短路径计算2.2.1 实现代码 2.3 案例二:负权重边的处理2.3…...
MYSQL安装(ubuntu系统)
rpm -qa 查询安装软件包 ps axj 查询服务 卸载mysql(万不得已) ps axj | grep mysql 查看是否存在mysql服务 systemctl stop mysqld 关闭该服务 rpm -qa | grep mysql 查安装mysql安装包 rmp -qa | grep mysql | xargs (yum apt) -y remove进行批量…...
Cpp二叉搜索树的讲解与实现(21)
文章目录 前言一、二叉搜索树的概念定义特点 二、二叉树的实现基本框架查找插入删除当只有0 ~ 1个孩子的时候当有2个孩子的时候 三、二叉树的应用K模型KV模型 四、二叉树的性能分析总结 前言 这是全新的一个篇章呢,二叉搜索树是我们接下来学习set、map的前提 迈过它…...
微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)
微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern) 定义 在云计算和分布式系统中,管理跨多个微服务或组件的事务一致性是一项极具挑战性的任务,补偿事务模式Compensating Transaction Pattern)是一种…...
20 实战:形状编码、运动补偿和纹理编码的实现(基于python)
在当今多媒体时代,视频处理与编码已经成为各个领域中不可或缺的一部分。无论是视频编辑、流媒体传输,还是计算机视觉应用,视频编码技术都扮演着关键角色。本文将详细解析一个基于Python的图形用户界面(GUI)视频编码器。通过对代码的逐行讲解、功能分析以及参数调节方法的探…...
区块链-C++挖矿软件XMRIG源码分析
C++挖矿软件源码分析 3rdpartybackendgrgon2Obfusheader.hmain 程序 xmrig.cppxmrig命名空间process类Entry::IdApp类CoreControllerbasetoolkernelinterfacesDonateStrategy.cppdonate.h/2/dmiCmake 跨平台的自动化构建系统CMakeLists.txt.cmake 13个引入算力哈希率 HashrateE…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
