質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.41%
Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

416閲覧

Gdiplusで、画像を半透明→透明にする描画が遅い

valval

総合スコア43

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2023/10/19 21:44

編集2023/10/19 22:00

実現したいこと

画面いっぱいに、二枚、画像を重ねて描画し、
フロントの画像の透明度を時間経過で上げて、二枚目の
画像を表示させて行きます。

前提

上記の処理が想像以上に遅く、1秒で非透明から半透明→透明に
かわるはずなのに、1分ぐらいかかっています。
どうすれば速くなりますか?

該当のソースコード

C++

1 2int TimeCounter;//カウンター用 3HBITMAP BitmapBack;//バックバッファ用 4HDC BitmapDC; 5 6HBITMAP hbmpOld; 7 8int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 9 _In_opt_ HINSTANCE hPrevInstance, 10 _In_ LPWSTR lpCmdLine, 11 _In_ int nCmdShow) 12{ 13 14 UNREFERENCED_PARAMETER(hPrevInstance); 15 UNREFERENCED_PARAMETER(lpCmdLine); 16 17 // TODO: ここにコードを挿入してください。 18 ret=CoInitialize(NULL); 19 20 if (FAILED(ret)) { 21 return FALSE; 22 } 23 24 25 26 GdiplusStartupInput gdiplusStartupInput; 27 ULONG_PTR gdiplusToken; 28 29 if (Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Gdiplus::Ok) {//Gdiplus開始 30 return false; 31 } 32 Stanby = false; 33 34 // グローバル文字列を初期化する 35 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 36 LoadStringW(hInstance, IDC_NOTCROSS, szWindowClass, MAX_LOADSTRING); 37 MyRegisterClass(hInstance); 38 39 40 TimeCounter = 0;//タイマーのリセット 41 SetTimer(hWnd, 1, 1, NULL);//タイマースタート 42 BitmapBack=NULL;//バックバッファ作成前 43 44 // アプリケーション初期化の実行: 45 if (!InitInstance (hInstance, nCmdShow)) 46 { 47 return FALSE; 48 } 49 50 HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_NOTCROSS)); 51 52 MSG msg; 53 54 // メイン メッセージ ループ: 55 while (GetMessage(&msg, nullptr, 0, 0)) 56 { 57 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 58 { 59 TranslateMessage(&msg); 60 DispatchMessage(&msg); 61 } 62 } 63 64 Gdiplus::GdiplusShutdown(gdiplusToken); 65 SelectObject(BitmapDC, hbmpOld); 66 67 return (int) msg.wParam; 68} 69 70LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 71{ 72 bool Destroy = false; 73 74 switch (message) 75 { 76 case WM_CREATE: { }break; 77 case WM_TIMER: 78 { 79 TimeCounter++;//時間経過 80 InvalidateRect(hWnd, NULL, TRUE);//再描画 81 } 82 break; 83 case WM_ERASEBKGND://背景の描画を処理したことにする。 84 return TRUE; 85 case WM_PAINT: 86 { 87 PAINTSTRUCT ps; 88 HDC hdc = BeginPaint(hWnd, &ps); 89 90 // TODO: HDC を使用する描画コードをここに追加してください... 91 if (BitmapBack == NULL) {//バックバッファ作成前 92 BitmapBack = CreateCompatibleBitmap(hdc, 1920, 1080);//バックバッファ作成 93 BitmapDC = CreateCompatibleDC(hdc); 94hbmpOld=(HBITMAP)SelectObject(BitmapDC, BitmapBack); 95ReleaseDC(hWnd, hdc); 96} 97 98 if (BitmapBack != NULL)//バックバッファ作成済み 99{ 100 Gdiplus::Graphics* g = new Gdiplus::Graphics(BitmapDC); 101 //バックバッファに、透明にならない裏側の絵を描画 102 g->DrawImage(BackPicImage, 0, 0, 1920, 1080); 103 104 //半透明処理 105 ImageAttributes attr; 106 ColorMatrix cmat = { 107 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Red 108 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // Green 109 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // Blue 110 0.0f, 0.0f, 0.0f, (1.0f - (0.001f * TimeCounter)), 0.0f, // Alpha (70%) 111 0.0f, 0.0f, 0.0f, 0.0f, 1.0f // must be 1 112 }; 113 attr.SetColorMatrix(&cmat); 114 115 //バックバッファに、半透明なフロントの描画 116 g->DrawImage(FrontPicImage, Gdiplus::Rect(0, 0, 1920, 1080), 0, 0, 1920, 1080, UnitPixel, &attr, NULL, NULL); 117 118 //バックバッファを本描画 119 BitBlt(hdc, 0, 0, 1920, 1080, BitmapDC, 0, 0, SRCCOPY); 120 121 122 delete g; 123 124} 125 EndPaint(hWnd, &ps); 126 break; 127} 128 case WM_KEYDOWN: 129{ 130 switch (wParam) 131 { 132 case VK_ESCAPE://ESCキーでアプリ終了 133 PostQuitMessage(0); 134 } 135} 136 137} 138} 139 140

試したこと

CPUの交換を考えていますが、根本解決にはならないとも思っています。
Gdiplusのプリミティブ図形描画や、今後png素材の利用を考えており、できるだけgdiplusを使いたいです。

補足情報(FW/ツールのバージョンなど)

Microsoft Visual Studio Community 2022 (64 ビット) - Current
Version 17.7.5
デスクトップアプリケーションをひな形にしています。

プロセッサ Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz 3.00 GHz
実装RAM 16GB

お忙しいところ恐縮ですが、宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOZ6.0

2023/10/19 23:57

SetTimer の間隔が短すぎです。精度はそれほど高くないので、1ms ごとにメッセージが飛んでくることを期待してコードを書くと破綻します。
valval

2023/10/21 05:38

コメントを、ありがとうございます。 参考になります。
guest

回答1

0

ベストアンサー

SetTimer(hWnd, 1, 1, NULL);//タイマースタート

1ミリ秒間隔で WM_TIMER が来る想定になっていますが、タイマーの精度はそんなに高くできません。実際には10ミリ秒以上になります。また、10を指定してもWM_TIMER が来る間隔が10ミリ秒を超えることがあります。

https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-settimer

uElapseUSER_TIMER_MINIMUM未満 (0x0000000A) の場合、タイムアウトは USER_TIMER_MINIMUM に設定されます。

タイマー開始時に時刻を保存しておき、WM_PAINT 内では現在時刻とタイマー開始時刻の差から現在の透明度を決めるようにしましょう。

マルチメディアタイマーを使うとより高精度にできた記憶がありますが、どちらにしても現在の一般的なディスプレイでは1秒間に1000回も描画できません。一般的には60回が上限です。

投稿2023/10/20 00:00

編集2023/10/20 00:25
int32_t

総合スコア21301

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

valval

2023/10/21 05:37

ご回答、ありがとうございます。 半透明になりはじめてからWM_PAINT時までの時間経過で、描画の際の透明度をきめます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.41%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問