キャンバス・イティ:C++ 用の極小で単一ヘッダーな `<canvas>` に似た 2D ラスタライザ―

2026/02/22 3:50

キャンバス・イティ:C++ 用の極小で単一ヘッダーな `<canvas>` に似た 2D ラスタライザ―

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

本段落は、C++ の

canvas_ity
ライブラリで描画された 256 × 256 ピクセルの星が、同一の図形を HTML5 Canvas 上で JavaScript によって描画した場合とまったく同じ視覚結果になることを示しています。

星の幾何学 – 両プログラムともに、座標 (128,28)、(157,87)、(223,97)、(175,143)、(186,208)、(128,178)、(69,208)、(80,143)、(32,97)、および (98,87) を用いて十角形の閉じたパスを定義します。

– 最初に塗りつぶす前に、

shadowBlur = 8.0f
shadowOffsetY = 4.0f
と色
rgba(0,0,0,0.5)
を持つドロップシャドウが適用されます。その後、そのアルファを 0 に設定して無効化します。

スタイリング – 星はまず純粋な黄色 (

rgba(1.0,0.9,0.2,1.0)
/
#ffe633
) で塗りつぶされます。その後、二度にわたりストロークが行われます。最初は太い赤色の線幅12、丸みを帯びたジョイン(
rgba(0.9,0,0.5,1.0)
/
#e60080
)で描画され、次に細めのオレンジの破線が線幅6、丸みのあるキャップ、ダッシュパターン
[21,9,1,9,7,9,1,9]
、オフセット10、色
rgba(0.95,0.65,0.15,1.0)
/
#f2a626
で描かれます。

グラデーションの輝き – (64,0) から (192,256) への線形グラデーションがキャンバス全体に追加され、停止点は 0.30(

rgba(1,1,1,0)
)、0.35 & 0.45(
rgba(1,1,1,0.8)
)、および 0.50(
rgba(1,1,1,0)
)です。合成モードは
source-atop
が使用されます。

エクスポート – 最終的な RGBA ピクセルバッファは BGRA 順序に変換され、32‑ビット深度の 12 バイトヘッダー付き TGA ファイル

example.tga
として書き込まれます。

JavaScript 等価 – 同伴する HTML ページでは、

getContext("2d")
を使用し、影・塗りつぶし・ストローク・ダッシュ・グラデーションのパラメータを一致させることで、この正確な描画手順が再現されます。

この例は、ネイティブ C++ の

canvas_ity
を用いたキャンバス描画とブラウザ上の HTML5 Canvas API との間でグラフィックロジックを移植できること、かつ視覚的な損失がないことを示しており、デスクトップとウェブプラットフォーム間で一貫したビジュアルを必要とする開発者にとって価値があります。

本文

