当前位置: 首页 > 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在项目中只需要把这个头文件放到项目中,后续编译打包时不需要考虑开发库是否安装,发布时不需要考虑动态库依赖问题。

http://www.xdnf.cn/news/502039.html

相关文章:

  • traceroute命令: -g与-i 参数
  • flutter长列表 ListView、GridView、SingleChildScrollView、CustomScrollView区别
  • 专题四:综合练习(组合问题的决策树与回溯算法)
  • 嘉立创EDA成图:文件管理
  • 【前端基础】11、CSS的属性特性(继承、层叠、元素类型、隐藏元素的四种方式)
  • 【笔记】正弦交流电路的特征量
  • MMDetection环境安装配置
  • 小蜗牛拨号助手用户使用手册
  • STM32中的DMA
  • Python自学笔记3 常见运算符
  • Redis 事务与管道:原理、区别与应用实践
  • 【JDBC】JDBC概述、历史版本及特征
  • 深入解析 React 的 useEffect:从入门到实战
  • (头歌作业)—6.1 葡萄酒评论分析报告(project)
  • DeepSeek超大模型的高效训练策略
  • 数据结构与算法——双向链表
  • 探秘 Java 字节缓冲流:解锁高效 IO 操作的进阶之路
  • Unity 人物模型学习笔记
  • MATLAB2025新功能
  • 开源项目实战学习之YOLO11:12.3 ultralytics-models-sam-encoders.py源码分析
  • gcc/g++常用参数
  • Go 语言的 GMP 模型
  • DeepSeek 赋能量子计算:突破与未来图景
  • Python时间处理全攻略:标准库与第三方库的实战应用
  • 如何 naive UI n-data-table 改变行移动光标背景色
  • Linux——shell编程
  • 线对板连接器的兼容性问题:为何老旧设计难以满足现代需求?
  • 前端-HTML元素
  • 匿名函数与闭包(Anonymous Functions and Closures)-《Go语言实战指南》原创
  • Java IO流进阶实战详解(含文件读写、拷贝、加密、字符集)