Transparent Blits

Hi!
I’m currently writing a piece of code, where I have to copy
several loaded images with an alpha channel into one offscreen
surface, before I blit the stuff to the screen. I want to do some
things with the alpha before that. But I don’t get the images
blended into the offscreen surface. If I’m blitting them directly
to the screen, they act, like they should, because the screen
has no alpha channel, but the offscreen surface has (and has to!).
How can I get the other images blended into the buffer correctly?
I havn’t set any of the SDL_SRCALPHA, like said in the docs.

Thanks in advance,
Andreas Podgurski

I’m currently writing a piece of code, where I have to copy
several loaded images with an alpha channel into one offscreen
surface, before I blit the stuff to the screen. I want to do some
things with the alpha before that. But I don’t get the images
blended into the offscreen surface. If I’m blitting them directly
to the screen, they act, like they should, because the screen
has no alpha channel, but the offscreen surface has (and has to!).
How can I get the other images blended into the buffer correctly?
I havn’t set any of the SDL_SRCALPHA, like said in the docs.

If you want images blended into your off-screen buffer, set SDL_SRCALPHA
on the source. Unfortunately the resulting alpha will be that of the
destination, not a combined alpha channel. I’m not entirely happy about that
so if you want a particular behaviour, say which one and why and we might
change it.

If you just want to copy a surface (including alpha), make sure SDL_SRCALPHA
is not set. SDL_image will set it automatically on surfaces having an
alpha channel

I’m currently writing a piece of code, where I have to copy
several loaded images with an alpha channel into one offscreen
surface, before I blit the stuff to the screen. I want to do some
things with the alpha before that. But I don’t get the images
blended into the offscreen surface. If I’m blitting them directly
to the screen, they act, like they should, because the screen
has no alpha channel, but the offscreen surface has (and has to!).
How can I get the other images blended into the buffer correctly?
I havn’t set any of the SDL_SRCALPHA, like said in the docs.

If you want images blended into your off-screen buffer, set SDL_SRCALPHA
on the source. Unfortunately the resulting alpha will be that of the
destination, not a combined alpha channel. I’m not entirely happy about that
so if you want a particular behaviour, say which one and why and we might
change it.
At least an additive blitting would be needed. This means, RGB pixels are
overwritten and alpha values are added. In my project this would be useful
e.g. to add several circles with antialiased borders into one surface, before
they are blitted to the screen.
Another thing I am missing is the possibility for target color keying. To be true,
I have always asked myself, what this feature (possible in DirectX I believe)
is good for, but after exploring the files of baldurs gate with the infinity explorer,
I learned, that this is VERY useful for watersurfaces or other big animated
surfaces.
Did I mentioned the hope for mask blittings with external masks? :wink:

Regards,
Andreas PodgurskiOn Sun, 5 Nov 2000 21:51:02 +0100 (MET), Mattias Engdeg?rd wrote:

At least an additive blitting would be needed. This means, RGB pixels are
overwritten and alpha values are added. In my project this would be useful
e.g. to add several circles with antialiased borders into one surface, before
they are blitted to the screen.

Just to make it clear, you are asking for

dest_RGB := src_RGB
dest_alpha := min(src_alpha + dest_alpha, 1.0),

right? It doesn’t quite seem right to me, since a low-alpha value written
over an opaque pixel would suddenly become entirely opaque (and thus have
its appearence changed completely). Or do you mean that the RGB values should
be added as well:

dest_RGB := min(src_RGB + dest_RGB, 1.0)
dest_alpha := min(src_alpha + dest_alpha, 1.0)

The above is the standard additive blitting (a.k.a. lighting, since it
simulates the result of two spots of light cast at the same place on a
white wall).

To composite several anti-aliased objects on an RGBA surface, people usually
want the classic alpha “over” operator, namely

dest_RGB := src_RGB * src_alpha + dest_RGB * (1 - src_alpha) * dst_alpha
dest_alpha := src_alpha + (1 - src_alpha) * dst_alpha

and this is all good, except that the resulting RGB colours now are
premultiplied and must be divided by the resulting alpha in order to
be blittable again. (If we start supporting premultiplied alpha in SDL
one day, and I’m getting more and more convinced that this is a good
thing, then this isn’t a problem.)

Another thing I am missing is the possibility for target color keying. To be true,
I have always asked myself, what this feature (possible in DirectX I believe)
is good for, but after exploring the files of baldurs gate with the infinity explorer,
I learned, that this is VERY useful for watersurfaces or other big animated
surfaces.

I presume you mean that a pixel should be drawn if and only if the
corresponding destination pixel is equal to a certain value. I can
certainly see the utility in this, but I wonder how well it could be
accelerated in hardware. Also, the naive implementation:

for(each x,y in image) {
if(destination(x,y) == colour_key)
destination(x,y) := source(x,y)
}

uses a read-modify-write cycle which would be nice to avoid. Perhaps
a target RLE mask would be useful for this?

Did I mentioned the hope for mask blittings with external masks? :wink:

Please explain this as well.

Cheers,

