#include <Bitmap.h>
#include <algorithm>
#include <limits.h>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Application.h>
#include <GraphicsDefs.h>
#include <Locker.h>
#include <Autolock.h>
#include <ObjectList.h>
#include "ColorConversion.h"
using namespace BPrivate;
static BObjectList<BBitmap> sBitmapList;
static BLocker sBitmapListLock;
static inline int32
get_raw_bytes_per_row(color_space colorSpace, int32 width)
{
int32 bpr = 0;
switch (colorSpace) {
case B_RGBA64: case B_RGBA64_BIG:
bpr = 8 * width;
break;
case B_RGB48: case B_RGB48_BIG:
bpr = 6 * width;
break;
case B_RGB32: case B_RGBA32:
case B_RGB32_BIG: case B_RGBA32_BIG:
case B_UVL32: case B_UVLA32:
case B_LAB32: case B_LABA32:
case B_HSI32: case B_HSIA32:
case B_HSV32: case B_HSVA32:
case B_HLS32: case B_HLSA32:
case B_CMY32: case B_CMYA32: case B_CMYK32:
bpr = 4 * width;
break;
case B_RGB24: case B_RGB24_BIG:
case B_UVL24: case B_LAB24: case B_HSI24:
case B_HSV24: case B_HLS24: case B_CMY24:
bpr = 3 * width;
break;
case B_RGB16: case B_RGB15: case B_RGBA15:
case B_RGB16_BIG: case B_RGB15_BIG: case B_RGBA15_BIG:
bpr = 2 * width;
break;
case B_CMAP8: case B_GRAY8:
bpr = width;
break;
case B_GRAY1:
bpr = (width + 7) / 8;
break;
case B_YCbCr422: case B_YUV422:
bpr = (width + 3) / 4 * 8;
break;
case B_YCbCr411: case B_YUV411:
bpr = (width + 3) / 4 * 6;
break;
case B_YCbCr444: case B_YUV444:
bpr = (width + 3) / 4 * 12;
break;
case B_YCbCr420: case B_YUV420:
bpr = (width + 3) / 4 * 6;
break;
case B_YUV9:
bpr = (width + 15) / 16 * 18;
break;
case B_NO_COLOR_SPACE:
case B_YUV12:
break;
}
return bpr;
}
namespace BPrivate {
int32
get_bytes_per_row(color_space colorSpace, int32 width)
{
int32 bpr = get_raw_bytes_per_row(colorSpace, width);
bpr = (bpr + 3) & 0x7ffffffc;
return bpr;
}
}
BBitmap::BBitmap(BRect bounds, uint32 flags, color_space colorSpace,
int32 bytesPerRow, screen_id screenID)
:
fBasePointer(NULL),
fSize(0),
fColorSpace(B_NO_COLOR_SPACE),
fBounds(0, 0, -1, -1),
fBytesPerRow(0),
fFlags(0),
fInitError(B_NO_INIT)
{
_InitObject(bounds, colorSpace, flags, bytesPerRow, screenID);
}
BBitmap::BBitmap(BRect bounds, color_space colorSpace, bool acceptsViews,
bool needsContiguous)
:
fBasePointer(NULL),
fSize(0),
fColorSpace(B_NO_COLOR_SPACE),
fBounds(0, 0, -1, -1),
fBytesPerRow(0),
fFlags(0),
fInitError(B_NO_INIT)
{
int32 flags = (acceptsViews ? B_BITMAP_ACCEPTS_VIEWS : 0)
| (needsContiguous ? B_BITMAP_IS_CONTIGUOUS : 0);
_InitObject(bounds, colorSpace, flags, B_ANY_BYTES_PER_ROW,
B_MAIN_SCREEN_ID);
}
BBitmap::BBitmap(const BBitmap* source, bool acceptsViews, bool needsContiguous)
:
fBasePointer(NULL),
fSize(0),
fColorSpace(B_NO_COLOR_SPACE),
fBounds(0, 0, -1, -1),
fBytesPerRow(0),
fFlags(0),
fInitError(B_NO_INIT)
{
if (source && source->IsValid()) {
int32 flags = (acceptsViews ? B_BITMAP_ACCEPTS_VIEWS : 0)
| (needsContiguous ? B_BITMAP_IS_CONTIGUOUS : 0);
_InitObject(source->Bounds(), source->ColorSpace(), flags,
source->BytesPerRow(), B_MAIN_SCREEN_ID);
if (InitCheck() == B_OK) {
memcpy(Bits(), source->Bits(), min_c(BitsLength(),
source->BitsLength()));
}
}
}
BBitmap::BBitmap(const BBitmap& source, uint32 flags)
:
fBasePointer(NULL),
fSize(0),
fColorSpace(B_NO_COLOR_SPACE),
fBounds(0, 0, -1, -1),
fBytesPerRow(0),
fFlags(0),
fInitError(B_NO_INIT)
{
if (!source.IsValid())
return;
_InitObject(source.Bounds(), source.ColorSpace(), flags,
source.BytesPerRow(), B_MAIN_SCREEN_ID);
if (InitCheck() == B_OK)
memcpy(Bits(), source.Bits(), min_c(BitsLength(), source.BitsLength()));
}
BBitmap::BBitmap(const BBitmap& source)
:
fBasePointer(NULL),
fSize(0),
fColorSpace(B_NO_COLOR_SPACE),
fBounds(0, 0, -1, -1),
fBytesPerRow(0),
fFlags(0),
fInitError(B_NO_INIT)
{
*this = source;
}
BBitmap::~BBitmap()
{
_CleanUp();
}
BBitmap::BBitmap(BMessage* data)
:
BArchivable(data),
fBasePointer(NULL),
fSize(0),
fColorSpace(B_NO_COLOR_SPACE),
fBounds(0, 0, -1, -1),
fBytesPerRow(0),
fFlags(0),
fInitError(B_NO_INIT)
{
int32 flags;
if (data->FindInt32("_bmflags", &flags) != B_OK) {
flags = 0;
bool acceptsViews;
if (data->FindBool("_view_ok", &acceptsViews) == B_OK && acceptsViews)
flags |= B_BITMAP_ACCEPTS_VIEWS;
bool contiguous;
if (data->FindBool("_contiguous", &contiguous) == B_OK && contiguous)
flags |= B_BITMAP_IS_CONTIGUOUS;
}
int32 rowBytes;
if (data->FindInt32("_rowbytes", &rowBytes) != B_OK) {
rowBytes = -1;
}
BRect bounds;
color_space cspace;
if (data->FindRect("_frame", &bounds) == B_OK
&& data->FindInt32("_cspace", (int32*)&cspace) == B_OK) {
_InitObject(bounds, cspace, flags, rowBytes, B_MAIN_SCREEN_ID);
}
if (InitCheck() == B_OK) {
ssize_t size;
const void* buffer;
if (data->FindData("_data", B_RAW_TYPE, &buffer, &size) == B_OK) {
if (size == BitsLength()) {
_AssertPointer();
memcpy(fBasePointer, buffer, size);
}
}
}
}
BArchivable*
BBitmap::Instantiate(BMessage* data)
{
if (validate_instantiation(data, "BBitmap"))
return new BBitmap(data);
return NULL;
}
status_t
BBitmap::Archive(BMessage* data, bool deep) const
{
status_t ret = BArchivable::Archive(data, deep);
if (ret == B_OK)
ret = data->AddRect("_frame", fBounds);
if (ret == B_OK)
ret = data->AddInt32("_cspace", (int32)fColorSpace);
if (ret == B_OK)
ret = data->AddInt32("_bmflags", fFlags);
if (ret == B_OK)
ret = data->AddInt32("_rowbytes", fBytesPerRow);
if (ret == B_OK) {
const_cast<BBitmap*>(this)->_AssertPointer();
ret = data->AddData("_data", B_RAW_TYPE, fBasePointer, fSize);
}
return ret;
}
status_t
BBitmap::InitCheck() const
{
return fInitError;
}
bool
BBitmap::IsValid() const
{
return InitCheck() == B_OK;
}
status_t
BBitmap::LockBits(uint32* state)
{
return B_OK;
}
void
BBitmap::UnlockBits()
{
}
void*
BBitmap::Bits() const
{
const_cast<BBitmap*>(this)->_AssertPointer();
return (void*)fBasePointer;
}
int32
BBitmap::BitsLength() const
{
return fSize;
}
int32
BBitmap::BytesPerRow() const
{
return fBytesPerRow;
}
color_space
BBitmap::ColorSpace() const
{
return fColorSpace;
}
BRect
BBitmap::Bounds() const
{
return fBounds;
}
uint32
BBitmap::Flags() const
{
return fFlags;
}
void
BBitmap::SetBits(const void* data, int32 length, int32 offset,
color_space colorSpace)
{
status_t error = (InitCheck() == B_OK ? B_OK : B_NO_INIT);
if (error == B_OK && (data == NULL || offset > fSize || length < 0))
error = B_BAD_VALUE;
int32 width = 0;
if (error == B_OK)
width = fBounds.IntegerWidth() + 1;
int32 inBPR = -1;
if (error == B_OK) {
if (colorSpace == B_RGB32 && (length % 3) == 0) {
colorSpace = B_RGB24_BIG;
inBPR = width * 3;
} else if (colorSpace == B_CMAP8 && fColorSpace != B_CMAP8) {
inBPR = width;
}
error = ImportBits(data, length, inBPR, offset, colorSpace);
}
}
status_t
BBitmap::ImportBits(const void* data, int32 length, int32 bpr, int32 offset,
color_space colorSpace)
{
_AssertPointer();
if (InitCheck() != B_OK)
return B_NO_INIT;
if (!data || offset > fSize || length < 0)
return B_BAD_VALUE;
int32 width = fBounds.IntegerWidth() + 1;
if (bpr <= 0) {
if (bpr == B_ANY_BYTES_PER_ROW)
bpr = get_bytes_per_row(colorSpace, width);
else
return B_BAD_VALUE;
}
return BPrivate::ConvertBits(data, (uint8*)fBasePointer + offset, length,
fSize - offset, bpr, fBytesPerRow, colorSpace, fColorSpace, width,
fBounds.IntegerHeight() + 1);
}
status_t
BBitmap::ImportBits(const void* data, int32 length, int32 bpr,
color_space colorSpace, BPoint from, BPoint to, BSize size)
{
_AssertPointer();
if (InitCheck() != B_OK)
return B_NO_INIT;
if (!data || length < 0 || size.IntegerWidth() < 0 || size.IntegerHeight() < 0)
return B_BAD_VALUE;
if (bpr <= 0) {
if (bpr == B_ANY_BYTES_PER_ROW)
bpr = get_bytes_per_row(colorSpace, fBounds.IntegerWidth() + 1);
else
return B_BAD_VALUE;
}
return BPrivate::ConvertBits(data, fBasePointer, length, fSize, bpr,
fBytesPerRow, colorSpace, fColorSpace, from, to,
size.IntegerWidth() + 1, size.IntegerHeight() + 1);
}
status_t
BBitmap::ImportBits(const void* data, int32 length, int32 bpr,
color_space colorSpace, BPoint from, BPoint to, int32 width, int32 height)
{
return ImportBits(data, length, bpr, colorSpace, from, to, BSize(width - 1, height - 1));
}
status_t
BBitmap::ImportBits(const BBitmap* bitmap)
{
if (InitCheck() != B_OK)
return B_NO_INIT;
if (!bitmap || bitmap->InitCheck() != B_OK || bitmap->Bounds() != fBounds)
return B_BAD_VALUE;
return ImportBits(bitmap->Bits(), bitmap->BitsLength(),
bitmap->BytesPerRow(), 0, bitmap->ColorSpace());
}
status_t
BBitmap::ImportBits(const BBitmap* bitmap, BPoint from, BPoint to, BSize size)
{
if (InitCheck() != B_OK)
return B_NO_INIT;
if (!bitmap || bitmap->InitCheck() != B_OK)
return B_BAD_VALUE;
return ImportBits(bitmap->Bits(), bitmap->BitsLength(),
bitmap->BytesPerRow(), bitmap->ColorSpace(), from, to, size);
}
status_t
BBitmap::ImportBits(const BBitmap* bitmap, BPoint from, BPoint to, int32 width, int32 height)
{
return ImportBits(bitmap, from, to, BSize(width - 1, height - 1));
}
status_t
BBitmap::GetOverlayRestrictions(overlay_restrictions* restrictions) const
{
return B_ERROR;
}
bool
BBitmap::Lock()
{
return false;
}
void
BBitmap::Unlock()
{
}
bool
BBitmap::IsLocked() const
{
return false;
}
BBitmap&
BBitmap::operator=(const BBitmap& source)
{
_CleanUp();
fInitError = B_NO_INIT;
if (!source.IsValid())
return *this;
_InitObject(source.Bounds(), source.ColorSpace(), source.Flags(),
source.BytesPerRow(), B_MAIN_SCREEN_ID);
if (InitCheck() == B_OK)
memcpy(Bits(), source.Bits(), min_c(BitsLength(), source.BitsLength()));
return *this;
}
status_t
BBitmap::Perform(perform_code d, void* arg)
{
return BArchivable::Perform(d, arg);
}
void BBitmap::_ReservedBitmap1() {}
void BBitmap::_ReservedBitmap2() {}
void BBitmap::_ReservedBitmap3() {}
void
BBitmap::_InitObject(BRect bounds, color_space colorSpace, uint32 flags,
int32 bytesPerRow, screen_id screenID)
{
status_t error = B_OK;
flags |= B_BITMAP_NO_SERVER_LINK;
_CleanUp();
if (!bounds.IsValid() || !bitmaps_support_space(colorSpace, NULL)) {
error = B_BAD_VALUE;
} else {
double realSize = bounds.Width() * bounds.Height();
if (realSize > (double)(INT_MAX / 4)) {
fprintf(stderr, "bitmap bounds is much too large: "
"BRect(%.1f, %.1f, %.1f, %.1f)\n",
bounds.left, bounds.top, bounds.right, bounds.bottom);
error = B_BAD_VALUE;
}
}
if (error == B_OK) {
int32 bpr = get_bytes_per_row(colorSpace, bounds.IntegerWidth() + 1);
if (bytesPerRow < 0)
bytesPerRow = bpr;
else if (bytesPerRow < bpr)
error = B_BAD_VALUE;
}
if (error == B_OK) {
int32 size = bytesPerRow * (bounds.IntegerHeight() + 1);
if ((flags & B_BITMAP_NO_SERVER_LINK) != 0) {
fBasePointer = (uint8*)malloc(size);
if (fBasePointer) {
fSize = size;
fColorSpace = colorSpace;
fBounds = bounds;
fBytesPerRow = bytesPerRow;
fFlags = flags;
} else
error = B_NO_MEMORY;
}
}
fInitError = error;
if (fInitError == B_OK) {
if (flags & B_BITMAP_CLEAR_TO_WHITE) {
if (fColorSpace == B_CMAP8) {
memset(fBasePointer, 65, fSize);
} else {
memset(fBasePointer, 0xff, fSize);
}
}
}
}
void
BBitmap::_CleanUp()
{
if (fBasePointer == NULL)
return;
if ((fFlags & B_BITMAP_NO_SERVER_LINK) != 0) {
free(fBasePointer);
}
fBasePointer = NULL;
}
void
BBitmap::_AssertPointer()
{
}