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


目次

SkiaとC++でデスクトップアプリを作る(Windows) その1
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]として書きます。

GLFWの導入

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

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ファイルを以下の内容で置き換えます。

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に変更し、保存します。

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

ビルドして実行

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

参考