LoginSignup
13
8

More than 3 years have passed since last update.

Flutterでも使われているGoogleが開発している2DグラフィックライブラリのSkiaを試してみる

Last updated at Posted at 2020-07-12

Skiaとは?

公式ページ

Skia(スキア)とは、Google が開発している、C++ で書かれたクロスプラットフォームかつ
オープンソースの2次元コンピュータグラフィックスライブラリ

Wikipediaより

Flutterでも使用されている!!。

:computer:環境構築


他の言語同様、メジャーなライブラリそうなので、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する為の
gitcurl, ビルドで使用する 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/Staticlibskia.a ができていれば成功です :sparkles:

:pencil: 実装


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.

描画対象を選択できるのは便利ですね :sparkles:
まずは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 が作成されれば成功です。
image.png

SkPDF- 簡単なサンプル

次に PDFを描画対象とする様に修正してみたいと思います。
とはいえ、sample.cpp を以下に修正してビルド + 実行すれば↑と同じ画像のpdfが作成される様になります :sparkles:

...
// 以下の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には他にも面白そうなものがあるので、時間があれば試してみたい :sparkles:
- Lottie Animation PlayerのSkottie
- Skia + WebAssembly - CanvasKit
- Geometry in the Browser - PathKit
- SkSL ("Skia Shading Language")

:bomb: バッドノウハウ


:sparkles: 感想


  • Skia が思ってた以上にできる事が多く、時間があれば色々試してみたいと思いました。
  • Flutter engineでどの様な使われ方をしているかも調べてみよう...

:link: 参考になったURL


13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8