intro

先前基于GDI已经能够显示BITMAP图像:windows下控制台程序实现窗口显示 ,其中BMP图像是使用LoadImage()这一Win32 API函数来做的。考虑到LoadImage()函数并不能读取png图像,因此需要libpng或stb等png编解码库的帮助。

网上找到相关代码不多,稍加修改可以运行,具备特点:

  • 纯C,单个文件(依赖的libpng和zlib可以忽略)

  • 直接读取png图像而不是通过读取.rc文件(资源文件)再读取png图像

  • png图像的读取:基于libpng(以及zlib),我直接用的opencv345 windows预编译报里的.h文件和库文件

  • 入口函数为main()而非WinMain(),也即控制台程序,方便作为库函数、移植

  • 不同于BMP的地方:在窗口处理函数的创建阶段有所不同:

    case WM_CREATE:
    if (image_type == BMP) {
    my_window->hBmp = (HBITMAP)LoadImage(NULL, "D:/work/libfc/imgs/lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    }
    else if (image_type == PNG) {
    ReadPngData("D:/work/libfc/imgs/Lena.png", &im_width, &im_height, &my_window->imdata);
    my_window->hBmp = CreateBitmap(im_width, im_height, 32, 1, my_window->imdata);
    }
  • 并没有处理alpha通道

    如果图像有alpha通道,透明区域将显示为黑色,因为用的是BitBlt()。如果要使用alpha通道(例如做融合,或者显示为黑色以外的颜色),则应当使用AlphaBlend()和msing32库:

    #pragma comment(lib, "msimg32.lib")
    ... BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, SRCCOPY);
    //AlphaBlend(hdc, 100, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, bm.bmWidth, bm.bmHeight, bf);

    opencv中看起来也是把透明区域显示为黑色。

  • 缺点:窗口client区域大小固定,没有能根据图像尺寸来变化

代码

#include <stdio.h>
#include <windows.h>
#include "png.h" #define CRTDBG_MAP_ALLOC
#include <crtdbg.h> #pragma comment(lib, "D:/work/libfc/lib/libpng.lib")
#pragma comment(lib, "D:/work/libfc/lib/zlib.lib") typedef struct MyRect {
int x, y, width, height;
} MyRect; const char* project_root = "D:/work/libfc";
char bitmap_im_pth[100]; typedef struct MyWindow {
HDC dc;
//HGDIOBJ image;
HBITMAP hBmp;
unsigned char* imdata;
} MyWindow; MyWindow* my_window;
enum ImageType {BMP, PNG}; long ReadPngData(const char *szPath, int *pnWidth, int *pnHeight, unsigned char **cbData)
{
FILE *fp = NULL;
long file_size = 0, pos = 0, mPos = 0;
int color_type = 0, x = 0, y = 0, block_size = 0; png_infop info_ptr;
png_structp png_ptr;
png_bytep *row_point = NULL; fp = fopen(szPath, "rb");
if (!fp) return -1; //文件打开错误则返回 FILE_ERROR png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); //创建png读取结构
info_ptr = png_create_info_struct(png_ptr); //png 文件信息结构
png_init_io(png_ptr, fp); //初始化文件 I\O
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0); //读取png文件 *pnWidth = png_get_image_width(png_ptr, info_ptr); //获得图片宽度
*pnHeight = png_get_image_height(png_ptr, info_ptr); //获得图片高度
color_type = png_get_color_type(png_ptr, info_ptr); //获得图片色彩深度
file_size = (*pnWidth) * (*pnHeight) * 4; //计算需要存储RGB(A)数据所需的内存大小
*cbData = (unsigned char *)malloc(file_size); //申请所需的内容, 并将 *cbData 指向申请的这块内容 row_point = png_get_rows(png_ptr, info_ptr); //读取RGB(A)数据 block_size = color_type == 6 ? 4 : 3; //根据是否具有a通道判断每次所要读取的数据大小, 具有Alpha通道的每次读4位, 否则读3位 //将读取到的RGB(A)数据按规定格式读到申请的内存中
for (x = 0; x < *pnHeight; x++)
for (y = 0; y < *pnWidth*block_size; y += block_size)
{
(*cbData)[pos++] = row_point[x][y + 2]; //B
(*cbData)[pos++] = row_point[x][y + 1]; //G
(*cbData)[pos++] = row_point[x][y + 0]; //R
(*cbData)[pos++] = row_point[x][y + 3]; //alpha
} png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(fp); return file_size;
} LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
int im_width, im_height; int image_type = PNG;
switch (msg)
{
case WM_CREATE:
if (image_type == BMP) {
my_window->hBmp = (HBITMAP)LoadImage(NULL, "D:/work/libfc/imgs/lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}
else if (image_type == PNG) {
ReadPngData("D:/work/libfc/imgs/Lena.png", &im_width, &im_height, &my_window->imdata);
my_window->hBmp = CreateBitmap(im_width, im_height, 32, 1, my_window->imdata);
}
if (my_window->hBmp == NULL)
MessageBox(window, "Could not load image!", "Error", MB_OK | MB_ICONEXCLAMATION);
break; case WM_PAINT:
{
BITMAP bm;
PAINTSTRUCT ps; HDC hdc = BeginPaint(window, &ps);
SetStretchBltMode(hdc, COLORONCOLOR); my_window->dc = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(my_window->dc, my_window->hBmp); GetObject(my_window->hBmp, sizeof(bm), &bm); #if 1
//原样拷贝,不支持拉伸
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, SRCCOPY);
#else
RECT rcClient;
GetClientRect(window, &rcClient);//获得客户区的大小
int nWidth = rcClient.right - rcClient.left;//客户区的宽度
int nHeight = rcClient.bottom - rcClient.top;//客户区的高度
StretchBlt(hdc, 0, 0, nWidth, nHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);//拉伸拷贝
#endif SelectObject(my_window->dc, hbmOld);
DeleteDC(my_window->dc); EndPaint(window, &ps);
}
break; case WM_DESTROY:
printf("\ndestroying window\n");
PostQuitMessage(0);
return 0L; case WM_LBUTTONDOWN:
printf("\nmouse left button down at (%d, %d)\n", LOWORD(lp), HIWORD(lp)); // fall thru
default:
//printf(".");
return DefWindowProc(window, msg, wp, lp);
}
} const char* szWindowClass = "myclass"; ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
/* Win 3.x */
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = WindowProcedure;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = 0;
wc.lpszClassName = szWindowClass;
/* Win 4.0 */
wc.hIconSm = LoadIcon(0, IDI_APPLICATION); return RegisterClassEx(&wc);
} BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
MyRect rect;
rect.x = 300;
rect.y = 300;
rect.width = 640;
rect.height = 480; DWORD defStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU; HWND hwnd = CreateWindowEx(0, szWindowClass, "title",
defStyle, rect.x, rect.y,
rect.width, rect.height, 0, 0, hInstance, 0); if (!hwnd)
{
return FALSE;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd); return TRUE;
} void create_my_window(MyWindow** _my_window) {
MyWindow* my_window = (MyWindow*)malloc(sizeof(MyWindow));
my_window->dc = NULL;
my_window->imdata = NULL;
my_window->hBmp = NULL; *_my_window = my_window; // write back
} void destroy_my_window(MyWindow* my_window) {
if (my_window) {
if (my_window->imdata) free(my_window->imdata);
free(my_window);
}
} int main()
{
printf("hello world!\n"); HINSTANCE hInstance = GetModuleHandle(0);
int nCmdShow = SW_SHOWDEFAULT; MyRegisterClass(hInstance);
create_my_window(&my_window); if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
} MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
DispatchMessage(&msg);
} destroy_my_window(my_window); return 0; }

