|
Kiran Satish wrote: ...just using SetPixel...
ouch!
I don't know the format of the data you need to display, but I bet you can
shave 100-200 ms off your display time.
If the data is in a bitmap form (rows and columns of pixels) then I would start
with using a DIBSection(). This gives you a DIB that you can select into a
device context AND you have a pointer to the pixel bits. You can directly
set pixels in memory, probably way faster than SetPixel().
The DIBSection can remain selected into a memory DC.
Then you can blt the entire bitmap to the screen in one shot.
What format is the pixel data in? Dimensions, bits-per-pixel, etc.??
Does this sound like something you can benefit from?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Yeh, sure DIBSection will be definitely faster than drawing pixel by pixel. But the problem is I am not sure how to convert the data that I use into usable format for this function. I will do some research and some testing to see if I can somehow change or redefine the data to go in this way. Anyway here is how the function is implemented for repainting one of the dialog boxes..
void ResImageDlg::color_AllBoxes(CWnd *ResDisp)<br />
{<br />
double resval;<br />
int i,j,x,y;<br />
RECT ResRect;<br />
COLORREF color;<br />
<br />
CDC *ResCDC = ResDisp->GetDC();<br />
for(i=0; i < GRID; i++)
{<br />
for(j=0; j < GRID; j++)<br />
{<br />
if( (j == 0 || j == (GRID-1)) && (i == 0 || i == (GRID-1)) )<br />
;
else<br />
{<br />
resval = (parent->calcfile)->FetchResData(i,j); <br />
ResRect.left = X_OFFSET + (i*(RES_BOX_SIZE+BOX_GAP));<br />
ResRect.right = ResRect.left + RES_BOX_SIZE;<br />
ResRect.top = Y_OFFSET + (j*(RES_BOX_SIZE+BOX_GAP));<br />
ResRect.bottom = ResRect.top + RES_BOX_SIZE;<br />
color = check_ColorLimits(resval); <br />
for(x=ResRect.left; x < ResRect.right; x++)<br />
for(y=ResRect.top; y < ResRect.bottom; y++)<br />
ResCDC->SetPixelV(x,y,color);<br />
}<br />
}<br />
}<br />
ResDisp->ReleaseDC(ResCDC);<br />
}
The actual data is nothing but some real numbers between 0 and 2.0; two other dialog boxes are also drawn in the same fashion, but since they are 8bit graphic type, the data is scaled to be in between 0 and 255 and uses SetPixelV to draw that pixel.
Hope I am not confusing...
PKNT
|
|
|
|
|
I can't read your code for the loops - can you modify your post so
HTML tags are ignored?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
what about the SetPixel loop?
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Heh cool, thanks
You can optimize this alot.
Move any code that calculates rects, positions, etc. to a more appropriate
place - the place where it changes (e.g. WM_SIZE). Usually that stuff stays the
same between draws so there's no reason to recalculate it every time you draw.
Same goes for any variables that don't change or don't change often.
Change the SetPixel loop to write directly to the pixel memory of a DIBSection.
SetPixel has to calculate the width of the destination bitmap(DC) in bytes and
calculate the offset in the destination pixel memory every time you call it.
In your own loop, most of this can be calculated once and you can increment your
way through the pixel data.
I'm still not clear on your data format...how do the 8-bit values relate to an RGB color?
Is it grayscale?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark Salsbery wrote: I'm still not clear on your data format...how do the 8-bit values relate to an RGB color?
Is it grayscale?
Yes, among 5 graphic dialog boxes two are grayscale while other 3 are color. The loop that I have shown you above is of a color graphic dialog box. Even for grayscale values, they use the same 8bit value (that they calculate according to the data value) for all three channels of RGB.
PKNT
|
|
|
|
|
The grayscale will be way faster if you stop using SetPixel().
And what about the color....is that 8-bit too? If so, how do you map 8-bit to RGB?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
For color values, its not 8 bits. RGB value is calculated using the following function.
<br />
double mid = get_MidVal();
double max = get_MaxVal() - mid;<br />
double min = get_MinVal() - mid;<br />
val -= mid;
int blueval=0;<br />
int redval=(int)(MID_RGB_VALUE + ((val/max)*MID_RGB_VALUE));<br />
int greenval=(int)(MID_RGB_VALUE - ((val/max)*MID_RGB_VALUE));<br />
if ((val <= min)||(val >= max)) <br />
{redval=255;blueval=255;greenval=0;}<br />
<br />
return RGB(redval,greenval,blueval);
then that RGB value is used to color all the pixels in that particular box of the loop in earlier post.
PKNT
|
|
|
|
|
Well that's cool....you have RGB covered for two data types. What did you need SetPixelV for then?
SetPixelV would be your slowest API call.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Honestly, I donnno why they used it. Its written like 4 years ago by two people and they are not here now .
Will see wht I can do to modify this using DIBSection. I haven't used it in any of my applications till now, so have to do some research/reading on it and also the way to implement in my case.
thanks for all your help, I really appreciate it.
PKNT
|
|
|
|
|
Kiran Satish wrote: so have to do some research/reading on it and also the way to implement in my case.
Hopefully this will help a bit...
<code>
LONG DIBSecWidth = ...;
LONG DIBSecHeight = ...;
LONG BitsPerPixel = 24;
LONG Stride = ((DIBSecWidth * BitsPerPixel + 31L) & (~31L)) / 8L;
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = DIBSecWidth;
bmi.bmiHeader.biHeight = DIBSecHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = (WORD)BitsPerPixel;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = Stride * abs(DIBSecHeight);
BYTE *pDIBSectionBits;
HBITMAP hbm = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDIBSectionBits, NULL, 0);
<code>
int x = ...;
int y = ...;
RGBTRIPLE *pPixel = (RGBTRIPLE *)(pDIBSectionBits + ((y * Stride) + (x * sizeof(RGBTRIPLE))));
<code>
pPixel[0].rgbtBlue = 0;
pPixel[0].rgbtGreen = 0;
pPixel[0].rgbtRed = 0;
<code>
RECT rect;
rect.left = 10;
rect.top = 10;
rect.right = 20;
rect.bottom = 20;
RGBTRIPLE *pCurRowPixel = (RGBTRIPLE *)(pDIBSectionBits + ((rect.top * Stride) + (rect.left * sizeof(RGBTRIPLE))));
for (int y = 0; y < rect.bottom - rect.top; ++y)
{
for (int x = 0; x < rect.right - rect.left; ++x)
{
pCurRowPixel[x].rgbtBlue = 0;
pCurRowPixel[x].rgbtGreen = 0;
pCurRowPixel[x].rgbtRed = 0;
}
pCurRowPixel = (RGBTRIPLE *)((BYTE*)pCurRowPixel + Stride);
}
You can probably optimize that stuff even further for specific implementations
but that generic code should be much faster than a SetPixel loop
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Thanks for the info, will help me in understanding it more easily. Just done with matlab, will start going thru this now.
PKNT
|
|
|
|
|
I found very little help on DIBSection while found many examples on using CBitmap class. Anyway I started off routine of my application where I color boxes in a window.
My window (with 12x12 grid) is 513x513 and the grid's horizontal and vertical offset is 20 (this would be the width of the border of the grid) and box width (each box in the grid) is 15 and box gap is 2(this would be the width of grid lines inside). These are constants and they stay the same all the time.
So while creating this window object I have to do the following--
LONG DIBSecWidth = 513;
LONG DIBSecHeight = 513;
LONG BitsPerPixel = 24;
CDC *ResCDC = ResDisp->GetDC();/*ResDisp is handle of this window and ResCDC need not be changed while redrawing so I dont need to call it everytime I call the above routine.*/
LONG Stride = ((DIBSecWidth * BitsPerPixel + 31L) & (~31L)) / 8L; /**I didnt understand this step, how can I calculate this in my case*/
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = DIBSecWidth;
bmi.bmiHeader.biHeight = DIBSecHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = (WORD)BitsPerPixel;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = Stride * abs(DIBSecHeight);
BYTE *pDIBSectionBits;
HBITMAP hbm = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDIBSectionBits, NULL, 0);/**I have to replace first argument NULL with my ResCDC */
From then I couldnt understand how I can find pixel address for each pixel in my application while considering box sizes and gap.
Now I have to color each box with some color, so is there any way to fill each box in memory with that same color rather going in two for loops, like using fillrect etc?? This way I can eliminate the inner two for loops in my code.
thanks,
PKNT
|
|
|
|
|
Kiran Satish wrote: 513x513 and the grid's horizontal and vertical offset is 20 and box width (each box in the grid) is 15 and box gap is 2.
How do you get 513? 12 cells at 15 pixels each is 180 pixels. 11 2-pixel gaps is 22 pixels. Total == 202 pixels (assuming no border).
Kiran Satish wrote: LONG Stride = ((DIBSecWidth * BitsPerPixel + 31L) & (~31L)) / 8L; /**I didnt understand this step, how can I calculate this in my case*/
Bytes per row in a DIB have to be aligned on a DWORD (4-byte) boundary. That's what this calculation does.
The calculation is always the same - it's always based on DIBSecWidth.
Kiran Satish wrote: HBITMAP hbm = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDIBSectionBits, NULL, 0);/**I have to replace first argument NULL with my ResCDC */
You don't need to worry about a DC here since the DIBSection is 24-bits-per-pixel.
You shouldn't be worrying about a DC anywhere here - this is al stuff that can be calculated once,
not every time you redraw - that's part of what will save you alot of CPU cycles
Kiran Satish wrote: From then I couldnt understand how I can find pixel address for each pixel in my application while considering box sizes and gap.
It depends on how you want to loop. If it's always by cell, the upper left corner pixel
coordinates (x,y) for a given cell (CellX,CellY) would be
x = CellX * CellWidthInPixels + CellX * GapWidthInPixels;
y = CellY * CellHeightInPixels + CellY * GapHeightInPixels;
With (x,y) you can use the calculation in my examples to get the byte offset in the pixel data.
Make youreslf a handy utility function to calculate these if you're using them alot.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark Salsbery wrote: How do you get 513? 12 cells at 15 pixels each is 180 pixels. 11 2-pixel gaps is 22 pixels. Total == 202 pixels (assuming no border).
Sorry its 265x250, they are creating this window with these dimensions and while drawing boxes, they add offset of 20 from top and bottom
Here is a snapshot of it. The four corner boxes are always black.
Hope this helps in understanding a lil better.
PKNT
|
|
|
|
|
Kiran Satish wrote: Hope this helps in understanding a lil better.
Yes! Thank you!
In that case the DIBSection can be 202x202.
The calculations I've explained previously will work.
The entire grid bitmap can then be blted to the window at whatever offset you want.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Before I start changing the code to use DIBSection, I started referin at BitBlt on msdn and came accross PatBlt and thought of quickly trying it with the following arguments-
within the loop
CBrush *pcolor,tcolor;<br />
for(i=0; i < GRID; i++)
{<br />
for(j=0; j < GRID; j++)<br />
{<br />
if( (j == 0 || j == (GRID-1)) && (i == 0 || i == (GRID-1)) )<br />
;
else<br />
{<br />
resval = (parent->calcfile)->FetchResData(i,j);<br />
ResRect.left = X_OFFSET + (i*(RES_BOX_SIZE+BOX_GAP));<br />
ResRect.right = ResRect.left + RES_BOX_SIZE;<br />
ResRect.top = Y_OFFSET + (j*(RES_BOX_SIZE+BOX_GAP));<br />
ResRect.bottom = ResRect.top + RES_BOX_SIZE;<br />
color = check_ColorLimits(resval); <br />
tcolor = new CBrush(GetRVal(color),GetGVal(color),GetBVal(color));<br />
pcolor = ResCDC->SelectObject(&tcolor);<br />
PatBlt(ResCDC->m_hDC, ResRECT.left, ResRECT.top, (ResRECT.right-ResRECT.left), (ResRECT.bottom-ResRECT.top), PATCOPY);<br />
}<br />
}<br />
}<br />
ResDisp->ReleaseDC(ResCDC);
The above loop is working fine for one grid location and draws one box filled with color, for the second time when it reaches selectobject statement, it is crashing with an error in wingdi.cpp. It seems like I am reselecting a new object without actually releasing the first color or soething like tht. How can I get rid of this error??
thanks,
PKNT
|
|
|
|
|
Any time you select an object into a DC, you should save the
previously selected object. When you're done with the DC, select the original
object back into the DC.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Yep.... just done that and here you are with your reply, Damn you beat me again . Slowly getting used to these. Now just using PatBlt instead of SetPixel for GRID window increased my application's closed loop freq from 2.84Hz to 8Hz whoa... Now I have to change the way of drawing other two windows with DIBSection as I cant use PatBlt, since each pixel will have different intensity.
PKNT
|
|
|
|
|
8Hz....your computations must be pretty intense (or your hardware is OLD)!
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
At first attempt of using DIBSection for another dialog window which has a drawing area of only 64x64. Here is how I did..
In the constructor of the dialog-
DIBSecWidth = 64;<br />
DIBSecHeight = 64;<br />
BitsPerPixel = 24;<br />
Stride = ((DIBSecWidth * BitsPerPixel + 31L) & (~31L)) / 8L;<br />
BITMAPINFO bmi;<br />
memset(&bmi, 0, sizeof(BITMAPINFO));<br />
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);<br />
bmi.bmiHeader.biWidth = DIBSecWidth;<br />
bmi.bmiHeader.biHeight = DIBSecHeight;<br />
bmi.bmiHeader.biPlanes = 1;<br />
bmi.bmiHeader.biBitCount = (WORD)BitsPerPixel;<br />
bmi.bmiHeader.biCompression = BI_RGB;<br />
bmi.bmiHeader.biSizeImage = Stride * abs(DIBSecHeight);<br />
hbm = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDIBSectionBits, NULL, 0);
In the OnPaint() function of this dialog box-
CWnd *Disp;<br />
CPaintDC dc(this);
Disp = GetDlgItem(ID_PSF);<br />
Disp->InvalidateRect (NULL, TRUE);<br />
Disp->UpdateWindow();<br />
DrawPSF(Disp);
In the DrawPSF(SVDisp) function-
int i,j;<br />
RECT rect;<br />
BYTE color;<br />
unsigned long align_top,align_left;<br />
CDC *SVCDC = SVDisp->GetDC();<br />
SVDisp->GetClientRect(&rect);<br />
align_top = (unsigned long) (rect.bottom - rect.top - row) / 2;<br />
align_left = (unsigned long) (rect.right - rect.left - col) / 2;<br />
RGBTRIPLE *pCurRowPixel = (RGBTRIPLE *)(pDIBSectionBits);<br />
for (int y = 0; y < col; ++y)<br />
{<br />
for (int x = 0; x < row; ++x)<br />
{<br />
color = (BYTE) data[x * col + (row - 1 - y)];<br />
pCurRowPixel[x].rgbtBlue = color;<br />
pCurRowPixel[x].rgbtGreen = color;<br />
pCurRowPixel[x].rgbtRed = color;<br />
}<br />
pCurRowPixel = (RGBTRIPLE *)((BYTE*)pCurRowPixel + Stride);<br />
}<br />
BitBlt((HDC)SVDisp, align_left, align_top, bmi.bmWidth, bmi.bmHeight, (HDC)hbm, 0, 0, SRCCOPY);<br />
SVDisp->ReleaseDC(SVCDC);
I am sure the code is pretty bad, how can get rid of calling DC every time I call the paint function?? Does this way work at all?? dont bother about data, its just the actual data used to generate the intensity at that location as well as row and col.
When I ran the applicaiton with this code, the application hangs at commant BitBlt and never comes back
thanks
PKNT
|
|
|
|
|
Having to cast things to HDC is a bad sign.
You need real device contexts.
The destination DC for BitBlt() should be the PaintDC or a DC you've obtained
for the window you want to draw on.
The source DC for BitBlt() needs to be a memory DC that you've selected the
DIBSection into
hbm = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDIBSectionBits, NULL, 0);<br />
DIBSectionDC.CreateCompatibleDC(0);
DIBSectionDC is a CDC member object<br />
hbmOldDIBSecBM = ::SelectObject( DIBSectionDC, hbm
);<br />
DIBSectionDC should be the source dc when you call BitBlt().
You'll need to pass a valid destination DC to your DrawPSF()
function to use as the destination DC.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Thanks for the reply, it seems the steps are shown in the example of CDC::CreateCompatibleDC in MSDN. But there they are using direct bitmaps (CBitmap class).
Here is how I modified my code based upon your suggesitons above and the exmpale in MSDN
SVDisp->GetClientRect(&rect);<br />
align_top = (unsigned long) (rect.bottom - rect.top - row) / 2;<br />
align_left = (unsigned long) (rect.right - rect.left - col) / 2;<br />
<br />
RGBTRIPLE *pCurRowPixel = (RGBTRIPLE *)(pDIBSectionBits);<br />
for (int y = 0; y < col; ++y)<br />
{<br />
for (int x = 0; x < row; ++x)<br />
{<br />
color = (BYTE) data[x * col + (row - 1 - y)];<br />
pCurRowPixel[x].rgbtBlue = color;<br />
pCurRowPixel[x].rgbtGreen = color;<br />
pCurRowPixel[x].rgbtRed = color;<br />
}<br />
pCurRowPixel = (RGBTRIPLE *)((BYTE*)pCurRowPixel + Stride);<br />
}<br />
<br />
CDC *SVCDC = SVDisp->GetDC();<br />
CDC DIBSectionDC;<br />
DIBSectionDC.CreateCompatibleDC(SVCDC);<br />
HGDIOBJ hbmOldDIBSecBM = ::SelectObject(DIBSectionDC, hbm);<br />
SVCDC->BitBlt(align_left, align_top, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, &DIBSectionDC, 0, 0, SRCCOPY);<br />
<br />
DIBSectionDC.SelectObject(hbmOldDIBSecBM);<br />
SVDisp->ReleaseDC(SVCDC);
it doesnt give any errors, but it shows up nothin on the window, I have seen different values being stored into pDIBSectionBits between (0-255).
The dialog box is filled with a textbox and I am using its handle for getting DC and all. The SVDisp in the above code is a window pointer to that text box.
PKNT
modified on Thursday, September 17, 2009 1:47 PM
|
|
|
|
|
Any suggestions ?? If I can get this to work, then I can use the same way for 3 other dialog boxes.
PKNT
|
|
|
|
|
Kiran Satish wrote: Any suggestions ??
Yes. Get it working just drawing right on the dialog.
I don't know that this control is (Disp = GetDlgItem(ID_PSF); )
that you're drawing to but it's adding unnecessary complexity.
The DIBSectionDC only needs to be created when you create the dibsection,
not every time you draw - we're trying to SAVE time here
Destroy it when you destroy the DIBSection - they go hand-in-hand.
But again, I recommend getting it working drawing right on the dialog.
That means your destination DC is the paintdc, that's fine as long as there's
no controls in the way. That way, you can see what's going on.
Also, go through the code line by line and make sure you understand it.
The same principles apply as you were doing before with setpixel.
the only difference is now you draw to an offscreen DC and then blt the final drawing
to the screen. You have a pointer to the offscreen bits so you can write pixel
values directly to memory and/or use gdi functions to draw offscreen (by
drawing to the DIBSectionDC)....that's the power.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|