Right, a blit mask uses the alpha channel, so that may be the source of the error.
Can't blit_rect_mask
So, I will need to set ALL the images as FORMAT_RGBA8, right?
The main images, source, mask, etc.
- Edited
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.
- Edited
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).
- Edited
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.