参考

使用 Libpng 配合 GDI 完成对 png 图片的解析与显示 , 讲解细致

How would I load a PNG image using Win32/GDI (no GDI+ if possible)? (没用)

使用libpng和GDI读取显示png图片 (代码无法运行)

基于GDI显示png图像的更多相关文章

  1. 使用GDI+显示OpenCV中的图像IplImage

    OpenCV虽然自带了轻量级的界面库HighGUI,但是支持的图像化元素实在是太少了,一般只在前期算法测试时使用.实际产品还是使用MFC库.因此本文记录了如何在GDI+中显示OpenCV中的IplIm ...

  2. 基于SURF特征的图像与视频拼接技术的研究和实现(一)

    基于SURF特征的图像与视频拼接技术的研究和实现(一)      一直有计划研究实时图像拼接,但是直到最近拜读西电2013年张亚娟的<基于SURF特征的图像与视频拼接技术的研究和实现>,条 ...

  3. [OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget (第一部分)

    本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-imm ...

  4. 基于AXI VDMA的图像采集系统

    基于AXI VDMA的图像采集系统 转载 2017年04月18日 17:26:43 标签: framebuffer / AXIS / AXI VDMA 2494 本课程将对Xilinx提供的一款IP核 ...

  5. EasyPlayerPro Windows播放器全屏模式下GDI显示出现黑屏问题解决

    问题来源 2017.12.21 前天有杭州某教育领域客户反馈有部分视频源在全屏模式下显示黑屏: 问题复现 EasyPlayerPro由于没有实现单个窗口完全全屏,故没有暴露该问题,晚上加班,加上单个窗 ...

  6. windows下用纯C实现一个简陋的imshow:基于GDI

    intro 先前实现了GDI显示图像时设定窗口大小为图像大小,不过并没有刻意封装函数调用接口,并不适合给其他函数调用.现在简单封装一下,特点: 纯C 基于GDI,因此只支持windows平台 类似于o ...

  7. 从视频文件中读入数据-->将数据转换为灰度图-->对图像做canny边缘检测-->将这三个结构显示在一个图像中

    //从视频文件中读入数据-->将数据转换为灰度图-->对图像做canny边缘检测-->将这三个结构显示在一个图像中 //作者:sandy //时间:2015-10-10 #inclu ...

  8. Kinect For Windows V2开发日志四:使用OpenCV显示深度图像

    代码示例: #include <Kinect.h> #include <iostream> #include <opencv2\highgui.hpp> using ...

  9. Android显示YUV图像

    需要流畅显示YUV图像需要使用Opengl库调用GPU资源,网上在这部分的资料很少.实际上Android已经为我们提供了相关的Opengl方法 主体过程如下: 1.建立GLSurfaceView 2. ...

随机推荐

  1. [Python] 项目的配置覆盖与合并

    参考来源: https://www.liaoxuefeng.com/wiki/1016959663602400/1018490750237280 代码稍微修改了一下 import os import ...

  2. Docker应用容器化

    Docker 的核心思想就是如何将应用整合到容器中,并且能在容器中实际运行. 将应用整合到容器中并且运行起来的这个过程,称为“容器化”(Containerizing),有时也叫作“Docker化”(D ...

  3. IDEA 2018.3.5,修改js文件,html页面不及时更新

    问题描述 使用IDEA 开发时,修改js文件,前端页面不能及时更新. 解决方法: 1. IDEA settings--> Compiler --> Build project automa ...

  4. 负载均衡Nginx和F5的区别

    今早上看书,看到为了保证Zuul的高可用性,在Zuul的前端可以使用Nginx或F5再次进行负载转发 使用过Nginx,那F5是什么,他们有什么区别吗? (1)F5 F5负载均衡器是应用交付网络的全球 ...

  5. Apache Kafka® is a distributed streaming platform

    Kafka Connect简介 我们知道过去对于Kafka的定义是分布式,分区化的,带备份机制的日志提交服务.也就是一个分布式的消息队列,这也是他最常见的用法.但是Kafka不止于此,打开最新的官网. ...

  6. mysql 更新与查询(排序 分组 链接查询)

    UPDATE更新 #每一次数据的更新都需要update UPDATE 命令修改 MySQL 数据表数据的通用 SQL 语法: UPDATE table_name SET field1=new-valu ...

  7. SpringBoot系列教程web篇之404、500异常页面配置

    接着前面几篇web处理请求的博文,本文将说明,当出现异常的场景下,如404请求url不存在,,403无权,500服务器异常时,我们可以如何处理 原文友链: SpringBoot系列教程web篇之404 ...

  8. c++11多线程记录4:死锁

    简单示例 举个例子,桌上有一支笔和一张纸,小A和小B都要拿到纸笔写字 小A拿了笔,小B拿了纸,这时就形成了死锁(两人都不愿意让出纸笔). 其实只要稍加控制就可以避免这种情况:规定必须先拿到纸再能去尝试 ...

  9. centos设置IP

    centos设置IP 原由:虚拟机里安装了很多软件,每天要使用,原来使用的动态IP,而且很长时间也没变,一直使用的很好,忽然一天访问不了了,找了几次才发现动态IP地址变了,这些后决定将虚拟机的IP地址 ...

  10. libevent源码分析一--io事件响应

    这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1.  select l ...