|
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++
|
|
|
|
|
At last got it running , removed the text box from the window and bilted it directly onto the dialog box as follows.
void CPSFDlg::OnPaint() <br />
{<br />
CPaintDC dc(this);
HGDIOBJ holdbm;<br />
RECT rect;<br />
BYTE color;<br />
int align_top,align_left;<br />
this->GetClientRect(&rect);<br />
align_top = (rect.bottom - rect.top - row) / 2;<br />
align_left = (rect.right - rect.left - col) / 2;<br />
this->InvalidateRect(NULL,TRUE);<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 />
holdbm = memDC.SelectObject(hbm);<br />
dc.BitBlt(align_left, align_top, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight,&memDC,0,0,SRCCOPY);<br />
memDC.SelectObject(holdbm);<br />
}
the problem is, the window keeps on refreshing all the time even though there is nothing happening on tht window and you can see the image flickering in it. How to get rid of it??
thanks,
PKNT
|
|
|
|
|
Never call InvalidateRect or InvalidateRgn from your WM_PAINT handler.
This causes endless WM_PAINT messages! Bad!
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Ooops.... I am just getting on track now... Actually I didnt have InvalidateRect in there at first, at that time, the window doesnt refresh at all. I started off with data filled with 255 and when I ran the program it showed me a nice whire box, but when I do some calculations and when the box is supposed to be changed it doesnt reflect the new data, it still stays white until I move another window over it or something like tht. This is why I added that in OnPaint function.
Anyway I got it now... replaced it into the calling function and tht did the trick. Now I can't wait to do the same for all other windows and see how the performance increases.
thanks,
PKNT
|
|
|
|
|
Whenever your data is updated, use InvalidateRect()/UpdateWindow() (or RedrawWindow()).
This will force an immediate WM_PAINT message to be sent to the window's window procedure.
Just don't do it from inside the WM_PAINT handler
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Did exactly the same.... now everything is fine... time to go home.. will modify other windows the same way tomm.
thanks for all your help and patience...
PKNT
|
|
|
|
|
Gr8, changing all the drawing to bitmaps improved the speed to ~11Hz. The main bottleneck as you thought before is from a very big matrix multiplication which is done in a for loop element by element. To sum up that multiplication, it includes 66x100x100 matrix (static) multiplied by a 1x66 matrix (variable). I changed this using matrix library from OptiVec and now the speed boosted to ~16Hz. I have to change another loop of the same kind but involves complex matricies of 66x64x64 and 1x66 sizes. That should improve speed close to ~20Hz. But for now ~16Hz is pretty impressive for our application.
Now I will start on developing a new application to display a TIFF image (greyscale images), zoom in and out able to select some points (mark them in red) on the image using mouse, delete any selected point or move any selected point in any mode (normal, zoom in/out) using mouse. Some basic IP functions like histogram equalization, brightness/contrast adjustment. I think single document interface is good enough for this. But whats the best way to draw image (our images may be very big anywhere from 512x512 to 2000x3000) ??
PKNT
|
|
|
|
|