I believe that error is for the final_image type. The source and final_image likely have to be the same.
Can't blit_rect_mask
But they are. They are all FORMAT_RGB8
This is how I create the final_image:
var final_image = Image.new()
final_image.create(2176,1536,true,Image.FORMAT_RGB8)
final_image.copy_from(imag)
And the imag from which is copied, it is also a FORMAT_RGB8
- Edited
Yes, it is. I forgot to delete the 4 when I pasted the code here.
I will edit the original post.
So, the problem is not with that.
I think you have to lock() the image before doing any reads or writes on the pixel level (and then unlock() after the blit).
Actually, I tried it... But I guess I only did it for the main images (imag and final_image).
Probably, I have to do it for the source and mask images too.
I will give it a try, tonight, when I get back home.
Oh, about the mask image, I get a warning telling me that I should avoid loading it directly and I should do it as a resource. How can I do that?
I can blit without locking the images. However, the documentation on blit_rect_mask() says that it copies "if the corresponding mask pixel's alpha value is not 0", so there's no logical way you could use it with a mask that's in FORMAT_RGB8 format. It would have to be FORMAT_RGBA8. In my case the test images I used were also in FORMAT_RGBA8 format, so I had to use that all around to clear the error. Can you post the images you're using?
You can use an export variable in that script, and then select the Image/Texture in the editor. I am not sure if you can link Images directly. Most likely the export variable would be a Texture, and then you get the Image from that. Or you might be able to use Image directly, I haven't tried it.
Right, a blit mask uses the alpha channel, so that may be the source of the error.
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.