• Advertisement
Sign in to follow this  

Converting a function from VB to C++

This topic is 4730 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

"What madness is this?!" I hear you say? Well, I have some perfectly good VB code from days gone by that will rotate an image by a specified angle and place the rotated image in a picturebox. I want to try and copy it over to c++, and I'm almost, but not quite, successful. Heres what I've got: The original method for rotating images. They are first rotated into a pictureBox as an intermediary, and then the contents of the picture box is drawn to the window after this function is called.
Private Sub RotatePic(Theta As Single, ByVal Index As Integer)
Dim c1x As Integer, c1y As Integer
Dim c2x As Integer, c2y As Integer
Dim p1x As Integer, p1y As Integer
Dim p2x As Integer, p2y As Integer
Dim a As Single
Dim n As Integer, r As Integer
Dim P1Hdc As Long, P2Hdc As Long
' Get the device context - saves time in loop
P1Hdc = hdcSrc(Index)
P2Hdc = Transfer(Index).hdc
c1x = hdcWidth(Index) \ 2
c1y = hdcHeight(Index) \ 2
c2x = Transfer(Index).ScaleWidth \ 2
c2y = Transfer(Index).ScaleHeight \ 2
Dim c0 As Long, c1 As Long, c2 As Long, c3 As Long, xret As Long
If c2x < c2y Then n = c2y Else n = c2x
n = n - 1

Dim test As Long

For p2x = 0 To n
   For p2y = 0 To n
      If p2x = 0 Then a = Pi / 2 Else a = Atn(p2y / p2x)
      r = Sqr(1& * p2x * p2x + 1& * p2y * p2y)
      p1x = r * Cos(a + Theta)
      p1y = r * Sin(a + Theta)
      c0 = GetPixel(P1Hdc, c1x + p1x, c1y + p1y)
      c1 = GetPixel(P1Hdc, c1x - p1x, c1y - p1y)
      c2 = GetPixel(P1Hdc, c1x + p1y, c1y - p1x)
      c3 = GetPixel(P1Hdc, c1x - p1y, c1y + p1x)
      
      If c0 <> -1 Then xret = SetPixel(P2Hdc, c2x + p2x, c2y + p2y, c0)
      If c1 <> -1 Then xret = SetPixel(P2Hdc, c2x - p2x, c2y - p2y, c1)
      If c2 <> -1 Then xret = SetPixel(P2Hdc, c2x + p2y, c2y - p2x, c2)
      If c3 <> -1 Then xret = SetPixel(P2Hdc, c2x - p2y, c2y + p2x, c3)
   Next
Next

Transfer(Index).Refresh
End Sub


The same function, as I have attempted to write in C++ I've tried my best to copy the original vb function, but for some reason, there is something wrong with this. It is drawing to the screen, but the image is mangled into four seperate bits, each bit is close to what it should look like, but is slightly stretched/warped. There is also a clear line between each 'section'
inline ErrorType rotateImage(MyPicture* image, MyPicture* transfer, double angle)
	{
	if(!image || !transfer)
		return FAILURE;

	int imgHalfWidth	= image->width/2;
	int imgHalfHeight	= image->height/2;

	int transferHalfWidth	= transfer->width/2;
	int transferHalfHeight	= transfer->height/2;

	int x1;
	int y1;
	double a;
	int r;

	int c0, c1, c2, c3;
	int n;

	if(transferHalfWidth>transferHalfHeight)
		n = transferHalfWidth;
	else
		n = transferHalfHeight;

	image->LockBackSurface();
	transfer->LockBackSurface();
	for(int x2=0;x2<=n;++x2)
		{
		for(int y2=0;y2<=n;++y2)
			{
			if(x2==0)
				a = M_PI_2;
			else
				a = atan(double(y2 / x2));

			r = sqrt(double(x2*x2+y2*y2));
			x1 = r * cos(a+angle);
			y1 = r * sin(a+angle);

			c0 = image->GetPoint(imgHalfWidth+x1, imgHalfHeight+y1);
			c1 = image->GetPoint(imgHalfWidth-x1, imgHalfHeight-y1);
			c2 = image->GetPoint(imgHalfWidth+y1, imgHalfHeight-x1);
			c3 = image->GetPoint(imgHalfWidth-y1, imgHalfHeight+x1);

			if(c0!=-1) transfer->DrawPoint(transferHalfWidth+x2, transferHalfHeight+y2, c0);
			if(c1!=-1) transfer->DrawPoint(transferHalfWidth-x2, transferHalfHeight-y2, c1);
			if(c2!=-1) transfer->DrawPoint(transferHalfWidth+y2, transferHalfHeight-x2, c2);
			if(c3!=-1) transfer->DrawPoint(transferHalfWidth-y2, transferHalfHeight+x2, c3);

			}
		}
	transfer->UnlockBackSurface();
	image->UnlockBackSurface();

	return SUCCESS;
	}


