root/src/add-ons/translators/wonderbrush/Layer.cpp
/*
 * Copyright 2006, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Stephan Aßmus <superstippi@gmx.de>
 */


#include "Layer.h"

#include <stdio.h>

#include <Bitmap.h>
#include <Catalog.h>
#include <Message.h>

#include "bitmap_compression.h"
#include "blending.h"
#include "lab_convert.h"
#include "support.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Layer"


// constructor
Layer::Layer()
        : fBitmap(NULL),
          fBounds(0.0, 0.0, -1.0, -1.0),
          fAlpha(1.0),
          fMode(MODE_NORMAL),
          fFlags(0)
{
}


// destructor
Layer::~Layer()
{
        delete fBitmap;
}


// Compose
status_t
Layer::Compose(const BBitmap* into, BRect area)
{
        if (!fBitmap || !fBitmap->IsValid()
                || (fBitmap->ColorSpace() != B_RGBA32
                        && fBitmap->ColorSpace() != B_RGB32))
                return B_NO_INIT;

        status_t status = B_BAD_VALUE;
        if (!into || !area.IsValid() || (status = into->InitCheck()) < B_OK)
                return status;

        // make sure we don't access memory outside of our bitmap
        area = area & fBitmap->Bounds();

        BRect r = ActiveBounds();
        if (!r.IsValid() || (fFlags & FLAG_INVISIBLE) || !r.Intersects(area))
                return B_OK;

        r = r & area;
        int32 left, top, right, bottom;
        rect_to_int(r, left, top, right, bottom);

        uint8* src = (uint8*)fBitmap->Bits();
        uint8* dst = (uint8*)into->Bits();
        uint32 bpr = into->BytesPerRow();
        src += 4 * left + bpr * top;
        dst += 4 * left + bpr * top;
        uint8 alphaOverride = (uint8)(fAlpha * 255);

        switch (fMode) {

                case MODE_SOFT_LIGHT:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                uint8 c1 = dstHandle[0] * srcHandle[0] >> 8;
                                                c1 += dstHandle[0] * (
                                                                255 - (
                                                                        (255 - dstHandle[0])
                                                                        * (255 - srcHandle[0])
                                                                        >> 8
                                                                ) - c1
                                                        ) >> 8;
                                                c1 = (c1 * dstHandle[3]
                                                                + srcHandle[0] * (255 - dstHandle[3])
                                                        ) >> 8;

                                                uint8 c2 = dstHandle[1] * srcHandle[1] >> 8;
                                                c2 += dstHandle[1] * (
                                                                255 - (
                                                                        (255 - dstHandle[1])
                                                                        * (255 - srcHandle[1])
                                                                        >> 8
                                                                ) - c2
                                                        ) >> 8;
                                                c2 = (c2 * dstHandle[3]
                                                                + srcHandle[1] * (255 - dstHandle[3])
                                                        ) >> 8;

                                                uint8 c3 = dstHandle[2] * srcHandle[2] >> 8;
                                                c3 += dstHandle[2] * (
                                                                255 - (
                                                                        (255 - dstHandle[2])
                                                                        * (255 - srcHandle[2])
                                                                        >> 8
                                                                ) - c3
                                                        ) >> 8;
                                                c3 = (c3 * dstHandle[3]
                                                                + srcHandle[2] * (255 - dstHandle[3])
                                                        ) >> 8;

                                                blend_colors(dstHandle,
                                                        (srcHandle[3] * alphaOverride) >> 8, c1, c2, c3);
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_LIGHTEN:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint8 c1
                                                        = (max_c(srcHandle[0], dstHandle[0]) * dstHandle[3]
                                                                + srcHandle[0] * (255 - dstHandle[3])) / 255;
                                                uint8 c2
                                                        = (max_c(srcHandle[1], dstHandle[1]) * dstHandle[3]
                                                                + srcHandle[1] * (255 - dstHandle[3])) / 255;
                                                uint8 c3
                                                        = (max_c(srcHandle[2], dstHandle[2]) * dstHandle[3]
                                                                + srcHandle[2] * (255 - dstHandle[3])) / 255;
                                                blend_colors(dstHandle,
                                                        (srcHandle[3] * alphaOverride) / 255, c1, c2, c3);
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_DARKEN:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint8 c1
                                                        = (min_c(srcHandle[0], dstHandle[0]) * dstHandle[3]
                                                                + srcHandle[0] * (255 - dstHandle[3])) / 255;
                                                uint8 c2
                                                        = (min_c(srcHandle[1], dstHandle[1]) * dstHandle[3]
                                                                + srcHandle[1] * (255 - dstHandle[3])) / 255;
                                                uint8 c3
                                                        = (min_c(srcHandle[2], dstHandle[2]) * dstHandle[3]
                                                                + srcHandle[2] * (255 - dstHandle[3])) / 255;
                                                blend_colors(dstHandle,
                                                        (srcHandle[3] * alphaOverride) / 255, c1, c2, c3);
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_REPLACE_RED:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint32 alpha = srcHandle[3] * alphaOverride;
                                                dstHandle[2] = (srcHandle[2] * alpha
                                                                + dstHandle[2] * (65025 - alpha)) / 65025;
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_REPLACE_GREEN:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint32 alpha = srcHandle[3] * alphaOverride;
                                                dstHandle[1] = (srcHandle[1] * alpha
                                                                + dstHandle[1] * (65025 - alpha)) / 65025;
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_REPLACE_BLUE:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint32 alpha = srcHandle[3] * alphaOverride;
                                                dstHandle[0] = (srcHandle[0] * alpha
                                                                + dstHandle[0] * (65025 - alpha)) / 65025;
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_MULTIPLY_INVERSE_ALPHA:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        // compose
                                        uint8 temp = min_c(dstHandle[3], 255 - srcHandle[3]);
                                        dstHandle[3] = (
                                                        dstHandle[3] * (255 - alphaOverride)
                                                        + temp * alphaOverride
                                                ) / 255;
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_MULTIPLY_ALPHA:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        // compose
                                        uint8 temp = min_c(dstHandle[3], srcHandle[3]);
                                        dstHandle[3] = (
                                                        dstHandle[3] * (255 - alphaOverride)
                                                        + temp * alphaOverride
                                                ) / 255;
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_LUMINANCE:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint8 r = dstHandle[2];
                                                uint8 g = dstHandle[1];
                                                uint8 b = dstHandle[0];
                                                uint8 alpha = dstHandle[3];
                                                replace_luminance(r, g, b, srcHandle[2], srcHandle[1],
                                                        srcHandle[0]);
                                                blend_colors(dstHandle,
                                                        (srcHandle[3] * alphaOverride) / 255, b, g, r);
                                                dstHandle[3] = alpha;
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_INVERSE_MULTIPLY:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint8 c1 = 255 - (
                                                                (((255 - srcHandle[0]) * (255 - dstHandle[0]))
                                                                        / 255) * dstHandle[3]
                                                                + (255 - srcHandle[0]) * (255 - dstHandle[3])
                                                        ) / 255;
                                                uint8 c2 = 255 - (
                                                                (((255 - srcHandle[1]) * (255 - dstHandle[1]))
                                                                        / 255) * dstHandle[3]
                                                                + (255 - srcHandle[1]) * (255 - dstHandle[3])
                                                        ) / 255;
                                                uint8 c3 = 255 - (
                                                                (((255 - srcHandle[2]) * (255 - dstHandle[2]))
                                                                        / 255) * dstHandle[3]
                                                                + (255 - srcHandle[2]) * (255 - dstHandle[3])
                                                        ) / 255;
                                                blend_colors(dstHandle,
                                                        (srcHandle[3] * alphaOverride) / 255, c1, c2, c3);
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_MULTIPLY:
                        for (; top <= bottom; top++) {
                                uint8* srcHandle = src;
                                uint8* dstHandle = dst;
                                for (int32 x = left; x <= right; x++) {
                                        if (srcHandle[3] > 0) {
                                                // compose
                                                uint8 c1 = (
                                                                ((srcHandle[0] * dstHandle[0]) / 255)
                                                                        * dstHandle[3]
                                                                + srcHandle[0] * (255 - dstHandle[3])
                                                        ) / 255;
                                                uint8 c2 = (
                                                                ((srcHandle[1] * dstHandle[1]) / 255)
                                                                        * dstHandle[3]
                                                                + srcHandle[1] * (255 - dstHandle[3])
                                                        ) / 255;
                                                uint8 c3 = (
                                                                ((srcHandle[2] * dstHandle[2]) / 255)
                                                                        * dstHandle[3]
                                                                + srcHandle[2] * (255 - dstHandle[3])
                                                        ) / 255;
                                                blend_colors(dstHandle,
                                                        (srcHandle[3] * alphaOverride) / 255, c1, c2, c3);
                                        }
                                        srcHandle += 4;
                                        dstHandle += 4;
                                }
                                src += bpr;
                                dst += bpr;
                        }
                        break;

                case MODE_NORMAL:
                default:
                        if (alphaOverride == 255) {
                                // use an optimized version that composes the bitmaps directly
                                for (; top <= bottom; top++) {
                                        uint8* srcHandle = src;
                                        uint8* dstHandle = dst;
                                        for (int32 x = left; x <= right; x++) {
                                                blend_colors(dstHandle, srcHandle);
                                                srcHandle += 4;
                                                dstHandle += 4;
                                        }
                                        src += bpr;
                                        dst += bpr;
                                }
                        } else {
                                for (; top <= bottom; top++) {
                                        uint8* srcHandle = src;
                                        uint8* dstHandle = dst;
                                        for (int32 x = left; x <= right; x++) {
                                                blend_colors(dstHandle, srcHandle, alphaOverride);
                                                srcHandle += 4;
                                                dstHandle += 4;
                                        }
                                        src += bpr;
                                        dst += bpr;
                                }
                        }
                        break;
        }

        return status;
}


// Unarchive
status_t
Layer::Unarchive(const BMessage* archive)
{
        if (!archive)
                return B_BAD_VALUE;

        // restore attributes
        float alpha;
        if (archive->FindFloat("alpha", &alpha) == B_OK) {
                constrain(alpha, 0.0, 1.0);
                fAlpha = alpha;
        } else
                fAlpha = 1.0;
        if (archive->FindInt32("mode", (int32*)&fMode) < B_OK)
                fMode = MODE_NORMAL;
        if (archive->FindInt32("flags", (int32*)&fFlags) < B_OK)
                fFlags = 0;

        // delete current contents
        delete fBitmap;
        fBitmap = NULL;

        status_t status = extract_bitmap(&fBitmap, archive, "current bitmap");
        if (status < B_OK)
                return status;

        // "bounds" is where the layer actually has content
        BRect bounds;
        if (archive->FindRect("bounds", &bounds) == B_OK)
                fBounds = bounds;
        else
                fBounds.Set(0.0, 0.0, -1.0, -1.0);

        // validate status of fBitmap
        if (!fBitmap)
                return B_ERROR;

        status = fBitmap->InitCheck();
        if (status < B_OK) {
                delete fBitmap;
                fBitmap = NULL;
        }

        return status;
}