当前位置: 首页 > news >正文

六、绘制图片

文章目录

    • 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,每个像素的存储采用的字节序是BGRAunsigned 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在项目中只需要把这个头文件放到项目中,后续编译打包时不需要考虑开发库是否安装,发布时不需要考虑动态库依赖问题。

相关文章:

  • Golang 范型
  • C# WPF .NET Core和.NET5之后引用System.Windows.Forms的解决方案
  • 数据库——SQL约束窗口函数介绍
  • 量化用到的机器学习书籍推荐
  • 广和通L610模块通过AT指令访问服务器方案:嵌赛使用
  • QT6 源(105)篇二:阅读与注释 QAction,给出源代码
  • RJ连接器的未来:它还会是网络连接的主流标准吗?
  • Java-Objects类高效应用的全面指南
  • Vue百日学习计划Day19-20天详细计划-Gemini版
  • 从数据分析到数据可视化:揭开数据背后的故事
  • ArrayList-集合使用
  • 【C语言练习】046. 编写插入排序算法
  • 编译原理实验五:LR语法分析器的控制程序
  • BrepGen中的几何特征组装与文件保存详解 deepwiki occwl OCC包装库
  • 亲测有效!OGG 创建抽取进程报错 OGG-08241,如何解决?
  • gRPC开发指南:Visual Studio 2022 + Vcpkg + Windows全流程配置
  • 深入理解 Java 字节码操作码
  • Rust 数据结构:HashMap
  • 【沉浸式求职学习day42】【算法题:滑动窗口】
  • NVC++ 介绍与使用指南
  • 网文书单|推荐4本网文,可以当作《绍宋》代餐
  • 2人恶意传播刘国梁谣言被处罚,媒体:以法律利剑劈谣斩邪,加快推进依法治体
  • 辽宁援疆前指总指挥王敬华已任新疆塔城地委副书记
  • 贞丰古城:新垣旧梦间的商脉与烟火
  • 我使馆就中国公民和企业遭不公正待遇向菲方持续提出严正交涉
  • 李成钢出席中国与《数字经济伙伴关系协定》成员部级会议