Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat
[2016.10] Sprite:setBlendMode(src, dst) - Gideros Forum

[2016.10] Sprite:setBlendMode(src, dst)

n1cken1cke Maintainer
edited November 2016 in Announcements
In Gideros 2016.10 `setBlendMode` has extended version with 2 parameters (blend functions): `src` and `dst`. They define how top sprite colors (`src`) will be blended with bottom sprite colors (`dst`). If you are familiar with image editors like Gimp or Photoshop you know they have many ready blend modes to mix layers in various ways for great effects. Those blend modes are just combinations of aforementioned blend functions so we have added direct support of those functions to Gideros.
There are 11 of them: Sprite.ZERO, Sprite.ONE, Sprite.SRC_COLOR, Sprite.ONE_MINUS_SRC_COLOR, Sprite.DST_COLOR, Sprite.ONE_MINUS_DST_COLOR, Sprite.SRC_ALPHA, Sprite.ONE_MINUS_SRC_ALPHA, Sprite.DST_ALPHA, Sprite.ONE_MINUS_DST_ALPHA, Sprite.SRC_ALPHA_SATURATE. This constants are represented in Lua as integers from 1 to 11. Therefore in Gideros we have 11 * 11 = 121 way to mix colors :)
To test extended `setBlendMode` you can use following snippet:
local topTex = Texture.new("top.png",true)
local btmTex = Texture.new("btm.png",true)
 
lh @ 10 -- label height
 
local w = (application:getDeviceWidth() - lh) / 11
local h = (application:getDeviceHeight() - lh) / 11
local s = math.min(w, h)
 
for x = 1, 11 do
	for y = 1, 11 do
		local top = Pixel.new(topTex, s, s)
		local btm = Pixel.new(btmTex, s, s)
		stage:addChild(btm)
		btm:addChild(top)
		btm:setBlendMode(x, y)
		btm:setPosition(lh + s * (x-1), lh + s * (y-1))
	end
end
 
local t = {}
for k,v in pairs(Sprite) do
	if tonumber(v) == v then t[v] = k end
end
 
for n = 1, 11 do
	local label = TextField.new(nil, t[n], "|")
	label:setX(lh + (n-1) * s + 2)
	label:setY(1)
	label:setClip(0, 0, s - 2, lh)
	stage:addChild(label)
	local line = Pixel.new(0x666666, 1, 1, lh + 11*s)
	line:setX(lh + n * s)
	stage:addChild(line)
end
 
for n = 1, 11 do
	local label = TextField.new(nil, t[n], "|")
	label:setY(lh + (n-0) * s - 2)
	label:setX(1)
	label:setRotation(-90)
	label:setClip(0, 0, s - 2, lh)
	stage:addChild(label)
	local line = Pixel.new(0x666666, 1, lh + 11*s, 1)
	line:setY(lh + n * s)
	stage:addChild(line)
end
Source images and screenshot are attached below but I suggest you to also experiment with other images to better understand how blend functions work.
blending.png
765 x 765 - 278K
btm.png
256 x 256 - 23K
top.png
256 x 256 - 2K
+1 -1 (+4 / -0 ) Share on Facebook
«1