Mattias.

At least an additive blitting would be needed. This means, RGB pixels are
overwritten and alpha values are added. In my project this would be useful
e.g. to add several circles with antialiased borders into one surface, before
they are blitted to the screen.

Just to make it clear, you are asking for

dest_RGB := src_RGB
dest_alpha := min(src_alpha + dest_alpha, 1.0),

In fact, this would do, what I want, but this

dest_RGB := src_RGB * src_alpha + dest_RGB * (1 - src_alpha) * dst_alpha
dest_alpha := src_alpha + (1 - src_alpha) * dst_alpha

sounds to me more right, too. I don’t have overlapping parts, so this seems to
be the more general version, but it has a lot of multiplications in it… Precalculation
should be fine so far. To get it right: Would a full non-transparent part be still
non transparent in the target, if the target is fully transparent there?

and this is all good, except that the resulting RGB colours now are
premultiplied and must be divided by the resulting alpha in order to
be blittable again. (If we start supporting premultiplied alpha in SDL
one day, and I’m getting more and more convinced that this is a good
thing, then this isn’t a problem.)
Definitly, to have real use of alpha blitting, it should be implemented.

Another thing I am missing is the possibility for target color keying. To be true,
I have always asked myself, what this feature (possible in DirectX I believe)
is good for, but after exploring the files of baldurs gate with the infinity explorer,
I learned, that this is VERY useful for watersurfaces or other big animated
surfaces.
I presume you mean that a pixel should be drawn if and only if the
corresponding destination pixel is equal to a certain value. I can
certainly see the utility in this, but I wonder how well it could be
accelerated in hardware. Also, the naive implementation:

for(each x,y in image) {
if(destination(x,y) == colour_key)
destination(x,y) := source(x,y)
}

uses a read-modify-write cycle which would be nice to avoid. Perhaps
a target RLE mask would be useful for this?
That is supported by DirectDraw, remember, that I have used it once, so
I assume, it is hardware accelerated. This RLE-mask thing is, I believe,
what I ment next:

Did I mentioned the hope for mask blittings with external masks? :wink:
This is, to use a keymask (blit or transparent) or even an alpha mask for
copying one image into another with exchangeble masks. This is the way,
we masked in the long forgotten amiga times. It prevents from wasting a
specific color for keying, which is hard to keep with ray-traced graphics.

Regards,
Andreas PodgurskiOn Mon, 6 Nov 2000 11:36:18 +0100 (MET), Mattias Engdeg?rd wrote:

dest_RGB := src_RGB * src_alpha + dest_RGB * (1 - src_alpha) * dst_alpha
dest_alpha := src_alpha + (1 - src_alpha) * dst_alpha

sounds to me more right, too. I don’t have overlapping parts, so this seems to
be the more general version, but it has a lot of multiplications in it…

Actually it’s the division needed when we don’t use premultiplied
alpha that’s the killer. But it is true that even multiplies are bad
enough, so if you think you can cheat by doing something cheaper
it’s even better.

How about:

dest_RGB := src_RGB
dest_alpha := max(src_alpha, dst_alpha)

? That doesn’t attempt to mix the colours, but the combination is far
simpler. It has still got that evil read-modify-write cycle, but it’s
perhaps an improvement.

Precalculation

should be fine so far. To get it right: Would a full non-transparent part be still
non transparent in the target, if the target is fully transparent there?

Sure. Look at the formula above: You have src_alpha=1.0, dest_alpha=0.0,
so dest_alpha is set to 1 + (1 - 1) * 0 = 1, that is fully opaque.

That is supported by DirectDraw, remember, that I have used it once, so
I assume, it is hardware accelerated. This RLE-mask thing is, I believe,
what I ment next:

Did I mentioned the hope for mask blittings with external masks? :wink:

I was rather thinking about using an RLE-coded destination mask,
like the RLE of a source surface but without the pixel data. In other
words, just a way of storing the shape without any colour.

SDL’s RLE data is right now stored as

(well, alpha-channeled RLE data has a slightly more complicated format).
We could create an abstract object, SDL_Mask, that specifies which
pixels a blit applies to. It could be stored as a traditional bitmap,
or in RLE form:

It can even be stored in a hardware surface for accelerated blits,
and we could make a Fill primitive that fills a masked region with
a single colour, or with a tiled surface… The possibilities!
And looking forward, we can augment this object to support partial
transparencies as well (alpha channel). This simple object can
encapsulate a lot of interesting capabilities of modern hardware.

We can even generalise our current colourkeyed or alpha-channeled
blits to take a source and destination mask object, and voil?, a
vastly powerful blitting call — with a lot of acceleration
potential.

dest_RGB := src_RGB * src_alpha + dest_RGB * (1 - src_alpha) * dst_alpha
dest_alpha := src_alpha + (1 - src_alpha) * dst_alpha
sounds to me more right, too. I don’t have overlapping parts, so this seems to
be the more general version, but it has a lot of multiplications in it…
Actually it’s the division needed when we don’t use premultiplied
alpha that’s the killer. But it is true that even multiplies are bad
enough, so if you think you can cheat by doing something cheaper
it’s even better.
The multiplies can be accelerated with multables, but the division is hard. Maybe
it could be shifted, till it fits into a table? Just a thought, havn’t had a view on the
possible values…