C++(

example.cpp

#include <algorithm>
#include <fstream>

#define CANVAS_ITY_IMPLEMENTATION
#include "canvas_ity.hpp"

int main()
{
    /*  キャンバスを構築する */
    static const int width = 256, height = 256;
    canvas_ity::canvas context(width, height);

    /*  星形パスを作成する */
    context.move_to(128.0f, 28.0f);   context.line_to(157.0f, 87.0f);
    context.line_to(223.0f, 97.0f);   context.line_to(175.0f, 143.0f);
    context.line_to(186.0f, 208.0f);  context.line_to(128.0f, 178.0f);
    context.line_to(69.0f, 208.0f);   context.line_to(80.0f, 143.0f);
    context.line_to(32.0f, 97.0f);    context.line_to(98.0f, 87.0f);
    context.close_path();

    /*  ドロップシャドウを設定する */
    context.set_shadow_blur(8.0f);
    context.shadow_offset_y = 4.0f;
    context.set_shadow_color(0.0f, 0.0f, 0.0f, 0.5f);

    /*  星形を黄色で塗りつぶす */
    context.set_color(canvas_ity::fill_style, 1.0f, 0.9f, 0.2f, 1.0f);
    context.fill();

    /*  太い赤のストローク(角は丸める)を描画する */
    context.line_join = canvas_ity::rounded;
    context.set_line_width(12.0f);
    context.set_color(canvas_iety::stroke_style, 0.9f, 0.0f, 0.5f, 1.0f);
    context.stroke();

    /*  より薄いオレンジの破線ストロークを描く */
    float segments[] = {21.0f, 9.0f, 1.0f, 9.0f, 7.0f, 9.0f, 1.0f, 9.0f};
    context.set_line_dash(segments, 8);
    context.line_dash_offset = 10.0f;
    context.line_cap = canvas_ity::circle;
    context.set_line_width(6.0f);
    context.set_color(canvas_ity::stroke_style, 0.95f, 0.65f, 0.15f, 1.0f);
    context.stroke();

    /*  ドロップシャドウを無効にする */
    context.set_shadow_color(0.0f, 0.0f, 0.0f, 0.0f);

    /*  星形の上に光沢レイヤーを追加する */
    context.set_linear_gradient(canvas_ity::fill_style,
                                64.0f, 0.0f, 192.0f, 256.0f);
    context.add_color_stop(canvas_ity::fill_style, 0.30f, 1.0f, 1.0f, 1.0f, 0.0f);
    context.add_color_stop(canvas_ity::fill_style, 0.35f, 1.0f, 1.0f, 1.0f, 0.8f);
    context.add_color_stop(canvas_iety::fill_style, 0.45f, 1.0f, 1.0f, 1.0f, 0.8f);
    context.add_color_stop(canvas_ity::fill_style, 0.50f, 1.0f, 1.0f, 1.0f, 0.0f);

    context.global_composite_operation = canvas_ity::source_atop;
    context.fill_rectangle(0.0f, 0.0f, 256.0f, 256.0f);

    /*  TGA ファイル(BGRA 順序)として結果をエクスポートする */
    unsigned char* image = new unsigned char[height * width * 4];
    context.get_image_data(image, width, height, width * 4, 0, 0);

    for (int pixel = 0; pixel < height * width; ++pixel)
        std::swap(image[pixel * 4 + 0], image[pixel * 4 + 2]);

    unsigned char header[] = {
        0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        width & 255, width >> 8, height & 255, height >> 8, 32, 40
    };

    std::ofstream stream("example.tga", std::ios::binary);
    stream.write(reinterpret_cast<char*>(header), sizeof(header));
    stream.write(reinterpret_cast<char*>(image), height * width * 4);

    delete[] image;
}

HTML(

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Example</title>
<style>
  canvas { border:1px solid #000; }
</style>
</head>

<body>
<canvas id="example" width="256" height="256"></canvas>
<script>
const ctx = document.getElementById('example').getContext('2d');

/* 星形パスを作成する */
ctx.moveTo(128.0, 28.0); ctx.lineTo(157.0, 87.0);
ctx.lineTo(223.0, 97.0); ctx.lineTo(175.0, 143.0);
ctx.lineTo(186.0, 208.0); ctx.lineTo(128.0, 178.0);
ctx.lineTo(69.0, 208.0); ctx.lineTo(80.0, 143.0);
ctx.lineTo(32.0, 97.0); ctx.lineTo(98.0, 87.0);
ctx.closePath();

/* ドロップシャドウを設定する */
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 4;
ctx.shadowColor = 'rgba(0,0,0,.5)';

/* 黄色で塗りつぶす */
ctx.fillStyle = '#ffe633';
ctx.fill();

/* 太い赤のストローク(角は丸める)を描く */
ctx.lineJoin = 'round';
ctx.lineWidth = 12;
ctx.strokeStyle = '#e60080';
ctx.stroke();

/* より薄いオレンジの破線ストローク */
const segments = [21.0, 9.0, 1.0, 9.0, 7.0, 9.0, 1.0, 9.0];
ctx.setLineDash(segments);
ctx.lineDashOffset = 10;
ctx.lineCap = 'round';
ctx.lineWidth = 6;
ctx.strokeStyle = '#f2a626';
ctx.stroke();

/* ドロップシャドウを無効にする */
ctx.shadowColor = 'rgba(0,0,0,.0)';

/* 光沢レイヤーを追加する */
const grad = ctx.createLinearGradient(64.0, 0.0, 192.0, 256.0);
grad.addColorStop(0.30, 'rgba(255,255,255,0)');
grad.addColorStop(0.35, 'rgba(255,255,255,.8)');
grad.addColorStop(0.45, 'rgba(255,255,255,.8)');
grad.addColorStop(0.50, 'rgba(255,255,255,0)');
ctx.fillStyle = grad;
ctx.globalCompositeOperation = 'source-atop';
ctx.fillRect(0, 0, 256, 256);
</script>
</body>
</html>

不要な改行や空白はすべて削除し、読みやすさのためにインデントを揃えました。

同じ日のほかのニュース

一覧に戻る →

2026/02/22 9:29

**Claude コードの使い方:計画と実行の分離**

## 日本語訳: 記事は約9か月の経験に基づくClaude Codeを使用するための規律あるワークフローを提示しています。研究、計画、および実行を分離し、各フェーズが進む前に承認済みのマークダウンアーティファクトを生成することを強調しています。 1. **リサーチ (research.md)** – Claude は対象フォルダーを徹底的にスキャンし、ユーザーが検証しなければならない詳細レポートを作成します。表面的な読み込みは推奨されません。 2. **計画 (plan.md)** – コードスニペット、ファイルパス、トレードオフ、および説明を含む別のマークダウン計画が用意されます。組み込みのプランモードは拒否され、この編集可能なドキュメントが採用されます。 3. **注釈サイクル** – ユーザーはエディタで計画をレビューし、インラインメモや制約を追加して「まだ実装しない」ガード付きで再送します。このサイクルは計画が完全に受理されるまで繰り返されます。 4. **実行** – 実装前に詳細なTODOリストが計画に追加されます。その後、著者は固定プロンプト「implement it all…」を発行し、Claude にすべてを実行させ、計画内の完了状況を更新させ、不必要なコメントや未知のタイプを避け、型チェックを継続的に実行させます。 5. **修正** – 実行中にユーザーは簡潔な修正(多くの場合単一文)を提供します。フロントエンドでの修正にはスクリーンショットや既存パターンへの参照が含まれる場合があります。 6. **制御と永続性** – 著者はアーキテクチャ的なコントロールを決して手放しません。Claude の提案を評価し、必要に応じて変更またはスキップします。3つのフェーズすべてが単一の長時間セッションで行われ、計画ファイルは自動圧縮を通じて保持され、主要な参照として機能します。 マークダウンファイルを共有可変状態として維持することで、このアプローチはノイズの多いチャットインタラクションを減らし、追跡性を向上させ、大規模プロジェクト全体で一貫したインターフェースを保ちます。

2026/02/22 9:21

**回答** 実際には、ほとんどの最新コンパイラは「決定的(deterministic)」です。 同じソースコードと同一のコンパイルオプション(使用するコンパイラのバージョンや基盤となるプラットフォームを含む)を与えれば、何度実行しても同一のオブジェクトファイルまたはバイナリが生成されます。 ただし、いくつか注意すべき点があります。 | 要因 | 決定性への影響 | |------|----------------| | **コンパイラ実装** | よく設計されたコンパイラは決定的ですが、不具合のあるものではそうでない場合もあります。 | | **ビルド環境** | OS、CPU アーキテクチャ、またはライブラリのバージョンが異なると、ソースコード自体に変更がなくても出力が変わることがあります。 | | **非決定的なパス** | 例としてランダム化されたレジスタ割り当てなど、一部の最適化は性能調査のために意図的にばらつきを導入します。 | | **タイムスタンプ/ビルドメタデータ** | バイナリにはしばしばタイムスタンプやビルド識別子が埋め込まれます。 これを削除(例:GCC/Clang の `-Wl,--build-id=none`)すると、バイト単位で完全に同一の出力が得られます。 | したがって、環境を統制し安定したコンパイラリリースを使用すれば決定的な結果が期待できます。 セキュリティや監査目的で確実な再現性が必要な場合は、**Reproducible Builds** のようなツールを使い、非決定的データを除去する手順を踏むと良いでしょう。

## Japanese Translation: 記事は、ソフトウェアビルドにおける真の決定論が実現しづらい理由を説明しています。入力状態のすべての部分―ソースコード、コンパイラフラグ、ツールチェーンバイナリ、環境変数、ファイルシステムレイアウト、ロケール、クロック、カーネル動作、さらにはハードウェア並列性までも―を完全に指定しなければ、「ノイズ」が出力の漂移を引き起こします。 再現可能ビルドの実践は、ツールチェーンを凍結し、タイムスタンプを正規化(`SOURCE_DATE_EPOCH`)、揮発性メタデータを除去し、`-ffile-prefix-map` でパスを標準化し、ヘルミティックコンテナ内でビルドし、アーカイブを決定的に作成(`ar -D`)することでこれらの問題を緩和します。そうしても、GCC 18574 のようなバグが示すように、内部ポインタハッシュの不安定性は同一ソースから生成されるコードを変化させる可能性があります。 コンパイラ契約はビット単位での同一性ではなく、セマンティクス(観測可能な I/O、揮発性アクセス、アトミック保証)の保持に焦点を当てています。不定動作がこの保証を弱めるため、再現可能ビルドはより厳格な要件となります。`__DATE__/__TIME__` のようなエントロピー源、デバッグ情報内の絶対パス、ロケール依存のソート(`LC_ALL`)、並列ビルドの競合順序、ランダムシード、ネットワークフェッチはすべて再現性を破る要因となり得ます。ASLR がコンパイラパスに間接的に影響することも同様です。 歴史的には、2013 年以降の Debian の再現可能ビルド取り組みが、同一ソースから同一アーティファクトを作ることを主流化し、コンパイラ・リンカ・パッケージング・ビルドシステム全体で意図的な設計が必要であることを示しています。 将来に向けて、記事は LLM で支援される開発チームが決定論的検証ゲート―制約付き入力、テスト可能な出力、再現性のある CI パイプライン―を導入して信頼できるデプロイを確保する必要があると主張しています。完全な決定論は必須ではありませんが、予測可能な振舞いと検証可能性は本番システムに不可欠です。 主要な結論は、多くのエコシステムが多くの境界ケースで意図的な取り組みを通じて再現可能ビルドをサポートしているということですが、Ken Thompson の「Reflections on Trusting Trust」からの根本的な警告は残ります―コンパイラは信頼できるように見えても妥協され得るのです。

2026/02/22 5:57

**HN掲示:NVMe→GPU バイパスでCPUを経由せず、単一のRTX 3090上でLlama 3.1 70B を動作させる**

## Japanese Translation: **NTransformer** は、依存関係のない軽量 C++/CUDA エンジンであり、ユーザーが PCIe 上でモデル層をストリーミングし、オプションで高速直接 I/O 用に NVMe を使用することで、70 B 変種を含むフルサイズ Llama モデルをコンシューマ GPU 上で直接実行できるようにします。 - **Resident mode(レジデントモード)**:Llama 3.1‑8 B Q8_0 は 10 GB の VRAM だけで 48.9 トークン/秒を達成し、tiered‑auto モードは 10.3 GB を使用して 48.8 トークン/秒を提供します。 - **70 B model(70 B モデル)**:ストリーミング(mmap)のみでは非常に遅く (0.006 トークン/秒、7.3 GB)、tiered auto はスループットを 0.2 トークン/秒まで向上させ、23.1 GB を消費します。Q4_K_M のレイヤー・スキップを使用すると速度が 0.5 トークン/秒に上昇し、わずか 22.9 GB で済みます。これは単一 RTX 3090 + 48 GB RAM システムでのプレーン mmap に対して 83 倍速です。 - **Bandwidth bottleneck(帯域幅ボトルネック)**:PCIe Gen3 x8 (~6.5 GB/s) がデータ転送を制限します。Q4_K_M は VRAM に 10 層多く収容でき (36 層対 26 層)、tier‑B 転送が削減され、スループットが向上します。 - **Layer‑skip(レイヤー・スキップ)**:コサイン類似度キャリブレーションを使用して、1 トークンあたり 20–80 層を最小限の品質低下でスキップし、大規模モデルの推論速度を向上させます。 - **Architecture(アーキテクチャ)**:3 タイヤの適応型キャッシュが VRAM‑resident、ピン留め RAM、および NVMe/mmap タイヤを自動的にサイズ決定します。エンジンはすべての GGUF 量子化 (Q4_0, Q8_0, Q4_K_M, Q5_K, Q6_K, F16, F32) をサポートし、レジデント、tiered‑auto、layer‑skip、および self‑speculative decoding の 4 種類の自動選択データパスを提供します。 - **System requirements(システム要件)**:Linux (Ubuntu kernel 6.17+)、CUDA 13.1、gcc‑14/g++‑14、CMake 3.24+、NVIDIA GPU CC 8.0+ (RTX 3090 テスト済み)。直接 I/O 用に別の PCIe スロットに NVMe SSD が必要です。セットアップスクリプトはカーネルモジュールをパッチし、AMD IOMMU を無効化し、NVMe を VFIO にバインドします(DMA 分離について注意)。 - **NVMe‑direct pipeline(NVMe 直接パイプライン)**:各層 (~670 MB for 70 B Q6_K) は約 202 ms の NVMe コマンドで CUDA‑ピン留めステージングメモリに読み込まれ、非同期 DMA により GPU バッファへ転送され、デュアルバッファ間で計算と重ね合わせて実行されます。 - **Roadmap(ロードマップ)**:完成済みフェーズ—基盤、SLEP ストリーミング、最適化、NVMe direct。今後の作業には speculative decoding の仕上げと公開 C API の追加が含まれます。 NTransformer は、大規模モデルをコストの高いサーバーインフラなしでコンシューマ GPU 上にローカル実行できるようにすることで、推論コストを低減し、オンプレミス AI サービスのレイテンシを削減し、研究・産業界全体での採用拡大を促進します。