Comments

  • AWESOME!
    My Gideros games: www.totebo.com
  • Hi. I'm trying to understand your example. It uses a lot of the newer features of Giderosmobile so it's really cool, but at the same time, slightly confusing for me.

    I can grasp the @ macro, Pixel and setBlendMode. What I don't get is the little bit in the middle.

    for k,v in pairs(Sprite) do
    if tonumber(v) == v then t[v] = k end
    end

    I don't think I've ever seen someone iterate over Sprite before. What exactly are you doing (besides generating the 11-item table)? Can you iterate over other classes/objects? Why does it show the BlendModes when you do this?

    Thanks in advance.

    John
  • n1cken1cke Maintainer
    @Greywine: I am just building reverse table: a table where keys are swapped with values.
    Since I know Sprite contains numeric constants (1 to 11) only for blend functions I can iterate over Sprite class and when value is a number I am adding it to reverse table: that number becomes a key and that Sprite key becomes a value. In the end I have following table:
    t = {
    [1] = "ZERO",
    [2] = "ONE",
    [3] = "SRC_COLOR"
    .....
    }
    And I use that strings as labels on the top and on the left side of the example app.
    In Gideros each Sprite class and subclass is represented as a Lua table. Try to iterate over some class with `for k,v in pairs(some_class) do print(k,v) end` and you will see it's methods, constants, etc. That's why we can easily create new subclasses: we are using usual Lua tables and we can store anything we want in them.
  • Iterating over classes: good stuff!

    I just wasn't sure why the Sprite class returns the blend constants, but once you know, it's definitely an elegant way to display everything.

    Great example and thanks for the explanation.
  • XmanXman Member
    edited November 2016
    isn't the current setBlendMode function the combination of original setBlendMode and setBlendFunc?

    At the beginning, only setBlendFunc is available, setBlendMode is just a simplified function for setBlendFunc most use case.

    If we need more blend modes, it's better to just make the setBlendFunc available for advanced users, and keep the setBlendMode simple .

    atilim discussed the idea here
    http://giderosmobile.com/forum/discussion/406/setblendfunc-help-/p1


  • n1cken1cke Maintainer
    edited November 2016
    @Xman: `setBlendMode` is overloaded method: one argument to set blend modes as before, two arguments to set blend functions.
    Do you mean it's better to create`setBlendFunc` method for this and avoid overloading?
  • XmanXman Member
    edited November 2016
    Exactly!
    setBlendFunc was set to nil after setBlendMode is created.
    It can be made available easily.


  • n1cken1cke Maintainer
    @Xman: see this commit:
    https://github.com/gideros/gideros/commit/0bfc155d7f1cb30598f4c9c4a7c6e71bf9bc2c33
    As you can see I have refactored Gideros blending source to make it much more clean and simple, without the need of extra utils like `bin2c` and lua-files. Also in this particular case overloading makes sense because setBlendMode(src, dst) still means you set blend mode, only composed from 2 blend functions. Compare it for example with overloaded `Sprite:setScale` where you can use both simplified 1-arg and more complex 2-arg versions.
  • XmanXman Member
    edited November 2016
    sprite.lua is not only a "complicated way" for the blending function, we can easily extends the api for Sprite without polluting the C++ code. It should not be deleted.
    The creator of the engine atilim won't do such a lot of work to just add a setBlendMode function instead of make the binder code complicated like this.
  • n1cken1cke Maintainer
    C++ code was not polluted because Lua C API is natural way for Lua to interact with lower level languages like C/C++ :D It is easy to write, it is loaded faster and it doesnt need external convertors to load Lua scripts. And 99% of Gideros source uses Lua C API so it is bin2c-scripts are polluting C++ code, not Lua C API ;)
  • XmanXman Member
    edited November 2016
    You may ask @atilim why some Lua functions are implemented in this way.
    I think @atilim won't make this modification according to the this thread:
    http://giderosmobile.com/forum/discussion/406/setblendfunc-help-/p1
    setBlendMode accept two parameters for blendfunc really NOT make sense.

    I think you can make the setBlendFunc be available in any way as you like, but just leave anything else as it was.
  • n1cken1cke Maintainer
    image
    pic.jpg
    460 x 523 - 20K
    pic.jpg 20.1K
  • Apollo14Apollo14 Member
    edited August 2018
    Hi guys!
    I'm trying to understand Blend Modes.
    How to choose blendmode{5,8}? (I've marked it on attached picture)
    I've tried
    local top = Bitmap.new(Texture.new("top.png",true))
    local btm = Bitmap.new(Texture.new("btm.png",true))
    stage:addChild(btm)
    stage:addChild(top)
     
    btm:setBlendMode(5,8)
    It doesn't give expected result.

    Btw, I've iterated through Sprite class, like in @n1cke 's first post:
    for k,v in pairs(Sprite) do
    	if tonumber(v) == v then print(k) end
    end
    I'm getting not 11, but 27 values (new values are STENCIL-related)
    Did these values break blendmode order of numbers?
    STENCIL_GEQUAL
    SRC_ALPHA_SATURATE
    DST_COLOR
    SRC_ALPHA
    STENCIL_EQUAL
    STENCIL_ZERO
    STENCIL_REPLACE
    STENCIL_NOTEQUAL
    STENCIL_LESS
    STENCIL_NEVER
    STENCIL_LEQUAL
    STENCIL_INVERT
    STENCIL_INCR_WRAP
    SRC_COLOR
    STENCIL_INCR
    ONE_MINUS_DST_ALPHA
    STENCIL_DECR_WRAP
    STENCIL_DECR
    ONE_MINUS_SRC_ALPHA
    ONE_MINUS_DST_COLOR
    ONE
    ZERO
    STENCIL_DISABLE
    DST_ALPHA
    ONE_MINUS_SRC_COLOR
    STENCIL_ALWAYS
    STENCIL_KEEP

    image
    what_blend_number.jpg
    765 x 765 - 430K
    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
    “The more you do coding stuff, the better you get at it.” - Aristotle (322 BC)
  • keszeghkeszegh Member
    edited March 9
    With default blending, putting a red thing with ~50% opacity on blue background gives me a color which is darker than any of the original colors. I'd prefer it to have an effect of the red dissolving on the blue. Is there a blending mode that can achieve that? (i'm sad and surprised that this is not how it behaves with the standard blending mode)

    see the attached image what happens now, the middle of the curve is 100% opacity and it is fading out gradually towards its ends. i hope you understand the effect i want to achieve instead of how it looks now.

    you can also notice that there is a boundary of the curve which tries to emulate anti-alias and that's also darker than both the curve's red color and the background's blue color, which is not the desired outcome.



    EDIT: i've checked and e.g. in Krita if i paint with this red on this blue then it also gives dark blue boundary instead of proper anti-aliasing, strange. yet i hope there is some combination of blend modes that gives what i'd like to achieve.
    redonblue.jpg
    1114 x 679 - 19K
  • keszeghkeszegh Member
    i don't believe that none of you have any ideas how to improve the blending on my figure, please help. (or should i make a separate topic for this?) thanks
  • MoKaLuxMoKaLux Member
    keszegh said:

    EDIT: i've checked and e.g. in Krita if i paint with this red on this blue then it also gives dark blue boundary instead of proper anti-aliasing, strange. yet i hope there is some combination of blend modes that gives what i'd like to achieve.

    If krita does it this way then I think you are stuck!?
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • rrraptorrrraptor Member
    edited March 12
    @keszegh I did a quick test:
    stage:addChild(Pixel.new(0x0000ff,1,128,128))
    stage:addChild(Pixel.new(0xff0000,0.5,128,64))
    local rt = RenderTarget.new(128,128,false)
    rt:draw(stage)
    rt:save("|D|test.png")
    The result color is (127,0,128) in 255 RGB format which is correct. So, maybe the problem is that it seems to you that the color is wrong?)
  • hgy29hgy29 Maintainer
    @keszegh, I tried to reproduce your issue with a simple code:
    local bg=Pixel.new(0xB0FF,1,300,100)
    stage:addChild(bg)
    local steps=20
    local stsz=300/steps
    for k=1,steps do
    	local s=Pixel.new(0xFF4000,1,stsz,100)
    	stage:addChild(s) s:setX((k-1)*stsz)
    	s:setAlpha((steps+1-k)/steps)
    end
    But it doesn't give the same results as you:


    GID1.png
    298 x 98 - 367B
  • keszeghkeszegh Member
    edited March 12
    @hgy29 , i'm not sure it's not the same result, the middle seems to be 'darker' in your case too.
    i use meshes though, so there may be a difference with a very low chance. i will try to make an example with both pixels and meshes.
    the issue is that i don't really know which would be the 'right' color halfway, but in any case something that does not give an antialias outline which feels darker, whatever this implies.
  • keszeghkeszegh Member
    edited March 14
    i've tried both pixels and meshes with the colors from my above example, it has the same result:
    i think the issue is that while this gradient is 'correct', it has darker colors in the middle than in the two endpoints. i've found some texts that seem to be somewhat related to this (perceived) brightness issue:
    https://medium.com/the-mvp/finally-a-definitive-way-to-make-gradients-beautiful-6b27af88f5f
    https://graphicdesign.stackexchange.com/questions/6246/if-these-colors-all-have-the-same-lightness-then-why-does-my-brain-tell-me-some
    but i did not find any solution to the issue using blend modes. neither do i know which would be a 'visually right' 'middle' color.


    Likes: MoKaLux

    Screenshot 2020-03-14 21.43.58.jpg
    333 x 199 - 2K
    +1 -1 (+1 / -0 ) Share on Facebook
  • keszeghkeszegh Member
    @rrraptor , many thanks, that's it, explained clearly. now browsing through it quickly the only thing i doubt is that there is a non-resource heavy solution (i.e. using blending or something like this) to create antialias border with gamma correction.
  • keszeghkeszegh Member
    thanks @rrraptor, i will try these shaders, as i'm anyhow not happy that i need 3x as many meshes for smooth borders, although it's not clear which is more taxing on hardware, an aa shader or +20000 meshes.

    another issue with the shader solution is that in my initial image you can see that strokes can fade out towards their end in my animation app, here also the non-correct gamma issue is apparent, and that won't be solved by an aa shader.

    it would be nice to be able to set what color mixing scheme (what is gamma) is used by the app, but i don't know if opengl can do something like this.

    ps: i am happy to be on this forum, lot's of knowledge here.

    Likes: MoKaLux

    +1 -1 (+1 / -0 ) Share on Facebook
  • rrraptorrrraptor Member
    edited March 15
    Also, added gamma correction shader:
    https://github.com/MultiPain/Gideros_examples/tree/master/GammaCorrection
    Its super simple :D

    Hm, the problem is that you cant apply shader to a Mesh :P
  • keszeghkeszegh Member
    @rrraptor , thanks for the gamma correction code as well, it helps to understand this. but in my case i cannot use it as i can't know from the final image (overlays of meshes) if a color of a pixel is intentionally dark or it is an overlay of two meshes with <100% opacity and so needs to be corrected.
  • keszeghkeszegh Member
    edited March 26
    finally having read the enlightening article recommended by @rrraptor and looking for a solution i've found this article: https://learnopengl.com/Advanced-Lighting/Gamma-Correction
    so wouldn't this little change help?:
    glEnable(GL_FRAMEBUFFER_SRGB);
    can you test it somehow, @hgy29 ?
    thanks guys
  • keszeghkeszegh Member
    basically what we need is that every sprite etc is converted to linear space from srgb, then compositing should be done in linear space and at the end only the final composite should be converted back to srgb before sending it to the screen. that would give gamma-correct results.
    for reference i attach a gradient between the two above colors which i think is the 'correct' one.
    gradient_good.jpg
    202 x 45 - 1K
  • keszeghkeszegh Member
    as a sidenote i vaguely remember that there was a way to change the default shader (the one which all sprites etc.s use). can you tell me if there is such a thing?
  • hgy29hgy29 Maintainer
    keszegh said:

    finally having read the enlightening article recommended by @rrraptor and looking for a solution i've found this article: https://learnopengl.com/Advanced-Lighting/Gamma-Correction
    so wouldn't this little change help?:
    glEnable(GL_FRAMEBUFFER_SRGB);
    can you test it somehow, @hgy29 ?
    thanks guys

    Tried, but no effect on my PC.

    For the Sprite's default shader: there is not one default shader but severals, nine I think. Gideros selects the appropriate shader depending n the sprite type and settings, but you can override the shader to use with a custom one on a sprite by sprite basis.

    Likes: MoKaLux

    +1 -1 (+1 / -0 ) Share on Facebook
Sign In or Register to comment.