How about:
dest_RGB := src_RGB
dest_alpha := max(src_alpha, dst_alpha)
? That doesn’t attempt to mix the colours, but the combination is far
simpler. It has still got that evil read-modify-write cycle, but it’s
perhaps an improvement.
For my needs that is ok and sounds quiet konstistent, too.

That is supported by DirectDraw, remember, that I have used it once, so
I assume, it is hardware accelerated. This RLE-mask thing is, I believe,
what I ment next:

Did I mentioned the hope for mask blittings with external masks? :wink:
I was rather thinking about using an RLE-coded destination mask,
like the RLE of a source surface but without the pixel data. In other
words, just a way of storing the shape without any colour.
It can even be stored in a hardware surface for accelerated blits,
and we could make a Fill primitive that fills a masked region with
a single colour, or with a tiled surface… The possibilities!
And looking forward, we can augment this object to support partial
transparencies as well (alpha channel). This simple object can
encapsulate a lot of interesting capabilities of modern hardware.
We can even generalise our current colourkeyed or alpha-channeled
blits to take a source and destination mask object, and voil?, a
vastly powerful blitting call — with a lot of acceleration
potential.
Yep, had something like that in mind. The limited blitting modes are
a big weakness of SDL, so if there’s a way to change that, SDL
can only win of it.
BTW: Have you read my mail about my OpenGL problems? I can’t
start a SDL-App with SDL_OPENGLBLIT as screen flag, a thing,
that should have change since 1.1.6…

Regards,
Andreas Podgurski

Howdy folks…

Don’t know if this is of much use but we were in need of some special blending modes which
supported “layered compositing” of RGBA images. As noted in the previous posts SDL didn’t
support this. I looked into it and what it came down to is a change to the ALPHA_BLEND
macro change in SDL_blit.h.

I would be nice if something along these lines gets back into SDL someday…

I implemented an “associative” blending as the following:

/**

  • This little macro correctly composites two RGBA pixels. Assuming
  • The source pixel (sR, sG, sB, sA) is to be layered on top
  • of the destination pixel (dR, dG, dB, dA). Pixel component
  • values are assumed to be 8-bit integers (0-255). All the
  • “+127” & “/255” calulations are for doing correct integer
  • arithmetic on these values.*
  • Note: There are two special cases which should be handled outside
  • the macro - if the source alpha (sA) equals 0 or 1. In these cases
  • (sa = 0) -> (dR = sR, dG = sG. dB = sB, dA = sA )
  • (sa = 1) -> (dR = dR, dG = dG. dB = dB, dA = dA )
  • For Further Reference:
  • Jim Blinn’s Corner - Dirty Pixels
  • Compositing - Theory (Chap 16, pg. 179)
  • Morgan Kaufman Publishers
  • ISBN: 1-55860-455-3
    /
    #define COMPOSITE_ALPHA_PIXELS(sR, sG, sB, sA, dR, dG, dB, dA)
    {
    unsigned cA = (dA + sA - ((dA * sA)+127)/255);
    dR = ((dA
    (255-sA) * dR + 127)/255 + sA * sR) / cA;
    dG = ((dA*(255-sA) * dG + 127)/255 + sA * sG) / cA;
    dB = ((dA*(255-sA) * dB + 127)/255 + sA * sB) / cA;
    dA = cA;
    }

Hope this helps (somebody)…

Cheers,

-Smitty


Pilot Tip #7. When in doubt, hold on to your altitude. No one has
ever collided with the sky.
      _____________________________________________________
       Dave Schmitz            Interact-TV
       smitty at interact-tv.com  http://www.interact-tv.com
       Ph: 720.406.9399        Fax: 720.406.8424

#define COMPOSITE_ALPHA_PIXELS(sR, sG, sB, sA, dR, dG, dB, dA)
{
unsigned cA = (dA + sA - ((dA * sA)+127)/255);
dR = ((dA*(255-sA) * dR + 127)/255 + sA * sR) / cA;
dG = ((dA*(255-sA) * dG + 127)/255 + sA * sG) / cA;
dB = ((dA*(255-sA) * dB + 127)/255 + sA * sB) / cA;
dA = cA;
}

The reason I didn’t implement this is the terrible cost. First, SDL
uses divisions by 256 rather than 255, since the former looks "good enough"
and is much faster. (There are ways to get increased accuracy without a
full division by using a power series expansion, but I had no compelling
reason to use them.)

What is really killing the above is the division by the resulting alpha
value, cA. Integer divisions are very slow even on modern processors,
and some cpus don’t even have divide instructions (ARM, Alpha, etc).
And you are doing three of them for every pixel. (Besides you
have to check for cA being zero, which requires a test and a conditional
branch per pixel.)

So while the above looks perfectly correct, it’s too slow. Using
premultiplied alpha would solve things