首页 > 编程语言 >大海捞针 Skia(C++) 第 4.1 期(特别篇):将绘制结果输出到窗口

大海捞针 Skia(C++) 第 4.1 期(特别篇):将绘制结果输出到窗口

时间:2024-03-29 21:45:32浏览次数:27  
标签:canvas 4.1 Draw C++ hInstance CSkiaDraw Skia bmpInfo cls

前言

由于本人(我)没有系统学习过图形学,无法提供准确的术语表达,如果哪位大佬看到我的一些错误,还请友善指出!

第四期之后,我一直纠结于应该讲些什么。图形学的东西我真的学的不多,未来也不是很想走这个方向。但是我仍然希望通过我的一些绵薄之力为一些苦苦寻找关于Skia资料的兄弟们提供方便。说实话,我也犹豫要不要继续写下去。说到底我并不了解这些东西,只因为用过Aseprite,惊叹于Skia为它做出那样的界面效果,因此产生兴趣。也许对我来说,写这些可能没办法帮到别人,更像是记录自己学习的一个“日记”吧。

第四期我们带领大家绘制了基本图形,想必通过代码绘制图形是一件非常兴奋的事。但我想也许你会疲倦于到D盘下寻找你的绘制结果。于是,我们这一期将来解决这个问题——让绘制结果直观可见!
因为没有具体的一些案例,仅仅是一种优化,于是我决定叫做 4.1 特别篇!


具体实现

思路分析

要想让图片直接就能看到,而不是打开生成的图片查看,最好的方式想必就是将生成的图片显示出来。那么我们就不难想到写一个GUI程序来显示,那么有什么方法可以让图片显示到窗口呢?这里我想到了以下几个方法:

EasyX,Win32

EasyX 实现

EasyX是一个免费绘图库,简单易用。

用EasyX,我们可以很快创建出一个窗口,并且将生成的图片显示出来。

值得注意的是,这里我选择将绘制代码单独放到Draw.h下声明,Draw.cpp下定义。这是由于不知什么原因,EasyX库的graphics.h头文件会导致绘图失败。我猜想是因为 graphics.h 的某些代码与 Skia 冲突了。所以为了避免相互干扰,只好将绘制部分单独放到另一个编译单元去。

// main.cpp
#include "pch.h"
#include "Draw.h"
#include <graphics.h>
#include <conio.h>

int main()
{
	Draw(); // 具体的绘图函数

	IMAGE img;
	loadimage(&img, L"D:/test.png"); //注意第二个参数是宽字节字符串,如果项目用多字节字符集,可使用窄字节
	initgraph(img.getwidth(), img.getheight()); // 根据图片长宽设置窗口大小
	putimage(0, 0, &img); // 从窗口客户区左上角开始绘制

	_getch(); // 阻塞程序,让窗口保持,直到我们需要关闭的时候
	closegraph();

	return 0;
}

Draw.h 中仅仅包含了预编译头和Draw函数的声明,这里就不给出代码了。

// Draw.cpp
#include "pch.h"
#include "Draw.h"

