SkiaとC++でデスクトップアプリを作る(Windows) その2

# 目次

[SkiaとC++でデスクトップアプリを作る(Windows) その1](https://qiita.com/Nageler_Kotls/items/7b6ac18da4dd9100715a)
SkiaとC++でデスクトップアプリを作る(Windows) その2

# 環境

- Windows 10 64bit 2004
- VisualStudio2019
- CMake version 3.17.20032601-MSVC_2

諸々のプロダクトは2020年8月時点の最新版です(多分)

# VisualStudioのプロジェクト(CMake)でSkiaを使う

クロスプラットフォームも見据えてCMakeでプロジェクトを作ります。

VisualStudioを開き、[CMake プロジェクト]を選択して作成しましょう。この先プロジェクト名は[SkiaDesktopSample]として書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177771/36d88d6f-819d-7946-bdb4-356b1264b552.png)

## GLFWの導入

Skia単体では画面出力ができないため、OpenGLのユーティリティライブラリであるGLFWの助けを借ります。

[glfw.org](https://www.glfw.org/)の右上のボタンからGLFWの最新版をダウンロードします。Zipファイルを解凍したら先程作ったプロジェクトの{ProjectRoot}/SkiaDesktopSample/third/glfw 辺りに入れておきましょう。
これによりCMakeで読み込めるようになります。

## CMakeLists.txtの書き換え

CMakeListsを書いてskiaとglfwをリンクしましょう。

```:{ProjectRoot}/SkiaDesktopSample/CMakeLists.txt
# CMakeList.txt : SkiaDesktopSample の CMake プロジェクト。ソースを含めて、次を定義します:
# プロジェクト専用ロジックはこちらです。
#
cmake_minimum_required (VERSION 3.8)

# ソースをこのプロジェクトの実行可能ファイルに追加します。
add_executable (SkiaDesktopSample "SkiaDesktopSample.cpp" "SkiaDesktopSample.h")

# TODO: テストを追加し、必要な場合は、ターゲットをインストールします。

include_directories("C:/src/skia")
include_directories("C:/src/skia/tools/sk_app")
link_directories("C:/src/skia/out/release")
target_link_libraries(SkiaDesktopSample opengl32)
target_link_libraries(SkiaDesktopSample "C:/src/skia/out/release/skia.lib")

add_subdirectory("third/glfw")
include_directories("third/glfw/include")
target_link_libraries(SkiaDesktopSample glfw)

set_property(TARGET SkiaDesktopSample PROPERTY CXX_STANDARD 17)
```

include_directoriesでskiaの2箇所をincludeパスに指定します。もっと深い階層でincludeすることもできるのですが、内部ファイルのインクルードの指定の影響でこの書き方でないとコンパイルできません。
あまり行儀よくないですが、target_link_librariesにその1で作ったskia.libファイルをリンクします。openglも必要なので一緒にリンクしましょう。

またGLFWはsubdirectoryとして追加します。

Skiaは現時点ではC++20に対応していないため、C++17を指定します。

ファイルをこれで書き換え、保存してエラーが出ないことを確認してください。

## コードを書く

プロジェクト作成時に作られているcppファイルを以下の内容で置き換えます。

```c++:SkiaDesktopSample.cpp
// SkiaDesktopSample.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "SkiaDesktopSample.h"

#define SK_GL

#include "GLFW/glfw3.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContext.h"
#include "include/gpu/gl/GrGLInterface.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkSurface.h"
#include "include/core/SkFont.h"
#include <stdio.h>
#include <stdlib.h>
#include "include/effects/Sk2DPathEffect.h"


using namespace std;

GrContext* sContext = nullptr;
SkSurface* sSurface = nullptr;

void errorCallback(int error, const char* description) {
	fputs(description, stderr);
}

void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
}

void initSkia(int w, int h) {
	GrContextOptions options;
	sContext = GrContext::MakeGL(nullptr, options).release();

	GrGLFramebufferInfo framebufferInfo;
	framebufferInfo.fFBOID = 0;
	framebufferInfo.fFormat = GL_RGBA8;

	SkColorType colorType;
	colorType = kRGBA_8888_SkColorType;
	GrBackendRenderTarget backendRenderTarget(w, h, 0, 0, framebufferInfo);

	sSurface = SkSurface::MakeFromBackendRenderTarget(
		sContext, backendRenderTarget, kBottomLeft_GrSurfaceOrigin, colorType, nullptr, nullptr
	).release();
	if (sSurface == nullptr) abort();
}

void cleanupSkia() {
	delete sSurface;
	delete sContext;
}

const int kWidth = 960;
const int kHeight = 640;

int main(int argc, char* argv[])
{
	cout << "Hello CMake." << endl;
	GLFWwindow* window;
	glfwSetErrorCallback(errorCallback);
	if (!glfwInit()) {
		exit(EXIT_FAILURE);
	}

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	glfwWindowHint(GLFW_STENCIL_BITS, 0);
	glfwWindowHint(GLFW_DEPTH_BITS, 0);

	window = glfwCreateWindow(kWidth, kHeight, "Simple Example", NULL, NULL);
	if (!window) {
		glfwTerminate();
		exit(EXIT_FAILURE);
	}
	glfwMakeContextCurrent(window);

	initSkia(kWidth, kHeight);

	glfwSwapInterval(1);
	glfwSetKeyCallback(window, keyCallback);

	SkCanvas* canvas = sSurface->getCanvas();

	const char message[] = "Hello, Skia!";

	while (!glfwWindowShouldClose(window)) {
		glfwPollEvents();
		SkPaint paint;
		canvas->clear(SK_ColorWHITE);
		paint.setColor(SK_ColorBLUE);
		canvas->drawRect({ 100,200,300,500 }, paint);
		paint.setColor(SK_ColorRED);
		SkFont font;
		font.setSubpixel(true);
		font.setSize(72);
		canvas->drawSimpleText(message, strlen(message), SkTextEncoding::kUTF8, kWidth / 2, kHeight / 2, font, paint);
		sContext->flush();

		glfwSwapBuffers(window);
	}

	cleanupSkia();
	glfwDestroyWindow(window);
	glfwTerminate();
	exit(EXIT_SUCCESS);
	return 0;
}

```

## CMakeの設定を変更

少し設定を弄ります。[プロジェクト]>[SkiaDesktopSampleのCMakeの設定]>[CMake変数とキャッシュ]で、[高度な変数を表示]にチェックを入れます。
すると複数の変数が表示されるのでCMAKE_CXX_FLAGS_DEBUGを探し出し、最初の`/MDd`を`/MTd`に変更し、保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177771/addb7d74-4752-11b8-c7cb-c6ab2ee3a450.png)

またUSE_MSVC_RUNTIME_LIBRARY_DLLのチェックを外します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177771/d2139c26-28a9-74cc-93e1-933639c915c7.png)

## ビルドして実行

[デバッグ]>[デバッグの開始]を選択すると、ウィンドウが表示されます！！！！！！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177771/25512861-a30c-a26a-f3bd-365214879271.png)

# 参考

- [building applications - GLFW](https://www.glfw.org/docs/latest/build_guide.html)