六、绘制图片

news2025/5/18 12:46:52

文章目录

    • 1.创建一个红色图片
    • 2.加载bmp图片
    • 3.加载png、jpg图片

前面的几个示例,我们已经展示过如果在Linux系统下使用xlib接口向窗口中绘制文本、线、矩形;并设置文本、线条的颜色。并利用xlib提供的接口结合事件处理机制完成了一个自绘按钮控件功能。有时我们可能需要将已有的图片贴到窗口中,以实现更炫丽的效果。在xlib窗口系统中我们可以使用XImage存储图片像素的RGB值(当前也有可能会有Alpha通道)值。

1.创建一个红色图片

第一个例子,我们使用XImage向窗口中放入一个红色块图片

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>

void DoPaint(Display *display, Window window) {
    int screen_num = DefaultScreen(display);
    int width = 200;
    int height = 100;
    //为每个像素分配4个字节,用于存储像素的ARGB值。
    unsigned char *pixelBuffer = (unsigned char *)malloc(width*height*4);
    for (int i=0;i<height;i++) {
        for (int j=0;j<width;j++) {
            //创建XCreateImage,每个像素的存储采用的字节序是BGRA

            unsigned char *pixel = pixelBuffer + (i*width+j)*4;
            pixel[3] = 0xff;
            pixel[2] = 0xff;
            pixel[1] = 0x0;
            pixel[0] = 0x0;
        }
    }
    XImage *image = XCreateImage(display,DefaultVisual(display,screen_num),DefaultDepth(display,screen_num),ZPixmap,
        0,
        (char *)pixelBuffer,
        width,height,32,0);
    GC gc = XCreateGC(display,window,0,nullptr);
    XPutImage(display,window,gc,image,0,0,30,30,200,100);
    XFreeGC(display,gc);
    XDestroyImage(image);
}

int main() {

    Display *display = XOpenDisplay(NULL);
    Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 300, 200, 1,
                                     BlackPixel(display, DefaultScreen(display)),
                                     WhitePixel(display, DefaultScreen(display)));

    XSelectInput(display, win, ExposureMask);
    XMapWindow(display, win);

    while (1) {
        XEvent e;
        XNextEvent(display, &e);
        if (e.type == Expose) {
            DoPaint(display,win);
        }
    }
    XDestroyWindow(display,win);
    XCloseDisplay(display);
    return 0;
}

以上的示例中我们没有直接从文件中加载一个png或是bmp图片,然后显示到窗口上。而是分配了一块内存,通过程序设置每个像素的颜色。这样做的目的能够让我们了解到xlib把图片绘制到窗口的基本原理,在不了解原理情况,如果我们直接把png图片加载显示到窗口上,没有出现我们想要的效果;很分析问题出现在哪里。编译以上程序,运行结果如下:

在这里插入图片描述

2.加载bmp图片

所使用的bmp图片如下:
在这里插入图片描述

接下来我们来加载一个bmp图片。程序代码如下

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <cstdint>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义 BMP 文件头结构体(Windows 标准)
#pragma pack(push, 1)
typedef struct {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffBits;
} BITMAPFILEHEADER;

typedef struct {
    uint32_t biSize;
    int32_t  biWidth;
    int32_t  biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int32_t  biXPelsPerMeter;
    int32_t  biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)

