Skiaとは?
Skia(スキア)とは、Google が開発している、C++ で書かれたクロスプラットフォームかつ
オープンソースの2次元コンピュータグラフィックスライブラリ
Flutterでも使用されている!!。
環境構築
他の言語同様、メジャーなライブラリそうなので、Conan (パッケージマネージャー)を使って
Skiaを導入できるものかと思ってましたが、
conan search "skia" --remote=conan-center
で検索しても見つからず... (探し方が悪い?)
色々調査したのですが、地道に1から環境を構築した方が早そうなので環境構築からやっていきたいと思います。
ベースとなる環境はこちらの環境を流用して構築していきます。
まずはDockerfileを以下で用意します。
FROM silkeh/clang:latest
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && \
apt-get install -y \
git \
python \
curl \
build-essential \
libfontconfig-dev \
libgl1-mesa-dev \
libglu1-mesa-dev \
libxi-dev \
vim \
cmake \
python-pip \
--no-install-recommends && \
rm -rf /var/lib/apt/lists/*
RUN pip install --upgrade pip setuptools && pip install conan
RUN cd /opt \
&& git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git' \
&& git clone 'https://skia.googlesource.com/skia.git'
ENV PATH="/opt/depot_tools:${PATH}"
RUN cd /opt/skia \
&& python tools/git-sync-deps \
&& gn gen out/Static --args='is_debug=false is_official_build=true skia_use_system_expat=false skia_use_system_icu=false skia_use_system_libjpeg_turbo=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_sfntly=false skia_use_freetype=true skia_use_harfbuzz=true skia_pdf_subset_harfbuzz=true skia_use_system_freetype2=false skia_use_system_harfbuzz=false target_cpu="x64" extra_cflags_cc=["-frtti"]' \
&& ninja -C out/Static
ADD . /usr/src/app
CMD clang++
ベースにした環境のDockerfileからの差分としては、skia
本体やビルド時に使用する depot_tools
をcloneする為の
git
や curl
, ビルドで使用する libxxx
を追加しています。
実際にビルドしている箇所は
RUN cd /opt/skia \
&& python tools/git-sync-deps \
&& gn gen out/Static --args='is_debug=false is_official_build=true skia_use_system_expat=false skia_use_system_icu=false skia_use_system_libjpeg_turbo=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_sfntly=false skia_use_freetype=true skia_use_harfbuzz=true skia_pdf_subset_harfbuzz=true skia_use_system_freetype2=false skia_use_system_harfbuzz=false target_cpu="x64" extra_cflags_cc=["-frtti"]' \
&& ninja -C out/Static
こちらで依存関係のモジュール等をcloneしビルド設定 + ビルド実行を行っています。
-
PythonのVersion
$ python --version Python 2.7.16
docker build
して /opt/skia/out/Static
に libskia.a
ができていれば成功です
実装
skia
はbackend(描画対象)として以下が選択できます。
- Raster - CPU-only.
- GPU - Skia's GPU-accelerated backend.
- SkPDF - PDF document creation.
- SkPicture - Skia's display list format.
- NullCanvas - Useful for testing only.
- SkXPS - Experimental XPS backend.
- SkSVG - Experimental SVG backend.
描画対象を選択できるのは便利ですね
まずはCPUのみで描画する Raster
を使用するサンプルを試してみたいと思います。
Raster- 簡単なサンプル
#include <string>
#include <fstream>
#include <iostream>
#include "spdlog/spdlog.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkEncodedImageFormat.h"
#include "SkImage.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkSurface.h"
// https://skia.org/user/api/skcanvas_overview こちらのサンプルを少し修正
void draw(SkCanvas* canvas) {
const SkScalar scale = 256.0f;
const SkScalar R = 0.45f * scale;
const SkScalar TAU = 6.2831853f;
SkPath path;
path.moveTo(R, 0.0f);
for (int i = 1; i < 7; ++i) {
SkScalar theta = 3 * i * TAU / 7;
path.lineTo(R * cos(theta), R * sin(theta));
}
path.close();
SkPaint p;
p.setAntiAlias(true);
p.setStyle(SkPaint::kFill_Style);
p.setColor(SK_ColorBLUE);
canvas->clear(SK_ColorWHITE);
canvas->translate(0.5f * scale, 0.5f * scale);
canvas->drawPath(path, p);
}
int main() {
spdlog::info("start skia sample");
// 描画対象キャンバスの準備
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(640, 480);
SkCanvas *canvas = surface->getCanvas();
canvas->clear(SK_ColorWHITE);
// 描画
draw(canvas);
// 画像の保存
sk_sp<SkImage> image(surface->makeImageSnapshot());
sk_sp<SkData> data(image->encodeToData(SkEncodedImageFormat::kPNG, 100));
std::ofstream ofs("sample.png", std::ios::binary);
ofs.write(reinterpret_cast<const char*>(data->data()), data->size());
ofs.close();
spdlog::info("end skia sample");
return 0;
}
上記のコードをビルドする為に、CMakeLists.txt
を以下の内容で作成します。
cmake_minimum_required(VERSION 3.13.4)
project(SkiaSample)
find_package (Threads)
include_directories(
/opt/skia
/opt/skia/include/core
/opt/skia/include/config
/opt/skia/include/utils
/opt/skia/include/gpu
/opt/skia/include/docs
)
add_definitions("-std=c++14")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
link_directories(
/opt/skia/out/Static
)
add_executable(sample sample.cpp)
target_link_libraries(sample
skia
z
dl
fontconfig
freetype
GL
GLU
${CMAKE_THREAD_LIBS_INIT}
${CONAN_LIBS}
)
ついでにビルド用のシェルも作成します。
ビルド用のシェルでは CC
, CXX
をClangを使う様に設定し、
conanのinstallも行っています。
#!/usr/bin/env bash
export CC=/usr/local/bin/clang
export CXX=/usr/local/bin/clang++
if [ -e build ]; then
rm -rf build
mkdir build
else
mkdir build
fi
cd build
conan install .. --build=fmt --build=spdlog -pr=../clang_profile
cmake ..
make
いざビルドして、成功すると ./build/bin/sample
が作成されているので
$ ./build/bin/sample
実行して、以下の様な画像の sample.png
が作成されれば成功です。
SkPDF- 簡単なサンプル
次に PDFを描画対象とする様に修正してみたいと思います。
とはいえ、sample.cpp
を以下に修正してビルド + 実行すれば↑と同じ画像のpdfが作成される様になります
...
// 以下のincludeを追加
#include "SkPDFDocument.h"
#include "SkStream.h"
// main関数を以下に修正
int main() {
spdlog::info("start skia sample");
// 描画対象キャンバスの準備
SkFILEWStream pdfStream("sample.pdf");
auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(640),
SkIntToScalar(480));
// 描画
draw(pdfCanvas);
pdfDoc->close();
spdlog::info("end skia sample");
return 0;
}
その他
skiaには他にも面白そうなものがあるので、時間があれば試してみたい
- Lottie Animation PlayerのSkottie
- Skia + WebAssembly - CanvasKit
- Geometry in the Browser - PathKit
- SkSL ("Skia Shading Language")
バッドノウハウ
-
skiaビルド中に
SkShaper_harfbuzz.cpp:34:10: fatal error: hb.h: No such file or directory
が発生する!
以下のPRを元に--args
のパラメータを変更
Ninja build failing on macOS Catalina · Issue #2293 · aseprite/aseprite
Update macOS command line (match it to the build.yml one) · aseprite/skia@3043aa8 -
conanも一緒に使用した際に
Incorrect 'clang', is not the one detected by CMake: 'GNU'
が発生する-
C
とCXX
の環境変数をClangのパスで設定する$ export CC=/usr/local/bin/clang $ export CXX=/usr/local/bin/clang++
-
感想
-
Skia
が思ってた以上にできる事が多く、時間があれば色々試してみたいと思いました。 - Flutter engineでどの様な使われ方をしているかも調べてみよう...