[UWP]使用Writeable?Bitmap创建HSV色轮
原文:[UWP]使用Writeable?Bitmap创建HSV色轮
1. HSV
1.1 HSV的定义
HSV都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法,这种表示法试图做到比RGB基于笛卡尔坐标系的几何结构更加直观。HSV即色相、饱和度、明度(英语:Hue, Saturation, Value),又称HSB,其中B即英语:Brightness。
- 色相(H)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等,取值0-360。红色是0,绿色是120,蓝色为240。
- 饱和度(S)是指色彩的纯度,越高色彩越纯,低则逐渐变灰,取0-100%的数值。
- 明度(V),数值越低越接近黑色。,取0-100%。

1.2 HSV与RGB
HSV在数学上定义为在RGB空间中的颜色的R, G和B的坐标的变换。

1.2.1 从RGB到HSL或HSV的转换
(r, g, b)分别是一个颜色的红、绿和蓝坐标,它们的值是在0到1之间的实数。设max等价于r, g和b中的最大者。设min等于这些值中的最小者:


1.2.2 从HSV到RGB的转换
给定在HSV中 (h, s, v)值定义的一个颜色,带有如上的h,和分别表示饱和度和明度的s和v变化于0到1之间,在RGB空间中对应的 (r, g, b)三原色可以计算为(R,G,B变化于0到1之间):

对于每个颜色向量 (r, g, b),

1.3 HSV的应用
HSV模型通常用于计算机图形应用中。在用户必须选择一个颜色应用于特定图形元素各种应用环境中,经常使用HSV 色轮。

另外,由于HSV对用户来说是一种直观的颜色模型,所以常用于调整图片,下图为Paint.Net中调整图片:


下图为UWPCommunityToolkit中通过Saturation调整图片:

1.4 HSV与色轮
很多设计方面的书籍都有介绍使用色轮为UI配色,由于篇幅较大这里就不在论述了,具体可以参考以下链接:网页设计中怎么配色
2. WriteableBitmap
WriteableBitmap 提供可写入并可更新的 BitmapSource。也就是说, 你可动态更改图像,然后重新呈现更新的图像。使用WinRTXamlToolkit可以轻松完成这个操作,代码如下:
var diameter = 100;
var source = new WriteableBitmap(diameter, diameter);
var pixels = source.PixelBuffer.GetPixels();
for (var i = 0; i < diameter * diameter; i++)
{
var color = Color.FromArgb(255, 255, 255, 255);
pixels.Bytes[i * 4] = color.B;
pixels.Bytes[i * 4 + 1] = color.G;
pixels.Bytes[i * 4 + 2] = color.R;
pixels.Bytes[i * 4 + 3] = color.A;
}
pixels.UpdateFromBytes();
source.Invalidate();
_imageElement.Source = source;
上面的代码将一个尺寸在100*100的WriteableBitmap中所有像素都设为白色,然后设置为图片的Source。在这里像素数据的格式为BitmapPixelFormat.Bgra8,即用四个Byte分别表示颜色的RGRA(通常颜色表示成ARGB,如#FFFF0000即Alpha:255,Red:255,Green:0,Blue:0)。
还可以将WriteableBitmap保存成文件,同样使用WinRTXamlToolkit实现:
await source.SaveAsync(KnownFolders.PicturesLibrary, "Wheel.png");
3. 使用WriteableBitmap创建HSV色轮
前面介绍了Hsv色轮,也介绍了如何使用WriteableBitmap,那么用WriteableBitmap实现一个HSV色轮是一件很简单的事,只需要计算每个像素点距离中心点的角度(Hue)和距离(Saturation)得出HsvColor,再转换成ArgbColor填入WriteableBitmap就实现了。具体代码如下:
var diameter = width < height ? width : height;
var radius = diameter / 2;
var source = new WriteableBitmap(diameter, diameter);
var pixels = source.PixelBuffer.GetPixels();
var array = new double[diameter, diameter];
for (var i = 0; i < diameter * diameter; i++)
{
var x = i % diameter;
var y = i / diameter;
var distance = Math.Sqrt(Math.Pow(radius - x, 2) + Math.Pow(radius - y, 2));
var saturation = distance / radius;
array[x, y] = saturation;
if (saturation >= 1)
{
pixels.Bytes[i * 4] = 0;
pixels.Bytes[i * 4 + 1] = 0;
pixels.Bytes[i * 4 + 2] = 0;
pixels.Bytes[i * 4 + 3] = 0;
}
else
{
var distanceOfX = x - radius;
var distanceOfY = y - radius;
var theta = Math.Atan2(distanceOfY, distanceOfX);
if (theta < 0)
theta += 2 * Math.PI;
var hue = theta / (Math.PI * 2) * 360.0;
var color = ColorHelper.FromHsv(hue, saturation, 1);
pixels.Bytes[i * 4] = color.B;
pixels.Bytes[i * 4 + 1] = color.G;
pixels.Bytes[i * 4 + 2] = color.R;
pixels.Bytes[i * 4 + 3] = 255;
}
}
pixels.UpdateFromBytes();
source.Invalidate();

有个小问题,即使不仔细看也能看到圆形的边缘锯齿很严重。当然可以在上面的代码里加入高斯模糊的算法处理这些锯齿,但毕竟这篇文章不打算讨论到这么深入。可以简单地使用WriteableBitmapEx对整个WriteableBitmap进行高斯模糊:
source.Convolute(WriteableBitmapExtensions.KernelGaussianBlur5x5);
The WriteableBitmapEx library is a collection of extension methods for the WriteableBitmap. The WriteableBitmap class is available for all XAML flavors including Windows Phone, WPF, WinRT Windows Store XAML, (Windows 10) UWP and Silverlight.
这样看起来就好很多了。

4. HSV转RGB的陷阱
上面代码中RGB和HSV互换使用了UWPCommunityToolkit中的ColorHelper,ColorHelper的介绍是这样的:
The Colors Helper lets users convert colors from text names, HTML hex, HSV, or HSL to Windows UI Colors (and back again of course).
但是这里有个陷阱。以下代码将一个RGB color转换成HSV color,再转换回RGB color,看起来没什么问题:
var color = Color.FromArgb(255, 255, 20, 200);
var hsv = ColorHelper.ToHsv(color);
Debug.WriteLine(string.Format("H:{0} S:{1} V:{2}", hsv.H,hsv.S , hsv.V));
color = ColorHelper.FromHsv(hsv.H, hsv.S , hsv.V );
Debug.WriteLine(string.Format("R:{0} G:{1} B:{2}", color.R, color.G, color.B));
但是看输出就能发现转回来的RBG color改变了:
H:314.042553191489 S:0.92156862745098 V:1
R:255 G:19 B:199
造成这个问题的原因在于RGB能表示的颜色范围有限,只是256 * 256 * 256=16777216种颜色。而HSV如果使用int值,只能表示360 * 100 * 100=3600000种颜色,如果用double则几乎有无数种组合,这样两种颜色模型间就不匹配了。这种情况下只能折衷一下限制HSV的精度了,改成下面的代码能解决上面的问题:
color = ColorHelper.FromHsv(Math.Round(hsv.H), Math.Round(hsv.S, 2), Math.Round(hsv.V, 2));
5. 参考
HSL and HSV - Wikipedia
WriteableBitmap Class
6. 源码
[UWP]使用Writeable?Bitmap创建HSV色轮的更多相关文章
- [UWP]使用WriteableBitmap创建HSV色轮
1. HSV 1.1 HSV的定义 HSV都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法,这种表示法试图做到比RGB基于笛卡尔坐标系的几何结构更加直观.HSV即色相.饱和度.明度(英语:Hue, ...
- [UWP]分享一个基于HSV色轮的调色板应用
1. 前言 上一篇文章介绍了HSV色轮,这次分享一个基于HSV色轮的调色板应用,应用地址:ColorfulBox - Microsoft Store 2. 功能 ColorfulBox是Adobe 色 ...
- UWP app HelloWorld 的创建
步骤 1:在 Visual Studio 中创建新项目 启动 Visual Studio 2015 RC.将出现 Visual Studio 2015 RC 起始页. (从现在开始,我们将 Visua ...
- [UWP]如何使用代码创建DataTemplate(或者ControlTemplate)
1. 前言 在UWP中DataTemplate是一个十分重要的功能,并且几乎无处不在,例如DataGrid中的DataGridTemplateColumn: <controls:DataGrid ...
- [UWP] 为WinRT组件创建Nuget包
Nuget 是 dotnet 开发中必不可少的包管理工具,但不仅仅局限于 dotnet 项目,在 VS 中使用 C++ 开发的时候,也可以使用 Nuget 来引用第三方组件.同样也可以用 Nuget ...
- vue2.0 之 douban (四)创建Swipe图片轮播组件
swiper中文文档:http://www.swiper.com.cn 1.我们在components文件夹里创建一个swipe组件,将需要用到的js以及css文件复制到assets/lib文件夹下, ...
- Bitmap 创建、转换、圆角、设置透明度
指定一个色值生成bitmap public Bitmap getBackGroundBitmap(int color) { Paint p = new Paint(); p.setColor(Col ...
- [UWP]新控件ColorPicker
1. 前言 Fall Creators Update中提供了一个新得ColorPicker控件,解决了以前选择颜色只能用Combo Box的窘境. 2. 一个简单的例子 <ColorPicker ...
- Wiki: HSL和HSV色彩空间
HSL 和 HSV(也叫做 HSB)是对RGB 色彩空间中点的两种有关系的表示,它们尝试描述比 RGB 更准确的感知颜色联系,并仍保持在计算上简单.HSL 表示 hue(色相).saturation( ...
随机推荐
- Hibernate与代理模式
代理模式:当须要调用某个对象的时候.不须要关心拿到的是不是一定是这个对象,它须要的是,我拿到的这个对象能够完毕我想要让它完毕的任务就可以,也就是说.这时调用方能够拿到一个代理的一个对象,这个对象能够调 ...
- python2.7一步步实现键盘和鼠标检测
由于现在python3的pyHook库还没有很完善: 所以选择了python2.7版本 关于环境的搭建 python2.7 用的是anaconda,直接去官网下载去安装就好用,不需要配置环境变量的, ...
- 【心情】"支NMLGB配树”
大视野oj坏了 那就做杭电呗 看看大触都做杭电里的哪些题 看到杭电的分类了 Tarjan算法诶,我好像会嘛,就是你了 诶,怎么不是求强连通分量? 哦,原来是Tarjan算法的另外一个应用叫做支配树 我 ...
- 【66.47%】【codeforces 556B】Case of Fake Numbers
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- [Docker] Run Short-Lived Docker Containers
Learn the benefits of running one-off, short-lived Docker containers. Short-Lived containers are use ...
- static,const,extern,以及全局常量
1:全局常量的定义:1:创建Header头文件,再创建与Header头文件类名相同的空文件作为.m文件,.h文件引用文件,.m文件负责定义常量 #import <UIKit/UIKit.h> ...
- 远离“精神乞丐”(IBM的前CEO郭士纳把员工分为四种类型)
语音丨吴伯凡 乞丐与其说是一种身份, 不如说是一种精神状态, 习惯性索取且心安理得, 习惯性寻求安慰,习惯性抱怨, 与之截然对立的, 是“操之在我”(Proactive)的精神, 乞丐型员工是公司内部 ...
- 【C++竞赛 A】xxx的项链
时间限制:2s 内存限制:64MB 问题描述 xxx有一个长度为n的宝石链,宝石有m种不同的颜色.xxx想截取其中连续的一段做一个项链.为了让项链更漂亮,xxx希望项链中的宝石包含所有颜色. 输入描述 ...
- ps如何制作gif(窗口->动画)(导出:存储为Web和设备所用格式)
ps如何制作gif(窗口->动画)(导出:存储为Web和设备所用格式) 一.总结 1.点击窗口,选择动画 2.导出的时候:存储为Web和设备所用格式 二.ps如何制作gif 1.首先我们安装并打 ...
- HDU 5072 Coprime (单色三角形+容斥原理)
题目链接:Coprime pid=5072"> 题面: Coprime Time Limit: 2000/1000 MS (Java/Others) Memory Limit: ...