diff --git a/src/add-ons/translators/Jamfile b/src/add-ons/translators/Jamfile index 314faa66bd..df0e22d6fc 100644 --- a/src/add-ons/translators/Jamfile +++ b/src/add-ons/translators/Jamfile @@ -12,3 +12,4 @@ SubInclude HAIKU_TOP src add-ons translators rtf ; SubInclude HAIKU_TOP src add-ons translators sgi ; SubInclude HAIKU_TOP src add-ons translators stxt ; SubInclude HAIKU_TOP src add-ons translators tga ; +SubInclude HAIKU_TOP src add-ons translators wonderbrush ; diff --git a/src/add-ons/translators/wonderbrush/Canvas.cpp b/src/add-ons/translators/wonderbrush/Canvas.cpp new file mode 100644 index 0000000000..b6a5c971b8 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/Canvas.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Canvas.h" + +#include +#include + +#include +#include +#include + +#include "defines.h" + +#include "Layer.h" + +using std::nothrow; + +// constructor +Canvas::Canvas(BRect frame) + : BList(10), + fBounds(frame) +{ +} + +// destructor +Canvas::~Canvas() +{ + MakeEmpty(); +} + +// IsValid +bool +Canvas::IsValid() const +{ + return fBounds.IsValid(); +} + +// MakeEmpty +void +Canvas::MakeEmpty() +{ + int32 count = CountLayers(); + for (int32 i = 0; i < count; i++) + delete LayerAtFast(i); + BList::MakeEmpty(); +} + +// AddLayer +bool +Canvas::AddLayer(Layer* layer) +{ + return AddLayer(layer, CountLayers()); +} + +// AddLayer +bool +Canvas::AddLayer(Layer* layer, int32 index) +{ + return layer && AddItem((void*)layer, index); +} + +// RemoveLayer +Layer* +Canvas::RemoveLayer(int32 index) +{ + return (Layer*)RemoveItem(index); +} + +// RemoveLayer +bool +Canvas::RemoveLayer(Layer* layer) +{ + return RemoveItem((void*)layer); +} + +// LayerAt +Layer* +Canvas::LayerAt(int32 index) const +{ + return (Layer*)ItemAt(index); +} + +// LayerAtFast +Layer* +Canvas::LayerAtFast(int32 index) const +{ + return (Layer*)ItemAtFast(index); +} + +// IndexOf +int32 +Canvas::IndexOf(Layer* layer) const +{ + return BList::IndexOf((void*)layer); +} + +// CountLayers +int32 +Canvas::CountLayers() const +{ + return CountItems(); +} + +// HasLayer +bool +Canvas::HasLayer(Layer* layer) const +{ + return HasItem((void*)layer); +} + +// SetBounds +void +Canvas::SetBounds(BRect bounds) +{ + if (bounds.IsValid()) + fBounds = bounds; +} + +// Bounds +BRect +Canvas::Bounds() const +{ + return fBounds; +} + +// Compose +void +Canvas::Compose(BBitmap* into, BRect area) const +{ + if (into && into->IsValid() + && area.IsValid() && area.Intersects(into->Bounds())) { + area = area & into->Bounds(); + int32 count = CountLayers(); + for (int32 i = count - 1; Layer* layer = LayerAt(i); i--) { + layer->Compose(into, area); + } + } +} + +// Bitmap +BBitmap* +Canvas::Bitmap() const +{ + BBitmap* bitmap = new BBitmap(fBounds, 0, B_RGBA32); + if (!bitmap->IsValid()) { + delete bitmap; + return NULL; + } + + // this bitmap is uninitialized, clear to black/fully transparent + memset(bitmap->Bits(), 0, bitmap->BitsLength()); + Compose(bitmap, fBounds); + // remove image data where alpha = 0 to improve compression later on + uint8* bits = (uint8*)bitmap->Bits(); + uint32 bpr = bitmap->BytesPerRow(); + uint32 width = bitmap->Bounds().IntegerWidth() + 1; + uint32 height = bitmap->Bounds().IntegerHeight() + 1; + while (height > 0) { + uint8* bitsHandle = bits; + for (uint32 x = 0; x < width; x++) { + if (!bitsHandle[3]) { + bitsHandle[0] = 0; + bitsHandle[1] = 0; + bitsHandle[2] = 0; + } + bitsHandle += 4; + } + bits += bpr; + height--; + } + + return bitmap; +} + + + +static const char* LAYER_KEY = "layer"; +static const char* BOUNDS_KEY = "bounds"; + +// Unarchive +status_t +Canvas::Unarchive(const BMessage* archive) +{ + if (!archive) + return B_BAD_VALUE; + + // restore bounds + BRect bounds; + if (archive->FindRect(BOUNDS_KEY, &bounds) < B_OK) + return B_ERROR; + + fBounds = bounds; + // restore each layer + BMessage layerMessage; + for (int32 i = 0; + archive->FindMessage(LAYER_KEY, i, &layerMessage) == B_OK; + i++) { + + Layer* layer = new (nothrow) Layer(); + if (!layer || layer->Unarchive(&layerMessage) < B_OK + || !AddLayer(layer)) { + delete layer; + return B_NO_MEMORY; + } + } + + return B_OK; +} + diff --git a/src/add-ons/translators/wonderbrush/Canvas.h b/src/add-ons/translators/wonderbrush/Canvas.h new file mode 100644 index 0000000000..3e97999e52 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/Canvas.h @@ -0,0 +1,58 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef CANVAS_H +#define CANVAS_H + +#include +#include +#include + +struct entry_ref; + +class BBitmap; +class BMessage; +class Layer; + +class Canvas : private BList { + public: + Canvas(BRect frame); + ~Canvas(); + + bool IsValid() const; + void MakeEmpty(); + + // list functionality + bool AddLayer(Layer* layer); + bool AddLayer(Layer* layer, int32 index); + + Layer* RemoveLayer(int32 index); + bool RemoveLayer(Layer* layer); + + Layer* LayerAt(int32 index) const; + Layer* LayerAtFast(int32 index) const; + int32 IndexOf(Layer* layer) const; + int32 CountLayers() const; + bool HasLayer(Layer* layer) const; + + void SetBounds(BRect bounds); + BRect Bounds() const; + + // composes layers on top of passed bitmap + void Compose(BBitmap* into, BRect area) const; + // returns entire composition in new bitmap + BBitmap* Bitmap() const; + + // loading + status_t Unarchive(const BMessage* archive); + + private: + BRect fBounds; +}; + +#endif // CANVAS_H diff --git a/src/add-ons/translators/wonderbrush/Jamfile b/src/add-ons/translators/wonderbrush/Jamfile new file mode 100644 index 0000000000..0b2535a33b --- /dev/null +++ b/src/add-ons/translators/wonderbrush/Jamfile @@ -0,0 +1,38 @@ +SubDir HAIKU_TOP src add-ons translators wonderbrush ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +# Include BaseTranslator code from shared directory +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons translators shared ] ; + +# Include support sub folder +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons translators wonderbrush support ] ; + +UseLibraryHeaders zlib ; + +Translator WonderBrushTranslator : + # shared classes + BaseTranslator.cpp + TranslatorSettings.cpp + TranslatorWindow.cpp + + # WonderBrushTranslator classes + Canvas.cpp + Layer.cpp + WonderBrushImage.cpp + WonderBrushMain.cpp + WonderBrushTranslator.cpp + WonderBrushView.cpp + + # support + bitmap_compression.cpp + blending.cpp + lab_convert.cpp + + : be translation z +; + +Package haiku-translationkit-cvs : + WonderBrushTranslator : + boot home config add-ons Translators ; + diff --git a/src/add-ons/translators/wonderbrush/Layer.cpp b/src/add-ons/translators/wonderbrush/Layer.cpp new file mode 100644 index 0000000000..f4ddec57bd --- /dev/null +++ b/src/add-ons/translators/wonderbrush/Layer.cpp @@ -0,0 +1,386 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Layer.h" + +#include + +#include +#include + +#include "bitmap_compression.h" +#include "blending.h" +#include "lab_convert.h" +#include "support.h" + +// 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 = 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 = 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 = 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; +} diff --git a/src/add-ons/translators/wonderbrush/Layer.h b/src/add-ons/translators/wonderbrush/Layer.h new file mode 100644 index 0000000000..ba07df12b5 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/Layer.h @@ -0,0 +1,68 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef LAYER_H +#define LAYER_H + +#include +#include + +// property flags +enum { + FLAG_INVISIBLE = 0x01, +}; + +// blending modes (as of WonderBrush 2.0) +enum { + MODE_NORMAL = 0, + MODE_MULTIPLY = 1, + MODE_INVERSE_MULTIPLY = 2, + MODE_LUMINANCE = 3, + MODE_MULTIPLY_ALPHA = 4, + MODE_MULTIPLY_INVERSE_ALPHA = 5, + + MODE_REPLACE_RED = 6, + MODE_REPLACE_GREEN = 7, + MODE_REPLACE_BLUE = 8, + + MODE_DARKEN = 9, + MODE_LIGHTEN = 10, + + MODE_HARD_LIGHT = 11, + MODE_SOFT_LIGHT = 12, +}; + +class BBitmap; +class BMessage; + +class Layer { + public: + Layer(); + ~Layer(); + + // active area of layer + inline BRect ActiveBounds() const + { return fBounds; } + + // composing + status_t Compose(const BBitmap* into, + BRect area); + + // loading + status_t Unarchive(const BMessage* archive); + + protected: + BBitmap* fBitmap; + BRect fBounds; + + float fAlpha; + uint32 fMode; + uint32 fFlags; +}; + +#endif // LAYER_H diff --git a/src/add-ons/translators/wonderbrush/WonderBrushImage.cpp b/src/add-ons/translators/wonderbrush/WonderBrushImage.cpp new file mode 100644 index 0000000000..cff05149db --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushImage.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "WonderBrushImage.h" + +#include "Canvas.h" + +WonderBrushImage::WonderBrushImage() + : fArchive(0UL) +{ +} + + +WonderBrushImage::~WonderBrushImage() +{ +} + + +status_t +WonderBrushImage::SetTo(BPositionIO* stream) +{ + if (!stream) + return B_BAD_VALUE; + + // try to load the stream as a BMessage and probe it + // to see wether it might be a WonderBrush image + fArchive.MakeEmpty(); + status_t status = fArchive.Unflatten(stream); + if (status < B_OK) + return status; + + if (fArchive.HasMessage("layer") && fArchive.HasRect("bounds")) + return B_OK; + + return B_ERROR; +} + + +BBitmap* +WonderBrushImage::Bitmap() const +{ + Canvas canvas(BRect(0.0, 0.0, -1.0, -1.0)); + + if (canvas.Unarchive(&fArchive) < B_OK) + return NULL; + + return canvas.Bitmap(); +} + + diff --git a/src/add-ons/translators/wonderbrush/WonderBrushImage.h b/src/add-ons/translators/wonderbrush/WonderBrushImage.h new file mode 100644 index 0000000000..1c1ce9838b --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushImage.h @@ -0,0 +1,31 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef WONDERBRUSH_IMAGE_H +#define WONDERBRUSH_IMAGE_H + +#include + +class BBitmap; +class BPositionIO; +class Canvas; + +class WonderBrushImage { + public: + WonderBrushImage(); + virtual ~WonderBrushImage(); + + status_t SetTo(BPositionIO* stream); + BBitmap* Bitmap() const; + + private: + BMessage fArchive; +}; + +#endif // WONDERBRUSH_IMAGE_H + diff --git a/src/add-ons/translators/wonderbrush/WonderBrushMain.cpp b/src/add-ons/translators/wonderbrush/WonderBrushMain.cpp new file mode 100644 index 0000000000..5babacd2fd --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushMain.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include + +#include "WonderBrushTranslator.h" +#include "TranslatorWindow.h" + +int +main() +{ + BApplication app("application/x-vnd.haiku-wbi-translator"); + status_t result; + result = LaunchTranslatorWindow(new WonderBrushTranslator, + "WBI Settings", BRect(0, 0, 225, 175)); + if (result == B_OK) { + app.Run(); + return 0; + } else + return 1; +} diff --git a/src/add-ons/translators/wonderbrush/WonderBrushTranslator.cpp b/src/add-ons/translators/wonderbrush/WonderBrushTranslator.cpp new file mode 100644 index 0000000000..55fa602fff --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushTranslator.cpp @@ -0,0 +1,241 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "WonderBrushTranslator.h" + +#include +#include +#include + +#include +#include + +#include "blending.h" + +#include "WonderBrushImage.h" +#include "WonderBrushView.h" + +using std::nothrow; + +// The input formats that this translator supports. +translation_format gInputFormats[] = { + /*{ + B_TRANSLATOR_BITMAP, + B_TRANSLATOR_BITMAP, + BBT_IN_QUALITY, + BBT_IN_CAPABILITY, + "image/x-be-bitmap", + "Be Bitmap Format (WonderBrushTranslator)" + },*/ + { + WBI_FORMAT, + B_TRANSLATOR_BITMAP, + WBI_IN_QUALITY, + WBI_IN_CAPABILITY, + "image/x-wonderbrush", + "WonderBrush image" + } +}; + +// The output formats that this translator supports. +translation_format gOutputFormats[] = { + { + B_TRANSLATOR_BITMAP, + B_TRANSLATOR_BITMAP, + BBT_OUT_QUALITY, + BBT_OUT_CAPABILITY, + "image/x-be-bitmap", + "Be Bitmap Format (WonderBrushTranslator)" + }/*, + { + WBI_FORMAT, + B_TRANSLATOR_BITMAP, + WBI_OUT_QUALITY, + WBI_OUT_CAPABILITY, + "image/x-wonderbrush", + "WonderBrush image" + }*/ +}; + + +// Default settings for the Translator +TranSetting gDefaultSettings[] = { + { B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false }, + { B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false } +}; + + +BTranslator* +make_nth_translator(int32 n, image_id you, uint32 flags, ...) +{ + if (!n) + return new WonderBrushTranslator(); + else + return NULL; +} + + +WonderBrushTranslator::WonderBrushTranslator() + : BaseTranslator("WonderBrush Images", "WonderBrush image translator", + WBI_TRANSLATOR_VERSION, + gInputFormats, sizeof(gInputFormats) / sizeof(translation_format), + gOutputFormats, sizeof(gOutputFormats) / sizeof(translation_format), + "WBITranslator_Settings", + gDefaultSettings, sizeof(gDefaultSettings) / sizeof(TranSetting), + B_TRANSLATOR_BITMAP, WBI_FORMAT) +{ +#if GAMMA_BLEND + init_gamma_blending(); +#endif +} + + +WonderBrushTranslator::~WonderBrushTranslator() +{ +#if GAMMA_BLEND + uninit_gamma_blending(); +#endif +} + + +status_t +identify_wbi_header(BPositionIO* inSource, translator_info* outInfo, + uint32 outType, WonderBrushImage** _wbImage = NULL) +{ + status_t status = B_NO_MEMORY; + // construct new SGIImage object and set it to the provided BPositionIO + WonderBrushImage* wbImage = new(nothrow) WonderBrushImage(); + if (wbImage) + status = wbImage->SetTo(inSource); + + if (status >= B_OK) { + if (outInfo) { + outInfo->type = WBI_FORMAT; + outInfo->group = B_TRANSLATOR_BITMAP; + outInfo->quality = WBI_IN_QUALITY; + outInfo->capability = WBI_IN_CAPABILITY; + strcpy(outInfo->MIME, "image/x-wonderbrush"); + strcpy(outInfo->name, "WonderBrush image"); + } + } else { + delete wbImage; + wbImage = NULL; + } + if (!_wbImage) { + // close WonderBrushImage if caller is not interested in handle + delete wbImage; + } else { + // leave WonderBrushImage open (if it is) and return handle if + // caller needs it + *_wbImage = wbImage; + } + + return status; +} + + +status_t +WonderBrushTranslator::DerivedIdentify(BPositionIO* inSource, + const translation_format* inFormat, BMessage* ioExtension, + translator_info* outInfo, uint32 outType) +{ + return identify_wbi_header(inSource, outInfo, outType); +} + + +status_t +WonderBrushTranslator::DerivedTranslate(BPositionIO* inSource, + const translator_info* inInfo, BMessage* ioExtension, + uint32 outType, BPositionIO* outDestination, int32 baseType) +{ + if (baseType == 0) + // if inSource is NOT in bits format + return _TranslateFromWBI(inSource, outType, outDestination); + else + // if BaseTranslator did not properly identify the data as + // bits or not bits + return B_NO_TRANSLATOR; +} + + +BView* +WonderBrushTranslator::NewConfigView(TranslatorSettings* settings) +{ + return new WonderBrushView(BRect(0, 0, 225, 175), "WBI Settings", + B_FOLLOW_ALL, B_WILL_DRAW, settings); +} + + +// #pragma mark - + + +status_t +WonderBrushTranslator::_TranslateFromWBI(BPositionIO* inSource, uint32 outType, + BPositionIO* outDestination) +{ + // if copying WBI_FORMAT to WBI_FORMAT + if (outType == WBI_FORMAT) { + translate_direct_copy(inSource, outDestination); + return B_OK; + } + + WonderBrushImage* wbImage = NULL; + ssize_t ret = identify_wbi_header(inSource, NULL, outType, &wbImage); + if (ret < B_OK) + return ret; + + bool headerOnly = false; + bool dataOnly = false; + + BBitmap* bitmap = wbImage->Bitmap(); + if (!bitmap) + return B_ERROR; + + uint32 width = bitmap->Bounds().IntegerWidth() + 1; + uint32 height = bitmap->Bounds().IntegerHeight() + 1; + color_space format = bitmap->ColorSpace(); + uint32 bytesPerRow = bitmap->BytesPerRow(); + + if (!dataOnly) { + // Construct and write Be bitmap header + TranslatorBitmap bitsHeader; + bitsHeader.magic = B_TRANSLATOR_BITMAP; + bitsHeader.bounds.left = 0; + bitsHeader.bounds.top = 0; + bitsHeader.bounds.right = width - 1; + bitsHeader.bounds.bottom = height - 1; + bitsHeader.rowBytes = bytesPerRow; + bitsHeader.colors = format; + bitsHeader.dataSize = bitsHeader.rowBytes * height; + if ((ret = swap_data(B_UINT32_TYPE, &bitsHeader, + sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN)) < B_OK) { + return ret; + } else + ret = outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); + } + + if (ret >= B_OK && !headerOnly) { + // read one row at a time and write out the results + uint8* row = (uint8*)bitmap->Bits(); + for (uint32 y = 0; y < height && ret >= B_OK; y++) { + ret = outDestination->Write(row, bytesPerRow); + row += bytesPerRow; + } + + } + + delete bitmap; + delete wbImage; + + if (ret >= B_OK) + ret = B_OK; + + return (status_t)ret; +} + + diff --git a/src/add-ons/translators/wonderbrush/WonderBrushTranslator.h b/src/add-ons/translators/wonderbrush/WonderBrushTranslator.h new file mode 100644 index 0000000000..71d534a304 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushTranslator.h @@ -0,0 +1,68 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef WONDERBRUSH_TRANSLATOR_H +#define WONDERBRUSH_TRANSLATOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BaseTranslator.h" + +#define WBI_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(1,0,0) + +#define WBI_IN_QUALITY 1.0 +#define WBI_IN_CAPABILITY 1.0 +#define WBI_OUT_QUALITY 1.0 +#define WBI_OUT_CAPABILITY 1.0 + +#define BBT_IN_QUALITY 0.4 +#define BBT_IN_CAPABILITY 0.6 +#define BBT_OUT_QUALITY 0.4 +#define BBT_OUT_CAPABILITY 0.6 + +enum { + WBI_FORMAT = 'WBI ', +}; + +class WonderBrushTranslator : public BaseTranslator { + public: + WonderBrushTranslator(); + + virtual status_t DerivedIdentify(BPositionIO* inSource, + const translation_format* inFormat, + BMessage* ioExtension, + translator_info* outInfo, uint32 outType); + + virtual status_t DerivedTranslate(BPositionIO* inSource, + const translator_info* inInfo, + BMessage* ioExtension, uint32 outType, + BPositionIO* outDestination, + int32 baseType); + + virtual BView* NewConfigView(TranslatorSettings* settings); + + protected: + virtual ~WonderBrushTranslator(); + // this is protected because the object is deleted by the + // Release() function instead of being deleted directly by + // the user + + private: + status_t _TranslateFromWBI(BPositionIO* inSource, + uint32 outType, BPositionIO* outDestination); +}; + +#endif // WONDERBRUSH_TRANSLATOR_H diff --git a/src/add-ons/translators/wonderbrush/WonderBrushView.cpp b/src/add-ons/translators/wonderbrush/WonderBrushView.cpp new file mode 100644 index 0000000000..d5c5af070a --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushView.cpp @@ -0,0 +1,182 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "WonderBrushView.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "WonderBrushImage.h" +#include "WonderBrushTranslator.h" + +const char* kAuthor = "Stephan Aßmus, "; +const char* kWBICopyright = "Copyright "B_UTF8_COPYRIGHT" 2006 Haiku Inc."; + + +void +add_menu_item(BMenu* menu, + uint32 compression, + const char* label, + uint32 currentCompression) +{ + BMessage* message = new BMessage(WonderBrushView::MSG_COMPRESSION_CHANGED); + message->AddInt32("value", compression); + BMenuItem* item = new BMenuItem(label, message); + item->SetMarked(currentCompression == compression); + menu->AddItem(item); +} + + +WonderBrushView::WonderBrushView(const BRect &frame, const char *name, + uint32 resize, uint32 flags, TranslatorSettings *settings) + : BView(frame, name, resize, flags), + fSettings(settings) +{ + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + // figure out where the text ends + font_height fh; + be_bold_font->GetHeight(&fh); + float xbold, ybold; + xbold = fh.descent + 1; + ybold = fh.ascent + fh.descent * 2 + fh.leading; + + font_height plainh; + be_plain_font->GetHeight(&plainh); + float yplain; + yplain = plainh.ascent + plainh.descent * 2 + plainh.leading; + + ResizeToPreferred(); +} + + +WonderBrushView::~WonderBrushView() +{ + fSettings->Release(); +} + + +void +WonderBrushView::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BView::MessageReceived(message); + } +} + + +void +WonderBrushView::AttachedToWindow() +{ + // Hack for DataTranslations which doesn't resize visible area to requested by view + // which makes some parts of bigger than usual translationviews out of visible area + // so if it was loaded to DataTranslations resize window if needed + BWindow *window = Window(); + if (!strcmp(window->Name(), "DataTranslations")) { + BView *view = Parent(); + if (view) { + BRect frame = view->Frame(); + float x, y; + GetPreferredSize(&x, &y); + if (frame.Width() < x || (frame.Height() - 48) < y) { + x -= frame.Width(); + y -= frame.Height() - 48; + if (x < 0) x = 0; + if (y < 0) y = 0; + + // DataTranslations has main view called "Background" + // change it's resizing mode so it will always resize with window + // also make sure view will be redrawed after resize + view = window->FindView("Background"); + if (view) { + view->SetResizingMode(B_FOLLOW_ALL); + view->SetFlags(B_FULL_UPDATE_ON_RESIZE); + } + + // The same with "Info..." button, except redrawing, which isn't needed + view = window->FindView("Info…"); + if (view) + view->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); + + window->ResizeBy( x, y); + } + } + } +} + + +void +WonderBrushView::Draw(BRect area) +{ + SetFont(be_bold_font); + font_height fh; + GetFontHeight(&fh); + float xbold = fh.descent + 1; + float ybold = fh.ascent + fh.descent * 2 + fh.leading; + + BPoint offset(xbold, ybold); + + const char* text = "WonderBrush Image Translator"; + DrawString(text, offset); + + SetFont(be_plain_font); + font_height plainh; + GetFontHeight(&plainh); + float yplain = plainh.ascent + plainh.descent * 2 + plainh.leading; + + offset.y += yplain; + + char detail[100]; + sprintf(detail, "Version %d.%d.%d %s", + static_cast(B_TRANSLATION_MAJOR_VERSION(WBI_TRANSLATOR_VERSION)), + static_cast(B_TRANSLATION_MINOR_VERSION(WBI_TRANSLATOR_VERSION)), + static_cast(B_TRANSLATION_REVISION_VERSION(WBI_TRANSLATOR_VERSION)), + __DATE__); + DrawString(detail, offset); + + offset.y += 2 * ybold; + + text = "written by:"; + DrawString(text, offset); + offset.y += ybold; + + DrawString(kAuthor, offset); + offset.y += 2 * ybold; + + DrawString(kWBICopyright, offset); +} + + +void +WonderBrushView::GetPreferredSize(float* width, float* height) +{ + if (width) { + // look at the two widest strings + float width1 = StringWidth(kWBICopyright) + 15.0; + float width2 = be_plain_font->StringWidth(kAuthor) + 15.0; + + *width = max_c(width1, width2); + } + + if (height) { + // take the height of the bold system font and + // the number of lines of text we render + font_height fh; + be_bold_font->GetHeight(&fh); + float ybold = fh.ascent + fh.descent * 2 + fh.leading; + + *height = 7 * ybold; + } +} diff --git a/src/add-ons/translators/wonderbrush/WonderBrushView.h b/src/add-ons/translators/wonderbrush/WonderBrushView.h new file mode 100644 index 0000000000..e2b42fcc1a --- /dev/null +++ b/src/add-ons/translators/wonderbrush/WonderBrushView.h @@ -0,0 +1,43 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef WONDERBRUSH_VIEW_H +#define WONDERBRUSH_VIEW_H + +#include +#include "TranslatorSettings.h" + +class BMenuField; + +class WonderBrushView : public BView { +public: + WonderBrushView(const BRect &frame, const char *name, uint32 resize, + uint32 flags, TranslatorSettings *settings); + // sets up the view + + ~WonderBrushView(); + // releases the SGITranslator settings + + virtual void AttachedToWindow(); + virtual void MessageReceived(BMessage *message); + + virtual void Draw(BRect area); + // draws information about the SGITranslator + virtual void GetPreferredSize(float* width, float* height); + + enum { + MSG_COMPRESSION_CHANGED = 'cmch', + }; + +private: + TranslatorSettings *fSettings; + // the actual settings for the translator, + // shared with the translator +}; + +#endif // WONDERBRUSH_VIEW_H diff --git a/src/add-ons/translators/wonderbrush/support/bitmap_compression.cpp b/src/add-ons/translators/wonderbrush/support/bitmap_compression.cpp new file mode 100644 index 0000000000..41c1948df5 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/bitmap_compression.cpp @@ -0,0 +1,166 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include +#include +#include +#include + +#include +#include + +enum { + COMPRESSION_NONE = 0, + COMPRESSION_ZLIB = 2, +}; + +// compress_bitmap_zlib +bool +compress_bitmap_zlib(const BBitmap* bitmap, void** buffer, unsigned* size) +{ + bool result = false; + if (bitmap) { + Bytef* src = (Bytef*)bitmap->Bits(); + uLong srcLength = bitmap->BitsLength(); + *size = (unsigned)ceilf(srcLength * 1.01) + 12; + *buffer = malloc(*size); + if (*buffer) { + int ret = compress2((Bytef*)*buffer, + (uLongf*)size, + src, + srcLength, + 3); + if (ret == Z_OK) { +//printf("zlib compressed %ld bytes bitmap into %d bytes (%f%%)\n", srcLength, *size, ((float)*size / (float)srcLength) * 100.0); + if ((unsigned)ceilf(srcLength * 1.01) + 12 != *size) + *buffer = realloc(*buffer, *size); + result = true; + } else { + // error compressing + free(*buffer); + *buffer = NULL; + *size = 0; + fprintf(stderr, "zlib compression error: %d\n", ret); + } + } else + *size = 0; + } + return result; +} + +// decompress_bitmap_zlib +BBitmap* +decompress_bitmap_zlib(const void* buffer, unsigned int size, + BRect frame, color_space format) +{ + BBitmap* bitmap = new BBitmap(frame, 0, format); + if (bitmap->IsValid()) { + if (buffer) { + Bytef* dst = (Bytef*)bitmap->Bits(); + uLongf dstLength = bitmap->BitsLength(); + + int ret = uncompress(dst, + &dstLength, + (const Bytef*)buffer, + (uLong)size); + if (ret != Z_OK || dstLength != (uint32)bitmap->BitsLength()) { + // decompression error! + fprintf(stderr, "decompress_bitmap_zlib() failed " + "- corrupted input buffer or file!\n"); + } + } else { + memset(bitmap->Bits(), 0, bitmap->BitsLength()); + } + } else { + delete bitmap; + bitmap = NULL; + } + return bitmap; +} + + + +// archive_bitmap +status_t +archive_bitmap(const BBitmap* bitmap, BMessage* into, const char* fieldName) +{ + status_t ret = B_BAD_VALUE; + if (bitmap && bitmap->IsValid() && into) { + void* buffer; + unsigned size; + if (compress_bitmap_zlib(bitmap, &buffer, &size)) { + ret = into->AddData(fieldName, B_RAW_TYPE, buffer, size); + if (ret >= B_OK) + ret = into->AddInt32("compression", COMPRESSION_ZLIB); + if (ret >= B_OK) + ret = into->AddRect("construction bounds", bitmap->Bounds()); + if (ret >= B_OK) + ret = into->AddInt32("format", bitmap->ColorSpace()); + } + } + return ret; +} + +// extract_bitmap +status_t +extract_bitmap(BBitmap** bitmap, const BMessage* from, const char* fieldName) +{ + status_t ret = B_BAD_VALUE; + if (bitmap && from) { + *bitmap = NULL; + // no compression (flattened BBitmap archive) + BMessage bitmapArchive; + if ((ret = from->FindMessage(fieldName, &bitmapArchive)) >= B_OK) { + *bitmap = new BBitmap(&bitmapArchive); + } + // compression + if (!*bitmap) { + const void* compressedData = NULL; + ssize_t compressedSize = 0; + compressedData = NULL; + compressedSize = 0; + BRect bounds; + color_space format; + uint32 compression; + if (((ret = from->FindData(fieldName, + B_RAW_TYPE, &compressedData, + &compressedSize)) >= B_OK + // this is for backward compatibility + || (ret = from->FindData("current compressed data", + B_RAW_TYPE, &compressedData, + &compressedSize)) >= B_OK) + && (ret = from->FindRect("construction bounds", + &bounds)) >= B_OK) { + + // compression defaults to NONE for backward compatibility + if (from->FindInt32("compression", (int32*)&compression) < B_OK) + compression = COMPRESSION_NONE; + // format defaults to B_RGBA32 for backward compatibility + if (from->FindInt32("format", (int32*)&format) < B_OK) + format = B_RGBA32; + + switch (compression) { + case COMPRESSION_ZLIB: + *bitmap = decompress_bitmap_zlib(compressedData, + compressedSize, + bounds, format); + break; + } + } + } + if (*bitmap) + ret = (*bitmap)->InitCheck(); + else if (ret >= B_OK) + ret = B_NO_MEMORY; + if (ret < B_OK) { + delete *bitmap; + *bitmap = NULL; + } + } + return ret; +} diff --git a/src/add-ons/translators/wonderbrush/support/bitmap_compression.h b/src/add-ons/translators/wonderbrush/support/bitmap_compression.h new file mode 100644 index 0000000000..f84740873a --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/bitmap_compression.h @@ -0,0 +1,23 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef BITMAP_COMPRESSION_H +#define BITMAP_COMPRESSION_H + +#include + +class BBitmap; +class BMessage; + +status_t +archive_bitmap(const BBitmap* bitmap, BMessage* into, const char* fieldName); + +status_t +extract_bitmap(BBitmap** bitmap, const BMessage* from, const char* fieldName); + +#endif // BITMAP_COMPRESSION_H diff --git a/src/add-ons/translators/wonderbrush/support/blending.cpp b/src/add-ons/translators/wonderbrush/support/blending.cpp new file mode 100644 index 0000000000..ec7dbafc63 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/blending.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include + +#include "blending.h" + +#if GAMMA_BLEND + +// speed tests done on Pentium M, 1450 MHz +// blending two 800x600 bitmaps with "50" on each component (including alpha): 1500000 usecs +// -"- using gamma LUT: 651000 usecs +// -"- no use of floorf(): 572000 usecs + +// -"- uint16 integer version: 72000 usecs +// -"- inline: 60200 usecs + +// for comparison: +// -"- inline only, no LUTs, no gamma correction: 44000 usecs +// -"- + premultiplied alpha (less MULs, no DIVs): 16500 usecs + + +const float kGamma = 2.2; +const float kInverseGamma = 1.0 / kGamma; + +uint16* kGammaTable = NULL; +uint8* kInverseGammaTable = NULL; + + +// convert_to_gamma +uint16 +convert_to_gamma(uint8 value) +{ + return kGammaTable[value]; +} + +// init_gamma_blending +void +init_gamma_blending() +{ + // init LUT R'G'B' [0...255] -> RGB [0...25500] + if (!kGammaTable) + kGammaTable = new uint16[256]; + for (uint32 i = 0; i < 256; i++) + kGammaTable[i] = (uint16)(powf((float)i / 255.0, kGamma) * 25500.0); + + // init LUT RGB [0...25500] -> R'G'B' [0...255] + if (!kInverseGammaTable) + kInverseGammaTable = new uint8[25501]; + for (uint32 i = 0; i < 25501; i++) + kInverseGammaTable[i] = (uint8)(powf((float)i / 25500.0, kInverseGamma) * 255.0); +} + +// init_gamma_blending +void +uninit_gamma_blending() +{ + delete[] kGammaTable; + kGammaTable = NULL; + delete[] kInverseGammaTable; + kInverseGammaTable = NULL; +} + + +#endif // GAMMA_BLEND diff --git a/src/add-ons/translators/wonderbrush/support/blending.h b/src/add-ons/translators/wonderbrush/support/blending.h new file mode 100644 index 0000000000..ffa911efdf --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/blending.h @@ -0,0 +1,613 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef BLENDING_H +#define BLENDING_H + +#include + +// faster bit error version: +//#define INT_MULT(a, b, t) ((t) = (a) * (b), ((((t) >> 8) + (t)) >> 8)) +// correct version +#define INT_MULT(a, b, t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) + +#define INT_LERP(p, q, a, t) ((p) + INT_MULT(a, ((q) - (p)), t)) + +#define INT_PRELERP(p, q, a, t) ((p) + (q) - INT_MULT(a, p, t)) + + + +#define GAMMA_BLEND 1 + +#if GAMMA_BLEND + +extern uint16* kGammaTable; +extern uint8* kInverseGammaTable; + +void init_gamma_blending(); +void uninit_gamma_blending(); + +// blend +inline void +blend_gamma(uint16 b1, uint16 b2, uint16 b3, uint8 ba, // bottom components + uint16 t1, uint16 t2, uint16 t3, uint8 ta, // top components + uint8* d1, uint8* d2, uint8* d3, uint8* da) // dest components +{ + if (ba == 255) { + uint32 destAlpha = 255 - ta; + *d1 = kInverseGammaTable[(b1 * destAlpha + t1 * ta) / 255]; + *d2 = kInverseGammaTable[(b2 * destAlpha + t2 * ta) / 255]; + *d3 = kInverseGammaTable[(b3 * destAlpha + t3 * ta) / 255]; + *da = 255; + } else { + uint8 alphaRest = 255 - ta; + register uint32 alphaTemp = (65025 - alphaRest * (255 - ba)); + uint32 alphaDest = ba * alphaRest; + uint32 alphaSrc = 255 * ta; + *d1 = kInverseGammaTable[(b1 * alphaDest + t1 * alphaSrc) / alphaTemp]; + *d2 = kInverseGammaTable[(b2 * alphaDest + t2 * alphaSrc) / alphaTemp]; + *d3 = kInverseGammaTable[(b3 * alphaDest + t3 * alphaSrc) / alphaTemp]; + *da = alphaTemp / 255; + } +} + +// blend +inline void +blend(uint8 b1, uint8 b2, uint8 b3, uint8 ba, // bottom components + uint8 t1, uint8 t2, uint8 t3, uint8 ta, // top components + uint8* d1, uint8* d2, uint8* d3, uint8* da) // dest components +{ + // convert to linear rgb + uint16 gt1 = kGammaTable[t1]; + uint16 gt2 = kGammaTable[t2]; + uint16 gt3 = kGammaTable[t3]; + + uint16 gb1 = kGammaTable[b1]; + uint16 gb2 = kGammaTable[b2]; + uint16 gb3 = kGammaTable[b3]; + + blend_gamma(gb1, gb2, gb3, ba, + gt1, gt2, gt3, ta, + d1, d2, d3, da); +} + +// convert_to_gamma +// +// converted value will be gamma corrected in the range [0...2550] +// and can be passed on to the other functions that take uint16 components +uint16 +convert_to_gamma(uint8 value); + +// blend_colors_copy +inline void +blend_colors_copy(uint8* bottom, uint8 alpha, uint8* dest, + uint8 c1, uint8 c2, uint8 c3, + uint16 gc1, uint16 gc2, uint16 gc3) +{ + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + dest[0] = c1; + dest[1] = c2; + dest[2] = c3; + dest[3] = alpha; + } else { + // only bottom components need to be gamma corrected + uint16 gb1 = kGammaTable[bottom[0]]; + uint16 gb2 = kGammaTable[bottom[1]]; + uint16 gb3 = kGammaTable[bottom[2]]; + + blend_gamma(gb1, gb2, gb3, bottom[3], + gc1, gc2, gc3, alpha, + &dest[0], &dest[1], &dest[2], &dest[3]); + } + } else { + *((uint32*)dest) = *((uint32*)bottom); + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8 alpha, + uint8 c1, uint8 c2, uint8 c3, + uint16 gc1, uint16 gc2, uint16 gc3) +{ + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + bottom[0] = c1; + bottom[1] = c2; + bottom[2] = c3; + bottom[3] = alpha; + } else { + // only bottom components need to be gamma corrected + uint16 gb1 = kGammaTable[bottom[0]]; + uint16 gb2 = kGammaTable[bottom[1]]; + uint16 gb3 = kGammaTable[bottom[2]]; + + blend_gamma(gb1, gb2, gb3, bottom[3], + gc1, gc2, gc3, alpha, + &bottom[0], &bottom[1], &bottom[2], &bottom[3]); + } + } +} + +// blend_colors_copy +inline void +blend_colors_copy(uint8* bottom, uint8 alpha, uint8* dest, + uint8 c1, uint8 c2, uint8 c3) +{ + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + dest[0] = c1; + dest[1] = c2; + dest[2] = c3; + dest[3] = alpha; + } else { + blend(bottom[0], bottom[1], bottom[2], bottom[3], + c1, c2, c3, alpha, + &dest[0], &dest[1], &dest[2], &dest[3]); + } + } else { + *((uint32*)dest) = *((uint32*)bottom); + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8 alpha, uint8 c1, uint8 c2, uint8 c3) +{ + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + bottom[0] = c1; + bottom[1] = c2; + bottom[2] = c3; + bottom[3] = alpha; + } else { + blend(bottom[0], bottom[1], bottom[2], bottom[3], + c1, c2, c3, alpha, + &bottom[0], &bottom[1], &bottom[2], &bottom[3]); + } + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8* source, uint8 alphaOverride) +{ + uint8 alpha = (source[3] * alphaOverride) / 255; + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + bottom[0] = source[0]; + bottom[1] = source[1]; + bottom[2] = source[2]; + bottom[3] = alpha; + } else { + blend(bottom[0], bottom[1], bottom[2], bottom[3], + source[0], source[1], source[2], alpha, + &bottom[0], &bottom[1], &bottom[2], &bottom[3]); + } + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8* source) +{ + if (source[3] > 0) { + if (bottom[3] == 0 || source[3] == 255) { + bottom[0] = source[0]; + bottom[1] = source[1]; + bottom[2] = source[2]; + bottom[3] = source[3]; + } else { + blend(bottom[0], bottom[1], bottom[2], bottom[3], + source[0], source[1], source[2], source[3], + &bottom[0], &bottom[1], &bottom[2], &bottom[3]); + } + } +} + +// blend_colors_copy +inline void +blend_colors_copy(uint8* dest, uint8* bottom, uint8* top) +{ + if (bottom[3] == 0 || top[3] == 255) { + dest[0] = top[0]; + dest[1] = top[1]; + dest[2] = top[2]; + dest[3] = top[3]; + } else { + blend(bottom[0], bottom[1], bottom[2], bottom[3], + top[0], top[1], top[2], top[3], + &dest[0], &dest[1], &dest[2], &dest[3]); + } +} + +// blend_pixels +inline void +blend_pixels(uint8* bottom, uint8* top, uint8 alpha) +{ + if (alpha > 0) { + if (alpha == 255) { + bottom[0] = top[0]; + bottom[1] = top[1]; + bottom[2] = top[2]; + bottom[3] = top[3]; + } else { + // convert to linear rgb + uint16 t1 = kGammaTable[top[0]]; + uint16 t2 = kGammaTable[top[1]]; + uint16 t3 = kGammaTable[top[2]]; + uint16 b1 = kGammaTable[bottom[0]]; + uint16 b2 = kGammaTable[bottom[1]]; + uint16 b3 = kGammaTable[bottom[2]]; + + uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; + uint8 invAlpha = 255 - mergeAlpha; + bottom[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; + bottom[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; + bottom[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; + bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } +} + +// blend_pixels_copy +inline void +blend_pixels_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alpha) +{ + if (alpha > 0) { + if (alpha == 255) { + dest[0] = top[0]; + dest[1] = top[1]; + dest[2] = top[2]; + dest[3] = top[3]; + } else { + // convert to linear rgb + uint16 t1 = kGammaTable[top[0]]; + uint16 t2 = kGammaTable[top[1]]; + uint16 t3 = kGammaTable[top[2]]; + uint16 b1 = kGammaTable[bottom[0]]; + uint16 b2 = kGammaTable[bottom[1]]; + uint16 b3 = kGammaTable[bottom[2]]; + + uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; + uint8 invAlpha = 255 - mergeAlpha; + dest[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; + dest[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; + dest[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; + dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } else { + dest[0] = bottom[0]; + dest[1] = bottom[1]; + dest[2] = bottom[2]; + dest[3] = bottom[3]; + } +} + +// blend_pixels_overlay +inline void +blend_pixels_overlay(uint8* bottom, uint8* top, uint8 alphaOverride) +{ + uint8 alpha = (top[3] * alphaOverride) / 255; + if (alpha > 0) { + if (alpha == 255) { + bottom[0] = top[0]; + bottom[1] = top[1]; + bottom[2] = top[2]; + bottom[3] = top[3]; + } else { + // convert to linear rgb + uint16 t1 = kGammaTable[top[0]]; + uint16 t2 = kGammaTable[top[1]]; + uint16 t3 = kGammaTable[top[2]]; + uint16 b1 = kGammaTable[bottom[0]]; + uint16 b2 = kGammaTable[bottom[1]]; + uint16 b3 = kGammaTable[bottom[2]]; + + uint8 mergeAlpha = bottom[3] ? alpha : 255; + uint8 invAlpha = 255 - mergeAlpha; + bottom[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; + bottom[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; + bottom[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; + bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } +} + +// blend_pixels_overlay_copy +inline void +blend_pixels_overlay_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alphaOverride) +{ + uint8 alpha = (top[3] * alphaOverride) / 255; + if (alpha > 0) { + if (alpha == 255) { + dest[0] = top[0]; + dest[1] = top[1]; + dest[2] = top[2]; + dest[3] = top[3]; + } else { + // convert to linear rgb + uint16 t1 = kGammaTable[top[0]]; + uint16 t2 = kGammaTable[top[1]]; + uint16 t3 = kGammaTable[top[2]]; + uint16 b1 = kGammaTable[bottom[0]]; + uint16 b2 = kGammaTable[bottom[1]]; + uint16 b3 = kGammaTable[bottom[2]]; + + uint8 mergeAlpha = bottom[3] ? alpha : 255; + uint8 invAlpha = 255 - mergeAlpha; + dest[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; + dest[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; + dest[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; + dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } else { + dest[0] = bottom[0]; + dest[1] = bottom[1]; + dest[2] = bottom[2]; + dest[3] = bottom[3]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +#else // GAMMA_BLEND + +// blend_colors_copy +inline void +blend_colors_copy(uint8* bottom, uint8 alpha, uint8* dest, + uint8 c1, uint8 c2, uint8 c3) +{ + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + dest[0] = c1; + dest[1] = c2; + dest[2] = c3; + dest[3] = alpha; + } else { + if (bottom[3] == 255) { + uint32 destAlpha = 255 - alpha; + dest[0] = (uint8)((bottom[0] * destAlpha + c1 * alpha) / 255); + dest[1] = (uint8)((bottom[1] * destAlpha + c2 * alpha) / 255); + dest[2] = (uint8)((bottom[2] * destAlpha + c3 * alpha) / 255); + dest[3] = 255; + } else { + uint8 alphaRest = 255 - alpha; + uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); + uint32 alphaDest = bottom[3] * alphaRest; + uint32 alphaSrc = 255 * alpha; + dest[0] = (bottom[0] * alphaDest + c1 * alphaSrc) / alphaTemp; + dest[1] = (bottom[1] * alphaDest + c2 * alphaSrc) / alphaTemp; + dest[2] = (bottom[2] * alphaDest + c3 * alphaSrc) / alphaTemp; + dest[3] = alphaTemp / 255; + } + } + } else { + *((uint32*)dest) = *((uint32*)bottom); + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8 alpha, uint8 c1, uint8 c2, uint8 c3) +{ + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + bottom[0] = c1; + bottom[1] = c2; + bottom[2] = c3; + bottom[3] = alpha; + } else { + if (bottom[3] == 255) { + uint32 destAlpha = 255 - alpha; + bottom[0] = (uint8)((bottom[0] * destAlpha + c1 * alpha) / 255); + bottom[1] = (uint8)((bottom[1] * destAlpha + c2 * alpha) / 255); + bottom[2] = (uint8)((bottom[2] * destAlpha + c3 * alpha) / 255); + } else { + uint8 alphaRest = 255 - alpha; + uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); + uint32 alphaDest = bottom[3] * alphaRest; + uint32 alphaSrc = 255 * alpha; + bottom[0] = (bottom[0] * alphaDest + c1 * alphaSrc) / alphaTemp; + bottom[1] = (bottom[1] * alphaDest + c2 * alphaSrc) / alphaTemp; + bottom[2] = (bottom[2] * alphaDest + c3 * alphaSrc) / alphaTemp; + bottom[3] = alphaTemp / 255; + } + } + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8* source, uint8 alphaOverride) +{ + uint8 alpha = (source[3] * alphaOverride) / 255; + if (alpha > 0) { + if (bottom[3] == 0 || alpha == 255) { + bottom[0] = source[0]; + bottom[1] = source[1]; + bottom[2] = source[2]; + bottom[3] = alpha; + } else { + if (bottom[3] == 255) { + uint32 destAlpha = 255 - alpha; + bottom[0] = (uint8)((bottom[0] * destAlpha + source[0] * alpha) / 255); + bottom[1] = (uint8)((bottom[1] * destAlpha + source[1] * alpha) / 255); + bottom[2] = (uint8)((bottom[2] * destAlpha + source[2] * alpha) / 255); + } else { + uint8 alphaRest = 255 - alpha; + uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); + uint32 alphaDest = bottom[3] * alphaRest; + uint32 alphaSrc = 255 * alpha; + bottom[0] = (bottom[0] * alphaDest + source[0] * alphaSrc) / alphaTemp; + bottom[1] = (bottom[1] * alphaDest + source[1] * alphaSrc) / alphaTemp; + bottom[2] = (bottom[2] * alphaDest + source[2] * alphaSrc) / alphaTemp; + bottom[3] = alphaTemp / 255; + } + } + } +} + +// blend_colors +inline void +blend_colors(uint8* bottom, uint8* source) +{ + if (source[3] > 0) { + if (bottom[3] == 0 || source[3] == 255) { + bottom[0] = source[0]; + bottom[1] = source[1]; + bottom[2] = source[2]; + bottom[3] = source[3]; + } else { + if (bottom[3] == 255) { + uint32 destAlpha = 255 - source[3]; + bottom[0] = (uint8)((bottom[0] * destAlpha + source[0] * source[3]) / 255); + bottom[1] = (uint8)((bottom[1] * destAlpha + source[1] * source[3]) / 255); + bottom[2] = (uint8)((bottom[2] * destAlpha + source[2] * source[3]) / 255); + } else { + uint8 alphaRest = 255 - source[3]; + uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); + uint32 alphaDest = bottom[3] * alphaRest; + uint32 alphaSrc = 255 * source[3]; + bottom[0] = (bottom[0] * alphaDest + source[0] * alphaSrc) / alphaTemp; + bottom[1] = (bottom[1] * alphaDest + source[1] * alphaSrc) / alphaTemp; + bottom[2] = (bottom[2] * alphaDest + source[2] * alphaSrc) / alphaTemp; + bottom[3] = alphaTemp / 255; + } + } + } +} + +// blend_colors_copy +inline void +blend_colors_copy(uint8* dest, uint8* bottom, uint8* top) +{ + if (bottom[3] == 0 || top[3] == 255) { + dest[0] = top[0]; + dest[1] = top[1]; + dest[2] = top[2]; + dest[3] = top[3]; + } else { + if (bottom[3] == 255) { + uint32 destAlpha = 255 - top[3]; + dest[0] = (uint8)((bottom[0] * destAlpha + top[0] * top[3]) / 255); + dest[1] = (uint8)((bottom[1] * destAlpha + top[1] * top[3]) / 255); + dest[2] = (uint8)((bottom[2] * destAlpha + top[2] * top[3]) / 255); + dest[3] = 255; + } else { + uint8 alphaRest = 255 - top[3]; + uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); + uint32 alphaDest = bottom[3] * alphaRest; + uint32 alphaSrc = 255 * top[3]; + dest[0] = (bottom[0] * alphaDest + top[0] * alphaSrc) / alphaTemp; + dest[1] = (bottom[1] * alphaDest + top[1] * alphaSrc) / alphaTemp; + dest[2] = (bottom[2] * alphaDest + top[2] * alphaSrc) / alphaTemp; + dest[3] = alphaTemp / 255; + } + } +} + +// blend_pixels +inline void +blend_pixels(uint8* bottom, uint8* top, uint8 alpha) +{ + if (alpha > 0) { + if (alpha == 255) { + bottom[0] = top[0]; + bottom[1] = top[1]; + bottom[2] = top[2]; + bottom[3] = top[3]; + } else { + uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; + uint8 invAlpha = 255 - mergeAlpha; + bottom[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; + bottom[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; + bottom[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; + bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } +} + +// blend_pixels_copy +inline void +blend_pixels_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alpha) +{ + if (alpha > 0) { + if (alpha == 255) { + dest[0] = top[0]; + dest[1] = top[1]; + dest[2] = top[2]; + dest[3] = top[3]; + } else { + uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; + uint8 invAlpha = 255 - mergeAlpha; + dest[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; + dest[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; + dest[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; + dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } else { + dest[0] = bottom[0]; + dest[1] = bottom[1]; + dest[2] = bottom[2]; + dest[3] = bottom[3]; + } +} + +// blend_pixels_overlay +inline void +blend_pixels_overlay(uint8* bottom, uint8* top, uint8 alphaOverride) +{ + uint8 alpha = (top[3] * alphaOverride) / 255; + if (alpha > 0) { + if (alpha == 255) { + bottom[0] = top[0]; + bottom[1] = top[1]; + bottom[2] = top[2]; + bottom[3] = top[3]; + } else { + uint8 mergeAlpha = bottom[3] ? alpha : 255; + uint8 invAlpha = 255 - mergeAlpha; + bottom[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; + bottom[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; + bottom[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; + bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } +} + +// blend_pixels_overlay_copy +inline void +blend_pixels_overlay_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alphaOverride) +{ + uint8 alpha = (top[3] * alphaOverride) / 255; + if (alpha > 0) { + if (alpha == 255) { + dest[0] = top[0]; + dest[1] = top[1]; + dest[2] = top[2]; + dest[3] = top[3]; + } else { + uint8 mergeAlpha = bottom[3] ? alpha : 255; + uint8 invAlpha = 255 - mergeAlpha; + dest[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; + dest[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; + dest[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; + dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; + } + } else { + dest[0] = bottom[0]; + dest[1] = bottom[1]; + dest[2] = bottom[2]; + dest[3] = bottom[3]; + } +} + +#endif // GAMMA_BLEND + +#endif // BLENDING_H diff --git a/src/add-ons/translators/wonderbrush/support/lab_convert.cpp b/src/add-ons/translators/wonderbrush/support/lab_convert.cpp new file mode 100644 index 0000000000..5da5e49cb0 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/lab_convert.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include +#include + +#include "support.h" + +#include "lab_convert.h" + +#define GAMMA_ZERO_ENTRIES 256 +#define GAMMA_ENTRIES 10240 +#define GAMMA_MAX_ENTRIES 256 +#define GAMMA_TOTAL_ENTRIES GAMMA_ZERO_ENTRIES + GAMMA_ENTRIES + GAMMA_MAX_ENTRIES + +// init_gamma_table +uint8* +init_gamma_table() +{ + uint8* table = new uint8[GAMMA_TOTAL_ENTRIES]; + for (int32 i = 0; i < GAMMA_ZERO_ENTRIES; i++) + table[i] = 0; + for (int32 i = 0; i < GAMMA_ENTRIES; i++) + table[i + GAMMA_ZERO_ENTRIES] = (uint8)(pow((float)i / (float)(GAMMA_ENTRIES - 1), 0.4) * 255.0 + 0.5); + for (int32 i = 0; i < GAMMA_MAX_ENTRIES; i++) + table[i + GAMMA_ZERO_ENTRIES + GAMMA_ENTRIES] = 255; + return table; +} + +// init_linear_table +float* +init_linear_table() +{ + float* table = new float[256]; + for (int32 i = 0; i < 256; i++) + table[i] = pow((float)i / 255.0, 2.5); + return table; +} + +// conversion from RGB (0...255) to linear and normalized RGB (0...1) +static uint8* gammaTable = init_gamma_table(); +static float* linearTable = init_linear_table(); + +// matrix entries: XYZ -> RGB (709 RGB, D65 Whitepoint) +/*const float Rx = 3.240479; +const float Ry = -1.537150; +const float Rz = -0.498535; +const float Gx = -0.969256; +const float Gy = 1.875992; +const float Gz = 0.041556; +const float Bx = 0.055648; +const float By = -0.204043; +const float Bz = 1.057311;*/ + +// matrix entries: XYZ -> sRGB (D65 Whitepoint) +const float Rx = 3.24071; +const float Ry = -1.53726; +const float Rz = -0.498571; +const float Gx = -0.969258; +const float Gy = 1.87599; +const float Gz = 0.0415557; +const float Bx = 0.0556352; +const float By = -0.203996; +const float Bz = 1.05707; + + +// matrix entries scaled: XYZ(0...1) -> RGB(0...255) +const float RX = Rx * 255; +const float RY = Ry * 255; +const float RZ = Rz * 255; +const float GX = Gx * 255; +const float GY = Gy * 255; +const float GZ = Gz * 255; +const float BX = Bx * 255; +const float BY = By * 255; +const float BZ = Bz * 255; + +// matrix etries: RGB -> XYZ +/*const float Xr = 0.412453; +const float Xg = 0.357580; +const float Xb = 0.189423; +const float Yr = 0.212671; +const float Yg = 0.715160; +const float Yb = 0.072169; +const float Zr = 0.019334; +const float Zg = 0.119193; +const float Zb = 0.950227;*/ + +// matrix etries: sRGB -> XYZ (D65 Whitepoint) +const float Xr = 0.412424; +const float Xg = 0.357579; +const float Xb = 0.180464; +const float Yr = 0.212656; +const float Yg = 0.715158; +const float Yb = 0.0721856; +const float Zr = 0.0193324; +const float Zg = 0.119193; +const float Zb = 0.950444; + +// matrix entries scaled: RGB(0...255) -> XYZ(0...1) +const float XR = Xr / 255; +const float XG = Xg / 255; +const float XB = Xb / 255; +const float YR = Yr / 255; +const float YG = Yg / 255; +const float YB = Yb / 255; +const float ZR = Zr / 255; +const float ZG = Zg / 255; +const float ZB = Zb / 255; + +// white point +const float Xn = Xr + Xg + Xb; +const float Yn = Yr + Yg + Yb; +const float Zn = Zr + Zg + Zb; + +// matrix entries scaled and white point normalized: RGB(0...255) -> XYZ(0...1/WP) +const float XRn = Xr / Xn; +const float XGn = Xg / Xn; +const float XBn = Xb / Xn; +const float YRn = Yr / Yn; +const float YGn = Yg / Yn; +const float YBn = Yb / Yn; +const float ZRn = Zr / Zn; +const float ZGn = Zg / Zn; +const float ZBn = Zb / Zn; + +// lab2rgb +void +lab2rgb(float L, float a, float b, uint8& R, uint8& G, uint8& B) +{ + float P = (L + 16) / 116; + float Pa500 = P + a / 500; + float Pb200 = P - b / 200; + float X = Xn * Pa500 * Pa500 * Pa500; + float Y = Yn * P * P * P; + float Z = Zn * Pb200 * Pb200 * Pb200; +/* float linearR = max_c(0.0, min_c(1.0, Rx * X + Ry * Y + Rz * Z)); + float linearG = max_c(0.0, min_c(1.0, Gx * X + Gy * Y + Gz * Z)); + float linearB = max_c(0.0, min_c(1.0, Bx * X + By * Y + Bz * Z)); + R = (uint8)(pow(linearR, 0.4) * 255.0 + 0.5); + G = (uint8)(pow(linearG, 0.4) * 255.0 + 0.5); + B = (uint8)(pow(linearB, 0.4) * 255.0 + 0.5);*/ + float linearR = Rx * X + Ry * Y + Rz * Z; + float linearG = Gx * X + Gy * Y + Gz * Z; + float linearB = Bx * X + By * Y + Bz * Z; + R = (uint8)constrain_int32_0_255((int32)(pow(linearR, 0.4) * 255.0 + 0.5)); + G = (uint8)constrain_int32_0_255((int32)(pow(linearG, 0.4) * 255.0 + 0.5)); + B = (uint8)constrain_int32_0_255((int32)(pow(linearB, 0.4) * 255.0 + 0.5)); +/* float linearR = Rx * X + Ry * Y + Rz * Z; + float linearG = Gx * X + Gy * Y + Gz * Z; + float linearB = Bx * X + By * Y + Bz * Z; + R = gammaTable[(uint32)(linearR * (GAMMA_ENTRIES - 1) + 0.5) + GAMMA_ZERO_ENTRIES]; + G = gammaTable[(uint32)(linearG * (GAMMA_ENTRIES - 1) + 0.5) + GAMMA_ZERO_ENTRIES]; + B = gammaTable[(uint32)(linearB * (GAMMA_ENTRIES - 1) + 0.5) + GAMMA_ZERO_ENTRIES];*/ +} + +inline float +f(float t) +{ + if (t > 0.008856) + return pow(t, 1.0 / 3.0); + return 7.787 * t + 16.0 / 116; +} + +// rgb2lab +void +rgb2lab(uint8 R, uint8 G, uint8 B, float& L, float& a, float& b) +{ +/* float linearR = pow((float)R / 255.0, 2.5); + float linearG = pow((float)G / 255.0, 2.5); + float linearB = pow((float)B / 255.0, 2.5);*/ + float linearR = linearTable[R]; + float linearG = linearTable[G]; + float linearB = linearTable[B]; + float Xq = XRn * linearR + XGn * linearG + XBn * linearB; // == X/Xn + float Yq = YRn * linearR + YGn * linearG + YBn * linearB; // == Y/Yn + float Zq = ZRn * linearR + ZGn * linearG + ZBn * linearB; // == Z/Zn + if (Yq > 0.008856) + L = 116.0 * pow(Yq, 1.0 / 3.0) - 16; + else + L = 903.3 * Yq; + a = 500 * (f(Xq) - f(Yq)); + b = 200 * (f(Yq) - f(Zq)); +} + +// replace_luminance +void +replace_luminance(uint8& r1, uint8& g1, uint8& b1, uint8 r2, uint8 g2, uint8 b2) +{ + float CIEL1, CIEa1, CIEb1, CIEL2;//, CIEa2, CIEb2; + rgb2lab(r1, g1, b1, CIEL1, CIEa1, CIEb1); +// rgb2lab(r2, g2, b2, CIEL2, CIEa2, CIEb2); + CIEL2 = ((linearTable[r2] + linearTable[g2] + linearTable[b2]) / 3.0) * 100.0; + lab2rgb(CIEL2, CIEa1, CIEb1, r1, g1, b1); +} diff --git a/src/add-ons/translators/wonderbrush/support/lab_convert.h b/src/add-ons/translators/wonderbrush/support/lab_convert.h new file mode 100644 index 0000000000..d93ea79001 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/lab_convert.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef LAB_CONVERT_H +#define LAB_CONVERT_H + +#include + +void lab2rgb(float L, float a, float b, uint8& R, uint8& G, uint8& B); + +// lab2rgb +inline void +lab2rgb(uint8 L, uint8 a, uint8 b, uint8& R, uint8& G, uint8& B) +{ + float CIEL = ((float)L / 255.0) * 100.0; + float CIEa = ((float)a - 128.0); + float CIEb = ((float)b - 128.0); + lab2rgb(CIEL, CIEa, CIEb, R, G, B); +} + +void rgb2lab(uint8 R, uint8 G, uint8 B, float& L, float& a, float& b); + +// rgb2lab +inline void +rgb2lab(uint8 R, uint8 G, uint8 B, uint8& L, uint8& a, uint8& b) +{ + float CIEL, CIEa, CIEb; + rgb2lab(R, G, B, CIEL, CIEa, CIEb); + L = uint8((CIEL / 100.0) * 255.0); + a = uint8(CIEa + 128); + b = uint8(CIEb + 128); +} + +void replace_luminance(uint8& r1, uint8& g1, uint8& b1, uint8 r2, uint8 g2, uint8 b2); + +#endif // LAB_CONVERT_H diff --git a/src/add-ons/translators/wonderbrush/support/support.h b/src/add-ons/translators/wonderbrush/support/support.h new file mode 100644 index 0000000000..b5ef10d2d0 --- /dev/null +++ b/src/add-ons/translators/wonderbrush/support/support.h @@ -0,0 +1,58 @@ +/* + * Copyright 2006, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SUPPORT_H +#define SUPPORT_H + +#include + +// constrain +inline void +constrain(float& value, float min, float max) +{ + if (value < min) + value = min; + if (value > max) + value = max; +} + +// constrain_int32_0_255_asm +inline int32 +constrain_int32_0_255_asm(int32 value) { + asm("movl $0, %%ecx; + movl $255, %%edx; + cmpl %%ecx, %%eax; + cmovl %%ecx, %%eax; + cmpl %%edx, %%eax; + cmovg %%edx, %%eax" + : "=a" (value) + : "a" (value) + : "%ecx", "%edx" ); + return value; +} + +inline int32 +constrain_int32_0_255_c(int32 value) { + return max_c(0, min_c(255, value)); +} + +#define constrain_int32_0_255 constrain_int32_0_255_asm + +// rect_to_int +inline void +rect_to_int(BRect r, + int32& left, int32& top, int32& right, int32& bottom) +{ + left = (int32)floorf(r.left); + top = (int32)floorf(r.top); + right = (int32)ceilf(r.right); + bottom = (int32)ceilf(r.bottom); +} + + +# endif // SUPPORT_H