LCUI
简体中文
简体中文
  • 开始
  • 基础
    • 安装
    • 介绍
    • 约定
    • XML
    • CSS
    • 架构
  • 实例
    • 主循环
    • 工作线程
    • 定时器
    • 事件
  • 组件
    • 属性
    • 样式
    • 原型
    • 事件
    • 生命周期
    • 绘制流程
  • 性能
  • CSS
    • CSS 数据库
    • CSS 解析器
  • 布局
    • 正常流布局
    • 弹性盒子布局
  • 图形
    • 绘制简单的图形
    • 绘制复杂的图形
    • 像素操作
    • 图像文件操作
  • 字体
    • 字体数据库
    • 字体渲染引擎
    • 文本排版与渲染
  • 驱动
    • 事件循环
    • 视频
    • 鼠标
    • 键盘
    • 触控
  • 输入法
由 GitBook 提供支持
在本页
  1. 图形

像素操作

上一页绘制复杂的图形下一页图像文件操作

最后更新于4年前

在前面我们已经了解到图像的数据结构、栅格、坐标系和坐标转下标的方法,仅凭 LCUI 提供的这点图形 API 无法让我们轻松绘制复杂的图形,所以我们有必要学会如何操作像素数据,以便自己手动编码或借助其它图形库的能力来绘制图形。

注意,虽然像素数据的类型名是 LCUI_ARGB,但这些颜色通道的值在内存中存储的顺序是 BGRA。之所以采用这种顺序,是因为 Linux 中的帧缓存(FrameBuffer) 和 Windows 中的位图对象(HBITMAP) 都是这样。

图片灰度和反相颜色

在这个例子里,我们将图片分成四个部分,其中三个分别进行反色(inverted)、灰度(grayscale)、褐色(sepia)处理,然后将处理结果输出到图片文件中。invert() 函数将颜色的最大色值 255 减去像素的色值作为结果,grayscale() 函数将红绿蓝三色值的平均值作为结果,你也可以用加权平均,例如 x = 0.299r + 0.587g + 0.114b 这个公式,更多资料请参考维基百科的 。sepia() 函数与 grayscale() 函数采用了类似的算法。

像素操作的流程是相似的,先使用两层 for 循环在指定区域内定位要操作的像素点,然后获取像素数据进行处理,之后写入数据到图像中。为了减少代码量,我们将重复的代码定义成 PixelManipulationBegin 宏和 PixelManipulationEnd 宏,其中像素操作用到GraphGetPixel() 和 GraphSetPixel() 是函数式宏,它封装了坐标转下标的计算代码,使得我们无需再编写冗长的代码,但需要注意的是,它会为每个像素点计算下标,而这计算中包含重复计算,所以效率比较低,如果你在实际项目中比较注重性能的话可以做一点优化,例如在 y 轴的循环里提前计算每行起点下标:row_start = y * graph.width + rect.x。

#include <math.h>
#include <LCUI.h>
#include <LCUI/image.h>

#define PixelManipulationBegin                                   \
        int x, y;                                                \
        LCUI_Color pixel;                                        \
                                                                 \
        for (y = rect.y; y < rect.y + rect.height; ++y) {        \
                for (x = rect.x; x < rect.x + rect.width; ++x) { \
                        Graph_GetPixel(graph, x, y, pixel);

#define PixelManipulationEnd                \
        Graph_SetPixel(graph, x, y, pixel); \
        }                                   \
        }

void invert(LCUI_Graph *graph, LCUI_Rect rect)
{
        PixelManipulationBegin;
        pixel.red = (unsigned char)(255 - pixel.red);
        pixel.green = (unsigned char)(255 - pixel.green);
        pixel.blue = (unsigned char)(255 - pixel.blue);
        PixelManipulationEnd;
}

void grayscale(LCUI_Graph *graph, LCUI_Rect rect)
{
        unsigned char avg;

        PixelManipulationBegin;
        avg = (unsigned char)((pixel.red + pixel.green + pixel.blue) / 3);
        pixel.red = avg;
        pixel.green = avg;
        pixel.blue = avg;
        PixelManipulationEnd;
}

void sepia(LCUI_Graph *graph, LCUI_Rect rect)
{
        PixelManipulationBegin;
        pixel.red = (unsigned char)min(
            round(0.393 * pixel.red + 0.769 * pixel.green + 0.189 * pixel.blue),
            255);
        pixel.green = (unsigned char)min(
            round(0.349 * pixel.red + 0.686 * pixel.green + 0.168 * pixel.blue),
            255);
        pixel.blue = (unsigned char)min(
            round(0.272 * pixel.red + 0.534 * pixel.green + 0.131 * pixel.blue),
            255);
        PixelManipulationEnd;
}

int main(void)
{
        int i;
        LCUI_Graph graph = { 0 };
        LCUI_Rect rects[4];

        if (LCUI_ReadImageFile("dog.jpg", &graph) != 0) {
                return -1;
        }
        for (i = 0; i < 4; ++i) {
                rects[i].height = graph.height;
                rects[i].width = graph.width / 4;
                rects[i].x = i * rects[i].width;
                rects[i].y = 0;
        }
        sepia(&graph, rects[1]);
        grayscale(&graph, rects[2]);
        invert(&graph, rects[3]);
        LCUI_WritePNGFile("test_pixel_manipulation.png", &graph);
        return 0;
}

源图片:

处理结果(原始,褐色,灰度,反色):

Grayscale
dog.jpg
test_pixel_manipulation.png