Here are some images to better explain the difference: VB: C++: Can anyone see any discrepancies between how each function works that could be causing the c++ function to misbehave? GetPoint() and DrawPoint() are equivalent functions afaik. The VB functions are declared thusly:
Declare Function SetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) As Long
Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long


Share this post


Link to post
Share on other sites
Advertisement
One thing that may be causing your problems, is conversions - vb rounds values on conversions, while C++ truncates values, if I recall correctly - check on any place where you do operations that might result in floating point values and do explicit rounding in C++, and see if that fixes it.

Alex

Share this post


Link to post
Share on other sites
Quote:
Original post by alexmoura
One thing that may be causing your problems, is conversions - vb rounds values on conversions, while C++ truncates values, if I recall correctly - check on any place where you do operations that might result in floating point values and do explicit rounding in C++, and see if that fixes it.

Alex
Doesn't seem to fix it. I seem to recall that in VB the operator "\" is 'integer' division and "/" is, er, not. Browsing the MSDN though, it didn't actually seem to want to tell me what that actually means. :/

Share this post


Link to post
Share on other sites
to me it seems like your picture and transfer object in the c++ code are the same, what could cause these strange rotation view, while in vb this may be possible cause images get updated evry frame and keep their pixel values till the next update or what if the picture is drawn while the pixelvalues are changed, you would get an pretty ugly picture like what you get now

sry for my bad english, i hope it helped

Share this post


Link to post
Share on other sites
'\' gives you only the integer part, like 10 \ 3 = 3

'/' gives you a floating point result, like 10 / 3 = 3.333333 (normal division)

Share this post


Link to post
Share on other sites
\ in vb means that whenever you use it, the type of the result is an integer type (long, integer, etc) - I think it will also do the rounding instead of truncating - in any case, you have several other places where you cast floats to integers, have you tried also make those rounding? (especially the lines:
x1 = r * cos(a+angle);
y1 = r * sin(a+angle);
)

also, how are you doing the rounding?

Share this post


Link to post
Share on other sites

inline double roundNum(const double &num)
{
if(num-(int)num>=0.5)
{
return ceil(num);
}
else
{
return floor(num);
}
}

inline ErrorType rotateImage(MyPicture* image, MyPicture* transfer, double angle)
{
if(!image || !transfer)
return FAILURE;

int imgHalfWidth = image->width/2;
int imgHalfHeight = image->height/2;

int transferHalfWidth = transfer->width/2;
int transferHalfHeight = transfer->height/2;

int x1;
int y1;
double a;
int r;

int c0, c1, c2, c3;
int n;

if(transferHalfWidth>transferHalfHeight)
n = transferHalfWidth;
else
n = transferHalfHeight;

image->LockBackSurface();
transfer->LockBackSurface();
for(int x2=0;x2<=n;++x2)
{
for(int y2=0;y2<=n;++y2)
{
if(x2==0)
a = M_PI_2;
else
a = atan(double(y2 / x2));

r = (int)roundNum(sqrt(double(x2*x2+y2*y2)));
x1 = (int)roundNum(r * cos(a+angle));
y1 = (int)roundNum(r * sin(a+angle));

c0 = image->GetPoint(imgHalfWidth+x1, imgHalfHeight+y1);
c1 = image->GetPoint(imgHalfWidth-x1, imgHalfHeight-y1);
c2 = image->GetPoint(imgHalfWidth+y1, imgHalfHeight-x1);
c3 = image->GetPoint(imgHalfWidth-y1, imgHalfHeight+x1);

if(c0!=-1) transfer->DrawPoint(transferHalfWidth+x2, transferHalfHeight+y2, c0);
if(c1!=-1) transfer->DrawPoint(transferHalfWidth-x2, transferHalfHeight-y2, c1);
if(c2!=-1) transfer->DrawPoint(transferHalfWidth+y2, transferHalfHeight-x2, c2);
if(c3!=-1) transfer->DrawPoint(transferHalfWidth-y2, transferHalfHeight+x2, c3);

}
}
transfer->UnlockBackSurface();
image->UnlockBackSurface();

return SUCCESS;
}



