軽量のグラフィックスライブラリ micro{gl} を試す
概要
今回は軽量のグラフィックスライブラリの micro{gl} を試してみようと思います。
micro{gl}とは?
高速、超スリム、組み込み可能、ヘッダのみの C++11 ベクターグラフィックスライブラリ。標準ライブラリは必要ありません。
GPU・FPU(浮動小数点演算器)不要、標準ライブラリ(STL)不要を掲げており、32/64bit環境で動かせる、軽量のCPUでレンダリングする“ベクターグラフィックス寄り”のライブラリという位置付けになっています。ウェアラブルデバイスやIoTデバイスなどの組み込み環境でも採用しやすいライブラリになっています。
動作環境
$ sw_vers
ProductName: macOS
ProductVersion: 15.5
BuildVersion: 24F74
$ c++ --version
Apple clang version 16.0.0 (clang-1600.0.26.4)
Target: arm64-apple-darwin24.5.0
Thread model: posix
セットアップ
まずはリポジトリをクローンして、exampleが動かせる様にしたいと思います。
examplesを試すのにcmake と SDL2 が必要なので事前にインストールしておきます。
$ brew install cmake
cmake --version
cmake version 4.0.3
CMake suite maintained and supported by Kitware (kitware.com/cmake).
$ brew install sdl2
リポジトリをcloneし、以下を実行します。
$ git clone git@github.com:micro-gl/micro-gl.git
$ cd micro-gl
$ mkdir cmake-build-release
$ cd cmake-build-release
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ cmake --build .
すると examples/bin 配下に実行ファイルができているので、好きなものを試すことができます。
例)
-
example_3d_sampler_shader_and_z_buffer

-
example_blocks_rounded_rect

-
example_draw_path_stroke

シンプルな実装を試す
まずは作業用ディレクトリを作成し最低限のシンプルな実装を試してみたいと思います。まずは単純に四角形を表示するだけのものを実装してみたいと思います。
mkdir example-microgl
コンパイル&実行確認
まずは以下を作成してコンパイル&実行までできることを確認しときます。
-
main.cpp
#include <iostream> int main() { std::cout << "Hello World!" << std::endl; return 0; } -
CMakeLists.txt
cmake_minimum_required(VERSION 4.2.1) project(example-microgl LANGUAGES CXX) add_executable(example-microgl main.cpp)
コンパイル & 実行して「Hello World!」が表示されとけば一旦準備完了です。
$ cmake -S . -B build
$ cmake --build build
./build/example-microgl
Hello World!
CMakeLists.txtの設定
includeパスの設定
現在のディレクトリ構成が以下の様になっているとして
.
├── micro-gl # git cloneしたもの
└── example-microgl
CMakeLists.txt に target_include_directories を追加します。
target_include_directories(example-microgl PRIVATE ../micro-gl/include)
SDL2の設定
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(example-microgl PRIVATE SDL2::SDL2)
これを受けた最終的な CMakeLists.txt は以下になります。
cmake_minimum_required(VERSION 4.0.3)
project(example-microgl LANGUAGES CXX)
add_executable(example-microgl main.cpp)
target_compile_features(example-microgl PRIVATE cxx_std_11)
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(example-microgl PRIVATE SDL2::SDL2)
target_include_directories(example-microgl PRIVATE ../micro-gl/include)
main.cppの実装
#include <SDL.h>
#include <cstdint>
#include <iostream>
#include <microgl/canvas.h>
#include <microgl/bitmaps/bitmap.h>
#include <microgl/pixel_coders/RGB888_PACKED_32.h>
#include <microgl/samplers/flat_color.h>
int main() {
using Canvas = canvas<bitmap<coder::RGB888_PACKED_32>>;
const int W = 512;
const int H = 512;
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n";
return 1;
}
SDL_Window* window = SDL_CreateWindow(
"micro{gl} rectangle",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
W, H, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Texture* texture = SDL_CreateTexture(
renderer,
SDL_PIXELFORMAT_RGB888,
SDL_TEXTUREACCESS_STATIC,
W, H);
Canvas canvas(W, H);
// 四角形の塗り(単色)
microgl::sampling::flat_color<> fill{{0, 0, 0, 255}}; // 黒
// 1回だけ描画(以後は表示更新だけ)
canvas.clear({255, 255, 255, 255}); // 背景:白
canvas.drawRect<microgl::blendmode::Normal,
microgl::porterduff::FastSourceOverOnOpaque,
false,
float>(
fill,
100, 100, 400, 400); // x, y, w, h
bool quit = false;
SDL_Event e{};
while (!quit) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) quit = true;
if (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) quit = true;
}
SDL_UpdateTexture(texture, nullptr, canvas.pixels(),
canvas.width() * canvas.sizeofPixel());
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
上記を実行すると以下が表示されます。

