Right, a blit mask uses the alpha channel, so that may be the source of the error.

rui_mac

I think they mean to just set the import as image, and load it as

var mask = load("res://images/mask4.png")

That's how I got rid of the error.

So, I will need to set ALL the images as FORMAT_RGBA8, right?
The main images, source, mask, etc.

    rui_mac

    I can't swear to that. I did a test, to try to figure the process out for myself. I got the same errors and warnings you did, using something like what you posted above. This is what I ended up with that worked, actually masking the image. Note that both images are imported as "image".

    func _ready():
    	var final_image = Image.new()
    	final_image.create(64,64,true,Image.FORMAT_RGBA8)
    	var mask = load("res://icon2.png")
    	var source = load("res://icon.png")
    	final_image.blit_rect_mask(source,mask,Rect2(0,0,64,64),Vector2(0,0))
    
    	# This is all just to show the images.
    	var t1 = ImageTexture.new()
    	var t2 = ImageTexture.new()
    	var t3 = ImageTexture.new()
    	t1.create_from_image(source)
    	t2.create_from_image(mask)
    	t3.create_from_image(final_image)
    	var tr1 = TextureRect.new()
    	var tr2 = TextureRect.new()
    	var tr3 = TextureRect.new()
    	tr1.texture = t1
    	tr2.texture = t2
    	tr3.texture = t3
    	tr2.rect_position = Vector2(80, 0)
    	tr3.rect_position = Vector2(160, 0)
    	add_child(tr1)
    	add_child(tr2)
    	add_child(tr3)

    Thank you so much, Duane. I will give it a try, when I get home.

    Just tried it and it works without errors... except... I was expecting that the values of the alpha would be taken into account.
    I even switched to using blend_rect_mask instead of blip_rect_mask.
    But it seems that the pixels are "blended" at full 100% opacity, for any value of the mask that is not 0.
    Isn't there a way to blend the pixels depending on the luminosity value of the alpha?

    Maybe this sheds some light into the problem.
    Even if I create my images set to Image.FORMAT_RGBA8, when I check for an alpha with print(img.detect_alpha()), I get 0... I mean, NO ALPHA!!!
    Even if I perform an img.convert(Image.FORMAT_RGBA8) right before the print(img.detect_alpha()), I still get 0, as in... NO ALPHA!!
    Why don't my images, created with img.create(128,512,true,Image.FORMAT_RGBA8) get any alpha?

    There is no blending. It is a 1-bit mask (either visible or invisible).

    You can always do it in code -- it's not complicated -- it's just not going to be as fast. Unless you're doing thousands of blends, I can't see it being a major problem.

    This does an alpha-based mask in about 20ms for a set of 64x64 images, so about 0.32 seconds for your example.

    func _ready():
    #	var final_image = Image.new()
    	var final_image = load('res://test_sheet.png')
    #	final_image.create(64,64,true,Image.FORMAT_RGBA8)
    #	var mask = load("res://icon2.png")
    	var source = load("res://icon.png")
    #	final_image.blit_rect_mask(source,mask,Rect2(0,0,64,64),Vector2(0,0))
    
    	var bl = Blender.new()
    	var mask = bl.gradient()
    
    	var t = OS.get_ticks_msec()
    	var comb = bl.blend(source, mask, final_image, Rect2(64,32,64,64), Vector2(64,32))
    	print('completed blend in %0.2fms' % [OS.get_ticks_msec() - t])
    
    	var t1 = ImageTexture.new()
    	var t2 = ImageTexture.new()
    	var t3 = ImageTexture.new()
    	t1.create_from_image(source)
    	t2.create_from_image(mask)
    	t3.create_from_image(comb)
    	var tr1 = TextureRect.new()
    	var tr2 = TextureRect.new()
    	var tr3 = TextureRect.new()
    	tr1.texture = t1
    	tr2.texture = t2
    	tr3.texture = t3
    	tr2.rect_position = Vector2(80, 0)
    	tr3.rect_position = Vector2(160, 0)
    	add_child(tr1)
    	add_child(tr2)
    	add_child(tr3)
    # blender.gd -- a simple, blend with an alpha mask
    
    extends Reference
    class_name Blender
    
    
    func gradient() -> Image:
    	# I can't get gimp to spit out a real alpha
    	#  gradient, so I'm making one here.
    
    	var grad := Image.new()
    	grad.create(64,64,true,Image.FORMAT_RGBA8)
    	var dat:PoolByteArray = []
    	for i in range(0, 64*64*4, 4):
    		var x = int(i / 4.0) % 64
    		dat.append(0)
    		dat.append(0)
    		dat.append(0)
    		dat.append(int(255 * x / 64.0))
    
    	grad.create_from_data(grad.get_width(), grad.get_height(),
    		false, grad.get_format(), dat)
    
    	return grad
    
    
    func blend(source:Image, mask:Image, final:Image, rect:Rect2, pos:Vector2) -> Image:
    	# This takes the source image according to the
    	#  alpha of the mask, and combines it with the
    	#  final image, creating and returning a new image.
    
    	# The source and mask must be the same size.
    	# *ALL* images must be in RGBA8 format.
    
    	var combine := Image.new()
    	var finalc := Image.new()
    	var out := Image.new()
    	var source_dat:PoolByteArray
    	var mask_dat:PoolByteArray
    	var final_dat:PoolByteArray
    	var combine_dat:PoolByteArray = []
    	var height = source.get_height()
    	var width = source.get_width()
    
    	finalc.create(width, height, true, Image.FORMAT_RGBA8)
    	finalc.blit_rect(final, Rect2(rect.position, Vector2(width, height)), Vector2.ZERO)
    
    	source_dat = source.get_data()
    	mask_dat = mask.get_data()
    	final_dat = finalc.get_data()
    
    #	print(source.get_format())
    	for i in range(0, len(source_dat), 4):
    		var alpha:float = mask_dat[i+3] * source_dat[i+3] / 255.0 / 255.0
    		var ialpha:float = 1.0 - alpha
    
    		combine_dat.append(int(source_dat[i+0] * alpha + final_dat[i+0] * ialpha))
    		combine_dat.append(int(source_dat[i+1] * alpha + final_dat[i+1] * ialpha))
    		combine_dat.append(int(source_dat[i+2] * alpha + final_dat[i+2] * ialpha))
    		combine_dat.append(int(alpha * 255.0 + final_dat[i+3] * ialpha))
    
    		if false and alpha > 0:
    			print('%0.2f, %0.2f' % [alpha, ialpha])
    			print('%d, %d, %d, %d' % [mask_dat[i+0], mask_dat[i+1], mask_dat[i+2], mask_dat[i+3]])
    			print('%d, %d, %d, %d' % [source_dat[i+0], source_dat[i+1], source_dat[i+2], source_dat[i+3]])
    			print('%d, %d, %d, %d' % [final_dat[i+0], final_dat[i+1], final_dat[i+2], final_dat[i+3]])
    			print('%d, %d, %d, %d' % [combine_dat[i+0], combine_dat[i+1], combine_dat[i+2], combine_dat[i+3]])
    
    	combine.create_from_data(width, height,
    		false, source.get_format(), combine_dat)
    
    	out.copy_from(final)
    	out.blit_rect(combine, Rect2(Vector2.ZERO, rect.size), pos)
    
    	return out

    Edit: Now it takes the correct piece of the final image, instead of assuming it will be at (0,0).

    I know how to make it in code.
    But I need to make it for 7 * 128 * 512 pixels. So, almost half a million pixels.
    That is why I was hopping to use internal commands.
    It is a shame that the alpha is only one bit. 🙁

      You might have to use a shader.

      What is your end goal? Is it possible to preprocess the images? For example, using get_pixel() / set_pixel() on setup and then cache the textures? Or do you need to do this in real time?

      Actually, I don't need it in realtime. I can pre-process it. But I'm doing lots of stuff, so I need all my processes to be as fast as possible, as it adds up.
      I guess I will be able to make it a bit faster by using the values of any of the channels of the mask as lookup table, to speed things a bit.

      So I would recommend using the image methods get_pixel/set_pixel. They are slow for real-time, but not that slow if you only do it sometimes on load. I think I did a 720P image and it was about half a second, that's about a million pixels. Then save it as a PNG image. That way you only have to recalculate if the source images have changed.

      I will do that. Of course, I can only try it tonight, after my day job 😉

      rui_mac But I need to make it for 7 * 128 * 512 pixels.

      Watch out, in markdown you make text strings italic if you surround them with asterisks without spaces. 😉