波形图透明选择叠加正常,频谱图实现遇阻求可行API方案
我太懂这种糟心的情况了——波形图上的选择叠加层好好的,一到频谱图就直接拉胯,毕竟频谱图哪有什么统一背景色啊,原来的cmSrcAnd模式完全吃不住这种复杂背景。不用折腾逐像素操作的话,咱们可以试试这几个VCL/WINAPI的方案,既能兼容现有视图,还能完美搞定频谱图的问题:
解决方案1:用异或模式(pmXor)绘制选择区域
异或模式的优势在于完全不挑背景——不管下方是纯色波形还是色彩复杂的频谱图,绘制的选区都会和背景自动形成鲜明对比,而且绘制两次就能还原原图,动态调整选区时特别顺手。修改代码很简单:
void TWaveDisplayContainer::DrawSelectedRegion(){ if(selRange.selStart.x == selRange.selEnd.x){ DrawCursorPosition( selRange.selStart.x); return; } // 把绘制逻辑封装成lambda,避免重复代码 auto DrawSelection = [&](TCanvas* pCanvas, Graphics::TBitmap* pBmp, const TRect& selRect){ pCanvas->Draw(0,0, pBmp); // 保存原有画布状态,用完要恢复 auto oldPenMode = pCanvas->Pen->Mode; auto oldBrushStyle = pCanvas->Brush->Style; auto oldBrushColor = pCanvas->Brush->Color; // 设置异或模式 pCanvas->Pen->Mode = pmXor; pCanvas->Brush->Color = clActiveBorder; // 想要边框就用bsClear,想要填充选区就用bsSolid pCanvas->Brush->Style = bsSolid; pCanvas->Rectangle(selRect); // 恢复原有状态 pCanvas->Pen->Mode = oldPenMode; pCanvas->Brush->Style = oldBrushStyle; pCanvas->Brush->Color = oldBrushColor; }; TRect selRect(selRange.selStart.x, 0, selRange.selEnd.x, pWaveLeft->Height); Graphics::TBitmap *pWaveBmp = eContainerView == WAVEFORM ? pWaveBmpLeft : pSfftBmpLeft; DrawSelection(pWaveLeft->Canvas, pWaveBmp, selRect); if(numChannels == 2){ Graphics::TBitmap *pWaveBmpRight = eContainerView == WAVEFORM ? pWaveBmpRight : pSfftBmpRight; DrawSelection(pWaveRight->Canvas, pWaveBmpRight, selRect); } }
解决方案2:用AlphaBlend实现半透明叠加
如果想要更专业的视觉效果(比如像音频软件里常见的淡蓝色半透明选区),可以用WINAPI的AlphaBlend函数直接绘制带透明度的叠加层,这种方式对任何背景都友好,还能保留下方视图的细节:
#include <Windows.h> // 需要包含WINAPI头文件 void TWaveDisplayContainer::DrawSelectedRegion(){ if(selRange.selStart.x == selRange.selEnd.x){ DrawCursorPosition( selRange.selStart.x); return; } auto DrawTransparentSelection = [&](TCanvas* pCanvas, Graphics::TBitmap* pBmp, const TRect& selRect){ pCanvas->Draw(0,0, pBmp); // 创建临时Bitmap用来绘制半透明区域(必须是32位格式支持Alpha通道) Graphics::TBitmap* tempBmp = new Graphics::TBitmap(); tempBmp->Width = selRect.Width(); tempBmp->Height = selRect.Height(); tempBmp->PixelFormat = pf32bit; // 填充选区颜色,再设置Alpha通道实现半透明 tempBmp->Canvas->Brush->Color = clActiveBorder; tempBmp->Canvas->FillRect(Rect(0,0,tempBmp->Width,tempBmp->Height)); for(int y=0; y<tempBmp->Height; y++){ DWORD* pRow = (DWORD*)tempBmp->ScanLine[y]; for(int x=0; x<tempBmp->Width; x++){ // 把Alpha位设为128(半透明,范围0-255,数值越小越透明) pRow[x] = pRow[x] | 0x80000000; } } // 配置Alpha混合参数 BLENDFUNCTION blend = {0}; blend.BlendOp = AC_SRC_OVER; blend.SourceConstantAlpha = 128; // 全局透明度,和像素Alpha叠加 blend.AlphaFormat = AC_SRC_ALPHA; // 绘制半透明选区到目标画布 AlphaBlend(pCanvas->Handle, selRect.Left, selRect.Top, selRect.Width(), selRect.Height(), tempBmp->Canvas->Handle, 0, 0, tempBmp->Width(), tempBmp->Height(), blend); delete tempBmp; }; TRect selRect(selRange.selStart.x, 0, selRange.selEnd.x, pWaveLeft->Height); Graphics::TBitmap *pWaveBmp = eContainerView == WAVEFORM ? pWaveBmpLeft : pSfftBmpLeft; DrawTransparentSelection(pWaveLeft->Canvas, pWaveBmp, selRect); if(numChannels == 2){ Graphics::TBitmap *pWaveBmpRight = eContainerView == WAVEFORM ? pWaveBmpRight : pSfftBmpRight; DrawTransparentSelection(pWaveRight->Canvas, pWaveBmpRight, selRect); } }
方案3:视图适配策略(兼容原有逻辑)
如果不想改动波形图的原有绘制效果,可以针对不同视图做分支处理——波形图用老方法,频谱图用新方案,以后扩展其他视图也能直接加对应的绘制逻辑:
void TWaveDisplayContainer::DrawSelectedRegion(){ if(selRange.selStart.x == selRange.selEnd.x){ DrawCursorPosition( selRange.selStart.x); return; } // 波形图原有绘制逻辑 auto DrawWaveformSelection = [&](TCanvas* pCanvas, Graphics::TBitmap* pBmp, const TRect& selRect){ int copyMode = pCanvas->CopyMode; pCanvas->Draw(0,0, pBmp); pCanvas->Brush->Color = clActiveBorder; pCanvas->CopyMode = cmSrcAnd; pCanvas->Rectangle(selRect); pCanvas->CopyRect(selRect, pBmp->Canvas, selRect); pCanvas->CopyMode = copyMode; }; // 频谱图专用绘制逻辑(这里用异或模式,也可以换成AlphaBlend) auto DrawSpectrogramSelection = [&](TCanvas* pCanvas, Graphics::TBitmap* pBmp, const TRect& selRect){ pCanvas->Draw(0,0, pBmp); auto oldPenMode = pCanvas->Pen->Mode; auto oldBrushStyle = pCanvas->Brush->Style; pCanvas->Pen->Mode = pmXor; pCanvas->Brush->Color = clActiveBorder; pCanvas->Brush->Style = bsSolid; pCanvas->Rectangle(selRect); pCanvas->Pen->Mode = oldPenMode; pCanvas->Brush->Style = oldBrushStyle; }; TRect selRect(selRange.selStart.x, 0, selRange.selEnd.x, pWaveLeft->Height); Graphics::TBitmap *pWaveBmp = eContainerView == WAVEFORM ? pWaveBmpLeft : pSfftBmpLeft; // 根据视图类型选择绘制逻辑 if(eContainerView == WAVEFORM){ DrawWaveformSelection(pWaveLeft->Canvas, pWaveBmp, selRect); } else { DrawSpectrogramSelection(pWaveLeft->Canvas, pWaveBmp, selRect); } if(numChannels == 2){ Graphics::TBitmap *pWaveBmpRight = eContainerView == WAVEFORM ? pWaveBmpRight : pSfftBmpRight; if(eContainerView == WAVEFORM){ DrawWaveformSelection(pWaveRight->Canvas, pWaveBmpRight, selRect); } else { DrawSpectrogramSelection(pWaveRight->Canvas, pWaveBmpRight, selRect); } } }
内容的提问来源于stack exchange,提问作者dsp_user
相关产品推荐
相关产品推荐

