26

I find it hard to describe this problem in words, which is why I made a video (45 seconds) to illustrate it. Here's a preview of the questions, please have a look at it on Vimeo: http://vimeo.com/epologee/perfect-crossfade

Using a linear crossfade the resulting image will have a 'dip' in transparency. How can I prevent this?

The issue of creating a flawless crossfade or dissolve of two images or shapes has been recurring to me in a number of fields over the last decade. First in video editing, then in Flash animation and now in iOS programming. When you start googling it, there are many workarounds to be found, but I really want to solve this without a hack this time.

The summary:
What is the name of the technique or curve to apply in crossfading two semi-transparent, same-colored bitmaps, if you want the resulting transparency to match the original of either one?

Is there a (mathematical) function to calculate the neccessary partial transparency/alpha values during the fade?

Are there programming languages that have these functions as a preset, similar to the ease in, ease out or ease in out functions found in ActionScript or Cocoa?

Update: In addition to the video, I've made a sample project (requires Xcode and iOS SDK) and posted it on github. It shows the same animation as the video but this time with squares: https://github.com/epologee/StackOverflow-Example-Code

epologee
  • 537
  • 4
  • 9
  • 7
    Don't know anything about it but +1 for the video. – sebastiangeiger Nov 12 '11 at 13:49
  • Depends on how the over operator is implemented. See http://en.wikipedia.org/wiki/Alpha_channel – wnrph Nov 12 '11 at 14:16
  • I agree, @artistoex, there's something to that over operator, thanks for the link! However, if the answer is buried in there somewhere, I'm not seeing it :( – epologee Nov 13 '11 at 16:48
  • The mathematical representation of the over operator leads to an equation. Example over operator: C=alpha*c1+(alpha-1)*c2 yields alpha=(C+c2)/(c1+c2) (C being the result of mixing the two colours c1, c2). This means for this over operator, there is no function, which satisfies your condition. – wnrph Nov 14 '11 at 08:03
  • For C = alpha1*c1+alpha2*c2 (0<=alpha1, alpha2<=1), there is such function: alpha1=(C-alpha2*c2)/c1. – wnrph Nov 14 '11 at 08:06
  • Now there's an answer! Could you post this in answer-form? It'd be worth the check mark. – epologee Nov 14 '11 at 10:35
  • the terms `ease in` and `ease out` is what is used to refer to the head and tail of the curves on the right –  Nov 16 '11 at 15:25
  • Those terms are already in the question. But that's not what this is about. Hmm. I think I have to rephrase the question and tweak the examples, because people keep answering to the curvature of one half, not the fact that the two curves blended together cause the dip. – epologee Nov 16 '11 at 21:03

5 Answers5

4

I am afraid that's not possible. This is how transparency is calculated when two objects overlay: http://en.wikipedia.org/wiki/Alpha_compositing

Alpha compositing

One of the objects must have an opacity of 1 if you don't want the overlay area to be seen through.

b123400
  • 156
  • 3
3

I don't know what the name might be in the realm of video editing, but I'd call the curve an S-curve or sigmoid curve. It should be very simple to produce the cross-fade you're looking for in iOS using Core Animation's kCAMediaTimingFunctionEaseInEaseOut timing function. Just animate the alpha property of two views in opposite directions (one 0->1, one 1->0) using that timing function:

[UIView beginAnimations:@"crossfade" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
view1.alpha = 0.0;
view2.alpha = 1.0;
[UIView commitAnimations];

Note: You should really use the blocks-based animation methods instead of UIView's convenience methods. I used UIView's methods above because it's very easy to understand, and easy to type from memory. Using blocks isn't much more difficult, though.

Addendum: If you don't like UIViewAnimationEaseInOut, you can create your own timing function as described in Timing, Timespaces, and CAAnimation. That's a bit more advanced than the simple animation illustrated above, but it gives you all the control you want.

Caleb
  • 38,959
  • 8
  • 94
  • 152
  • Sigmoid! That's already one step closer, thanks! The piece of code you provide doesn't work right away, for the setAnimationCurve takes a different parameter than `kCAMediaTimingFunctionEaseInEaseOut`. Could you have meant `UIViewAnimationCurveEaseInOut`? The problem with that curve is the same though (dip in the middle), because at any point in time, the two alpha values in a simple addition will equal `1.0`, where as in my manually tweaked curve it needs to exceed `1.0` to get the desired results. – epologee Nov 12 '11 at 14:39
  • I see now... you're right about the constant -- I've updated the code in the answer. I've also added a link to docs that explain how to create your own timing functions. – Caleb Nov 12 '11 at 15:11
  • I do my animations with blocks, yet the gist is the same. The built in curves still don't apply, because of their symmetry, but the addendum paved the way to a custom CAMediaTimingFunction that matched the one I used in the After Effects video. Troubling is that there is *not one sigmoid curve that fits every scenario*, different starting levels of opacity will require different bezier positions to get rid of the 'dip'. There must be something I'm missing. – epologee Nov 13 '11 at 16:46
  • Hi @Caleb, I posted a sample project on GitHub (see post). The custom timing is helpful, but if you change the `initialTransparency` value, it's no use. You wouldn't have a clue what to try next? – epologee Nov 13 '11 at 23:39
  • 3
    A few more little bits to help Google searches; graphics programmers call the graph that you're talking about a "smoothstep". Highly mathy programmers will often call it a "hermite interpolator". And a simple equation to calculate one without trigonometry is: "3t^2 - 2t^3". – Trevor Powell Nov 14 '11 at 00:10
1

In computer graphics, this kind of function is called smoothstep. When used to crossfade, it determines the global alpha value for the composition.

If the input images have an alpha channel, you should first make sure that the alpha is premultiplied. Then, you can do the crossfade composition straightforwardly, using the smoothstep alpha on each channel [X = A*alpha + B*(1-alpha)], and expect reasonable results.

comingstorm
  • 2,727
  • 1
  • 13
  • 14
1

While it's true that the perfect result cannot be achieved, it is also true that, as you have discovered, the problems can be mitigated somewhat.

And we can do something more formal than "some manual curve" (though that's fine in many cases, too).

The crux of the problem with the plain linear version is that at the midpoint, where you have each object half transparent, the full transparency dips low. Even using an unmodified smoothstep does not fix this, since it also has a crossing point at 0.5. This is made more clear with some graphs:

You can see a live version here, but here are some screenshots:

In these graphs, f5(x) and f6(x) are the final output alpha values, which we'd like to stay "1" across the fade.

Plain linear crossfade:

enter image description here

Plain smoothstep, with the linear version left for comparison:

enter image description here

As you can see, in both cases there is a "dip" in the value in the middle. In your "fixed" version from the question, the actual reason it works better is that the crossing point is above 0.5 - not due to the curves being smooth.

Here is a version using a circular easing function, so the crossing point is quite high:

enter image description here

As you can see, the dip is much less exaggerated, since the two "parent" easing curves cross at a much higher point.

What easing curves you want to use is up to you, of course, but hopefully this discussion helps ground those investigations.


Aside: I want to point out that there actually is a perfect solution, but unfortunately it is the step function — that is, just blinking from one to the other, without a smooth dissolve. Probably not very helpful, but it became apparent from fiddling with easing curves that it does in fact fit the bill (:

jwd
  • 111
  • 1
0

You can use the exponential decay function:

Alpha1(time) = 1 - exp(-time / (0.2 * transitionTime)); // fade in
Alpha2(time) = 1 - Alpha1(time); // fade out

Your graph looks more like:

Alpha1(time) = sin(time * 0.5 * pi / transitionTime); // fade in
Alpha2(time) = cos(time * 0.5 * pi / transitionTime); // fade out
Danny Varod
  • 1,148
  • 6
  • 14
  • Thanks @Danny. Actually, performing the `1 - Alpha1(time)` equation won't take care of the dip, because two blended 0.5 alpha images won't result in an composited image with 1.0 alpha. The custom curves I drew are not mirrored vertically. – epologee Nov 16 '11 at 11:10
  • 1
    The 1-alpha was for the exponential decay, which I think is more suitable. For the sin/cos functions of course sin(t)=sqrt(1-sqr(cos(t)), however, calculating each one separately should be faster. – Danny Varod Nov 16 '11 at 12:17
  • If the issue were what the function of a similar curve might look like, your math is correct. However the issue is how to deal with the blending of two colors, e.g. how to get 40% blue + 40% blue to result in 80% blue. – epologee Nov 16 '11 at 13:36
  • That is easy to do but would result in bad looking saturation when sum of colors > 100%. – Danny Varod Dec 03 '11 at 11:55