aardio - 使用GDI+创建渐变圆弧

这是我的第一篇关于aardio的文章,在草稿箱里躺了好多天,一直想等再完善一些再发表出来。
今早才知道aardio作者一鹤,因为妻子病重的原因,暂时无法兼顾aardio的维护,我深感震惊与悲痛。
aardio是一个很好的开发工具,一鹤免费维护了这么多年,很是难得。个人也做不了什么,略捐了一点表绵薄之力,希望一鹤及家人身体安康。
你永远不知道明天和意外哪个先来,所以不管了,既然写了就发出来吧,作为一个学习记录,要完善以后再说。

最近用aardio做一个小工具,需要创建一段使用渐变色填充的圆弧。

翻了一下GDI+的资料,发现GDI+的线性渐变画刷 LinearGradientBrush 和路径渐变画刷 PathGradientBrush 都不适用。

想起aardio自带的调色工具里有一个渐变的色相环,查看了源码,根据作者的思路实现了渐变圆弧的绘制。

实现原理:

简单来说,就是用逐渐变化的颜色画一连串首尾相连的小短弧,拼成一个大圆弧。

  1. 把圆弧平均分成若干段小短弧,每1角度分1段。比如一个90度角的弧分成90段,一个整圆分成360段。

  1. 以圆弧的分段数作为2个颜色之间渐变的总步数,计算出渐变过程中每一步的色值。比如90度的圆弧,从首到尾要经过90个颜色的变化,通过算法把这90个颜色的色值计算出来。

  1. 用计算出来的色值,画出一段一段的单色小短弧,拼成一个大圆弧,就形成了渐变。比如下图,每一个小方块都是1段单色的小短弧(为了方便演示,短弧画得比较长,短弧之间也留了间隙):

颜色渐变算法

算法就不说了,我并不擅长,大家可以参考这里的资料,提供了好几个方案,并且做了对比:https://www.codenong.com/22607043/

简单来说,如果直接用RGB色彩模式计算渐变色,过渡色会偏暗,显得不自然。

正解是使用HSB色彩模式计算,看起来舒服多了,而且从红过渡到绿的中间是黄,绿过渡到蓝的中间是青,才是正确的。

//计算两个颜色之间的渐变色,并分解成若干个色值,用数组返回
//基于RGB生成的渐变色,色彩偏暗,效果不佳
var gradientToColorsByRGB = function(startColor, endColor, step){
    // 得到起始和末尾颜色的rgb色值
    var sR,sG,sB = color.getRgba(startColor);
    var eR,eG,eB = color.getRgba(endColor);
    var rStep = (eR - sR) / step;
    var gStep = (eG - sG) / step;
    var bStep = (eB - sB) / step;
    
    var colorArr = {};
    for (i = 0; step; 1) {
        var r = sR + i * rStep;
        var g = sG + i * gStep;
        var b = sB + i * bStep;
        table.push(colorArr, color.argb(r, g, b, 255)); //透明度暂不考虑,统一设置为不透明
    }
    return colorArr;
}
//计算两个颜色之间的渐变色,并分解成若干个色值,用数组返回
var gradientToColors = function(startColor, endColor, step){
    // 得到起始和末尾颜色的HSL色值
    var sH,sS,sB = color.getHsb(startColor);
    var eH,eS,eB = color.getHsb(endColor);
    var hStep = (eH - sH) / (step - 1);
    var sStep = (eS - sS) / (step - 1);
    var bStep = (eB - sB) / (step - 1);
    
    var colorArr = {};
    for (i = 1; step; 1) {
        var h = sH + hStep * i;
        var s = sS + sStep * i;
        var b = sB + bStep * i;
        table.push(colorArr, color.argb(color.hsb2rgb(h, s, b)));
    }
    return colorArr;
}

画渐变圆弧

自定义画渐变圆弧的函数,支持单色和渐变色,支持设置圆弧末端样式:

drawGradientArc = (

graphics,

arcColors/*圆弧默认颜色或颜色组*/,

x, y/*圆弧所在椭圆的左上角坐标*/,

arcWidth, arcHeight/*圆弧的宽和高*/,

arcStartAngle/*起始角度*/,

arcSweepAngle/*扫描角度*/,

lineWidth/*圆弧本身的线宽*/,

arcCapStyle/*弧线端点样式*/

)

以下为完整代码,可以直接在aardio里运行:

import win.ui;
/*DSG{{*/
var winform = win.form(text="渐变圆弧";right=415;bottom=407;bgcolor=16777215)
winform.add(
plus={cls="plus";left=56;top=48;right=356;bottom=348;ah=1;aw=1;z=1}
)
/*}}*/

//计算两个颜色之间的渐变色,并分解成若干个色值,用数组返回
import color; //导入颜色库
var gradientToColors = function(startColor, endColor, step){
    // 得到起始和末尾颜色的HSL色值
    var sH,sS,sB = color.getHsb(startColor);
    var eH,eS,eB = color.getHsb(endColor);
    var hStep = (eH - sH) / (step - 1);
    var sStep = (eS - sS) / (step - 1);
    var bStep = (eB - sB) / (step - 1);
    
    var colorArr = {};
    for (i = 1; step; 1) {
        var h = sH + hStep * i;
        var s = sS + sStep * i;
        var b = sB + bStep * i;
        if (b < 0) b = 0
        elseif (b > 1) b = 1;
        table.push(colorArr, color.argb(color.hsb2rgb(h, s, b)));
    }
    return colorArr;
}

