VTKはvtkRendererをレイヤーとして複数登録できる。描画順が決まっているので、手前のレイヤーのオブジェクトは必ず手前に表示される。
//VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkConeSource.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->SetInteractor(interactor); renderWindow->Render(); ////////////////////////////////////// //////////////////////////////////////
auto renderer_1st = vtkSmartPointer<vtkRenderer>::New(); auto renderer_2nd = vtkSmartPointer<vtkRenderer>::New(); // レイヤー番号の指定 // 番号が若いほど背面に描画される renderer_1st->SetLayer(0); renderer_2nd->SetLayer(1); renderWindow->SetNumberOfLayers(2);// レイヤー数を指定しておいたほうが行儀がいいらしい renderWindow->AddRenderer(renderer_1st); renderWindow->AddRenderer(renderer_2nd); renderer_1st->ResetCamera(); // カメラの共有 renderer_2nd->SetActiveCamera(renderer_1st->GetActiveCamera());
////////////////////////////////////// ////////////////////////////////////// //////////////////////////////////////
// レイヤー1にCylinderを表示 { vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); actor->GetProperty()->SetColor(1.0, 0.0, 0.0); // レイヤー1にアクタを追加 renderer_1st->AddActor(actor); }
//////////////////////////////////////
// レイヤー2にConeを表示 { vtkSmartPointer<vtkConeSource> coneSource = vtkSmartPointer<vtkConeSource>::New(); coneSource->SetCenter(0.0, 0.0, 0.0); coneSource->SetRadius(5.0); coneSource->SetHeight(7.0); coneSource->SetResolution(100); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(coneSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); actor->GetProperty()->SetColor(0.0, 1.0, 0.0); // レイヤー2にアクタを追加 renderer_2nd->AddActor(actor); }
interactor->Start(); return 0; }
SkBitmapは、SetPixels関数を使うと書き込む先のメモリを自分で管理している者に設定することができる。
以下はWin32APIのDIBSectionを指定してSkia経由で描画した例。
// Skiaがminmaxを使用するので、Windowsと一緒に使う場合はNOMINMAXを定義 #define NOMINMAX #include <Windows.h> #include <tchar.h> // skia #include "include/core/SkCanvas.h" #include "include/core/SkBitmap.h"
// DIB Sectionに設定するピクセルの構造体 struct rgbu_t { unsigned char b; unsigned char g; unsigned char r; unsigned char u;// unused rgbu_t(unsigned char R, unsigned char G, unsigned char B) :b(B), g(G), r(R) {} static const rgbu_t White; static const rgbu_t Black; }; // DIBSectionを作成する関数 void createDIBsection32( HBITMAP* hBitmap, HDC* hMemDC, BITMAPINFO* bmpInfo, rgbu_t** m_lpPixel, LONG width, LONG height) { //DIBの情報を設定する bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo->bmiHeader.biWidth = width; bmpInfo->bmiHeader.biHeight = -height; //-を指定しないと上下逆になる bmpInfo->bmiHeader.biPlanes = 1; bmpInfo->bmiHeader.biBitCount = 32; bmpInfo->bmiHeader.biCompression = BI_RGB; HDC hdc = CreateDC(_T("DISPLAY"), 0, 0, 0); *hBitmap = CreateDIBSection(hdc, bmpInfo, DIB_RGB_COLORS, (void**)m_lpPixel, NULL, 0); *hMemDC = CreateCompatibleDC(hdc); DeleteDC(hdc); SelectObject(*hMemDC, *hBitmap); } // DIBSectionを削除する関数 void deleteDIBsection32(HDC hMemDC, HBITMAP hBitmap) { DeleteDC(hMemDC); DeleteObject(hBitmap); } // DIBSectionの情報 HBITMAP dib_hBitmap; HDC dib_hMemDC; BITMAPINFO dib_bmpInfo; rgbu_t* dib_lpPixel;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; RECT rect; static int width; static int height; static SkBitmap skbitmap; switch (msg) { case WM_CREATE: GetClientRect(hwnd, &rect); //DIB Sectionを作成(32Bit) createDIBsection32( &dib_hBitmap, &dib_hMemDC, &dib_bmpInfo, &dib_lpPixel, rect.right, rect.bottom ); width = rect.right; height = rect.bottom; // dib_lpPixel 経由でDIBSectionを赤で塗りつぶす for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { dib_lpPixel[y * width + x] = rgbu_t(0, 0, 255); } }
{ skbitmap.setInfo(SkImageInfo::MakeN32Premul(width, height)); // SkiaのBitmapにDIBSectionの情報を設定 skbitmap.setPixels(dib_lpPixel); SkCanvas canvas(skbitmap); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorRED); canvas.drawCircle(200, 200, 100, paint); }
break; case WM_PAINT: { // 画像を表示 PAINTSTRUCT ps; HDC hDC = BeginPaint(hwnd, &ps); BitBlt(hDC, 0, 0, width, height, dib_hMemDC, 0, 0, SRCCOPY); EndPaint(hwnd, &ps); break; } case WM_DESTROY: deleteDIBsection32(dib_hMemDC, dib_hBitmap); PostQuitMessage(0); return 0; case WM_LBUTTONDOWN: hdc = GetDC(hwnd); ReleaseDC(hwnd, hdc); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL_WINDOW"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL_WINDOW"), TEXT("szl window"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
Bold,Italicで描画する際には、フォントが対応していなければいけない。
SkTypefaceのisBold() , isItalic() で対応を調べることができる。
#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" /////////////////////////////////////
// フォント一覧表示 void disp_fonts(sk_sp<SkFontMgr> fmng) { int ffcount = fmng->countFamilies();
for (int ffi = 0; ffi < ffcount; ffi++) { sk_sp<SkFontStyleSet> typeface = fmng->createStyleSet(ffi);
for (int ftf = 0; ftf < typeface->count(); ftf++) { sk_sp<SkTypeface> face = typeface->createTypeface(ftf); SkString familyName; face->getFamilyName(&familyName); printf("%3d %3d : %-30s %s %s \n", ffi, ftf, familyName.c_str(), face->isBold() ? "Bold" : " ", // Boldに対応している場合に表示 face->isItalic() ? "Italic" : " "// Italicに対応している場合に表示 ); } } }
// テキスト描画のテスト void TextTest() { // フォントマネージャ作成 sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Directory("C:\\Windows\\Fonts\\"); // フォントが格納されているディレクトリ指定 disp_fonts(fontMgr); }
#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" ///////////////////////////////////// void save(const SkBitmap& bitmap, const char* filename);
// 文字列のバウンディングボックスを描画 void RenderBounding(SkScalar x, SkScalar y, SkString text,SkFont* font,SkCanvas* canvas, SkPaint* paint) { // バウンディングボックスを取得 SkRect bounds; font->measureText( text.c_str(), strlen(text.c_str()), SkTextEncoding::kUTF8, &bounds, paint ); // バウンディングボックスをオフセットして描画位置を調整 bounds.offset(x, y); // バウンディングボックスの描画 paint->setStyle(SkPaint::kStroke_Style); canvas->drawRect(bounds, *paint); paint->setStyle(SkPaint::kFill_Style); }
// テキスト描画のテスト void TextTest() { // フォントマネージャ作成 sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Directory("C:\\Windows\\Fonts\\"); // フォントが格納されているディレクトリ指定 // フォント一覧作成 sk_sp<SkTypeface> typeface[4] = { fontMgr->matchFamilyStyle("Amiri", SkFontStyle::Normal() ), fontMgr->matchFamilyStyle("Amiri", SkFontStyle::Italic() ), fontMgr->matchFamilyStyle("Amiri", SkFontStyle::Bold() ), fontMgr->matchFamilyStyle("Amiri", SkFontStyle::BoldItalic() ) }; // 画像作成 SkBitmap bmp; bmp.allocN32Pixels(600, 200); bmp.eraseColor(SK_ColorGRAY); // テキストの描画 SkPaint paint; paint.setColor(SK_ColorBLACK); // テキストの色 paint.setAntiAlias(false); // アンチエイリアスを有効にする SkCanvas* canvas = new SkCanvas(bmp); // フォントの設定 SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); // エッジングの設定 font.setSize(50.f * 72 / 96); // テキストのサイズ。setSizeがポイントを受け取るのでピクセルから変換 SkScalar x = 30.f; // テキストの位置(横)を設定 SkScalar y = 0; // テキストの位置(縦)を設定 SkFontMetrics metrics; for (auto face = std::begin(typeface); face != std::end(typeface); ++face) { font.setTypeface(*face); // フォントの設定 font.getMetrics(&metrics); y -= metrics.fAscent;// テキストの位置(高さ)を設定 SkString text; (*face)->getFamilyName(&text); // フォントファミリー名を取得 RenderBounding(x, y, text, &font, canvas, &paint); // バウンディングボックスの描画 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); }
文字列を囲むボックスを描画する
#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" ///////////////////////////////////// void save(const SkBitmap& bitmap, const char* filename);
// 文字列のバウンディングボックスを描画 void RenderBounding(SkScalar x, SkScalar y, SkString text,SkFont* font,SkCanvas* canvas, SkPaint* paint) { // バウンディングボックスを取得 SkRect bounds; font->measureText( text.c_str(), strlen(text.c_str()), SkTextEncoding::kUTF8, &bounds, paint ); // バウンディングボックスをオフセットして描画位置を調整 bounds.offset(x, y); // バウンディングボックスの描画 paint->setStyle(SkPaint::kStroke_Style);// 輪郭モード canvas->drawRect(bounds, *paint); paint->setStyle(SkPaint::kFill_Style);// 塗りつぶしモード }
// テキスト描画のテスト void TextTest() { std::vector<sk_sp<SkData>> fontDataList; // フォントマネージャ作成 sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Directory("C:\\Windows\\Fonts\\"); // フォントが格納されているディレクトリ指定 auto sk = fontMgr->matchFamily("MS PGothic"); sk_sp<SkTypeface> typeface = sk->createTypeface(0); // フォントの取得 0 は通常。1~はボールド・イタリック等 // 画像作成 SkBitmap bmp; bmp.allocN32Pixels(600, 200); bmp.eraseColor(SK_ColorGRAY); // テキストの描画 SkPaint paint; paint.setColor(SK_ColorBLACK); // テキストの色 paint.setAntiAlias(false); // アンチエイリアスを有効にする SkCanvas* canvas = new SkCanvas(bmp); // フォントの設定 SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); // エッジングの設定 font.setSize(50.f * 72 / 96); // テキストのサイズ。setSizeがポイントを受け取るのでピクセルから変換 font.setTypeface(typeface); // フォントの設定 SkScalar x = 30.f; // テキストの位置(横)を設定 SkScalar y = 10.0f; // テキストの位置(縦)を設定 SkFontMetrics metrics; font.getMetrics(&metrics); y -= metrics.fAscent;// テキストの位置(高さ)を設定 SkString text; typeface->getFamilyName(&text); // フォントファミリー名を取得 text.append(u8" : 日本語"); RenderBounding(x, y, text, &font, canvas, &paint); // バウンディングボックスの描画 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); }
前回はフォントの格納されたディレクトリから一括読み込みをした。
今回はフォントファイルからフォントを読み込む。
まずSkDataとしてフォントファイルを読み込んでから、SkDataの配列として管理し、SkFontMgr_New_Custom_Dataに与えるとフォントとして解釈してくれる。
今回使用したのは一つのフォントファイルだけだが、一つのフォントファイルの中には複数のフォントが入っている場合があるのでループして取り出している。
#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_data.h" // フォントをファイルから読み込むために必要 #include "include/core/SkFontMetrics.h" ///////////////////////////////////// void save(const SkBitmap& bitmap, const char* filename);
// テキスト描画のテスト void TextTest() { std::vector<sk_sp<SkData>> fontDataList; // フォントファイルを読み込み sk_sp<SkData> fontdata = SkData::MakeFromFileName("C:\\test\\msmincho.ttc");
// フォントデータをリストに追加 fontDataList.push_back(fontdata); // フォントデータ一覧からフォントマネージャ作成 auto fontMgr = SkFontMgr_New_Custom_Data(SkSpan<sk_sp<SkData>>(fontDataList.data(), fontDataList.size())); // 画像作成 SkBitmap bmp; bmp.allocN32Pixels(600, 300); bmp.eraseColor(SK_ColorGRAY); // テキストの描画 SkPaint paint; paint.setColor(SK_ColorBLACK); // テキストの色 paint.setAntiAlias(true); // アンチエイリアスを有効にする SkCanvas* canvas = new SkCanvas(bmp); // フォントの設定 SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); // エッジングの設定 font.setSize(50.f * 72 / 96); // テキストのサイズ。setSizeがポイントを受け取るのでピクセルから変換 SkScalar x = 0.0f; // テキストの位置(横)を設定 SkScalar y = 0.0f; // テキストの位置(縦)を設定 SkFontMetrics metrics; int fc = fontMgr->countFamilies(); // フォントマネージャ内のフォントファミリの個数
for (int i = 0; i < fc; i++) { SkString familyName; fontMgr->getFamilyName(i, &familyName);// フォントファミリの名前を取得 printf("FamilyName: %s\n", familyName.c_str());
// フォント名からフォントフェイス取得 sk_sp<SkTypeface> typeface = fontMgr->matchFamilyStyle(familyName.c_str(), SkFontStyle::Normal());
font.setTypeface(typeface); // フォントの設定 font.getMetrics(&metrics); y -= metrics.fAscent;// テキストの位置(高さ)を設定 SkString text(u8"FamilyName "); // 描画するテキスト text += familyName.c_str(); text += u8"日本語"; 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); }
検索するとまず出てくる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);