// 加载 24 位 BMP 图像
unsigned char* load_bmp_24bit(const char *filename, int *out_width, int *out_height) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "Failed to open file: %s\n", filename);
        return NULL;
    }

    BITMAPFILEHEADER file_header;
    BITMAPINFOHEADER info_header;

    fread(&file_header, sizeof(BITMAPFILEHEADER), 1, file);
    fread(&info_header, sizeof(BITMAPINFOHEADER), 1, file);

    if (file_header.bfType != 0x4D42 || info_header.biBitCount != 24 || info_header.biCompression != 0) {
        fprintf(stderr, "Unsupported BMP format.\n");
        fclose(file);
        return NULL;
    }

    *out_width = info_header.biWidth;
    *out_height = info_header.biHeight;

    // 计算每行字节数(注意:每行要对齐 4 字节)
    int row_padded = ((info_header.biWidth * 3 + 3) / 4) * 4;
    int row_unpadded = info_header.biWidth * 3;
    int size_data = row_padded * info_header.biHeight;

    unsigned char *data = (unsigned char*)malloc(size_data);
    unsigned char *img_data = (unsigned char *)malloc(info_header.biWidth * info_header.biHeight * 3);

    fseek(file, file_header.bfOffBits, SEEK_SET);
    for (int y = 0; y < info_header.biHeight; y++) {
        fread(data, 1, row_padded, file);
        memcpy(img_data + (info_header.biHeight - 1 - y) * row_unpadded, data, row_unpadded);
    }

    free(data);
    fclose(file);
    return img_data;
}

void DoPaint(Display *display, Window window) {
    int screen_num = DefaultScreen(display);
    int width = 0;
    int height = 0;

    unsigned char *buffer = load_bmp_24bit("flower.bmp",&width,&height);
    unsigned char *imageData = (unsigned char*)malloc(width*height*4);
    for (int i=0;i<height;i++) {
        for (int j=0;j<width;j++) {
            unsigned char *originPixel = buffer + (i*width+j)*3;
            unsigned char *pixel = imageData + (i*width+j)*4;
            pixel[3] = 0xff;
            pixel[0] = originPixel[0];
            pixel[1] = originPixel[1];
            pixel[2] = originPixel[2];
        }
    }
    free(buffer);
    XImage *image = XCreateImage(display, DefaultVisual(display,screen_num), DefaultDepth(display,screen_num), ZPixmap, 0,
                                  (char *)imageData, width, height, 32, 0);
    GC gc = XCreateGC(display,window,0,nullptr);
    XPutImage(display,window,gc,image,0,0,0,0,width,height);
    XFreeGC(display,gc);
    XDestroyImage(image);
}

int main() {

    Display *display = XOpenDisplay(NULL);
    Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 800, 600, 1,
                                     BlackPixel(display, DefaultScreen(display)),
                                     WhitePixel(display, DefaultScreen(display)));

    XSelectInput(display, win, ExposureMask);
    XMapWindow(display, win);

    while (1) {
        XEvent e;
        XNextEvent(display, &e);
        if (e.type == Expose) {
            DoPaint(display,win);
        }
    }
    XDestroyWindow(display,win);
    XCloseDisplay(display);
    return 0;
}

这里我们把一个bmp图片加载到内存,并且没有使用任何第三方的图片加载库,这样我们编译代码就不需要再去考虑三方库的安装问题。在DoPaint函数,我们把从bmp文件加载的每个像素3个字节转换为使用每个像素使用4个字节存储,如果XCreateImage使用每个像素使用3个字节表示,不能得到我们想要的效果。使用g++编译以上代码运行,结果如下:

在这里插入图片描述

3.加载png、jpg图片

png、jpg类型的图片,是对原始的像素数据进行了压缩存储,所以我们首先需要将png或jpg真实的像素值还原加载到内存,再创建XImage图片,显示到窗口。对于png可以使用libpng、对于jpg可以使用libjpeg将真实的像素还加载到内存。这里给出一个使用libpng来加载png图片的示例。实现代码如下:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <png.h>
#include <stdlib.h>

// 加载 PNG 文件的函数
unsigned char *load_png(const char *filename, int *width, int *height) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        fprintf(stderr, "无法打开文件: %s\n", filename);
        return NULL;
    }

    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png) {
        fclose(fp);
        return NULL;
    }

    png_infop info = png_create_info_struct(png);
    if (!info) {
        png_destroy_read_struct(&png, NULL, NULL);
        fclose(fp);
        return NULL;
    }

    if (setjmp(png_jmpbuf(png))) {
        png_destroy_read_struct(&png, &info, NULL);
        fclose(fp);
        return NULL;
    }

    png_init_io(png, fp);
    png_read_info(png, info);

    *width = png_get_image_width(png, info);
    *height = png_get_image_height(png, info);
    int color_type = png_get_color_type(png, info);
    int bit_depth = png_get_bit_depth(png, info);

    // 转换为 8 位 RGB 或 RGBA 格式
    if (bit_depth == 16)
        png_set_strip_16(png);
    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_palette_to_rgb(png);
    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
        png_set_expand_gray_1_2_4_to_8(png);
    if (png_get_valid(png, info, PNG_INFO_tRNS))
        png_set_tRNS_to_alpha(png);
    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
        color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png);

    png_read_update_info(png, info);

    int rowbytes = png_get_rowbytes(png, info);
    unsigned char *image_data = static_cast<unsigned char *>(malloc(rowbytes * (*height)));
    png_bytep *row_pointers = static_cast<png_bytep *>(malloc(sizeof(png_bytep) * (*height)));

    for (int y = 0; y < *height; y++)
        row_pointers[y] = image_data + y * rowbytes;

    png_read_image(png, row_pointers);

    free(row_pointers);
    png_destroy_read_struct(&png, &info, NULL);
    fclose(fp);

    return image_data;
}

void DoPaint(Display *display, Window window) {
    int screen_num = DefaultScreen(display);
    int width = 0;
    int height = 0;

    unsigned char *buffer = load_png("icon.png",&width,&height);
    unsigned char *imageData = (unsigned char*)malloc(width*height*4);
    for (int i=0;i<height;i++) {
        for (int j=0;j<width;j++) {
            unsigned char *originPixel = buffer + (i*width+j)*4;
            unsigned char *pixel = imageData + (i*width+j)*4;
            pixel[3] = originPixel[3];
            pixel[0] = originPixel[2];
            pixel[1] = originPixel[1];
            pixel[2] = originPixel[0];
        }
    }
    free(buffer);
    XImage *image = XCreateImage(display, DefaultVisual(display,screen_num), DefaultDepth(display,screen_num), ZPixmap, 0,
                                 (char *)imageData, width, height, 32, 0);
    GC gc = XCreateGC(display,window,0,nullptr);
    XPutImage(display,window,gc,image,0,0,0,0,width,height);
    XFreeGC(display,gc);
    XDestroyImage(image);
}

int main() {

    Display *display = XOpenDisplay(NULL);
    Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 800, 600, 1,
                                     BlackPixel(display, DefaultScreen(display)),
                                     WhitePixel(display, DefaultScreen(display)));

    XSelectInput(display, win, ExposureMask);
    XMapWindow(display, win);

    while (1) {
        XEvent e;
        XNextEvent(display, &e);
        if (e.type == Expose) {
            DoPaint(display,win);
        }
    }
    XDestroyWindow(display,win);
    XCloseDisplay(display);
    return 0;
}

要编译以上代码我们需要安装libpng开发库。ubuntu下安装libpng开发库的脚本如下

sudo apt install libpng-dev

使用g++编译以上程序,需要加上链接X11、png库

g++ xxx.cpp -o app -lX11 -lpng

运行结果示例如下:

在这里插入图片描述

另外还有一些开源库用于加载到内存,如stb可以用来加载bmp、png、jpg图片,该项目的代码地址GitHub - nothings/stb: stb single-file public domain libraries for C/C++

我们只需要使用该项目中的stb_image.h头文件(是的,只需要包含一个头文件)。我们在项目中倾向使用stb_image在项目中只需要把这个头文件放到项目中,后续编译打包时不需要考虑开发库是否安装,发布时不需要考虑动态库依赖问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 面向对象详解和JVM底层内存分析