Canvas
今回 SDL2 の詳細は触れず micro{gl} に関する箇所を深掘りしていきたいと思います。
上の実装でまず出てくるのが Canvas になります。
キャンバスは次のように説明できます。
- 最小限の状態を持つオブジェクト
- 描画するビットマップを保持するオブジェクト
- サンプラーと3Dシェーダーを使用してビットマップ上に図形を描くための一連のメソッド
※ キャンバスの原点(0, 0)はPhotoshopのように左上にあり、下と右に広がります。
using Canvas でのCanvas型宣言時に以下のBitmapsを指定しています。
Bitmaps
ピクセル配列とピクセルコーダーを格納するオブジェクト。
- 主なインターフェース:
- 位置指定でピクセルを取得/設定。
- 位置指定で色を取得/設定。
- 位置指定で輝度を取得/設定。
- ピクセル値でビットマップをクリア/塗りつぶし。
Bitmaps指定時に以下のピクセルコーダーを指定しています。
Pixel Coders
色や輝度をピクセル表現にエンコード(符号化)し、またピクセルを色にデコード(復号)できるオブジェクト
microgl/pixel_coders には予め用意されているピクセルコーダーが定義されています。低リソース環境でもメモリを節約して使えるように様々な形式が用意されています。
| サイズ | ピクセルコーダー名 | 概要 |
|---|---|---|
| 32ビット | RGB888_PACKED_32 | rgb(8|8|8)チャンネルを32ビット符号なし整数に変換 |
| RGBA8888_PACKED_32 | rgba(8|8|8|8)チャネルを 32 ビットの符号なし整数に変換 | |
| RGBA8888_ARRAY | rgba(8|8|8|8)チャンネルを配列に格納 (より高速な IO) | |
| RGB888_ARRAY | rgb(8|8|8)チャンネルを配列に格納(より高速なIO) | |
| 16ビット | RGB555_PACKED_16 | rgb(5|5|5)チャネルを 16 ビットの符号なし整数 (word) に変換 |
| RGB565_PACKED_16 | rgb(5|6|5)チャネルを16ビットの符号なし整数(word)に変換 | |
| RGBA4444_PACKED_16 | rgba(4|4|4|4)チャネルを 16 ビットの符号なし整数 (word) に変換 | |
| RGBA5551_PACKED_16 | rgba(5|5|5|1)チャネルを 16 ビットの符号なし整数 (word) に変換 | |
| ARGB1555_PACKED_16 | argb(1|5|5|5)チャネルを 16 ビットの符号なし整数 (word) に変換 | |
| 8ビット | RGB332_PACKED_8 | rgb(3|3|2)チャネルを 8 ビットの符号なし整数 (char) に変換 |
アニメーションを試す
次に上記の四角形を時間とともにサイズを変更するサンプルを実装してみたいと思います。今回は SDL_GetTicks を使用してSDK初期化後からの経過時間(ms)を取得し、その値を元に縦、横のサイズを変更します。
// 以下を追加
auto render = [&] {
// 時間で縦、横のサイズを変更する
const uint32_t t = SDL_GetTicks(); // ms
const float phase = (t % 2000) / 2000.0f; // 0..1
canvas.clear({255, 255, 255, 255});
canvas.drawRect<microgl::blendmode::Normal,
microgl::porterduff::FastSourceOverOnOpaque,
false,
float>(
fill,
100, 100, static_cast<int>(400 * phase), static_cast<int>(400 * phase));
};
while (!quit) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) quit = true;
if (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) quit = true;
}
render(); // 追加
SDL_UpdateTexture(texture, nullptr, canvas.pixels(),
canvas.width() * canvas.sizeofPixel());
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
上記を実行すると以下のよう時間と共に縦横のサイズが変更するかと思います。

まとめ
今回軽く触り程度ですが、micro{gl}を試してみて特にPixel Codersの低リソースでも動作できるような工夫などが見れて勉強になりました!また機会があれば深掘りしてみたいと思います。
Discussion