• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 828
  • Last Modified:

Strange problem with MoveTo and LineTo

This is odd.  I have the following which is drawing two lines, one vertical, one horizontal:

      // now draw vertical and horizontal axis
      pDC->MoveTo((m_iWidth/2), 0);
      pDC->LineTo((m_iWidth/2), m_iHeight);

      pDC->MoveTo(0, (m_iHeight/2));
      pDC->LineTo(m_iWidth, (m_iHeight/2));


height and width are 1000.  As shown above, I get no errors, and my vertical line is drawn, but the horizontal line is missing.

I have another module that follows the mouse, and draws horizontal/vertical "crosshairs" at the location, and I noticed that when my mouse rolled vertically through the Y=500 point, the horizontal line that would be drawn disappeared.

I then changed the above code to do the 2nd line like this:

      pDC->MoveTo(0, (m_iHeight/2)-50);
      pDC->LineTo(m_iWidth, (m_iHeight/2)-50);

and now the horizontal line is visible, albeit offset by 50 pixels from where it should be.. With the crosshair lines that follow my mouse, the horizontal line still is visible as expected, until my mouse is at exactly the center point (Y=500) at which time it disappears, until I move it just above or below it.

It's almost as if there is a 1pixel space cutting across the entire horizontal width of my device context, into which I can't draw anything.

Does anybody have any idea why this might be??

Thanks!
-paul


0
PMH4514
Asked:
PMH4514
  • 24
  • 15
  • 3
2 Solutions
 
SteHCommented:
LineTo returns a BOOL of 0 if no line was drawn. Does the LineTo return 0 in the case of y=500?
0
 
PMH4514Author Commented:
both the horizontal and vertical LineTo commands return 1..  only the vertical is visible, unless I put it anywhere other than  Y=500..

0
 
RuskialtCommented:
What happens if you go

pDC->MoveTo(0,0);
pDC->LineTo(1000,1000);

does it look like the line has a "break" in the middle - are you maybe using stretchblt() function for double buffering or something?
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
PMH4514Author Commented:
Ruskialt -

First, I'm not sure what change I made the other day, but now, the problem "axis" isn't at y=500,  now, the missing line is at y=250 AND at y=750   (250 from the top and 250 from the bottom of the screen)