先关注、点赞再看、人生灿烂&#xff01;&#xff01;&#xff01;&#xff08;谢谢&#xff09; 神速熟悉面向对象 表格结构和类结构 我们在现实生活中&#xff0c;思考问题、发现问题、处理问题&#xff0c;往往都会用“表格”作为工具。实际上&#xff0c;“表格思维”就是…

深度学习---知识蒸馏(Knowledge Distillation, KD)

一、知识蒸馏的本质与起源 定义&#xff1a; 知识蒸馏是一种模型压缩与迁移技术&#xff0c;通过将复杂高性能的教师模型&#xff08;Teacher Model&#xff09;所学的“知识”迁移到轻量级的学生模型&#xff08;Student Model&#xff09;&#xff0c;使学生模型在参数量和计…

基于CNN卷积神经网络的带频偏QPSK调制信号检测识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2024b 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

【DAY21】 常见的降维算法

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 目录 PCA主成分分析 t-sne降维 线性判别分析 (Linear Discriminant Analysis, LDA) 作业&#xff1a; 什么时候用到降维 降维的主要应用场景 知识点回顾&#xff1a; PCA主成分分析t-sne降维LDA线性判别 通常情况下&#xff0c;…

校园社区小程序源码解析

基于ThinkPHP、FastAdmin和UniApp开发的校园社区小程序源码&#xff0c;旨在为校园内的学生和教职员工提供一个便捷的在线交流和服务平台。 该小程序前端采用UniApp进行开发&#xff0c;具有良好的跨平台兼容性&#xff0c;可以轻松发布到iOS和Android平台。同时&#xff0c;后…

第6章:文件权限

一、文件权限概述 Linux为了保证系统中每个文件的安全&#xff0c;引入了文件权限机制。针对于系统中的每一个文件Linux都可以提供精确的权限控制。它可以做到不同的用户对同一个文件具有不同的操作权利。而通常这个权利包括以下3个&#xff1a; 读的权利&#xff08;Read&…

C语言| 指针变量的定义

C语言| 指针的优点-CSDN博客 * 表示“指向”&#xff0c;为了说明指针变量和它所指向的变量之间的联系。 int * i&#xff1b;//表示指针变量i里面存放的地址&#xff0c;所指向的存储单元里的【数据】。 【指针变量的定义】 C语言规定所有变量&#xff0c;在使用前必须先定…

IntelliJ IDEA给Controller、Service、Mapper不同文件设置不同的文件头注释模板、Velocity模板引擎

通过在 IntelliJ IDEA 中的 “Includes” 部分添加多个文件头模板&#xff0c;并在 “Files” 模板中利用这些包含来实现不同类型文件的注释。以下是为 Controller、Service、Mapper 文件设置不同文件头的完整示例&#xff1a; 1. 设置 Includes 文件头模板 File > Settin…

【论文阅读】BEVFormer

〇、Introduction BEVFormer是现在端到端无人驾驶中常使用的一个Backbone&#xff0c;用于将六个视角下的图像转换为鸟瞰图视角下的特征&#xff0c;转换出的BEV特征则会被用于后续模块的特征交互。然而在这个模型设计的初期&#xff0c;其最本质的意图是为了提取用于各种CV任…

IDEA编辑器设置的导出导入

背景 最近新换了电脑&#xff0c;因为之前是 Intel 芯片的 Mac&#xff0c;这次换了 arm 架构的 M 芯片的 Mac&#xff0c;旧 Mac 上的很多软件不兼容&#xff0c;所以就没有选择换机数据迁移&#xff0c;一点一点下载、配置了所有环境。 导出 IDEA 支持设置的导入导出&…

成功案例丨从草图到鞍座:用先进的发泡成型仿真技术变革鞍座制造

