スポンサーリンク

C++、skiaで結合文字のレイアウトを行って文字列描画

Skiaでフォントを指定して文字列を描画する。Freetype2とHarfbuzzが必要。

Skiaでフォント使用(1)文字列描画

#pragma comment(lib,"skia.dll.lib")
#pragma comment(lib,"skshaper.dll.lib")

/////////////////////////////////////

#include "skia/include/core/SkCanvas.h"
#include "skia/include/core/SkBitmap.h"
#include "skia/include/core/SkSurface.h"

#include "include/core/SkSurface.h"
/////////////////////////////////////
// テキスト描画
#include "include/core/SkTypeface.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMgr.h"
#include "include/ports/SkFontMgr_directory.h" // フォントマネージャ作成
#include "include/core/SkFontMetrics.h"

// 文字のレイアウト
#include "modules/skshaper/include/SkShaper.h"
/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////
// Png保存に必要
#include "include/encode/SkPngEncoder.h"
#include "include/core/SkStream.h"

/////////////////////////////////////

// SkShaperで文字列を描画するためのクラス
class CanvasRunHandler : public SkShaper::RunHandler {
public:
    CanvasRunHandler(SkCanvas* canvas, SkPoint origin, const SkFont& font, const SkPaint& paint)
        : canvas_(canvas), origin_(origin), font_(font), paint_(paint) {}


    void runInfo(const RunInfo& info) override {
        glyphCount_ = info.glyphCount;
        glyphs_.resize(glyphCount_);
        positions_.resize(glyphCount_);
    }

    Buffer runBuffer(const RunInfo& info) override {
        return {
            glyphs_.data(),    // glyphs
            positions_.data(), // positions
            nullptr,           // offsets
            nullptr,           // clusters
            origin_            // point   offset to add to all positions
        };
    }

    void commitRunBuffer(const RunInfo& info) override {
        canvas_->drawGlyphs(
            glyphCount_,
            glyphs_.data(),
            positions_.data(),
            runOrigin_,
            font_,
            paint_
        );

    }
    void beginLine() override { }
    void commitRunInfo() override { }
    void commitLine() override { }

private:
    SkCanvas* canvas_;
    SkPoint origin_;
    SkPoint runOrigin_;
    SkFont font_;
    SkPaint paint_;
    int glyphCount_ = 0;
    SkVector runOffset_;
    std::vector<SkGlyphID> glyphs_;
    std::vector<SkPoint> positions_;
};

// テキスト描画のテスト
SkBitmap  TextTest() {

    // フォントマネージャ作成
    sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Directory("C:\\Windows\\Fonts\\"); // フォントが格納されているディレクトリ指定

    //auto typeface = fontMgr->matchFamilyStyle("Segoe UI Emoji", SkFontStyle::Normal()); // カラー絵文字はうまく描画できない
    auto typeface = fontMgr->matchFamilyStyle("MS Gothic", SkFontStyle::Normal()); // フォントの取得
    

    ///////////////////////////////////////////////////////////////////////////////////////////
    SkImageInfo imginfo = SkImageInfo::Make(500, 100, kN32_SkColorType, kPremul_SkAlphaType);
    sk_sp<SkSurface> surface = SkSurfaces::Raster(imginfo);
    SkCanvas* canvas = surface->getCanvas();
    canvas->clear(SK_ColorWHITE); // 背景を白にクリア
    ///////////////////////////////////////////////////////////////////////////////////////////
    // テキストの描画
    SkPaint paint;
    paint.setColor(SK_ColorBLACK); // テキストの色
    paint.setAntiAlias(false);     // アンチエイリアスを有効にする

    ///////////////////////////////////////////////////////////////////////////////////////////
    // フォントの設定
    SkFont font;
    font.setEdging(SkFont::Edging::kAntiAlias); // エッジングの設定
    font.setSize(50.f * 72 / 96);               // テキストのサイズ
    font.setTypeface(typeface);                 // フォントの設定

    ///////////////////////////////////////////////////////////////////////////////////////////
    SkScalar x = 30.f;  // テキストの位置(横)を設定
    SkScalar y = 50.0f; // テキストの位置(縦)を設定

    SkFontMetrics metrics;
    font.getMetrics(&metrics);
    y -= metrics.fAscent;// テキストの位置(高さ)を設定

    ///////////////////////////////////////////////////////////////////////////////////////////
    // カラー絵文字はうまく描画できない
    //SkString text((const char*)u8"😊👩‍👨‍👦‍👧");
    SkString text((const char*)u8"がぎぐげご");
    ///////////////////////////////////////////////////////////////////////////////////////////



#if 1
    // テキストの描画(結合文字対応)
    int width = 500;
    std::unique_ptr<SkShaper> shaper = SkShaper::Make();

    SkPoint origin = SkPoint::Make(x, y);
    CanvasRunHandler runHandler(canvas, origin, font, paint);
    shaper->shape(text.data(), strlen(text.c_str()), font, true, width, &runHandler);
#else
    // テキストの描画
    canvas->drawString(text, x, y, font, paint);
#endif
    //////////////////////////////////////////////////
    // 
    // PNG出力
    SkPixmap pixmap;
    if (surface->peekPixels(&pixmap)) {
        SkFILEWStream stream("output.png");
        SkPngEncoder::Options options;
        options.fZLibLevel = 9;
        SkPngEncoder::Encode(&stream, pixmap, options);
    }

    // Bitmap化
    SkBitmap bitmap;
    bitmap.allocPixels(imginfo);
    surface->readPixels(bitmap.pixmap(), 0, 0);

    return bitmap;
}

カラー絵文字についてもフォントをSegoe UI Emojiなどにすれば一応動くのだが、デバッグモードでは正しく出力できるのにリリースモードでは透明度かなにかがおかしくなる。意味が分からない。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: