Various fixes to alpha blending for pre-multipled alpha (as Cairo uses). Fix merge...
authorCarl Hetherington <cth@carlh.net>
Fri, 13 Jun 2014 11:01:39 +0000 (12:01 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 13 Jun 2014 11:01:39 +0000 (12:01 +0100)
src/lib/image.cc

index 8e7a51fd8d39371c278b65c125e710396565be21..2ce3738b9c5ac0af98b6bde390ecfe2caed8add8 100644 (file)
@@ -359,16 +359,19 @@ Image::make_transparent ()
 void
 Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
 {
-       int this_bpp = 0;
-       int other_bpp = 0;
+       assert (other->pixel_format() == PIX_FMT_RGBA);
+       int const other_bpp = 4;
 
-       if (_pixel_format == PIX_FMT_BGRA && other->pixel_format() == PIX_FMT_RGBA) {
+       int this_bpp = 0;
+       switch (_pixel_format) {
+       case PIX_FMT_BGRA:
+       case PIX_FMT_RGBA:
                this_bpp = 4;
-               other_bpp = 4;
-       } else if (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA) {
+               break;
+       case PIX_FMT_RGB24:
                this_bpp = 3;
-               other_bpp = 4;
-       } else {
+               break;
+       default:
                assert (false);
        }
 
@@ -389,13 +392,15 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
        }
 
        for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
-               uint8_t* tp = data()[0] + ty * stride()[0] + position.x * this_bpp;
+               uint8_t* tp = data()[0] + ty * stride()[0] + start_tx * this_bpp;
                uint8_t* op = other->data()[0] + oy * other->stride()[0];
                for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
                        float const alpha = float (op[3]) / 255;
-                       tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha;
-                       tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha;
-                       tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha;
+                       tp[0] = op[0] + (tp[0] * (1 - alpha));
+                       tp[1] = op[1] + (tp[1] * (1 - alpha));
+                       tp[2] = op[2] + (tp[2] * (1 - alpha));
+                       tp[3] = op[3] + (tp[3] * (1 - alpha));
+                       
                        tp += this_bpp;
                        op += other_bpp;
                }
@@ -656,6 +661,10 @@ merge (list<PositionImage> images)
                return PositionImage ();
        }
 
+       if (images.size() == 1) {
+               return images.front ();
+       }
+
        dcpomatic::Rect<int> all (images.front().position, images.front().image->size().width, images.front().image->size().height);
        for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
                all.extend (dcpomatic::Rect<int> (i->position, i->image->size().width, i->image->size().height));
@@ -664,7 +673,7 @@ merge (list<PositionImage> images)
        shared_ptr<Image> merged (new Image (images.front().image->pixel_format (), dcp::Size (all.width, all.height), true));
        merged->make_transparent ();
        for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
-               merged->alpha_blend (i->image, i->position);
+               merged->alpha_blend (i->image, i->position - all.position());
        }
 
        return PositionImage (merged, all.position ());