案例简介 在鞍座制造中&#xff0c;聚氨酯泡沫成型工艺是关键环节&#xff0c;传统依赖实验测试的方法耗时且成本高昂。为解决这一问题&#xff0c;意大利自行车鞍座制造商 Selle Royal与Altair合作&#xff0c;采用Altair Inspire PolyFoam软件进行发泡成型仿真。 该工具帮助团…

电机试验平台:创新科技推动电动机研究发展

电机试验平台是电机制造和研发过程中不可或缺的重要设备&#xff0c;其功能涵盖了电机性能测试、电机寿命测试、电机质量评估等多个方面。随着科技的不断发展和电机应用领域的日益扩大&#xff0c;对电机试验平台的要求也越来越高。本文将从现代化电机试验平台的设计与应用两个…

【springcloud学习(dalston.sr1)】Zuul路由访问映射规则配置及使用(含源代码)(十二)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; springcloud学习&#xff08;dalston.sr1&#xff09;系统文章汇总如下&#xff1a; 【springcloud学习(dalston…

Qt与Hid设备通信

什么是HID&#xff1f; HID&#xff08;Human Interface Device&#xff09;是‌直接与人交互的电子设备‌&#xff0c;通过标准化协议实现用户与计算机或其他设备的通信&#xff0c;典型代表包括键盘、鼠标、游戏手柄等。‌ 为什么HID要与qt进行通信&#xff1f; 我这里的应…

2024 山东省ccpc省赛

目录 I&#xff08;签到&#xff09; 题目简述&#xff1a; 思路&#xff1a; 代码&#xff1a; A&#xff08;二分答案&#xff09; 题目简述&#xff1a; 思路&#xff1a; 代码&#xff1a; K&#xff08;构造&#xff09; 题目&#xff1a; 思路&#xff1a; 代…

SAP HCM 0008数据存储逻辑

0008信息类型&#xff1a;0008信息类型是存储员工基本薪酬的地方&#xff0c;因为很多企业都会都薪酬带宽&#xff0c;都会按岗定薪&#xff0c;所以在上线前为体现工资体系的标准化&#xff0c;都会在配置对应的薪酬关系&#xff0c;HCM叫间接评估&#xff0c;今天我们就分析下…

如何使用通义灵码辅助学习C++编程 - AI编程助手提升效率

一、引言 C 是一门功能强大且灵活的编程语言&#xff0c;在软件开发、系统编程、游戏开发等领域广泛应用。然而&#xff0c;其复杂的语法和丰富的特性使得学习曲线较为陡峭。对于初学者而言&#xff0c;在学习过程中难免会遇到各种问题&#xff0c;如语法理解困难、代码调试耗…

【Docker】CentOS 8.2 安装Docker教程

目录 1.卸载 2.安装依赖 3.设置yum源 4.安装Docker 5.启动Docker 6.设置Docker开机自启 7.验证Docker是否安装成功 8.配置多个国内镜像地址 9.重启Docker 10.Docker指令大全 10.1.启动与关闭Docker 10.2.Docker镜像操作 10.3.Docker容器操作 10.4.Docker Compose操作…

K230 ISP:一种新的白平衡标定方法

第一次遇见需要利用光谱响应曲线进行白平衡标定的方法。很好奇是如何利用光谱响应曲线进行白平衡标定的。 参考资料参考&#xff1a;K230 ISP图像调优指南 K230 介绍 嘉楠科技 Kendryte 系列 AIoT 芯片中的最新一代 AIoT SoC K230 芯片采用全新的多核异构单元加速计算架构&a…

桃芯ingchips——windows HID键盘例程无法同时连接两个,但是安卓手机可以的问题

目录 环境 现象 原理及解决办法 环境 PC&#xff1a;windows11 安卓&#xff1a;Android14 例程使用的是HID Keyboard&#xff0c;板子使用的是91870CQ的开发板&#xff0c;DB870CC1A 现象 连接安卓手机时并不会出现该现象&#xff0c;两个开发板都可以当做键盘给手机发按…