基于GDI显示png图像

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的地方:在窗口处理函数的创建阶段有所不同:
    C 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); }
  • 缺点:窗口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图片 (代码无法运行)

原文地址:https://www.cnblogs.com/zjutzz/p/10850382.html

时间: 2024-11-06 09:32:02

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

C#中基于GDI+(Graphics)图像处理系列之前言

前言 图像处理工具类的全部源码 完整示例程序源码下载 示例程序截图 前言 图像处理是开发工程师们学习某种语言入门时就会遇到的问题,笔者刚开始接触C#使用GDI+进行图像处理,觉得太简单了,就没有深入研究,随着工作经验的积累,踏遍若干坑以后突然觉得还是有必要将这块的知识好好总结一下,毕竟还是有一些比较冷门的知识在实际应用中给我们的程序带来更多的灵活性,比如将图片保存成jpeg时进一步控制图片的质量.怎样获取任意角度旋转后的图像.怎样获取透明图像等等. 本文后面将直接放出图像处理工具类的全部源码和示

基于imgAreaSelect的用户图像截取

前言:想到用户资料中一般有个图像自我截取的部分,为什么要截取呢,因为好看了.so,经过我各种百度,各种参考,终于打工搞成了,写下纪念纪念,让以后拿来就用也好. 一:想前端ui这东西,我就懒得说话了,哎,没艺术啊!毫不犹豫的选择上网找资料了,发现一般有两种方法1:Jcrop:2:imgAreaSelect:呵呵,我选了imgAreaSelect;使用很简单么? 二:获取这个插件包:点我就来了开心吧! 三:使用方法简介: $('#img').imgAreaSelect({ 各种参数配置,回调函数 }

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

基于SURF特征的图像与视频拼接技术的研究和实现(一) 一直有计划研究实时图像拼接,但是直到最近拜读西电2013年张亚娟的<基于SURF特征的图像与视频拼接技术的研究和实现>,条理清晰.内容完整.实现的技术具有市场价值.因此定下决心以这篇论文为基础脉络,结合实际情况,进行“基于SURF特征的图像与视频拼接技术的研究和实现”. 一.基于opencv的surf实现 3.0以后,surf被分到了"opencv_contrib-master"中去,操作起来不习惯,这里仍然选择一直在

基于AXI VDMA的图像采集系统

基于AXI VDMA的图像采集系统 转载 2017年04月18日 17:26:43 标签: framebuffer / AXIS / AXI VDMA 2494 本课程将对Xilinx提供的一款IP核--AXI VDMA(Video Direct Memory Access) 进行详细讲解,为后续的学习和开发做好准备.内容安排如下:首先分析为什么要使用VDMA.VDMA的作用:然后详细介绍VDMA的特点.寄存器作空间: 最后阐述如何使用VDMA,包括IP核的配置方法.代码编写流程等. 本章主要是

Android视频录制从不入门到入门系列教程(二)————显示视频图像

1.创建一个空的工程,注意声明下列权限: <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 2.布局文件 <?xml version="1.0" encoding="utf-8"

图像处理之积分图应用四(基于局部均值的图像二值化算法)

图像处理之积分图应用四(基于局部均值的图像二值化算法) 基本原理 均值法,选择的阈值是局部范围内像素的灰度均值(gray mean),该方法的一个变种是用常量C减去均值Mean,然后根据均值实现如下操作: pixel = (pixel > (mean - c)) ? object : background 其中默认情况下参数C取值为0.object表示前景像素,background表示背景像素. 实现步骤 1. 彩色图像转灰度图像 2. 获取灰度图像的像素数据,预计算积分图 3. 根据输入的参数

基于深度学习的图像语义编辑

深度学习在图像分类.物体检测.图像分割等计算机视觉问题上都取得了很大的进展,被认为可以提取图像高层语义特征.基于此,衍生出了很多有意思的图像应用. 为了提升本文的可读性,我们先来看几个效果图. 图1. 图像风格转换 图2. 图像修复,左上图为原始图,右下图为基于深度学习的图像 图3. 换脸,左图为原图,中图为基于深度学习的算法,右图为使用普通图像编辑软件的效果 图4. 图像超清化效果图,从左到右,第一张为低清图像三次插值结果,第二张残差网络的效果,第三张为使用对抗神经网络后的结果,第四张为原图.

【DSP开发】利用CCS5.4开发基于DSP6455的JPEG2000图像解压缩过程

[DSP开发]利用CCS5.4开发基于DSP6455的JPEG2000图像解压缩过程 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明:前端是时间基于VS2010工程,在windows上实现了对openjpeg2000的改写,实现了从内存中读取数据进行解压缩的工作.由于某些技术储备需要,将其移植到DSP6455中进行解压缩.本文记录的就是整个移植过程. 0. 异想天开:试图在CCS上直接使用VS2010生成好的编译库 这个想法是一直就有的,在调试VS

1.1.3 显示大图像

加载并且显示一幅图像对内存使用情况具有显著的影响.例如,HTC G1电话带有一个320万像素的摄像头.320万像素的摄像头通常会捕获2048 X 1536像素的图像.显示如此大小的32位图像将需要超过100663kb或大约13MB的内存.虽然我们的应用程序不一定会因此耗尽内存,但是这肯定会使得内存更加的容易耗尽. Android提供了一个名为BitmapFactory的应用程序类,该程序类提供了一系列的静态方法,允许通过各种来源加载Bitmap图像.针对我们的需求,将从文件加载图像,并且在最初的