Viewport + virtualresolution + tileimage

Archives Forums/BlitzMax Bug Reports/Viewport + virtualresolution + tileimage

ziggy(Posted 2010) [#1]
This code reproduces the issue:
Graphics(800, 600)
SetVirtualResolution(400, 300)
Local image:TImage = LoadImage("block0101.png")
SetViewport(100, 100, 300, 100)
While Not KeyHit(KEY_ESCAPE)
	Cls
	TileImage(image, 100, 100)
	Flip
Wend

The image is never drawn, or it is drawn with artifacts. Tested with a small PNG but I can see this being reproduced with any image, using any Max driver DX9, DX7 and GL.


marksibly(Posted 2010) [#2]
Ha!

This is gonna be *very* tricky to get right (knew I was gonna regret TileImage!) as viewport is in 'window coords', while image and supposedly tile x,y params are in 'virtual coords'.

There are therefore several ways to interpret what it should be doing.

Perhaps modifying viewport so it's in virtual coords too would be preferable?

Time to think about letter boxing too perhaps?


ziggy(Posted 2010) [#3]
I think modifing SetViewport would be a good idea but, anyway, in the example above we should see at last a 300x100 square of tiled images, but nothing is shown in my computer. Sometimes it works, but changing the width and height of the viewport does show the error.

This is what I get without SetViewPort:


And this with viewport uncommented:


We should, at last, see a square of tiles. I mean, coords could be wrong if it is using screen coords, but it should at last display a 300x100 square, shouldn't it?

[EDIT] Even using a 'virtual resolution' viewport I get the same issues:
'Armitage 1982 function for virtualviewport:
Function SetVirtualViewport(X:Double, Y:Double, width:Double, Height:Double)
	
	Local gcw:Double = VirtualResolutionWidth() / GraphicsWidth()
	Local gch:Double = VirtualResolutionHeight() / GraphicsHeight()
	
	X:/gcw
	Y:/gch
	width:/gcw
	Height:/gch
	
	SetViewport(ccRound(X, 0), ccRound(Y, 0), ccRound(width, 0), ccRound(Height, 0))

End Function

rem
	bbdoc: This function rounds a decimal value with a given digits precision.
end rem
Function ccRound:Double(number:Double, decimals:Byte)
	Local t:Long = 10 ^ decimals
	Local result:Double = Long(number * t + 0.5:Double * Sgn(number))
	Return result / Double(t)
End Function
So no idea what can be happening here...


Floyd(Posted 2010) [#4]
Having the viewport use virtual coordinates seems more consistent.

Something goes wrong when viewport x,y exceed the image width, height.

' Does two trials. The first is with ordinary graphics.
' The second uses SetVirtualResolution. In the second case
' SetViewPort gets confused about width and height.

Graphics( 800, 600)
HideMouse

Global image:TImage = CreateImage( 100, 100 )
BuildImage

Local x:Int = 0
Local trial:Int = 0

While True

	SetViewport( 0, 0, 800, 600 )
	Cls
	
	SetViewport( x, x, 200, 200 )
	x :+ 5 
	If x = 400 And trial = 0
		x = 0
		trial = 1
		SetVirtualResolution( 400, 300)
	End If	
	TileImage(image, 0, 0)
	Flip

	Delay 100
	If x = 400 And trial = 1 Then End

Wend


Function BuildImage( )
	Cls
	Local x:Int, y:Int
	For x = 0 To 99
		For y = 0 To 99
			SetColor 20 + x + y, 30 + 1.5*x, 40 + 2*y
			Plot x, y
		Next
	Next
	GrabImage image, 0, 0  
End Function



ziggy(Posted 2010) [#5]
Yes, that's another very good example of the same bug. It has nothing to do with the coordinates being used in viewport. The command itself with any values does not work properly when virtual resolution is "on"


marksibly(Posted 2010) [#6]
Hi,

Ok, Changing SetViewport in max2d.bmx to work with virtual coords fixes it (I think):

Function SetViewport( x,y,width,height )
	gc.viewport_x=x
	gc.viewport_y=y
	gc.viewport_w=width
	gc.viewport_h=height
	Local x0=Floor( x / gc.vres_mousexscale )
	Local y0=Floor( y / gc.vres_mouseyscale )
	Local x1=Ceil( (x+width) / gc.vres_mousexscale )
	Local y1=Ceil( (y+height) / gc.vres_mouseyscale )
	_max2dDriver.SetViewport x0,y0,(x1-x0),(y1-y0)
End Function


Note: Wont work with d3d7 driver as-is - you'll also need to change SetResolution in d3d7max2d.bmx to:

	Method SetResolution( width#,height# )
		Local gw=GraphicsWidth()
		Local gh=GraphicsHeight()
		Local world#[]=[..
			gw/width,0.0,0.0,0.0,..
			0.0,gh/height,0.0,0.0,..
			 0.0,0.0,1.0,0.0,..
			 0.0,0.0,0.0,1.0 ]
		device.SetTransform D3DTS_WORLD,world
		Local proj#[]=[..
			2.0/gw,0.0,0.0,0.0,..
			 0.0,-2.0/gh,0.0,0.0,..
			 0.0,0.0,1.0,0.0,..
			 -1-(1.0/gw),1+(1.0/gh),1.0,1.0]
		device.SetTransform D3DTS_PROJECTION,proj
	End Method



ziggy(Posted 2010) [#7]
It fix the visual bug :D BUT, In the other hand, it seems that sometimes the conversion of coordinates is one pixel smaller than it should be, I supose this is caused by rounding/truncation or something like that.

Is this possible?


ziggy(Posted 2010) [#8]
This is a sample showing the one pixel artifacts after latest fix:



marksibly(Posted 2010) [#9]
Hi,

Can you put together some sample code?

I have no idea what graphics/virtualgraphics/viewport etc settings you're using.

[edit]Actually, try changing the 'Ceils' to 'Floors' too[/edit]


ziggy(Posted 2010) [#10]
I'll provide some source code to you tomorrow (it is too late here and I should be sleeping since some hours ago).
Thanks for taking a look to this.


ziggy(Posted 2010) [#11]
@marksibly: While trying to write sample of the one pixel issue, I've noticed that if I ensure everything was properly rounded (instead of being truncated) in the game itself the issue seems to not replicate. So not sure there's anything to fix there, but I'll make further tests in the forthcoming days.

UPDATE: It seems like, as a side effect of this, when a graphics canvas is resized, the virtual resolution has to be set again, to maintain drawing properly.


TaskMaster(Posted 2010) [#12]
Yes, I discovered that as well. If you resize your canvas, you need to resize your virtual resolution.