検索するとまず出てくるSkFontMgr::RefDefault()、SkTypeface::MakeFromFile()、...、あたりが、全部removedだった。SkFontMgr::RefEmpty()は失敗したときにエラーにならないための空機能フォントマネージャなので使えない。
とりあえずSkFontMgr_New_Custom_Directoryでフォントマネージャを作れることが分かった。
以前vcpkg install skiaでSkiaを導入したが、どうやらこれではフォントや文字列関係が導入できないらしい。そこでvcpkg search skiaを打ってみる。
すると、freetypeなどをサポートしたSkiaのパッケージ一覧が表示される。
関連ありそうなfreetype,harfbuzz,icuのサポート付きのものをインストールする
以下のようにするとstatic link libraryになるのでdllが不要になる。
#include "include/core/SkCanvas.h" #include "include/core/SkBitmap.h" #include "include/core/SkImage.h" // Png保存に必要 #include "include/encode/SkPngEncoder.h" #include "include/core/SkStream.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 "include/utils/SkTextUtils.h" ///////////////////////////////////// void save(const SkBitmap& bitmap, const char* filename);
// テキスト描画のテスト void TextTest() { ////////////////////////////////////////////////// // フォントマネージャ作成 sk_sp<SkFontMgr> fontmgr = SkFontMgr_New_Custom_Directory("C:\\Windows\\Fonts\\"); // フォントが格納されているディレクトリ指定 // フォントの取得 sk_sp<SkTypeface> typeface = fontmgr->makeFromFile("C:\\Windows\\Fonts\\Msgothic.ttc", 0); // フォントの設定 SkFont font; font.setTypeface(typeface); // フォントを設定 font.setEdging(SkFont::Edging::kAntiAlias); font.setSize(50.f * 72 / 96); // テキストのサイズ。setSizeがポイントを受け取るのでピクセルから変換 ////////////////////////////////////////////////// // テキストの設定 SkString text(u8"日本語表示"); // 描画するテキスト SkScalar x = 0.0f; // テキストの位置(横)を設定 SkFontMetrics metrics; font.getMetrics(&metrics); SkScalar y = -metrics.fAscent; // テキストの位置(高さ)を設定 ////////////////////////////////////////////////// // 画像作成 SkBitmap bmp; bmp.allocN32Pixels(400, 300); bmp.eraseColor(SK_ColorGRAY); ////////////////////////////////////////////////// // テキストの描画の設定 SkPaint paint; paint.setColor(SK_ColorBLACK); // テキストの色 paint.setAntiAlias(true); // アンチエイリアスを有効にする SkCanvas* canvas = new SkCanvas(bmp); canvas->drawString(text, x, y, font, paint);// テキストの描画 ////////////////////////////////////////////////// // ファイルに保存 save(bmp, "test.png"); }
// 作成した画像を出力 void save(const SkBitmap& bitmap, const char* filename) { // optionsの設定 SkPngEncoder::Options options; options.fZLibLevel = 9; // 圧縮レベル options.fFilterFlags = SkPngEncoder::FilterFlag::kAll; // フィルタの種類 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream(filename); SkPngEncoder::Encode(&stream, pixmap, options); }
ファイルから画像を読み込む方法。それから矩形で画像を切り出す方法。表示はwxWidgetsに出力している。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #pragma comment(lib,"skia.dll.lib") #include "skia/include/core/SkCanvas.h" #include "skia/include/core/SkBitmap.h" #include "skia/include/core/SkImage.h" // 画像読み込み #include "skia/include/core/SkStream.h" #include "skia/include/codec/SkCodec.h" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> void getPixelRGB(std::vector<std::uint8_t>& rgb,const SkBitmap& bitmap) { if (!bitmap.readyToDraw()) { return; } if (bitmap.colorType() != kRGBA_8888_SkColorType && bitmap.colorType() != kBGRA_8888_SkColorType) { return; // Unsupported format } int Width = bitmap.width(); int Height = bitmap.height(); size_t rowBytes = bitmap.rowBytes(); const auto& info = bitmap.info(); int channels = info.bytesPerPixel(); std::uint8_t* pixels = (std::uint8_t*)bitmap.getPixels(); rgb.resize(Width * Height * 3); for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { size_t pos_ppm = (i * Width + j) * 3; size_t pos_sk = (i * rowBytes + j * channels); rgb[pos_ppm + 0] = pixels[pos_sk + 2]; rgb[pos_ppm + 1] = pixels[pos_sk + 1]; rgb[pos_ppm + 2] = pixels[pos_sk + 0]; } } } //////////////////////////////////////////////////////////////////
//! @brief 画像読み込み関数 //! @param path 画像ファイルのパス //! @param bitmap 読み込んだ画像を格納するビットマップ //! @return 読み込みに成功したらtrue bool LoadImageToBitmap(const std::string& path, SkBitmap* bitmap) { // ファイルからデータを読み込む
sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str()); if (!data) { return false; // データの読み込みに失敗 } // データからSkCodecを作成 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); if (!codec) { return false; // コーデックの作成に失敗 } // ロード先のビットマップの情報を設定 SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); // ロード先のメモリ確保 if (!bitmap->tryAllocPixels(info)) { return false; // メモリ確保に失敗 } // ロード先へ画像をデコード SkCodec::Result ret = codec->getPixels( info, // 画像の情報 bitmap->getPixels(), // ロード先のメモリ bitmap->rowBytes() // ロード先の1行のバイト数 ); if (ret != SkCodec::kSuccess) { return false; // 画像のデコードに失敗 } return true; }
//////////////////////////////////////////////////////////////////
//! @brief 画像の一部を切り出す //! @param dst 切り出した画像を格納するビットマップ //! @param src 元画像 //! @param croprect 切り出す範囲 //! @return なし void CropImage(SkBitmap* dst, const SkBitmap& src, const SkIRect croprect) { sk_sp<SkImage> src_bmp_img = src.asImage();// SkBitmapをSkImageに変換 SkCanvas canv(src); dst->allocN32Pixels(croprect.width(), croprect.height());// 画像のメモリ確保 SkCanvas croppedCanvas(*dst);// 結果のSkBitmapからSkCanvas作成 SkIRect dstRect = SkIRect::MakeWH(croprect.width(), croprect.height());// 結果の書き込み範囲作成 SkSamplingOptions samplingOptions(SkFilterMode::kLinear); croppedCanvas.drawImageRect( src_bmp_img, // 元画像 SkRect::Make(croprect), // 切り取り範囲 SkRect::Make(dstRect), // 描画範囲 samplingOptions, // サンプリングオプション nullptr, SkCanvas::kStrict_SrcRectConstraint // 切り取り範囲を超えた描画を禁止 ); }
// ウィンドウ作成 class MyFrame : public wxFrame { SkBitmap _bmp; public: void PostCreate() {
// 画像をファイルから読み込む LoadImageToBitmap("test.png", &_bmp); Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } void OnPaint(wxPaintEvent& event) { // 切り出したい範囲をSkIRectで定義 SkIRect cropRect = SkIRect::MakeXYWH(300, 300, 400, 200); // 切り出した画像を格納する画像 SkBitmap croppedBitmap; // 切り出し処理 CropImage(&croppedBitmap, _bmp, cropRect); ////////////////////////////////////////// SkBitmap* dispbmp = &croppedBitmap; std::vector<std::uint8_t> rgb; getPixelRGB(rgb,*dispbmp); wxImage img(dispbmp->width(), dispbmp->height(), (unsigned char*)rgb.data(), true); wxBitmap wxbitmap(img); wxPaintDC dc(this); dc.DrawBitmap(wxbitmap, 0, 0, true); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
SkiaのSkBitmapをwxWidgetsのwxBitmapに変換して表示する。
wxWidgetsがRGBなのに対して、SkiaはRGBをサポートしていないので、32bit→24bitへの変換が必要になる。変換関数らしきものを見つけたがどうしてもうまく動かなかったので手動で変換している。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #ifdef _DEBUG #pragma comment(lib,"wxbase32ud.lib") #pragma comment(lib,"wxbase32ud_net.lib") #pragma comment(lib,"wxbase32ud_xml.lib") #pragma comment(lib,"wxmsw32ud_adv.lib") #pragma comment(lib,"wxmsw32ud_aui.lib") #pragma comment(lib,"wxmsw32ud_core.lib") #pragma comment(lib,"wxmsw32ud_gl.lib") #pragma comment(lib,"wxmsw32ud_html.lib") #pragma comment(lib,"wxmsw32ud_media.lib") #pragma comment(lib,"wxmsw32ud_propgrid.lib") #pragma comment(lib,"wxmsw32ud_qa.lib") #pragma comment(lib,"wxmsw32ud_ribbon.lib") #pragma comment(lib,"wxmsw32ud_richtext.lib") #pragma comment(lib,"wxmsw32ud_stc.lib") #pragma comment(lib,"wxmsw32ud_webview.lib") #pragma comment(lib,"wxmsw32ud_xrc.lib") #else #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") #endif #pragma comment(lib,"skia.dll.lib") ///////////////////////////////////// #include "skia/include/core/SkCanvas.h" #include "skia/include/core/SkBitmap.h" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string>
// SkBitmapをRGBの配列に変換
void PixelRGB(std::vector<std::uint8_t>& rgb,const SkBitmap& skbmp) { if (!skbmp.readyToDraw()) { return; } if (skbmp.colorType() != kRGBA_8888_SkColorType && skbmp.colorType() != kBGRA_8888_SkColorType) { return; } rgb.clear(); int Width = skbmp.width(); int Height = skbmp.height(); size_t rowBytes = skbmp.rowBytes(); const SkImageInfo& info = skbmp.info(); int channels = info.bytesPerPixel(); std::uint8_t* pixels = (std::uint8_t*)skbmp.getPixels(); rgb.resize(Width * Height * 3); for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { size_t pos_ppm = (i * Width + j) * 3; size_t pos_sk = (i * rowBytes + j * channels); rgb[pos_ppm + 0] = pixels[pos_sk + 2]; rgb[pos_ppm + 1] = pixels[pos_sk + 1]; rgb[pos_ppm + 2] = pixels[pos_sk + 0]; } } }
// ウィンドウ作成 class MyFrame : public wxFrame { SkBitmap my_bitmap; public: void PostCreate() {
// 画像の作成・描画 my_bitmap.setInfo(SkImageInfo::Make(400, 400, kBGRA_8888_SkColorType, kPremul_SkAlphaType)); my_bitmap.allocPixels(); SkCanvas canvas(my_bitmap); // 背景を白にクリア canvas.clear(SK_ColorWHITE); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorRED); canvas.drawCircle(200, 200, 100, paint);
// OnPaintイベントを設定 Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } // OnPaintイベント void OnPaint(wxPaintEvent& event) {
// SkBitmapをRGBへ変換 int width = my_bitmap.width(); int height = my_bitmap.height(); std::vector<std::uint8_t> rgb; rgb.resize(width * height * 3); // 変換関数(自作)呼び出し PixelRGB(rgb, my_bitmap); // RGBをwxImageへ変換 wxImage img(width,height, (unsigned char*)rgb.data(), true); wxBitmap wxbitmap(img); // wxWidgetsのウィンドウに表示 wxPaintDC dc(this); dc.DrawBitmap(wxbitmap, 0, 0, true);
} }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(550, 550)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
// SkDashPathEffectに必要 #include "skia/include/core/SkPathEffect.h" #include "skia/include/effects/SkDashPathEffect.h"
void draw(SkCanvas& canvas) { // 背景を白にクリア canvas.clear(SK_ColorWHITE); SkPaint paint; paint.setStrokeWidth(5); // 線の太さ paint.setColor(SK_ColorRED); // 線の色 paint.setStyle(SkPaint::kStroke_Style);// 直線を描画 canvas.drawLine(50, 50, 300, 300, paint); // #include "skia/include/effects/SkDashPathEffect.h" const float intervals[] = { 10, 10 }; // 破線で描画 paint.setPathEffect(SkDashPathEffect::Make(intervals,2, 0)); canvas.drawLine(50, 80, 300, 330, paint); }
//SkDiscretePathEffect #include "skia/include/core/SkPath.h" #include "include/effects/SkDiscretePathEffect.h"
void draw(SkCanvas& canvas) { // 背景を白にクリア canvas.clear(SK_ColorWHITE); SkPaint paint; paint.setStrokeWidth(5); // 線の太さ paint.setColor(SK_ColorRED); paint.setStyle(SkPaint::kStroke_Style); // 直線を描画 auto pathEffect = SkDiscretePathEffect::Make(5.0f, 4.0f); // 乱数シード paint.setPathEffect(pathEffect); SkPath path; path.moveTo(50, 50); // 開始点 path.lineTo(300, 300); // 終了点 // パスを描画 canvas.drawPath(path, paint); }
void draw(SkCanvas& canvas) { // 背景を白にクリア canvas.clear(SK_ColorWHITE); SkPaint paint; paint.setColor(SK_ColorRED); // 線の色を設定 paint.setStrokeWidth(40); // 線の幅を設定 paint.setStyle(SkPaint::kStroke_Style); // 描画スタイルをストロークに設定 paint.setStrokeCap(SkPaint::kButt_Cap); // デフォルト canvas.drawLine(50, 50-20, 300, 300-20, paint); paint.setStrokeCap(SkPaint::kRound_Cap); // 両端を丸くする canvas.drawLine(50, 110, 300, 360, paint); }
SkiaでPng保存するには、SkPngEncorder.hとSkStream.hをインクルードし、SkPngEncoder::Optionsで圧縮などの設定、SkFILEWStream でファイルストリーム作成、SkPngEncoder::Encodeで書き込みを行う。
#pragma comment(lib,"skia.dll.lib") #include "skia/include/core/SkCanvas.h" // SkBitmap #include "skia/include/core/SkBitmap.h" // Png保存に必要 #include "skia/include/encode/SkPngEncoder.h" #include "skia/include/core/SkStream.h" #include <cstdio> #include <vector> // 必要なDLL // skia.dll // jpeg62.dll // zlib1.dll #pragma warning(disable:4996) int main() { ////////////////////////////////////////////////////// SkBitmap bitmap; bitmap.setInfo(SkImageInfo::MakeN32Premul(400, 400)); bitmap.allocPixels(); SkCanvas canvas(bitmap); // 背景を白にクリア canvas.clear(SK_ColorWHITE); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorRED); canvas.drawCircle(200, 200, 100, paint); ////////////////////////////////////////////////////// // optionsの設定 SkPngEncoder::Options options; options.fZLibLevel = 9; // 圧縮レベル options.fFilterFlags = SkPngEncoder::FilterFlag::kAll; // フィルタの種類 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream("output.png"); SkPngEncoder::Encode(& stream, pixmap, options); return 0; }
Jpegの場合はSkJpegEncoder.hをインクルードする。
#include "skia/include/encode/SkJpegEncoder.h"
後はほぼ同じ。ただしoptionsの指定内容はJpeg用になっている。
SkJpegEncoder::Options options; options.fQuality = 100; // 画質 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream("output.jpg"); SkJpegEncoder::Encode(&stream, pixmap, options);
Webpの場合はSkWebpEncoder.hをインクルードする。
#include "skia/include/encode/SkWebpEncoder.h"
Webpは非可逆圧縮なのでjpegと同じようにfQualityがある。
SkWebpEncoder::Options options; options.fQuality = 100; // 画質 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream("output.jpg"); SkWebpEncoder::Encode(&stream, pixmap, options);