bool Draw()
{
	SkBitmap bitmap;
	SkImageInfo bitmapInfo = SkImageInfo::Make(600, 400, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
	bitmap.allocPixels(bitmapInfo);
	SkCanvas canvas(bitmap);
	canvas.clear(0xffffffff);

	SkColor4f color = SkColor4f::FromColor(SkColor(0xff00ffff));
	SkPaint paint = SkPaint::SkPaint(color);
	canvas.drawCircle(SkPoint::Make(100, 100),
		SkScalar(50), paint);

	SkFILEWStream stream("D:/test.png");
	return SkEncodeImage(&stream, bitmap, SkEncodedImageFormat::kPNG, 0);
}

Win32 实现

Win32实现原理是通过StrechDIBits函数将内容显示到窗口中。这里可以参考如下文章。
【skia】win32中使用skia图形库

那么由于上述代码在实际测试的时候发现有一定问题,并且为了让读者专注于绘制过程而非展示过程,我将绘制操作封装成一个类,并且对上述链接中的代码进行改动(按照文中代码实现,当调整成窗口大小时,若窗口客户区宽高为0将导致程序崩溃)。

WinMain.cpp
#include "pch.h"
#include "CSkiaDraw.h"

// Global Variable
CSkiaDraw skd;

ATOM MyRegisterClass(HINSTANCE hInstance);
HWND InitWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, LPARAM lParam, WPARAM wParam);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, INT iCmdShow)
{
	MyRegisterClass(hInstance);

	HWND hWnd = InitWindow(hInstance);

	ShowWindow(hWnd, iCmdShow);
	UpdateWindow(hWnd);

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{

		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX cls;
	cls.cbClsExtra = NULL;
	cls.cbSize = sizeof(cls);
	cls.lpfnWndProc = (WNDPROC)WndProc;
	cls.lpszClassName = "SkiaPaintingResult";
	cls.hInstance = hInstance;
	cls.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
	cls.cbWndExtra = NULL;
	cls.hCursor = LoadCursor(hInstance, IDC_ARROW);
	cls.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
	cls.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
	cls.lpszMenuName = NULL;
	cls.style = CS_HREDRAW | CS_VREDRAW;

	return RegisterClassEx(&cls);
}

HWND InitWindow(HINSTANCE hInstance)
{
	return CreateWindow("SkiaPaintingResult", "Skia Painting Result", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, NULL);
}

LRESULT WndProc(HWND hWnd, UINT uMsg, LPARAM lParam, WPARAM wParam)
{
	switch (uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);

		RECT rt;
		GetClientRect(hWnd, &rt);
		int bmpw = rt.right - rt.left;
		int bmph = rt.bottom - rt.top;

		const size_t bmpSize = sizeof(BITMAPINFOHEADER) + bmpw * bmph * sizeof(uint32_t);
		BITMAPINFO* bmpInfo = (BITMAPINFO*)new BYTE[bmpSize]();
		bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmpInfo->bmiHeader.biWidth = bmpw;
		bmpInfo->bmiHeader.biHeight = -bmph;

		bmpInfo->bmiHeader.biPlanes = 1;
		bmpInfo->bmiHeader.biBitCount = 32;
		bmpInfo->bmiHeader.biCompression = BI_RGB;
		void* pixels = bmpInfo->bmiColors;

		SkImageInfo info = SkImageInfo::Make(bmpw, bmph,
			kBGRA_8888_SkColorType, kPremul_SkAlphaType);

		sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(info, pixels, bmpw * sizeof(uint32_t));

		if (bmpw > 0 && bmph > 0) //改动部分!!!
		{
			skd.SetCanvas(surface->getCanvas());
			skd.Draw(bmpw, bmph);

			StretchDIBits(hdc, 0, 0, bmpw, bmph,
				0, 0, bmpw, bmph,
				pixels, bmpInfo,
				DIB_RGB_COLORS, SRCCOPY);

			delete[] bmpInfo;
		}

		EndPaint(hWnd, &ps);
	}
	break;
	default:
		break;
	}
	return DefWindowProc(hWnd, uMsg, lParam, wParam);
}

CSkiaDraw.h
#pragma once
#include "pch.h"

class CSkiaDraw
{
public:
	CSkiaDraw();
	~CSkiaDraw();

	void Draw(int width, int height);
	void SetCanvas(SkCanvas* canvas);

private:
	SkBitmap _bitmap;
	SkImageInfo _bitmapInfo;
	SkCanvas* _canvas;
	bool _selfCreatedCanvas;

	SkColor4f _defaultBackgroundColor;
};
CSkiaDraw.cpp
#include "CSkiaDraw.h"

CSkiaDraw::CSkiaDraw()
{
	_defaultBackgroundColor = SkColor4f::FromColor(0xffffffff);
	_bitmapInfo = SkImageInfo::Make(600, 400, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
	_bitmap.setInfo(_bitmapInfo, 0);
	_bitmap.allocPixels(_bitmapInfo);
	_canvas = new SkCanvas(_bitmap);
	_canvas->clear(_defaultBackgroundColor);
	_selfCreatedCanvas = true;
}

CSkiaDraw::~CSkiaDraw()
{
	if (_canvas != nullptr && _selfCreatedCanvas == true)
		delete _canvas;
}

void CSkiaDraw::Draw(int width, int height)
{
	// 我们将在此编写绘制代码
	SkColor4f color = SkColor4f::FromColor(SkColor(0xff00ffff));
	SkPaint paint = SkPaint::SkPaint(color);
	paint.setAntiAlias(true);
	_canvas->clear(_defaultBackgroundColor);
	_canvas->drawCircle(SkPoint::Make(100, 100),
		SkScalar(50), paint);
	_canvas->flush();
}

void CSkiaDraw::SetCanvas(SkCanvas* canvas)
{
	if (_canvas != nullptr && _selfCreatedCanvas == true)
		delete _canvas;

	_canvas = canvas;
	_selfCreatedCanvas = false;
}

标签:canvas,4.1,Draw,C++,hInstance,CSkiaDraw,Skia,bmpInfo,cls
From: https://www.cnblogs.com/AquerKing/p/18091910

相关文章

  • 华为OD机试 - 传递悄悄话(Java & JS & Python & C & C++)
    须知哈喽,本题库完全免费,收费是为了防止被爬,大家订阅专栏后可以私信联系退款。感谢支持文章目录须知题目描述输入描述输出描述解题思路:题目描述给定一个二叉树,每个节点上站一个人,节点数字表示父节点到该节点传递悄悄话需要花费的时间。初始时,根节点所在......
  • 华为OD机试 - 剩余银饰的重量(Java & JS & Python & C & C++)
    须知哈喽,本题库完全免费,收费是为了防止被爬,大家订阅专栏后可以私信联系退款。感谢支持文章目录须知题目描述输入描述输出描述解题思路:题目描述有N块二手市场收集的银饰,每块银饰的重量都是正整数,收集到的银饰会被熔化用于打造新的饰品。每一回合,从中选......
  • 2024年03月CCF-GESP编程能力等级认证C++编程八级真题解析
    本文收录于专栏《C++等级认证CCF-GESP真题解析》,专栏总目录:点这里。订阅后可阅读专栏内所有文章。一、单选题(每题2分,共30分)第1题为丰富食堂菜谱,炒菜部进行头脑风暴。肉类有鸡肉、牛肉、羊肉、猪肉4种,切法有肉排、肉块、肉末3种,配菜有圆白菜、油菜、豆腐3种,辣度有......
  • 2024年03月CCF-GESP编程能力等级认证C++编程七级真题解析
    本文收录于专栏《C++等级认证CCF-GESP真题解析》,专栏总目录:点这里。订阅后可阅读专栏内所有文章。一、单选题(每题2分,共30分)第1题下列关于排序的说法,正确的是()。A.冒泡排序是最快的排序算法之一。B.快速排序通常是不稳定的。C.最差情况,N个元素做归并排序......
  • ccfcsp-2019-12-2回收站选址(c++满分题解)
    该题就是考察点的保存以及索引的保存和遍历,看了他的用例说明,我原先以为暴力只能得50分,但是又没有想到别的优化方法,就写了一下暴力,发现居然AC下面是代码:#include<iostream>#include<vector>#include<map>usingnamespacestd;intmain(){ intn; cin>>n; vector<pair<......
  • UE4 C++ Widget的NativeConstruct 与 NativePreConstruct
    构造函数由于Widget是由UE的反射系统创建的,其生命周期由UE引擎管理,所以并不存在构造函数,UE为Widget类定义了两个虚函数NativeConstruct与NativePreConstruct来充当构造函数的作用。而这两个函数的调用都必须在Widget被实例化之后才能进行调用如何在Widget中获取角色在蓝图节......
  • C++精品小案例:C++中的多态性及其实现、模板元编程及其在C++中的应用
    1.C++中的多态性及其实现多态性是面向对象编程的三大特性之一,它允许使用父类类型的指针或引用来指向子类对象,并通过这个父类类型的指针或引用来调用实际子类的成员函数。这样,就可以在运行时确定应该调用哪个具体的函数实现,从而实现一个接口多种形态。多态性主要通过虚函数来......
  • C++继承详解
    文章目录前言一、继承的概念二、基类和派生类对象赋值转换三、继承中的作用域,隐藏四、菱形继承,菱形虚拟继承五、子类的默认成员函数六.继承和组合总结前言在本篇文章中我们将会学到有关继承方面的知识,其中C++中三大特性分别为:封装,继承多态。由此可见继承在学习中的......
  • 【C++杂货铺】内管管理
    目录......
  • 阶乘(C++实现)
    阶乘是基斯顿·卡曼(ChristianKramp,1760~1826)于1808年发明的运算符号,是数学术语。一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。公式表示为:0!=1n!= ()阶乘运算在C++语言中的实现,代......