I couldn't see any predefined round() function, so I made my own. Tested with rounding on and off the the *HalfWidth variables as well, doesn't appear to make any kind of noticable difference

Share this post


Link to post
Share on other sites
little trick for random function :) (yours works fine, but this one should be faster):
return cint(i + 0.5)

the only thing I can see that's different is that you're not setting n= n-1 on the C++ code, but I don't know if that's intentional - it would seem to imply that since both for cycles are zero based, you're going one extra point on the C++ cycles, but I've got a nagging feeling I'm missing something and that was intentional :)

Share this post


Link to post
Share on other sites
bah! I should have thought of that, I've seen it before as well, that little trick. Cheers :)

as for not setting n=n-1; I've tried that, no visible difference. I suspect in the vb code it's there before the for next goes from "0 to n", whereas the c++ for next is "while x<n" If you see what I mean.

Share this post


Link to post
Share on other sites
in vb, for x= o to n will go through all the values including n - the truth is that it probably doesn't change the end result, as long as it's inside the image - the original code might just not be translating the right edge of the picture.

there's one last thing that I could see being the issue, if for any reason on the C++ code you're invoking the method with different sized images, but that sounds unlikely.

What I'd suggest now would be to start printing values for identical inputs and find out where they differ :)

Sorry I couldn't be of more help.

Share this post


Link to post
Share on other sites
hey hey, I thought of that, but the methods use different formats to store the numbers inside an int. Theres also the problem of which pixel(s) do I choose to print out? it's going through a loop every frame. In VB it's about 30fps if the framerate counter is accurate (I have no idea). In C++ it's 300fps (and that I know is accurate).

Share this post


Link to post
Share on other sites
The idea was more to copy the methods to separate projects that you can actually debug (no actual graphics), and debug through a single call.

You could also trigger the printing for a given angle and print only once for only that angle on some file...

In any case, you should print the output for a whole image - print out cycle position in the file as a header(p2x, p2y), and then all the values used inside the fors.

Share this post


Link to post
Share on other sites
Alex, I fixed the problem! Well, a friend did. Our initial line of thinking in that there were rounding errors was correct, but we were looking at the wrong numbers:

Offending line:

a = atan(double(y2 / x2));


s3xy c0d3:

a = atan((double)y2 / (double)x2);

Share this post


Link to post
Share on other sites
Calculating the inverse tangent, sine, cosine, a square root, a division and four multiplications and a dozen additions in the inner-loop is a little extreme. With a "rotozooming" based approach you could cut that down to two additions instead.

Here's a pseudo-code-ish version of how such a function might look. It's probably buggy and you'll need to fill in some of the blanks but I hope you get the idea.
innerDeltaU = cos(angle);
innerDeltaV = sin(angle);
outerDeltaU = innerDeltaV; // the inner delta rotated 90 degrees
outerDeltaV = -innerDeltaU;

outerU = -0.5 * (innerDeltaU * xRes + outerDeltaU * yRes);
outerV = -0.5 * (innerDeltaV * xRes + outerDeltaV * yRes);

for(y = 0; y < yRes; ++y) {
innerU = outerU;
innerV = outerV;

for(x = 0; x < xRes; ++x) {
plotPixel(x, y, getPixel(innerU, innerV));

innerU += innerDeltaU;
innerV += innerDeltaV;
}

outerU += outerDeltaU;
outerV += outerDeltaV;
}
Of course if your current function is already fast enough then it might not be worth the effort but it's still an interesting alternative.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dan Forever
Alex, I fixed the problem! Well, a friend did. Our initial line of thinking in that there were rounding errors was correct, but we were looking at the wrong numbers:

Offending line:

a = atan(double(y2 / x2));


s3xy c0d3:

a = atan((double)y2 / (double)x2);


Even better:
a = atan2( y2, x2 );


faster and more accurate ( both in digits of precision and in sign of the angle ).

Share this post


Link to post
Share on other sites
Original post by Dan Forever
Alex, I fixed the problem! (...)quote]

Gotcha - it was doing integer division also and while for closer to 90 and 270 degrees the difference wasn't that much, as it approached ~70 degrees it would start getting stripped (actually, it would always have the save value below 45degees :) )

Glad it's fixed.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement