• Godot Help2D
  • Struggling with pixel manipulation using Image class

Hi, I'm using the Image class for manipulation of ImageTexture, and the result is really hard to understand. Specifically:

  • This is my code initiating the TextureRect node with an ImageTexture
    var canvas = $SubViewport/Canvas # TextureRect Node to a white
    var overlay = $SubViewport/OverlayLayer/Overlay # Overlay is also a TextureRect layered on top
    og = Image.create(canvas.get_size().x, canvas.get_size().y, false, Image.FORMAT_RGBA8)
    overlay.set_texture(ImageTexture.create_from_image(og))
  • This is the code for turning pixel (1,1) red
    og.set_pixelv(Vector2(1, 1), Color.RED)
    overlay.get_texture().update(og)
  • This is somehow the result I got:

I really don't understand why I got this, I really need someone to point out where I got it wrong.
Also, I'm on Godot 4.0.3. There is no tutorial on using this Image class that I could find useful, and the docs for it even have 1 method needing description.

  • When I went to the documentations, to the size property of the SubViewport, I saw this:

    Note: If the parent node is a SubViewportContainer and its SubViewportContainer.stretch is true, the viewport size cannot be changed manually.

    • So I tried to recreate the problem with enabling stretch and setting stretch_shrink of the container to 512.
    • Interestingly when I printed the SubViewport size, it returned (2, 2)
    • What surprised me, the blur around the pixel ... just didn't show up! I took no measures against it!

    Here is the result, with the scene tree and the output:

    And the code:

    extends SubViewportContainer
    
    
    @onready var canvas: TextureRect = $SubViewport/Canvas
    @onready var overlay: TextureRect = $SubViewport/Overlay
    
    
    func _ready() -> void:
    	var og: Image = Image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
    	overlay.texture = ImageTexture.create_from_image(og)
    	
    	og.set_pixelv(Vector2(1, 1), Color.RED)
    	overlay.texture.update(og)
    	
    	print($SubViewport.get_size())
    	print(overlay.get_size())
    	print(overlay.texture.get_size())
    	print("Stretch: ", stretch)
    	print("Stretch: ", stretch_shrink)
    	print("Container size: ", size)
    • Note: I set the layout of the container to be anchored and be a full rect

    • Even if I set the texture filter to linear, it just won't blur!

    • I do no more know what is actually happening, and also when I set the stretch in in the inspector, the SubViewport size is like this:

    • Do you perhaps stretch the container through code?

    I am sorry if any of the above wasn't helpful, I am not sure what is actually happening

If your problem is the blurred edges:

The solution is as simple as setting the overlay's texture filter from inherit to be nearest either from the inspector or through code

overlay.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST

Since your overlay is inside a SubViewport, and the default texture filter of that is Linear by default, and the overlay's texture filter is set to inherit (I will just filter textures as my parent does), it inherits Linear from the OverLayer which inherited it ultimately from SubViewport, if what I said makes any sense.

So you can alternative to the previous solution, in the inspector of the SubViewport under Viewport under Canvas Items set the default texture filter to nearest.

As far as I understand it, Nearest makes it so that a certain pixel does not affect adjacent pixels, so small drawings and pixel art are not blurred or smudged. While Linear makes the image smooth, so lines are not jagged or pixelated on larger scale images, but take that with a grain of salt.

If you problem is the position of the red "dot" taking half the canvas:

I think the SubViewport size is set to (2, 2) or something. Setting it to larger values, while also making sure to set the canvas to the same value will zoom the view out, and make the canvas use all of the viewport, I believe.
I hope this helps

It's actually both of them.

  • The blurry edges problem has been fixed by your solution.

  • The red "dot" taking half the canvas problem is really tricky: SubViewport size is controlled by the Viewport container and cannot be modified, its size fixed at:

    However, when I use print($SubViewport.get_size()) to get its size in code the output is (2, 2).
    I tried print($SubViewport.get_texture().get_size()) to get the size of the viewport texture in pixels (as per the documentation) but still got (2, 2) returned as the result. I then tried .get_width() and .get_height() on the texture and unsurprisingly got back two 2s.

    Also, my response is suddenly taking so long get published (keep getting “Waiting for approval”). Does anybody know why?
    *Response above still waiting to be approved

    thanhannam It's actually both of them.

    The blurry edges problem has been fixed by your solution.

    The red "dot" taking half the canvas problem is really tricky: SubViewport size is controlled by the Viewport container and cannot be modified, its size fixed at:

    However, when I use print($SubViewport.get_size()) to get its size in code the output is (2, 2).
    I tried print($SubViewport.get_texture().get_size()) to get the size of the viewport texture in pixels (as per the documentation) but still got (2, 2) returned as the result. I then tried .get_width() and .get_height() on the texture and unsurprisingly got back two 2s.

    That is my response above not getting approved. *Testing to see why this takes so long to get approved while my next reply got posted immediately

    When I went to the documentations, to the size property of the SubViewport, I saw this:

    Note: If the parent node is a SubViewportContainer and its SubViewportContainer.stretch is true, the viewport size cannot be changed manually.

    • So I tried to recreate the problem with enabling stretch and setting stretch_shrink of the container to 512.
    • Interestingly when I printed the SubViewport size, it returned (2, 2)
    • What surprised me, the blur around the pixel ... just didn't show up! I took no measures against it!

    Here is the result, with the scene tree and the output:

    And the code:

    extends SubViewportContainer
    
    
    @onready var canvas: TextureRect = $SubViewport/Canvas
    @onready var overlay: TextureRect = $SubViewport/Overlay
    
    
    func _ready() -> void:
    	var og: Image = Image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
    	overlay.texture = ImageTexture.create_from_image(og)
    	
    	og.set_pixelv(Vector2(1, 1), Color.RED)
    	overlay.texture.update(og)
    	
    	print($SubViewport.get_size())
    	print(overlay.get_size())
    	print(overlay.texture.get_size())
    	print("Stretch: ", stretch)
    	print("Stretch: ", stretch_shrink)
    	print("Container size: ", size)
    • Note: I set the layout of the container to be anchored and be a full rect

    • Even if I set the texture filter to linear, it just won't blur!

    • I do no more know what is actually happening, and also when I set the stretch in in the inspector, the SubViewport size is like this:

    • Do you perhaps stretch the container through code?

    I am sorry if any of the above wasn't helpful, I am not sure what is actually happening

    Thanh you very much, turning off the stretch property of the ViewportContainer solved my problem immediately.