That said, when i do the 0,0 to 1000,1000, I get a diagonal line from the lower left to the upper right (my Y axis appears to be flipped, but that's another thread.) -  and the line is broken by 1 pixel at each of those two Y positions.

very odd..
0
 
SteHCommented:
As this idea came up from Ruskialt and from a previous thread I think to remember that you are doing buffering. Can you post the all code pieces which act on that line before displaying? (It could be related to the other Q of you as well.) Especially each BitBlt or StreachBlt is of interest.
0
 
RuskialtCommented:
If you stretchblt the membuffer (with the diagonal line drawn onto it) to the screen, this it probably where you'll find the "cracked-line" problem. There may be a plus/minus one problem in your destination size calculation!

Do you use (int) typecast truncation somewhere, to satisfy the streatchblt?
0
 
PMH4514Author Commented:
Hmm.. unfortunately, the acutal blitting of the buffer to the screen is handled by the API for this digital camera I'm interfacing with.. I'll dig deeper into their dox, and double-check all the paramaters I'm passing..

maybe I'll just have to do all the drawing myself.

0
 
PMH4514Author Commented:
I wonder if it has to do with the device context I'm creating to draw my overlays into:


pData is the data from the camera in a callback function prior to the camera API drawing it to an owner created window.

-----------------------
void CCameraPage::RGBPreviewCallback(BYTE *pData, ULONG length, ULONG unused)
{
   // pData is the frame stream coming from the camera, before the camera API displays it.
   // length is the size of the buffer

   // ..

   HDC hDC = CreateCompatibleDC(NULL);
   ASSERT(hDC);
   CDC* pDC = CDC::FromHandle(hDC);

   // Create a bitmap to hold the overlayed result
   HBITMAP hbmOverlaid = ::CreateDIBSection(hDC, pbmInfo, DIB_PAL_COLORS, (void**)pData, NULL,  NULL);
   int iSetCount = ::SetDIBits(hDC, hbmOverlaid, 0, m_Height, pData, pbmInfo, DIB_PAL_COLORS);

   HBITMAP hbmOldDest = (HBITMAP)::SelectObject(hDC, hbmOverlaid );

   m_pReticle->DrawReticle(pDC);      // a class I wrote to draw measurement lines, a X/Y ruler essentially.

   // select the new image back into pData, for Lucam API to display in view window.
   int iGetCount = ::GetBitmapBits(hbmOverlaid, length, pData);  // length

   // ..
}

-----------------------

Now, I'm drawing my measurement overlay into a device context:
CreateCompatibleDC(NULL);

is there a problem with using NULL? Maybe I need to pass a handle to the same DC the camera API is using for drawing?


0
 
SteHCommented:
>is there a problem with using NULL? Maybe I need to pass a handle to the same DC the camera API is using for drawing?

It depends on the cameras driver. NULL here means to create a DC compatible with the desktop. If this is identical to the camera used one there should be no problem but in the case there are some differences these could make the difficulties you are facing.

The usual case here would be that a DC is passed as parameter which was created from the API. Since this doesn't exist you could check if your buffering is already giving the steps. Can you save at least one overlay as file and check that file for fragmentation?

Or the other approach for simple overlays could be to modify the frame buffer directly. One vertical line to yellow should be fast and the other should not take too much time either.

A general remark is to be as fast as possible in such callback functions. That means in this case create the DC and the bitmap outside (mouse move handler?!) and do only the bitblt here.
0
 
PMH4514Author Commented:
>>Or the other approach for simple overlays could be to modify the frame buffer directly
Can you show me how I would draw lines into the frame buffer directly w/o the steps of creating a DC and bitmap as I was doing?

>>A general remark is to be as fast as possible in such callback functions. That means in this case create the DC and the >>bitmap outside (mouse move handler?!) and do only the bitblt here.
Yeah, I was wondering which was the best way, I wasn't sure if I could have a class level bitmap and DC that wasn't recreated with each frame
0
 
SteHCommented:
You need to know how the buffer is organized. Normally it should contain RGB values in either 8,16,24 or 32 bits. A vertical line is now a sequence of consecutive memory cells which should be filled with an object of accordingly 8,16,24 or 32 bit representing your deisired color. A simple memcpy can do so.
For a horizontal line you need to use a loop to access the correct cells in this structure which should be available like:
offset * size + i * linelength
with offset being the position inside the line, size the pixels size in bytes and linelength the number of cells inside a full line.

>Yeah, I was wondering which was the best way, I wasn't sure if I could have a class level bitmap and DC that wasn't recreated with each >frame
That is what memory DCs are used for. Created them once and use them often.
0
 
PMH4514Author Commented:
>>The usual case here would be that a DC is passed as parameter which was created from the API

I think that might help too.. I didn't think it was available, but closer inspection of the API, the context is passed, I can derive it from there.. lemme try that..
0
 
PMH4514Author Commented:
SteH - woops, I posted, didn't see your comment.

The buffer is 24bit RGB data. I should note that there is more than just a single vertical and horizontal line.. This is the "centering" - there is also a 400x400 rectangle, and then lines of varying heights from the center point out to the edges representing 2mm sub-divisions..

(picture like, looking through a microscope viewfinder with an overlay of X/Y rulers.. that sort of thing)

So there is first calculation of pixels per millimeter, and then looping form the center to the edges, drawing the lines etc.. I'm not sure a direct draw would be appropriate, but I could try.

That said, I just derived the DC from the context paramater in the callback:
CCameraPage *pdlgCamera = (CCameraPage *) context;
pDC = pdlgCamera->GetDC()


I then use this DC rather than the one derived from CreateCompatibleDC(NULL)

Suddenly, I can see that the "broken lines" are no longer broken, my measurement overlay which was centered is now offset top/left by 5pixels, and the up/down motion of the mouse is inverted again to how it should have been.. BUT, the overlay is flashing on and off very quickly, whereas before it did not..  


0
 
SteHCommented:
Are you drawing into the dlg DC or are you using it in the call to CreateCompatibleDC? Latter should be the way to go.

To improve it further I think more knowledge of the APIs would be reqired. But since I have something similar I can tell you how I am doing the overlay. Perhaps it can be done the same way in your case as well.

Start camera (live mode)
create overlay (DC is created by API)
draw into DC
// now all images have the overlay without flickering.
release DC
stop camera (live mode).

0
 
PMH4514Author Commented:
Well, at this moment, I'm drawing directly to the DC - and it clearly solves both the "broken/missing pixels" AND the upside down Y axis problems, BUT there is flashing with each frame.. (flashing of my overlay lines, not the camera stream itself.)

Previously, I was using CreateCompatibleDC(NULL) and I had all the described problems, but no flash.

0
 
SteHCommented:
Forget to say that in this case the bitblt should be faster and more effective than drawing directly into each frame.
0
 
SteHCommented:
CCameraPage *pdlgCamera = (CCameraPage *) context;
pDC = pdlgCamera->GetDC();
m_pDC = CreateCompatibleDC (pDC);

should solve the flickering.
0
 
PMH4514Author Commented:
error C2664: 'CreateCompatibleDC' : cannot convert parameter 1 from 'class CDC *' to 'struct HDC__ *'

0
 
PMH4514Author Commented:
should it be:

CreateCompatibleDC(pDC->GetSafeHdc())

??
0
 
SteHCommented:
sorry, it should be
m_pDC.CreateCompatibleDC (pDC);
0
 
PMH4514Author Commented:
oh, so m_pDC is type CDC*  right?
0
 
SteHCommented:
>CreateCompatibleDC(pDC->GetSafeHdc())
You can use either the GDI version as you proposed or the member function of a CDC. In that case:
m_pDC->CreateCompatibleDC (pDC);
0
 
SteHCommented:
> oh, so m_pDC is type CDC*  right?
The name suggests it:
m_ is MFC convention for a class member.
p is for a pointer and
DC should be evident.
0
 
PMH4514Author Commented:
yup.. just double checking

I still get very confused between CDC, HDC and how to derive which from which
0
 
SteHCommented:
CDC is the MFC class which encapsulates the windows/GDI resource HDC.
When you are using MFC classes elsewhere you should use the CDC approach. If the remainder is only using GDI APIs try to stick with that.
0
 
PMH4514Author Commented:
>> sorry, it should be
>>m_pDC.CreateCompatibleDC (pDC);

...

>>You can use either the GDI version as you proposed or the member function of a CDC. In that case:
>>m_pDC->CreateCompatibleDC (pDC);

I don't understand how it could be either of these.. if m_pDC is a pointer, doesn't it have to use "->" ?

also, context is coming in via a global callback:
void __stdcall GlobalRGBPreviewCallback(PVOID context, BYTE *pData, ULONG length, ULONG unused)

so should i define m_pDC as a class level CDC*, initialize it to NULL in the constructor, and then add a get/set method for it, so that I can get the pointer in my global handler, call CreateCompatibleDC on it and then set the class member once retrived?


0
 
PMH4514Author Commented:

>>CDC is the MFC class which encapsulates the windows/GDI resource HDC.
>>When you are using MFC classes elsewhere you should use the CDC approach. If the remainder is only using GDI APIs try >>to stick with that.

Oh I see.. I have to keep remembering that MFC is a huge wrapper around everything..
0
 
SteHCommented:
>I don't understand how it could be either of these.. if m_pDC is a pointer, doesn't it have to use "->" ?
Typo in my first comment. -> is correct.

>so should i define m_pDC as a class level CDC*, initialize it to NULL in the constructor, and then add a get/set method for it, so that I can >get the pointer in my global handler, call CreateCompatibleDC on it and then set the class member once retrived?
correct if you decide to go the CDC route. Else call the var m_hDC and only use the HDC value.

>void __stdcall GlobalRGBPreviewCallback(PVOID context, BYTE *pData, ULONG length, ULONG unused)
without more details about the API a PVOID can't be deciphered. But if you solution above works use it.


0
 
PMH4514Author Commented:
I can cast PVOID context to CCameraPage*

>>correct if you decide to go the CDC route. Else call the var m_hDC and only use the HDC value.

but either way, it has to be a class level variable right?

it just confused me, 'cause it's like I need both a CDC* and the HDC, so "getting one from the other" is still confusing for me.  The CreateDIBSection call needs an HDC.. drawing the lines needs a CDC*

0
 
SteHCommented:
I think if you want to go the MFC way you should recode that part using a CBitmap instead of HBITMAP. That way should remove the need to create a device independant bitmap (DIB). Is the API really needing a DIB? Since all operations are for a device there should be no conversion between a DIB and a DDB representation. A color DIB has 32 bits per cell but a DDB can have variations. Try to do all drawings for the used device and stay in that context.

But to correctly answering this I would need to know what pData contains.
0
 
PMH4514Author Commented:
pData contains 24bit RGB values
0
 
PMH4514Author Commented:
>>Is the API really needing a DIB?
The API doesn't need anything.. it's just giving me access to the frame buffer (24bit RGB) before it displays it (it's not documented how it is doing so) - I was creating the HBITMAP and DIB because that was all I knew to be able to draw into it..  

0
 
SteHCommented:
From the facts up to now I would tend using the GDI way: it allows a bitblt to a specified HDC. Only the missing info on pData is making me stop. If this contains the pure pixel data BitBlt won't work at all. And drawing directly into that data could be a big performance problem. Is there some other API function available to do so or should the drawing be done using DirectDraw? I haven't used latter yet.
0
 
PMH4514Author Commented:
I still feel like I'm overthinking this.. Lets step back a moment. The following code works just fine, with no "missing pixels" and proper Y orientation. There are no DIBs or HBITMAPs or anything.. Live camera display does not appear to be affected at all with regards to frame rate or performance. The only problem is flicker..



void __stdcall GlobalRGBPreviewCallback(PVOID context, BYTE *pData, ULONG length, ULONG unused)
{
   CCameraPage *pdlgMacro = (CCameraPage *) context;
   CDC* pDC;
   pDC = pdlgMacro->GetDC();
   pdlgMacro->RGBPreviewCallback(pDC, pData, length, unused);
}



void CCameraPage::RGBPreviewCallback(CDC* pDC, BYTE *pData, ULONG length, ULONG unused)
{
  if (m_bOverlayVisible)
  {
     m_pReticle->DrawReticle(pDC);      
  }
}



void CReticle::DrawReticle(CDC* pDC)
{
  // define a pen for drawing
  CPen penRed;
  CBrush pNullBrush;

  penRed.CreatePen(PS_SOLID, m_iPenWidth, m_PenColor );
  pNullBrush.CreateStockObject(NULL_BRUSH);

  CPen* pOldPen = (CPen*)pDC->SelectObject(&penRed);
  CBrush* pOldBrush = (CBrush *)pDC->SelectObject(pNullBrush);


  pDC->MoveTo(iCenterX, 0);
  pDC->LineTo(iCenterX, m_iHeight);

  pDC->MoveTo(0, iCenterY);
  pDC->LineTo(m_iWidth, iCenterY);
 
  // etc..

  // remove the pen and brush from the device context and delete them
  pDC->SelectObject(&pOldPen);
  pDC->SelectObject(&pOldBrush);
}
0
 
PMH4514Author Commented:
edit: "The only problem is flicker.." - it is my overlay that flickers, the camera feed does not.
0
 
SteHCommented:
OK so then it should work the following way.

Have memory DC creation function


void __stdcall GlobalRGBPreviewCallback(PVOID context, BYTE *pData, ULONG length, ULONG unused)
{
   if (NULL == m_pDC) {
      m_pDC->CreateCompatibleDC (pDC); // pDC is the device context of the camera like in the call backs.
   }
}

Inside the mouse move handler draw into this memory DC. (The normal recticle could be drawn into another memory DC since it will updated less frequent.) This resembles the DrawRecticle like it is only the DC should be your memory DC.

And inside  
DrawRecticle ()
{
    pDC->BitBlt (ix, iy, iw, ih, m_pDC, 0, 0, SRCCOPY); // ix, iy should be 0 and iw and ih 1000 if I followed all correctly.
}
just bitblt the recticle to the camera image.
0
 
PMH4514Author Commented:
that Global function can't directly access member properties (right?) so I added\



CDC* CMacroPage::GetMemDC(CDC* a_pDC)
{
  if (NULL == m_pDC)
    m_pDC->CreateCompatibleDC (a_pDC);
 
  return m_pDC;
}

which was to be called in the global callback:

void __stdcall GlobalRGBPreviewCallback(PVOID context, BYTE *pData, ULONG length, ULONG unused)
{
  CMacroPage *pdlgMacro = (CMacroPage *) context;

  CDC* pDC;
  pDC = pdlgMacro->GetDC();

  pdlgMacro->RGBPreviewCallback(pdlgMacro->GetMemDC(pDC), pData, length, unused);
}


but the CreateCompatibleDC line crashes with an assesrtion failure:

---
BOOL CDC::Attach(HDC hDC)
{
      ASSERT(m_hDC == NULL);      // only attach once, detach on destroy
---      

in WINGDI.CPP


0
 
PMH4514Author Commented:
(whoops "Macro" = "Camera" - some reason I applied a search/replace on that post.. :)
0
 
PMH4514Author Commented:
I dunno what is wrong here.. regardless where I call it this line fails:

m_pDC->CreateCompatibleDC (a_pDC);
0
 
PMH4514Author Commented:
Alright folks, this thread has kinda disintegrated into a jumble of stuff all pointing to my need to study device context's a bit more.

I did actually step back to my code point from yesterday, and realized that I was setting the bitmapinfo width/height header 3 pixels too big, and fixing that, my "broken axis" problem was fixed..  everything else here is good stuff, stuff I will implement once I've better understood it.. So, technically Russ had the right answer:
---
If you stretchblt the membuffer (with the diagonal line drawn onto it) to the screen, this it probably where you'll find the "cracked-line" problem. There may be a plus/minus one problem in your destination size calculation!
---

and I'll accept that as the answer just in case future people are reading this, but I gotta split the points in favor of SteH for the depth of conversation.

thanks!
-Paul
0
 
RuskialtCommented:
Thanks, fair enough :-)

About creating memorydc's: there's a nice article on codeproject on a memdc class. It is used for double buffering to make flicker free drawing. Check it out for tips on creating dc's and memory bitmaps, it's fairly simple..!

Btw: If you try to create a DC twice, it will probably fail.. (Just noticed that "m_" prefix on the dc you are creating in your last post)
0
 
PMH4514Author Commented:
Thanks, I'll check that out!
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 24
  • 15
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now