スレッドを待機させるためのcondition_variableの使い方を確認中。
とりあえず使い方や注意点をまとめたコードを書いてみたがなんか何をやりたいのかよくわからないコードになったのでもう少し考える。
#include <iostream> #include <vector> #include <thread> #include <mutex> #include <condition_variable> #include <random> #include <chrono> std::vector<double> data; std::mutex mtx; std::condition_variable cv; // データが10個以上あればメインスレッドを起こすという条件を定義 // 判断中にdata.size()が変化する可能性があるので、必ずロック中に呼び出すこと bool allow_to_main_wakeup() { return data.size() >= 10; }
// 無限にデータを生成し続ける関数 // ただし、10個データが貯まったらメインスレッドで処理する要求を出す // メインスレッドがデータを移動する間、このスレッドは休止する void Factory() { // 値を生成する関数 int count = 0; auto create_value = [&count]() { return count++; }; // 無限にデータを生成してdataに追加し続ける while (true) { bool allow_to_wakeup = false; { // 仮に、先にメインスレッドでlockした場合、このスレッドはlock(mtx); でブロック中となる // メインスレッド側ですぐにcv.wait()で待機開始するため、すぐにこのブロックは解除されることが期待できる std::unique_lock<std::mutex> lock(mtx); //================================== // データ作成処理本体 data.push_back( create_value() ); //================================== // メインスレッドで処理してよいかを判断。 // data.size()が別スレッドで変化しないために、ロック中に判断する allow_to_wakeup = allow_to_main_wakeup(); // 仮に、先にFactoryでlockした場合、メインスレッドはlock(mtx); でブロック中となっている // ここでスコープを抜けてunlockすることでメインスレッドがlockできるようになり、すぐにcv.wait()で待機開始 } // 現在メインスレッドはcv.wait()で待機中となっている。 // 特定の条件を満たしたら、メインスレッドで処理するためにメインスレッドを起こす処理をする if (allow_to_wakeup == true) { // notify_oneは同じcv.waitにより待機中のスレッドを一つだけ起こす。 cv.notify_one(); // notify_one()はこのスレッドには何もしないのでここはすぐに処理される // これ以降の処理はmainスレッドとの並列処理となる } // あまり速く動いても動作確認がしづらいのでスリープを入れる std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }
int main() { // Factory関数を別スレッドで実行 // 以後、別スレッドでデータが自動生成され続ける std::thread producer(Factory); std::vector<double> tmp; // while (true) { { // ここでロック。ただしFactory関数側とどちらが先にロックするかは不定 std::unique_lock<std::mutex> lock(mtx); // waitすると内部でmtx.unlock()が呼ばれる // これによりFactory側でlock(mtx)ができるようになり、データの生成が可能になる // 同時に、このスレッドはFactory側でnotify_one()が呼ばれるまでここで待機 cv.wait(lock, [] { return allow_to_main_wakeup(); }); // cv.waitのラムダ式はSpurious Wakeup対策。 // 希にwait()中に何もしていないのにスレッドが起こされることがある // そのため、起こされたときに本当に起こされるべきかを関数により確認する // ここから先はcv.notify_one()により起こされてから実行される // これ以降の処理はFactory関数との並列処理となる。 // なおwait()を抜けた時点で、lockは再びlock状態となる //================================== // 別スレッドで作成されたデータを移動 tmp = std::move(data); //================================== } std::cout << "Generated numbers: "; for (double num : tmp) { std::cout << num << " "; } std::cout << std::endl; } producer.join(); return 0; }
以下のようにコンテナを作ったらpython 3.9のインストールに一手間かかったのでメモ。
なおapt update && apt upgrade はしてある。
まずそのまま
とすると
が出てくる。ubuntu:latestで導入すると最小構成で入るのでデフォルトのレポジトリに入っていなかったりするらしい。
update-alternativesは複数バージョンあるコマンドに対して、どのバージョンを使うかを指定する。
python --version
Python 3.9.21
pip --version
pip 25.0.1 from /usr/local/lib/python3.9/dist-packages/pip (python 3.9)
SkSurfaceの使い方がずっとわからなかった。SkSurfaces::Rasterでインスタンスを作成できる。
void Test() { // #include "skia/include/core/SkSurface.h" SkImageInfo imginfo = SkImageInfo::Make(400, 400, kBGRA_8888_SkColorType, kOpaque_SkAlphaType); sk_sp<SkSurface> surface = SkSurfaces::Raster(imginfo); SkCanvas* canvas = surface->getCanvas(); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorBLUE); canvas->clear(SK_ColorWHITE); canvas->drawCircle(200, 200, 100, paint); // PNG出力 SkPixmap pixmap; if (surface->peekPixels(&pixmap)) { SkFILEWStream stream("test_sksurface.png"); SkPngEncoder::Options options; options.fZLibLevel = 9; SkPngEncoder::Encode(&stream, pixmap, options); } }
ユーザー環境変数からPATHを編集しようとしたところ、「この環境変数は大きすぎます。このダイアログで設定できる値の長さは最大2047 文字です。」が発生し、パスを追加することも削除することもできなくなった。
1.PATHの中の記述を、他の変数(例えばPATH_ANACONDA)に分離し、PATHの文字数を2047以下にする。
2.PATHには%変数名%を入れておく
これでPATHの内容を維持しつつ文字数を減らせる。
マウスの右・左・ホイールで回転、移動、スケーリングする。
#pragma once #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/dcgraph.h> #include <wx/dcbuffer.h> #include <wx/geometry.h> #include <cmath> #include <wx/gdicmn.h>
//! @brief 画像を回転させる //! @param transform 回転させる画像のアフィン変換行列 //! @param imageCoordPos 回転の中心となる画像座標系の座標 //! @param rad 回転角度(ラジアン) void RotateAroundPoint(wxAffineMatrix2D& transform, const wxPoint2DDouble& imageCoordPos, const double rad); //! @brief 画像をクライアント座標系で移動させる //! @param transform 移動させる画像のアフィン変換行列 //! @param diff 移動量(パネルのクライアント座標系) void TranslateInScreenSpace(wxAffineMatrix2D& transform, const wxPoint& diff); //! @brief クライアント座標を元画像の座標系に変換 //! @param clientPos クライアント座標 //! @param transform 画像のアフィン変換行列 //! @return 元画像の座標系の座標 wxPoint2DDouble ClientToImage(const wxPoint& clientPos, const wxAffineMatrix2D& transform); //! @brief 画像を拡大・縮小する //! @param transform 拡大・縮小する画像のアフィン変換行列 //! @param scalefactor 拡大・縮小率 //! @param clientpos 拡大・縮小の中心座標(クライアント座標系) //! @details clientposに表示されているピクセルが移動しないように拡大・縮小する //! @return なし void Zoom(wxAffineMatrix2D& transform, double scalefactor, const wxPoint clientpos); //! @brief ウィンドウサイズの変化から、拡大・縮小率を計算する //! @param oldSize リサイズ前のウィンドウサイズ //! @param newSize リサイズ後のウィンドウサイズ //! @return 拡大・縮小率 double CalcResizeScaleFactor(const wxSize& oldSize, const wxSize& newSize); //! @brief マウスドラッグによる拡大・縮小率を計算する //! @param oldpos ドラッグ開始時のマウス座標 //! @param newpos ドラッグ中のマウス座標 //! @return 拡大・縮小率 double CalcDragScaleFactor(const wxAffineMatrix2D& transform, const wxPoint& oldpos, const wxPoint& newpos, const int pixelratio); //! @brief ウィンドウサイズが変化したときに、現在画面中央に表示されているピクセルが、リサイズ後も中央に表示されるようにする //! @param oldSize リサイズ前のウィンドウサイズ //! @param newSize リサイズ後のウィンドウサイズ //! @return なし void ResizeKeepCenter(wxAffineMatrix2D& transform, const wxSize& oldSize, const wxSize& newSize); //! @brief 画像をパネルにフィットさせるためのアフィン変換行列を計算 //! @param transform アフィン変換行列 //! @param imageSize 元画像のサイズ //! @param panelSize パネルのサイズ //! @return なし void FitImage(wxAffineMatrix2D& transform, const wxSize& imageSize, const wxSize& panelSize); //! @brief 3点の座標から回転角を計算する //! @param a 1つ目の座標(始点) //! @param b 2つ目の座標(回転中心) //! @param c 3つ目の座標(終点) //! @details cを中心に、aからbへ回転した時の回転角を計算する //! @return 回転角(ラジアン) double CalculateAngle(const wxPoint& a, const wxPoint& b, const wxPoint& c);
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // マウスによる移動量を角度で計算するクラス class MouseDrag { bool _during; // ドラッグ中かどうか wxPoint _center; // 角度計算の中心座標 wxPoint _from; // 開始地点のマウス座標 wxPoint _to; // 現在のマウス座標 public: MouseDrag(const wxPoint center) : _during(false), _center(center) {} MouseDrag() : _during(false), _center(0, 0) {} //! @brief ドラッグの開始地点 wxPoint GetFrom()const { return _from; } //! @brief ドラッグの現在地点 wxPoint GetTo()const { return _to; } //! @brief ドラッグ開始 void Start(const wxPoint newPos) { _from = newPos; _during = true; } //! @brief ドラッグ終了 void Stop() { _during = false; } //! @brief 現在回転中かどうか bool During() { return _during; } //! @brief マウスの移動量を更新 void Update(const wxPoint& newPos) {_to = newPos;} //! @brief マウスの移動量から角度を計算 double Angle() const{return CalculateAngle(_to, _center, _from);} //! @brief マウスの移動量を取得 wxPoint Difference()const {return _to - _from;} };
class ImagePanel : public wxPanel { enum class DragMode { NONE, MOVE, ROTATE, ZOOM }; wxAffineMatrix2D _transform; //!< アフィン変換行列 wxAffineMatrix2D _transform_back; //!< 移動・回転前のアフィン変換行列のバックアップ wxSize _winsize_back; //!< ウィンドウサイズが変更されるたびに更新されるウィンドウサイズのバックアップ // ウィンドウサイズの変化に関しては、変更開始イベントを拾えないため、変化するたびに前回からの差分に基づいて計算する DragMode _drag_mode; //!< ドラッグモード MouseDrag _mov_dragging; //!< マウスドラッグ間の移動量を計算・管理 std::weak_ptr<wxBitmap> _imageBitmap;//!< 画像データ public: ImagePanel(wxWindow* parent); //! @brief イベントのバインド void EventBind(); private: //! @brief ペイントイベントハンドラ void OnPaint(wxPaintEvent& event); //! @brief イベントハンドラ(移動開始イベントにバインドする用) void OnEventMoveStart(wxMouseEvent& event); //! @brief イベントハンドラ(移動終了イベントにバインドする用) void OnEventMoveStop(wxMouseEvent& event); //! @brief イベントハンドラ(回転開始イベントにバインドする用) void OnEventRotateStart(wxMouseEvent& event); //! @brief イベントハンドラ(回転終了イベントにバインドする用) void OnEventRotateStop(wxMouseEvent& event); //! @brief イベントハンドラ(ドラッグでスケーリング開始イベントにバインドする用) void OnEventScaleStart(wxMouseEvent& event); //! @brief イベントハンドラ(ドラッグでスケーリング終了イベントにバインドする用) void OnEventScaleStop(wxMouseEvent& event); //! @brief マウス移動イベントハンドラ void OnMouseMove(wxMouseEvent& event); //! @brief イベントハンドラ(ホイール回転でスケーリングイベントにバインドする用) void OnEventScaleChange(wxMouseEvent& event); //! @brief ウィンドウサイズ変更イベントハンドラ void OnResize(wxSizeEvent& event); public: //! @brief 表示する画像をセット //! @param bitmap 表示する画像データ //! return なし void SetImage(const std::shared_ptr<wxBitmap>& bitmap); //! @brief 画像が画面に収まるように調節する void FitImageToPanel(); private: //! @brief クライアント領域の中央の座標を取得 //! @return クライアント領域のサイズの1/2 wxPoint GetClientCenter(); //! @brief ドラッグしてズーム処理の開始 //! @param pos ドラッグ開始時のマウス座標 //! @return なし void ZoomStart(const wxPoint& pos); //! @brief ドラッグしてズーム処理の終了 //! @return なし void ZoomStop(); //! @brief ドラッグ中のズーム処理 //! @param pos ドラッグ中のマウス座標 //! @return 画面更新が必要かどうか bool ZoomDuring(const wxPoint& pos); //! @brief ドラッグして移動処理の開始 //! @param pos ドラッグ開始時のマウス座標 //! @return なし void MoveStart(const wxPoint& pos); //! @brief ドラッグして移動処理の終了 //! @return なし void MoveStop(); //! @brief ドラッグして移動中の処理 //! @param pos ドラッグ中のマウス座標 //! @return 画面更新が必要かどうか bool MoveDuring(const wxPoint& pos); //! @brief ドラッグして回転処理の開始 //! @param pos ドラッグ開始時のマウス座標 //! @return なし void RotateStart(const wxPoint& pos); //! @brief ドラッグして回転処理の終了 //! @return なし void RotateStop(); //! @brief ドラッグして回転中の処理 //! @param pos ドラッグ中のマウス座標 //! @return 画面更新が必要かどうか bool RotateDuring(const wxPoint& pos); };
#include "ImagePanel.hpp" ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void RotateAroundPoint(wxAffineMatrix2D& transform, const wxPoint2DDouble& imageCoordPos, const double rad) { // 基準点 (px, py) を原点に移動 transform.Translate(imageCoordPos.m_x, imageCoordPos.m_y); // 回転を適用 (角度はラジアン単位) transform.Rotate(rad); // 元の位置に戻す transform.Translate(-imageCoordPos.m_x, -imageCoordPos.m_y); } void TranslateInScreenSpace(wxAffineMatrix2D& transform, const wxPoint& diff) { // アフィン変換行列の現在の回転・スケーリングを取得 wxMatrix2D mat2D; wxPoint2DDouble translation; transform.Get(&mat2D, &translation); // 回転を取り除くために逆回転行列を作成 double rad = std::atan2(mat2D.m_12, mat2D.m_11); // 現在の回転角を計算(ラジアン) wxAffineMatrix2D inverseRotation; inverseRotation.Rotate(-rad); // スケーリング値を計算 double scaleX = std::sqrt(mat2D.m_11 * mat2D.m_11 + mat2D.m_21 * mat2D.m_21); double scaleY = std::sqrt(mat2D.m_12 * mat2D.m_12 + mat2D.m_22 * mat2D.m_22); // 移動量 (dx, dy) を回転の影響を受けない座標系に変換 wxPoint2DDouble adjustedMove = inverseRotation.TransformPoint(wxPoint2DDouble(diff.x, diff.y)); // 変換行列に移動を適用 transform.Translate(adjustedMove.m_x / scaleX, adjustedMove.m_y / scaleX); } wxPoint2DDouble ClientToImage(const wxPoint& clientPos, const wxAffineMatrix2D& transform) { wxAffineMatrix2D inverse = transform; inverse.Invert(); wxPoint2DDouble imagePos = inverse.TransformPoint(wxPoint2DDouble(clientPos)); return imagePos; } void Zoom(wxAffineMatrix2D& transform, double scalefactor, const wxPoint clientpos) { // クライアント座標を元画像の座標系に変換 wxPoint2DDouble imagePos = ClientToImage(clientpos, transform); // スケーリングの中心をマウス座標に固定 transform.Translate(imagePos.m_x, imagePos.m_y); transform.Scale(scalefactor, scalefactor); transform.Translate(-imagePos.m_x, -imagePos.m_y); } double CalcResizeScaleFactor(const wxSize& oldSize, const wxSize& newSize) { double diffx = newSize.GetWidth() - oldSize.GetWidth(); double diffy = newSize.GetHeight() - oldSize.GetHeight(); double scalex = newSize.GetWidth() / (double)oldSize.GetWidth(); double scaley = newSize.GetHeight() / (double)oldSize.GetHeight(); double scalefactor = 1.0; if ((int)diffy != 0) scalefactor = scaley; return scalefactor; } double CalcDragScaleFactor(const wxAffineMatrix2D& transform, const wxPoint& oldpos, const wxPoint& newpos, const int pixelratio) { // 原点からの距離を計算 double olddist = std::sqrt(oldpos.x * oldpos.x + oldpos.y * oldpos.y); double newdist = std::sqrt(newpos.x * newpos.x + newpos.y * newpos.y); double scalefactor = 1.0; if (olddist < newdist) { // 拡大 scalefactor = 1.0 + (newdist - olddist) / pixelratio; } else { // 縮小 scalefactor = 1.0 - (olddist - newdist) / pixelratio; } return scalefactor; } void ResizeKeepCenter(wxAffineMatrix2D& transform, const wxSize& oldSize, const wxSize& newSize) { wxPoint oldwndCenter = wxPoint(oldSize.GetWidth() / 2, oldSize.GetHeight() / 2); wxPoint newwndCenter = wxPoint(newSize.GetWidth() / 2, newSize.GetHeight() / 2); // リサイズ前の画面中央に表示されていた画像の座標を取得 wxPoint2DDouble oldImageCenter = ClientToImage(oldwndCenter, transform); // 画面サイズの変化に応じた新しい中央座標を取得 wxPoint2DDouble newImageCenter = ClientToImage(newwndCenter, transform); // 変位を計算し、元の画像位置を新しい中央に移動 wxPoint2DDouble shift = oldImageCenter - newImageCenter; transform.Translate(-shift.m_x, -shift.m_y); } void FitImage(wxAffineMatrix2D& transform, const wxSize& imageSize, const wxSize& panelSize) { double scaleX = static_cast<double>(panelSize.GetWidth()) / imageSize.GetWidth(); double scaleY = static_cast<double>(panelSize.GetHeight()) / imageSize.GetHeight(); double scale = std::min(scaleX, scaleY); // 中心を計算 wxPoint offset( (panelSize.GetWidth() - imageSize.GetWidth() * scale) / 2, (panelSize.GetHeight() - imageSize.GetHeight() * scale) / 2); // アフィン変換を設定 transform = wxAffineMatrix2D(); transform.Translate(offset.x, offset.y); transform.Scale(scale, scale); } double CalculateAngle(const wxPoint& a, const wxPoint& b, const wxPoint& c) { wxRealPoint ab(b.x - a.x, b.y - a.y); wxRealPoint cb(b.x - c.x, b.y - c.y); // 内積を計算 double dotProduct = ab.x * cb.x + ab.y * cb.y; // ベクトルの大きさを計算 double magnitudeAB = std::sqrt(ab.x * ab.x + ab.y * ab.y); double magnitudeCB = std::sqrt(cb.x * cb.x + cb.y * cb.y); // コサイン double cosTheta = dotProduct / (magnitudeAB * magnitudeCB); cosTheta = std::min(1.0, std::max(-1.0, cosTheta)); // 角度を計算(ラジアン) double angleRad = std::acos(cosTheta); // 外積を使って角度の符号を決定 double cross = (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x); if (cross < 0.0) angleRad = -angleRad; return angleRad;// angleRad * 180.0 / M_PI; }
////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ImagePanel::ImagePanel(wxWindow* parent) : wxPanel(parent) { SetBackgroundStyle(wxBG_STYLE_PAINT); SetBackgroundColour(wxColour(0, 0, 0)); _transform = wxAffineMatrix2D(); _drag_mode = DragMode::NONE; EventBind(); } void ImagePanel::OnPaint(wxPaintEvent& event) { wxAutoBufferedPaintDC dc(this); dc.Clear(); std::shared_ptr<wxBitmap> pbitmap = _imageBitmap.lock(); if (!pbitmap) return; wxGraphicsContext* gc = wxGraphicsContext::Create(dc); if (gc) { wxGraphicsMatrix matrix = gc->CreateMatrix(_transform); gc->SetTransform(matrix); gc->DrawBitmap(*pbitmap, 0, 0, pbitmap->GetWidth(), pbitmap->GetHeight()); delete gc; } } void ImagePanel::OnEventMoveStart(wxMouseEvent& event) { MoveStart(event.GetPosition()); CaptureMouse(); } void ImagePanel::OnEventMoveStop(wxMouseEvent& event) { MoveStop(); if (HasCapture()) ReleaseMouse(); } void ImagePanel::OnEventRotateStart(wxMouseEvent& event) { RotateStart(event.GetPosition()); CaptureMouse(); } void ImagePanel::OnEventRotateStop(wxMouseEvent& event) { RotateStop(); if (HasCapture()) ReleaseMouse(); } void ImagePanel::OnEventScaleStart(wxMouseEvent& event) { ZoomStart(event.GetPosition()); CaptureMouse(); } void ImagePanel::OnEventScaleStop(wxMouseEvent& event) { ZoomStop(); if (HasCapture()) ReleaseMouse(); } void ImagePanel::OnMouseMove(wxMouseEvent& event) { if (_mov_dragging.During()) { switch (_drag_mode) { case DragMode::MOVE: if (MoveDuring(event.GetPosition())) { Refresh(); } break; case DragMode::ROTATE: if (RotateDuring(event.GetPosition())) { Refresh(); } break; case DragMode::ZOOM: if (ZoomDuring(event.GetPosition())) { Refresh(); } break; } } } void ImagePanel::OnEventScaleChange(wxMouseEvent& event) { int wheelRotation = event.GetWheelRotation(); // ホイールの回転量によって拡大・縮小率を変更 double scaleFactor = (wheelRotation > 0) ? 1.1 : 0.9; // マウスのクライアント座標を取得 wxPoint clientPos = event.GetPosition(); Zoom(_transform, scaleFactor, clientPos); Refresh(); } void ImagePanel::OnResize(wxSizeEvent& event) { if (GetClientSize().GetWidth() == 0 || GetClientSize().GetHeight() == 0) return; if (_imageBitmap.lock()) { wxSize oldSize = _winsize_back; wxSize newSize = GetClientSize(); ResizeKeepCenter(_transform, oldSize, newSize); double scalefactor = CalcResizeScaleFactor(oldSize, newSize); if ((scalefactor > 0.001) && !isnan(scalefactor) && !isinf(scalefactor)) Zoom(_transform, scalefactor, GetClientCenter()); } // 旧ウィンドウサイズを記録 _winsize_back = GetClientSize(); Refresh(); } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// void ImagePanel::EventBind() { Bind(wxEVT_PAINT, &ImagePanel::OnPaint, this); // 移動に割り当て Bind(wxEVT_LEFT_DOWN, &ImagePanel::OnEventMoveStart, this); Bind(wxEVT_LEFT_UP, &ImagePanel::OnEventMoveStop, this); // 回転に割り当て Bind(wxEVT_RIGHT_DOWN, &ImagePanel::OnEventRotateStart, this); Bind(wxEVT_RIGHT_UP, &ImagePanel::OnEventRotateStop, this); // ホイールドラッグによる拡大・縮小 Bind(wxEVT_MIDDLE_DOWN, &ImagePanel::OnEventScaleStart, this); Bind(wxEVT_MIDDLE_UP, &ImagePanel::OnEventScaleStop, this); /* // 拡大・縮小に割り当て Bind(wxEVT_MOUSEWHEEL, &ImagePanel::OnEventScaleChange, this); */ Bind(wxEVT_MOTION, &ImagePanel::OnMouseMove, this); Bind(wxEVT_SIZE, &ImagePanel::OnResize, this); } void ImagePanel::SetImage(const std::shared_ptr<wxBitmap>& bitmap) { _imageBitmap = bitmap; Refresh(); } void ImagePanel::FitImageToPanel() { std::shared_ptr<wxBitmap> pbitmap = _imageBitmap.lock(); if (!pbitmap) return; // スケール計算 wxSize panelSize = GetClientSize(); wxSize imageSize = pbitmap->GetSize(); FitImage(_transform, imageSize, panelSize); _winsize_back = GetClientSize(); Refresh(); } wxPoint ImagePanel::GetClientCenter() { wxSize panelSize = GetClientSize(); return wxPoint(panelSize.GetWidth() / 2, panelSize.GetHeight() / 2); } void ImagePanel::ZoomStart(const wxPoint& pos) { _drag_mode = DragMode::ZOOM; _transform_back = _transform; _mov_dragging.Start(pos); } void ImagePanel::ZoomStop() { _mov_dragging.Stop(); } bool ImagePanel::ZoomDuring(const wxPoint& pos) { _mov_dragging.Update(pos); wxPoint oldpos = _mov_dragging.GetFrom(); wxPoint newpos = _mov_dragging.GetTo(); wxPoint diff = _mov_dragging.Difference(); if (diff.x == 0 && diff.y == 0) { return false; } wxPoint winoldpos = ClientToScreen(oldpos); wxPoint winnewpos = ClientToScreen(newpos); double scalefactor = CalcDragScaleFactor(_transform, winoldpos, winnewpos, GetClientSize().GetWidth()); if (!isnan(scalefactor) && !isinf(scalefactor)) { _transform = _transform_back; Zoom(_transform, scalefactor, oldpos); } else { return false; } return true; } void ImagePanel::MoveStart(const wxPoint& pos) { _drag_mode = DragMode::MOVE; _transform_back = _transform; _mov_dragging.Start(pos); } void ImagePanel::MoveStop() { _mov_dragging.Stop(); } bool ImagePanel::MoveDuring(const wxPoint& pos) { _mov_dragging.Update(pos); wxPoint diff = _mov_dragging.Difference(); if (diff.x == 0 && diff.y == 0) return false; _transform = _transform_back; // 移動量を画面座標系で適用 TranslateInScreenSpace(_transform, diff); return true; } void ImagePanel::RotateStart(const wxPoint& pos) { _drag_mode = DragMode::ROTATE; _transform_back = _transform; // クライアント領域の中心座標を取得 wxSize panelSize = GetClientSize(); _mov_dragging = MouseDrag(GetClientCenter()); _mov_dragging.Start(pos); } void ImagePanel::RotateStop() { _mov_dragging.Stop(); } bool ImagePanel::RotateDuring(const wxPoint& pos) { _mov_dragging.Update(pos); double rad = _mov_dragging.Angle(); if (isnan(rad) || isinf(rad)) return false; _transform = _transform_back; auto imgpos = ClientToImage(GetClientCenter(), _transform); RotateAroundPoint(_transform, imgpos, rad); return true; }
// 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に必要 ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> #include "ImagePanel.hpp" // ウィンドウ作成 class MyFrame : public wxFrame { ImagePanel* _imagePanel;// 画像表示パネル std::shared_ptr<wxBitmap> _bitmap; public: void PostCreate() { // wxImage::AddHandler(new wxPNGHandler); _bitmap = std::make_shared<wxBitmap>(); wxImage image; if (image.LoadFile(R"(C:\temp\test.png)", wxBITMAP_TYPE_PNG)) {// 画像読み込み *_bitmap = wxBitmap(image); } this->Layout(); // レイアウトの更新 _imagePanel->SetImage(_bitmap);// パネルに画像を指定 _imagePanel->FitImageToPanel(); } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); // ImagePanel作成 _imagePanel = new ImagePanel(this); // 画面いっぱいに貼り付ける wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(_imagePanel, 1, wxEXPAND); this->SetSizer(sizer); } 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);
libzipでファイル一覧を取得すると、元のディレクトリ構造が取得できるが、ツリーになっていない。これをツリーに変換する。
#include <iostream> #include <vector> #include <string> #include <sstream> #include <filesystem> // SetConsoleOutputCP のために必要 #include <windows.h> // libzip #include <zip.h> #pragma comment(lib,"zip.lib")
enum class FileType { Ignore, Directory, File };
struct DirTree { std::string name; FileType type; std::vector<DirTree> children; DirTree(const std::string& name, const FileType type) : name(name), type(type){} };
// 既に登録済みかどうかをチェックし、既にあるノードならイテレータを返す std::vector<DirTree>::iterator getNodeIfAlreadyRegistered(DirTree& root, const std::string& name) { return std::find_if( root.children.begin(), root.children.end(), [&](const DirTree& child) { return child.name == name; } ); }
void AddPath(DirTree& root, const std::string& path) { namespace fs = std::filesystem; std::istringstream ss(path); std::string segment; DirTree* current = &root; // 次の'/'までのパスを取得 while (std::getline(ss, segment, '/')) { // 過去にツリーに登録されたノードかどうかをチェック auto currentnode = getNodeIfAlreadyRegistered(*current, segment); // すでに登録済みなら次回以降のノードはそのノードより下に追加 if (currentnode != current->children.end()) { current = &(*currentnode); } else { // 最後の要素以外はディレクトリであることは自明なので、ディレクトリとして登録 current->children.push_back({ segment, FileType::Directory }); current = ¤t->children.back(); } } // 最後の要素はファイルの可能性があるので、pathに'/'があるかどうかで判別 bool isDir = path.back() == '/'; current->type = isDir ? FileType::Directory : FileType::File; }
// ファイルリストをツリー構造に変換 DirTree BuildDirTree(const std::vector<std::string>& files) { DirTree root( "root", FileType::Ignore); for (const auto& file : files) { AddPath(root, file); } return root; }
// ファイルツリーを表示 void printTree(const DirTree& tree, int depth = 0) { auto to_string = [](const FileType type) { switch (type) { case FileType::Ignore: return "*"; case FileType::Directory: return "[DIR]"; case FileType::File: return "[FILE]"; } return "Unknown"; }; std::string indent(depth * 2, ' '); std::cout << indent << to_string(tree.type) << tree.name << "\n"; for (const auto& child : tree.children) { printTree(child, depth + 1); } }
std::vector<std::string> FilesInZip(const char* pathname) { std::vector<std::string> files; // エラーコードを格納する変数 int error = 0; // zipファイルを読み取り専用でオープン zip_t* zipArchive = zip_open(pathname, ZIP_RDONLY, &error); if (zipArchive == nullptr) { return std::vector<std::string>(); } // zipファイル内のエントリ数を取得 zip_int64_t numEntries = zip_get_num_entries(zipArchive, 0); // 各エントリの名前を取得して表示 for (zip_uint64_t i = 0; i < static_cast<zip_uint64_t>(numEntries); ++i) { const char* entryName = zip_get_name(zipArchive, i, 0); files.emplace_back(entryName); // zipファイル内のファイル名を格納 } // zipファイルをクローズ zip_close(zipArchive); return files; }
int main() { std::vector<std::string> files = FilesInZip((const char*)u8R"(C:\test\ConsoleApplication31.zip)"); // windowsのコンソールでUTF-8を表示するために必要 SetConsoleOutputCP(CP_UTF8); // ファイルのリストをツリー構造に変換 DirTree root = BuildDirTree(files); printTree(root); for(auto& f : files) { std::cout << f << std::endl; } }
まずfilesをそのまま表示すると以下のようになる。
これをツリーにして、printTreeで出力すると以下になる。
*root [FILE]ConsoleApplication31.sln [DIR].vs [DIR]ConsoleApplication31 [DIR]copilot-chat [DIR]ff91ad84 [DIR]sessions [DIR]FileContentIndex [FILE]b5c23407-6b8a-446b-82cf-97b7abb15edd.vsidx [DIR]v17 [FILE].suo [FILE]Browse.VC.db [FILE]DocumentLayout.json
以下のように、-filter_complexのdrawtextで以下のように指定。Windowsの場合、フォントの指定でドライブ指定を「C\\:」としなければならない。
ffmpeg
-i sequential_images/img_%04d.png -filter_complex "drawtext=text='Title':fontfile=C\\:/Windows/Fonts/arial.ttf:fontsize=36:fontcolor=white:x=(w-text_w)/2:y=10" -r 30
output.mp4
以下のスクリプトでテスト用の連番画像を作成する。
import numpy as np import cv2 import os # 保存ディレクトリを作成 output_dir = "sequential_images" os.makedirs(output_dir, exist_ok=True) # 画像サイズ width, height = 256, 256 # 画像を生成 for i in range(256): # 背景色の設定 (赤から青へ変化) color = (255 - i, 0, i) # (B, G, R) # 画像を作成 img = np.full((height, width, 3), color, dtype=np.uint8) # ファイル名を作成 filename = os.path.join(output_dir, f"img_{i:04d}.png") # 画像を保存 cv2.imwrite(filename, img)
なおなんだか知らないがwordpressに動画をアップロードするときは -pix_fmt yuv420p -vcodec libx264 のオプションもつけてやらないといけなかった。
std::filesystemを使えばディレクトリ構造を辿ることができるが、これを自作した構造体に格納する。
#include <iostream> #include <vector> #include <string> #include <filesystem> // SetConsoleOutputCP のために必要 #include <windows.h>
enum class FileType { Ignore, Directory, File };
struct DirTree { std::string name; FileType type; std::vector<DirTree> children; DirTree(const std::string& name, const FileType type) : name(name), type(type){} };
// ディレクトリツリーを構築する関数 DirTree buildDirTree(const std::filesystem::path& path) { std::string name = path.filename().string(); FileType type = std::filesystem::is_directory(path) ? FileType::Directory : FileType::File; DirTree node(name, type); if (type == FileType::Directory) { for (const auto& entry : std::filesystem::directory_iterator(path)) { node.children.push_back(buildDirTree(entry.path())); } } return node; }
// ファイルツリーを表示 void printTree(const DirTree& tree, int depth = 0) { auto to_string = [](const FileType type) { switch (type) { case FileType::Ignore: return "*"; case FileType::Directory: return "[DIR]"; case FileType::File: return "[FILE]"; } return "Unknown"; }; std::string indent(depth * 2, ' '); std::cout << indent << to_string(tree.type) << tree.name << "\n"; for (const auto& child : tree.children) { printTree(child, depth + 1); } }
int main() { // windowsのコンソールでUTF-8を表示するために必要 SetConsoleOutputCP(CP_UTF8); std::string path = "C:/tmp/ConsoleApplication2/"; DirTree root = buildDirTree(path); // ファイルのリストをツリー構造に変換 printTree(root); }
以下のコードを実行して、例外が発生。
std::filesystem::directory_iterator dir_itr = std::filesystem::directory_iterator(path);
directory_iterator::directory_iterator: アクセスが拒否されました。: "C:\Program Files (x86)\Google\CrashReports"
例外はキャッチすればいいが再現できるようにしておきたい。
1.コマンドプロンプトから、以下のコマンドでディレクトリのアクセス制限を設定する。
icacls C:\test\myaccess\accesstest_directory1 /deny Users:(OI)(CI)F
2.以下を実行
#include <iostream> #include <string> #include <filesystem> int main() { std::string pathstr = R"(C:\test\myaccess\accesstest_directory1)"; std::filesystem::path path(pathstr); try { std::filesystem::directory_iterator dir_itr = std::filesystem::directory_iterator(path); } catch (const std::filesystem::filesystem_error& e) { std::cout << e.what() << std::endl; return 1; } }
3.アクセス制限を戻す
icacls C:\test\myaccess\accesstest_directory1 /remove:d Users
これは文字列の変換で発生するのでstd::exceptionでキャッチする。
1.「你好」などCP932で表示できない文字列のフォルダを作成。
2.以下を実行
#include <iostream> #include <string> #include <filesystem> int main() { std::string pathstr = R"(C:\test\myaccess\)"; std::filesystem::path path(pathstr); std::filesystem::directory_iterator dir_itr; // ディレクトリイテレータを取得 try { dir_itr = std::filesystem::directory_iterator(path); } catch (const std::filesystem::filesystem_error& e) { std::cout << e.what() << std::endl; return 1; } //////////////////////////////////////////////////////////
std::string fname; for (const auto& entry : dir_itr) { try { fname = entry.path().filename().string(); std::cout << fname << std::endl; } catch (const std::exception& e) { // Unicode 文字のマッピングがターゲットのマルチバイト コード ページにありません。 std::cout << e.what() << std::endl; } catch (const std::filesystem::filesystem_error& e) { std::cout << e.what() << std::endl; } }
}
libzipでzipファイルから画像データを取り出し、skiaでデコードし、wxWidgetsで表示する。
左右カーソルで画像切り替えをする。
zipから画像を取り出す関数群。
#pragma once #include <include/core/SkBitmap.h> #include <zip.h> #include <memory> #include <string> #include <vector> struct ImageData { std::unique_ptr<SkBitmap> bitmap; std::string filename; }; std::vector<ImageData> FilesInZip(const char* pathname); std::vector<std::uint8_t> GetZipData(zip_t* zipArchive, zip_int64_t index); std::unique_ptr<SkBitmap> ImageFromBinary(const std::vector<std::uint8_t>& contents);
#include <include/core/SkStream.h> #include <include/codec/SkCodec.h> #include <iostream> #include <zip.h> #include <vector> #include "LoadImageZip.hpp"
// zipファイルから画像データを取り出す std::vector<ImageData> FilesInZip(const char* pathname) { std::vector<ImageData> files; int error = 0; // zipファイルを読み取り専用で開く zip_t* zipArchive = zip_open(pathname, ZIP_RDONLY, &error); if (zipArchive == nullptr) { return std::vector<ImageData>(); } // zipファイル内のエントリ数を取得 zip_int64_t item_count = zip_get_num_entries(zipArchive, 0); // 各エントリの名前を取得して表示 for (zip_uint64_t i = 0; i < static_cast<zip_uint64_t>(item_count); ++i) { // ファイル名を取得 const char* file_name = zip_get_name(zipArchive, i, 0); // Zipファイル内のデータを取得 std::vector<std::uint8_t> binary = GetZipData(zipArchive, i); // バイナリデータから画像を作成 std::unique_ptr<SkBitmap> bitmap = ImageFromBinary(binary); // ファイル名と画像を一覧に追加 files.emplace_back(ImageData{ std::move(bitmap),file_name }); } // zipファイルを閉じる zip_close(zipArchive); return files; }
// zipファイルからバイナリデータを取得 std::vector<std::uint8_t> GetZipData(zip_t* zipArchive, zip_int64_t index) { struct zip_stat sb; zip_stat_index(zipArchive, index, 0, &sb); // 2番目のファイルのファイルサイズと同じメモリを確保する std::vector<std::uint8_t> contents(sb.size); // ファイルの内容をメモリに読み込む zip_file* zf = zip_fopen(zipArchive, sb.name, 0); zip_fread(zf, contents.data(), sb.size); zip_fclose(zf); return contents; }
// バイナリデータをSkiaの画像に変換 std::unique_ptr<SkBitmap> ImageFromBinary(const std::vector<std::uint8_t>& contents) { sk_sp<SkData> skdata = SkData::MakeWithoutCopy(contents.data(), contents.size()); if (!skdata) { std::cerr << "SkData作成に失敗しました" << std::endl; return nullptr; } // データからSkCodecを作成 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(skdata)); if (!codec) { return nullptr; } SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>(); if (!bitmap->tryAllocPixels(info)) { return nullptr; // メモリ確保に失敗 } SkCodec::Result ret = codec->getPixels( info, // 画像の情報 bitmap->getPixels(), // ロード先のメモリ bitmap->rowBytes() // ロード先の1行のバイト数 ); return bitmap; }
// 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 #if defined(_DEBUG) #pragma comment(lib, "skia.dll.lib") #pragma comment(lib, "skshaper.dll.lib") #else #pragma comment(lib, "skia.dll.lib") #endif #pragma comment(lib, "zip.lib") ///////////////////////////////////// #include <include/core/SkCanvas.h> #include <include/core/SkBitmap.h> #include <include/core/SkSurface.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include "LoadImageZip.hpp" #include <string> // SkBitmapからRGBデータを取得 void PixelRGB(std::vector<std::uint8_t>& rgb, const SkBitmap& skbmp); // ウィンドウ作成 class MyFrame : public wxFrame { std::vector<ImageData> files; std::vector<wxBitmap> wxbitmaps; size_t index = 0; public: void ToBitmaps() { for (auto& file : files) { const SkBitmap& my_bitmap = *file.bitmap; 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); wxImage img(width, height, (unsigned char*)rgb.data(), true); wxBitmap wxbitmap(img); wxbitmaps.push_back(wxbitmap); } } void PostCreate() { files = FilesInZip("C:/temp/images.zip"); ToBitmaps(); index = 0; // OnPaintイベントを設定 Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); // キーボードイベント Bind(wxEVT_KEY_DOWN, &MyFrame::OnKeyDown, this); this->Layout(); // レイアウトの更新 } // キーボードイベント void OnKeyDown(wxKeyEvent& event) { if (event.GetKeyCode() == WXK_RIGHT) { index++; if (index >= files.size()) { index = 0; } Refresh(); } else if (event.GetKeyCode() == WXK_LEFT) { if (index == 0) { index = files.size() - 1; } else { index--; } Refresh(); } } 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) { if (wxbitmaps.empty()) { return; } const wxBitmap& wxbitmap = wxbitmaps[index]; // 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); 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]; } } }