//画渐变圆弧
var drawGradientArc = function(
    graphics,
    arcColors/*圆弧默认颜色或颜色组*/,
    x, y/*圆弧所在椭圆的左上角坐标*/,
    arcWidth, arcHeight/*圆弧的宽和高*/,
    arcStartAngle/*起始角度*/,
    arcSweepAngle/*扫描角度*/,
    lineWidth/*圆弧本身的线宽*/,
    arcCapStyle/*弧线端点样式*/){    
    
    //因为是用pan画笔画圆弧,而画笔的对齐方式是居中对齐
    //所以画出的圆弧会大于设定的值,需要进行修正
    arcWidth -= lineWidth;
    arcHeight -= lineWidth;
    x += lineWidth/2;
    y += lineWidth/2;
    
    var arcEndAngle = arcStartAngle + arcSweepAngle - 360; //圆弧结束角度
    
    //如果是只有单色,则一笔画成
    if(type(arcColors)==type.number){
        var pen = gdip.pen(arcColors, lineWidth);
        if(arcCapStyle){ //如果设定了端点样式:0平头,2圆头,3尖头
            pen.startCap = arcCapStyle; 
            pen.endCap = arcCapStyle;
        }
        graphics.drawArc(pen, x, y, arcWidth, arcHeight, arcStartAngle ,arcSweepAngle);
        pen.delete();
        return; 
    }    
    
    //不是色彩数组,则退出
    if(type(arcColors) != type.table) return;
    
    var pen = gdip.pen(0, lineWidth);
    
    var gradientCount = #arcColors - 1; //每两个颜色构成一个渐变区间,所以 渐变区间数量 = 色彩数 - 1
    var gradientAngle = arcSweepAngle/gradientCount; //计算每个渐变区间扫略过的角度
    
    for(i=1; gradientCount; 1){ //循环处理每个渐变区间
        var colorArr = gradientToColors(arcColors[i], arcColors[i+1], gradientAngle); //分解渐变色到数组
        var sweepAngle = gradientAngle / #colorArr; //每段小弧对应的角度
        var startAngle = arcStartAngle + (i - 1) * gradientAngle; //每个渐变区间的起始角度
        for(n=1; #colorArr; 1){
            if(arcCapStyle){ //如果设定了端点样式:0无,1方头,2圆头,3尖角
                if(n = 1 && i = 1){ //弧线开端设置样式
                    pen.startCap = arcCapStyle; 
                    pen.endCap = 0/*_LineCapFlat*/;
                }
                elseif(n = #colorArr && i = #arcColors - 1){ //弧线末端设置样式
                    pen.startCap = 0/*_LineCapFlat*/;
                    pen.endCap = arcCapStyle;
                }
                else { //中间小弧没有端点
                    pen.startCap = 0/*_LineCapFlat*/;
                    pen.endCap = 0/*_LineCapFlat*/;
                }                
            }
            
            //用每个颜色画很短的一小段弧,拼接成大圆弧
            pen.color = colorArr[n]; 
            //每段小弧的长度略长一些,可以防止曲率过大时中间出现空隙。这个值可以根据曲率计算出来,但是实际上不需要这么精确,经过测试使用3能基本满足要求。
            graphics.drawArc(pen, x, y, arcWidth, arcHeight, startAngle + (n - 1) * sweepAngle, 3); 
        }
    }    
    pen.delete();
}

var arcColors = {0xFF69EACB, 0xFFEACCF8, 0xFF6654F1}; //圆弧默认颜色或渐变颜色组
var arcStartAngle = 120; //圆弧开口的角度
var arcSweepAngle = 360 - (arcStartAngle - 90)*2; //扫描角度
var lineWidth = 60; //圆弧线的宽度
var arcCapStyle = 2; //圆弧端点样式:0无,1方头,2圆头,3尖角

winform.plus.onDrawContent = function(graphics,rc,txtColor,rcContent,foreColor){
    var x,y = 0, 0;
    var arcWidth, arcHeight = owner.width, owner.height;
    drawGradientArc(graphics, arcColors, x, y, arcWidth, arcHeight, arcStartAngle, arcSweepAngle, lineWidth, arcCapStyle)
    winform.plus.redrawTransparent();
}
            
winform.show() 
win.loopMessage();

从左到右,分别是:双色渐变+圆角端点,单色填充+方角端点,三色渐变+尖角端点

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在 C 语言中使用 GDI 绘图,需要使用 Windows API 中的 GDI 函数。以下是一个简单的示例: ```c #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 创建窗口 WNDCLASS wc = {0}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszClassName = "MyWindowClass"; RegisterClass(&wc); HWND hWnd = CreateWindow("MyWindowClass", "My Window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); // 绘制图形 HDC hdc = GetDC(hWnd); Rectangle(hdc, 100, 100, 400, 400); MoveToEx(hdc, 100, 100, NULL); LineTo(hdc, 400, 400); ReleaseDC(hWnd, hdc); // 消息循环 MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } ``` 在上面的示例中,我们创建了一个窗口,并在窗口的客户区绘制了一个矩形和一条对角线。具体来说,我们在 `WinMain` 函数中获取了窗口的设备上下文(Device Context,简称 DC),然后使用 GDI 函数绘制了图形。最后,在消息循环中处理窗口消息。 需要注意的是,GDI 绘图是基于设备的,因此需要获取窗口的 DC 才能进行绘制。在绘制完成后,需要使用 `ReleaseDC` 函数释放 DC。此外,还有很多其他的 GDI 函数可以用于绘制各种不同的图形,例如 `Ellipse`、`Polygon`、`TextOut` 等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值