* implemented a Translator for WonderBrush image files, these files contain

the layer bitmaps, all the Translater has to do is compose the layers, tested
  and works with all my WonderBrush images, hopefully contains no memory leaks...


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19211 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2006-11-05 21:47:28 +00:00
parent 55b40aa53a
commit 73a2ffba3b
20 changed files with 2583 additions and 0 deletions

View File

@ -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 ;

View File

@ -0,0 +1,215 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "Canvas.h"
#include <new>
#include <stdio.h>
#include <Bitmap.h>
#include <Entry.h>
#include <Message.h>
#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;
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef CANVAS_H
#define CANVAS_H
#include <List.h>
#include <Rect.h>
#include <String.h>
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

View File

@ -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 ;

View File

@ -0,0 +1,386 @@
/*
* 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 <Message.h>
#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;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef LAYER_H
#define LAYER_H
#include <Rect.h>
#include <String.h>
// 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

View File

@ -0,0 +1,55 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#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();
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef WONDERBRUSH_IMAGE_H
#define WONDERBRUSH_IMAGE_H
#include <Message.h>
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

View File

@ -0,0 +1,26 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <Application.h>
#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;
}

View File

@ -0,0 +1,241 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "WonderBrushTranslator.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <Bitmap.h>
#include <OS.h>
#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;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef WONDERBRUSH_TRANSLATOR_H
#define WONDERBRUSH_TRANSLATOR_H
#include <ByteOrder.h>
#include <DataIO.h>
#include <File.h>
#include <fs_attr.h>
#include <GraphicsDefs.h>
#include <InterfaceDefs.h>
#include <TranslationDefs.h>
#include <Translator.h>
#include <TranslatorFormats.h>
#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

View File

@ -0,0 +1,182 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "WonderBrushView.h"
#include <stdio.h>
#include <string.h>
#include <MenuBar.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <Window.h>
#include "WonderBrushImage.h"
#include "WonderBrushTranslator.h"
const char* kAuthor = "Stephan Aßmus, <superstippi@gmx.de>";
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<int>(B_TRANSLATION_MAJOR_VERSION(WBI_TRANSLATOR_VERSION)),
static_cast<int>(B_TRANSLATION_MINOR_VERSION(WBI_TRANSLATOR_VERSION)),
static_cast<int>(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;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef WONDERBRUSH_VIEW_H
#define WONDERBRUSH_VIEW_H
#include <View.h>
#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

View File

@ -0,0 +1,166 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <zlib.h>
#include <Bitmap.h>
#include <Message.h>
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;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef BITMAP_COMPRESSION_H
#define BITMAP_COMPRESSION_H
#include <SupportDefs.h>
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

View File

@ -0,0 +1,70 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <math.h>
#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

View File

@ -0,0 +1,613 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef BLENDING_H
#define BLENDING_H
#include <SupportDefs.h>
// 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

View File

@ -0,0 +1,200 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <math.h>
#include <stdio.h>
#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);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef LAB_CONVERT_H
#define LAB_CONVERT_H
#include <SupportDefs.h>
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

View File

@ -0,0 +1,58 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef SUPPORT_H
#define SUPPORT_H
#include <Rect.h>
// 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