ニコニコ実況からコメントを取得した (2) 表示
気が付いたら1週間経ってしまいました。今日は後半、画面表示部分です。ここを綺麗に作ってあれば、コメント取得部分を差し替えるだけで済んだのでしょうが、こっちも残念な品質です。ほぼ1から作り直したいです。元のコードはTVTest 0.6.4のVMR9 Renderlessモードでの描画部分です。ここで取り上げているコードは、改造後のDirectShowFilter/VMR9Renderless.cppのCVMR9Allocator::PresentHelper関数(442行目以降)より一部抜粋となります。
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | static int pos = 0; static std::vector<CommentInfo> comment; { extern CCritSec MsgQueueLock; extern std::queue<std::wstring> MsgQueue; CommentInfo info; info.StartCount = GetTickCount(); UINT i = 0; CAutoLock msgLock(&MsgQueueLock); while (!MsgQueue.empty()) { info.Comment = MsgQueue.front(); info.Y = pos; MsgQueue.pop(); comment.push_back(info); info.StartCount += 200; pos += 80; if (pos >= 1000) pos = 0; } } |
ifは必ず括弧{}で囲むというここ数年の自分の書き方をやぶっているところが、時間なくて焦っている何よりの証ですね。posはコメントのy座標になります。とりあえず重ならなければいいやというやっつけ仕事の位置決めです。MsgQueueは、別スレッドで獲得したメッセージが放り込まれます。ヘッダファイルすら使わないというあたりからは、余裕のなさが伺えます。
532 533 534 535 536 537 | static CComPtr<IDirect3DTexture9> txVideo; if (!txVideo) hr = m_D3DDev->CreateTexture(1920, 1080, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &txVideo, NULL); CComPtr<IDirect3DSurface9> surfaceVideo; hr = txVideo->GetSurfaceLevel(0, &surfaceVideo); m_D3DDev->StretchRect(lpPresInfo->lpSurf, NULL, surfaceVideo, NULL, D3DTEXF_NONE); |
ここで静的変数としてテクスチャを作っているのも、なかなかポイント高いです、悪い意味で。デバイスロストなどの都合があるので、クラスのメンバ変数辺りに置いておかないとどうしようもありません。よい子のみんなは真似してはいけません。
lpPresInfo->lpSurfに次のフレームの映像が入っています。元のコードでは、それをそのまま画面に表示させるだけでした(バックバッファへStretchRect)。一方、このコードでは一旦テクスチャであるtxVideoにコピーしています。VMR9にはlpPresInfo->lpSurfをテクスチャにする方法もありますが、それよりもこうしたほうがたまたまCPU使用率が低かったことと、ついでに1920×1080に大きさを揃えたかったという理由により、ここでStretchRectで拡大コピーしていたのだと思います。
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 | static CComPtr<IDirect3DTexture9> tx; if (!tx) { hr = m_D3DDev->CreateTexture(1920, 1080, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tx, NULL); } D3DLOCKED_RECT lr; hr = tx->LockRect(0, &lr, NULL, 0); static ULONG_PTR gdipToken; if (!gdipToken) { Gdiplus::GdiplusStartupInput si; Gdiplus::GdiplusStartup(&gdipToken, &si, NULL); } { Gdiplus::Bitmap bitmap(1920, 1080, lr.Pitch, PixelFormat32bppARGB, static_cast<BYTE*>(lr.pBits)); Gdiplus::Graphics g(&bitmap); Gdiplus::Font f(L"Tahoma", 48); Gdiplus::SolidBrush br(Gdiplus::Color(255, 255, 255, 255)); Gdiplus::SolidBrush br2(Gdiplus::Color::Black); g.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); g.Clear(Gdiplus::Color::Transparent); g.SetCompositingMode(Gdiplus::CompositingModeSourceOver); g.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias); g.DrawString(L"Test", 4, &f, Gdiplus::PointF(4, 5), &br2); g.DrawString(L"Test", 4, &f, Gdiplus::PointF(0, 0), &br); HDC hdc = g.GetHDC(); SetTextAlign(hdc, TA_RIGHT); SetBkMode(hdc, TRANSPARENT); //RECT rc = {0, 0, 480, 480}; //FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); DWORD current = GetTickCount(); HGDIOBJ hOldFont = SelectObject(hdc, hfnt); for (std::size_t i = 0; i < comment.size(); ++i) { int pos = 1920 - static_cast<int>(current - comment[i].StartCount) / 3; if (pos >= 0) { SetTextColor(hdc, RGB(0, 0, 0)); TextOut(hdc, pos + 4, comment[i].Y + 5, comment[i].Comment.data(), static_cast<int>(comment[i].Comment.length())); SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); TextOut(hdc, pos, comment[i].Y, comment[i].Comment.data(), static_cast<int>(comment[i].Comment.length())); } else { comment.erase(comment.begin() + i); --i; } } SelectObject(hdc, hOldFont); g.ReleaseHDC(hdc); g.Flush(); } tx->UnlockRect(0); |
txやgdipTokenを静的変数としているのが……、というのはさっきも書いたとおりです。それはともかくここがコメントを書き込む一番中核の部分です。GDI+が出てきたかと思えばGDIというチャンポンは相変わらず余裕のなさの表れです。当初、GDIでの描画を試行していたことがあり、そのときのテキスト描画のコードをコピーしてきたという突貫工事が原因で、Graphics::DrawStringを使うように書き換えようと思ったはずですが、直されぬままです。
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 | hr = m_D3DDev->SetStreamSource(0, vb, 0, sizeof (CUSTOMVERTEX)); hr = m_D3DDev->SetFVF(D3DFVF_CUSTOMVERTEX); hr = m_D3DDev->SetTexture(0, txVideo); hr = m_D3DDev->SetTexture(1, tx); hr = m_D3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); hr = m_D3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); hr = m_D3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); hr = m_D3DDev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); hr = m_D3DDev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); hr = m_D3DDev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); hr = m_D3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); hr = m_D3DDev->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT); hr = m_D3DDev->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT); hr = m_D3DDev->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); hr = m_D3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2); |
テクスチャステージで映像とコメントを合成して描画しています。映像の上にコメントをアルファブレンドしています。本当はもちろんテクスチャを貼り付けるポリゴンを作るコードがあるのですが、これまた酷い(デバイスロストとか解放とか何も考えていない)ですし、どうせ本来は定型的な処理なのでここでは省略します。
これで大体全部です。あとは、パッチを見てください。今から作り直すとしたら、Twitterの特定のハッシュタグ付きの発言を取り込むようにもしたいです。
スポンサード リンク |