* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "DrawingTidbits.h"
#include <Bitmap.h>
#include <Debug.h>
#include <Screen.h>
#include <math.h>
#include <string.h>
inline uchar
ShiftComponent(uchar component, float percent)
{
if (percent >= 1)
return (uchar)(component * (2 - percent));
else
return (uchar)(255 - percent * (255 - component));
}
rgb_color
ShiftColor(rgb_color color, float percent)
{
rgb_color result = {
ShiftComponent(color.red, percent),
ShiftComponent(color.green, percent),
ShiftComponent(color.blue, percent),
0
};
return result;
}
void
ReplaceColor(BBitmap *bitmap, rgb_color from, rgb_color to)
{
ASSERT(bitmap->ColorSpace() == B_COLOR_8_BIT);
BScreen screen(B_MAIN_SCREEN_ID);
uint32 fromIndex = screen.IndexForColor(from);
uint32 toIndex = screen.IndexForColor(to);
uchar *bits = (uchar *)bitmap->Bits();
int32 bitsLength = bitmap->BitsLength();
for (int32 index = 0; index < bitsLength; index++)
if (bits[index] == fromIndex)
bits[index] = toIndex;
}
void
ReplaceTransparentColor(BBitmap *bitmap, rgb_color with)
{
ASSERT(bitmap->ColorSpace() == B_COLOR_8_BIT);
BScreen screen(B_MAIN_SCREEN_ID);
uint32 withIndex = screen.IndexForColor(with);
uchar *bits = (uchar *)bitmap->Bits();
int32 bitsLength = bitmap->BitsLength();
for (int32 index = 0; index < bitsLength; index++)
if (bits[index] == B_TRANSPARENT_8_BIT)
bits[index] = withIndex;
}
inline void
ycbcr_to_rgb( uint8 y, uint8 cb, uint8 cr,
uint8& r, uint8& g, uint8& b)
{
r = (uint8)max_c( 0, min_c( 255, 1.164 * ( y - 16 ) + 1.596 * ( cr - 128 ) ) );
g = (uint8)max_c( 0, min_c( 255, 1.164 * ( y - 16 ) - 0.813 * ( cr - 128 )
- 0.391 * ( cb - 128 ) ) );
b = (uint8)max_c( 0, min_c( 255, 1.164 * ( y - 16 ) + 2.018 * ( cb - 128 ) ) );
}
inline void
mix_colors( uint8 ra, uint8 ga, uint8 ba,
uint8 rb, uint8 gb, uint8 bb,
uint8& r, uint8& g, uint8& b, float mixLevel )
{
float mixA = ( 1.0 - mixLevel );
float mixB = mixLevel;
r = (uint8)(mixA * ra + mixB * rb);
g = (uint8)(mixA * ga + mixB * gb);
b = (uint8)(mixA * ba + mixB * bb);
}
status_t
scale_bitmap( BBitmap* bitmap, uint32 fromWidth, uint32 fromHeight )
{
status_t status = B_BAD_VALUE;
if ( bitmap && bitmap->IsValid()
&& ( bitmap->ColorSpace() == B_RGB32 || bitmap->ColorSpace() == B_RGBA32 ) )
{
status = B_MISMATCHED_VALUES;
uint32 destWidth = bitmap->Bounds().IntegerWidth() + 1;
uint32 destHeight = bitmap->Bounds().IntegerHeight() + 1;
if ( fromWidth <= destWidth && fromHeight <= destHeight )
{
status = B_OK;
uint32 bpr = bitmap->BytesPerRow();
if ( fromWidth < destWidth )
{
uint8* src = (uint8*)bitmap->Bits();
uint8* p = new uint8[fromWidth * 4];
for ( uint32 y = 0; y < fromHeight; y++ )
{
memcpy( p, src, fromWidth * 4 );
for ( uint32 x = 0; x < destWidth; x++ )
{
float xPos = ( (float)x / (float)destWidth ) * (float)fromWidth;
uint32 leftIndex = (uint32)floorf( xPos ) * 4;
uint32 rightIndex = (uint32)ceilf( xPos ) * 4;
rgb_color left;
left.red = p[leftIndex + 2];
left.green = p[leftIndex + 1];
left.blue = p[leftIndex + 0];
rgb_color right;
right.red = p[rightIndex + 2];
right.green = p[rightIndex + 1];
right.blue = p[rightIndex + 0];
rgb_color mix;
mix_colors( left.red, left.green, left.blue,
right.red, right.green, right.blue,
mix.red, mix.green, mix.blue, xPos - floorf( xPos ) );
uint32 destIndex = x * 4;
src[destIndex + 2] = mix.red;
src[destIndex + 1] = mix.green;
src[destIndex + 0] = mix.blue;
}
src += bpr;
}
delete[] p;
}
if ( fromHeight < destHeight )
{
uint8* src = (uint8*)bitmap->Bits();
uint8* p = new uint8[fromHeight * 3];
for ( uint32 x = 0; x < destWidth; x++ )
{
for ( uint32 y = 0; y < fromHeight; y++ )
{
uint32 destIndex = y * 3;
uint32 srcIndex = x * 4 + y * bpr;
p[destIndex + 0] = src[srcIndex + 0];
p[destIndex + 1] = src[srcIndex + 1];
p[destIndex + 2] = src[srcIndex + 2];
}
for ( uint32 y = 0; y < destHeight; y++ )
{
float yPos = ( (float)y / (float)destHeight ) * (float)fromHeight;
uint32 upperIndex = (uint32)floorf( yPos ) * 3;
uint32 lowerIndex = (uint32)ceilf( yPos ) * 3;
rgb_color upper;
upper.red = p[upperIndex + 2];
upper.green = p[upperIndex + 1];
upper.blue = p[upperIndex + 0];
rgb_color lower;
lower.red = p[lowerIndex + 2];
lower.green = p[lowerIndex + 1];
lower.blue = p[lowerIndex + 0];
rgb_color mix;
mix_colors( upper.red, upper.green, upper.blue,
lower.red, lower.green, lower.blue,
mix.red, mix.green, mix.blue, yPos - floorf( yPos ) );
uint32 destIndex = x * 4 + y * bpr;
src[destIndex + 2] = mix.red;
src[destIndex + 1] = mix.green;
src[destIndex + 0] = mix.blue;
}
}
delete[] p;
}
}
}
return status;
}
status_t
convert_bitmap( BBitmap* inBitmap, BBitmap* outBitmap )
{
status_t status = B_BAD_VALUE;
if ( inBitmap && inBitmap->IsValid()
&& outBitmap && outBitmap->IsValid() )
{
status = B_MISMATCHED_VALUES;
if ( inBitmap->Bounds().Width() <= outBitmap->Bounds().Width()
&& inBitmap->Bounds().Height() <= outBitmap->Bounds().Height()
&& ( outBitmap->ColorSpace() == B_RGB32
|| outBitmap->ColorSpace() == B_RGBA32) )
{
int32 width = inBitmap->Bounds().IntegerWidth() + 1;
int32 height = inBitmap->Bounds().IntegerHeight() + 1;
int32 srcBpr = inBitmap->BytesPerRow();
int32 dstBpr = outBitmap->BytesPerRow();
uint8* srcBits = (uint8*)inBitmap->Bits();
uint8* dstBits = (uint8*)outBitmap->Bits();
switch (inBitmap->ColorSpace())
{
case B_YCbCr422:
for ( int32 y = 0; y < height; y++ )
{
for ( int32 x = 0; x < width; x += 2 )
{
int32 srcOffset = x * 2;
int32 dstOffset = x * 4;
ycbcr_to_rgb( srcBits[srcOffset + 0],
srcBits[srcOffset + 1],
srcBits[srcOffset + 3],
dstBits[dstOffset + 2],
dstBits[dstOffset + 1],
dstBits[dstOffset + 0] );
ycbcr_to_rgb( srcBits[srcOffset + 2],
srcBits[srcOffset + 1],
srcBits[srcOffset + 3],
dstBits[dstOffset + 6],
dstBits[dstOffset + 5],
dstBits[dstOffset + 4] );
dstBits[x * 4 + 3] = 255;
dstBits[x * 4 + 7] = 255;
}
srcBits += srcBpr;
dstBits += dstBpr;
}
status = B_OK;
break;
case B_YCbCr420:
status = B_ERROR;
break;
case B_YUV422:
status = B_ERROR;
break;
case B_RGB32:
case B_RGBA32:
memcpy( dstBits, srcBits, inBitmap->BitsLength() );
status = B_OK;
break;
case B_RGB16:
for ( int32 y = 0; y < height; y ++ )
{
for ( int32 x = 0; x < width; x++ )
{
int32 srcOffset = x * 2;
int32 dstOffset = x * 4;
uint8 blue = srcBits[srcOffset + 0] & 0x1f;
uint8 green = ( srcBits[srcOffset + 0] >> 5 )
| ( ( srcBits[srcOffset + 1] & 0x07 ) << 3 );
uint8 red = srcBits[srcOffset + 1] & 0xf8;
dstBits[dstOffset + 0] = (blue << 3) | (blue >> 2);
dstBits[dstOffset + 1] = (green << 2) | (green >> 4);
dstBits[dstOffset + 2] = red | (red >> 5);
}
srcBits += srcBpr;
dstBits += dstBpr;
}
status = B_OK;
break;
default:
status = B_MISMATCHED_VALUES;
break;
}
if ( status == B_OK )
{
if ( width < outBitmap->Bounds().IntegerWidth() + 1
|| height < outBitmap->Bounds().IntegerHeight() + 1 )
{
scale_bitmap( outBitmap, width, height );
}
}
}
}
return status;
}
inline uint8
clip_float(float value)
{
if (value < 0)
value = 0;
if (value > 255)
value = 255;
return (uint8)value;
}
status_t
dim_bitmap(BBitmap* bitmap, rgb_color center, float dimLevel)
{
status_t status = B_BAD_VALUE;
if (bitmap && bitmap->IsValid())
{
switch (bitmap->ColorSpace())
{
case B_CMAP8:
{
BScreen screen(B_MAIN_SCREEN_ID);
if (screen.IsValid())
{
int32 length = bitmap->BitsLength();
uint8* bits = (uint8*)bitmap->Bits();
for (int32 i = 0; i < length; i++)
{
if (bits[i] != B_TRANSPARENT_MAGIC_CMAP8)
{
rgb_color c = screen.ColorForIndex(bits[i]);
float dist = (c.red - center.red) * dimLevel;
c.red = clip_float(center.red + dist);
dist = (c.green - center.green) * dimLevel;
c.green = clip_float(center.green + dist);
dist = (c.blue - center.blue) * dimLevel;
c.blue = clip_float(center.blue + dist);
bits[i] = screen.IndexForColor(c);
}
}
status = B_OK;
}
break;
}
case B_RGB32:
case B_RGBA32:
{
uint8* bits = (uint8*)bitmap->Bits();
int32 bpr = bitmap->BytesPerRow();
int32 pixels = bitmap->Bounds().IntegerWidth() + 1;
int32 lines = bitmap->Bounds().IntegerHeight() + 1;
for (int32 y = 0; y < lines; y++) {
for (int32 x = 0; x < pixels; x++) {
int32 offset = 4 * x;
float dist = (bits[offset + 0] - center.blue) * dimLevel;
bits[offset + 0] = clip_float(center.blue + dist);
dist = (bits[offset + 1] - center.green) * dimLevel;
bits[offset + 1] = clip_float(center.green + dist);
dist = (bits[offset + 2] - center.red) * dimLevel;
bits[offset + 2] = clip_float(center.red + dist);
}
bits += bpr;
}
status = B_OK;
break;
}
default:
status = B_ERROR;
break;
}
}
return status;
}
rgb_color
dimmed_color_cmap8(rgb_color color, rgb_color center, float dimLevel)
{
BScreen screen(B_MAIN_SCREEN_ID);
if (screen.IsValid())
{
float dist = (color.red - center.red) * dimLevel;
color.red = clip_float(center.red + dist);
dist = (color.green - center.green) * dimLevel;
color.green = clip_float(center.green + dist);
dist = (color.blue - center.blue) * dimLevel;
color.blue = clip_float(center.blue + dist);
int32 index = screen.IndexForColor(color);
color = screen.ColorForIndex(index);
}
return color;
}