#include "ColorField.h"
#include <stdio.h>
#include <Bitmap.h>
#include <ControlLook.h>
#include <LayoutUtils.h>
#include <OS.h>
#include <Region.h>
#include <Window.h>
#include "support_ui.h"
#include "rgb_hsv.h"
#define round(x) (int)(x +.5)
enum {
MSG_UPDATE = 'Updt',
};
#define MAX_X 255
#define MAX_Y 255
ColorField::ColorField(BPoint offsetPoint, SelectedColorMode mode,
float fixedValue, orientation orient, border_style border)
: BControl(BRect(0.0, 0.0, MAX_X + 4.0, MAX_Y + 4.0)
.OffsetToCopy(offsetPoint),
"ColorField", "", new BMessage(MSG_COLOR_FIELD),
B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS)
{
_Init(mode, fixedValue, orient, border);
FrameResized(Bounds().Width(), Bounds().Height());
}
ColorField::ColorField(SelectedColorMode mode, float fixedValue,
orientation orient, border_style border)
: BControl("ColorField", "", new BMessage(MSG_COLOR_FIELD),
B_WILL_DRAW | B_FRAME_EVENTS)
{
_Init(mode, fixedValue, orient, border);
}
ColorField::~ColorField()
{
delete fBitmap;
}
BSize
ColorField::MinSize()
{
BSize minSize;
if (fOrientation == B_VERTICAL)
minSize = BSize(4 + MAX_X / 17, 4 + MAX_Y / 5);
else
minSize = BSize(4 + MAX_X / 5, 4 + MAX_Y / 17);
return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize);
}
BSize
ColorField::PreferredSize()
{
return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
}
BSize
ColorField::MaxSize()
{
BSize maxSize(4 + MAX_X, 4 + MAX_Y);
return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
}
status_t
ColorField::Invoke(BMessage* message)
{
if (message == NULL)
message = Message();
if (message == NULL)
return BControl::Invoke(message);
message->RemoveName("value");
float v1 = 0;
float v2 = 0;
switch (fMode) {
case R_SELECTED:
case G_SELECTED:
case B_SELECTED:
v1 = fMarkerPosition.x / Width();
v2 = 1.0 - fMarkerPosition.y / Height();
break;
case H_SELECTED:
if (fOrientation == B_VERTICAL) {
v1 = fMarkerPosition.x / Width();
v2 = 1.0 - fMarkerPosition.y / Height();
} else {
v1 = fMarkerPosition.y / Height();
v2 = 1.0 - fMarkerPosition.x / Width();
}
break;
case S_SELECTED:
case V_SELECTED:
v1 = fMarkerPosition.x / Width() * 6.0;
v2 = 1.0 - fMarkerPosition.y / Height();
break;
}
message->AddFloat("value", v1);
message->AddFloat("value", v2);
return BControl::Invoke(message);
}
void
ColorField::AttachedToWindow()
{
}
void
ColorField::Draw(BRect updateRect)
{
if (fBitmapDirty && fBitmap != NULL) {
_FillBitmap(fBitmap, fMode, fFixedValue, fOrientation);
fBitmapDirty = false;
}
BRect bounds = Bounds();
if (fBorderStyle == B_FANCY_BORDER) {
rgb_color color = LowColor();
be_control_look->DrawTextControlBorder(this, bounds, updateRect,
color);
BRegion region(bounds);
ConstrainClippingRegion(®ion);
}
if (fBitmap != NULL)
DrawBitmap(fBitmap, bounds.LeftTop());
else {
SetHighColor(255, 0, 0);
FillRect(bounds);
}
SetHighColor(0, 0, 0);
StrokeEllipse(fMarkerPosition + bounds.LeftTop(), 5.0, 5.0);
SetHighColor(255.0, 255.0, 255.0);
StrokeEllipse(fMarkerPosition + bounds.LeftTop(), 4.0, 4.0);
}
void
ColorField::FrameResized(float width, float height)
{
BRect r = _BitmapRect();
_AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1);
Invalidate();
}
void
ColorField::MouseDown(BPoint where)
{
fMouseDown = true;
SetMouseEventMask(B_POINTER_EVENTS,
B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
PositionMarkerAt(where);
if (Message() != NULL) {
BMessage message(*Message());
message.AddBool("begin", true);
Invoke(&message);
} else
Invoke();
}
void
ColorField::MouseUp(BPoint where)
{
fMouseDown = false;
}
void
ColorField::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (dragMessage != NULL || !fMouseDown ) {
BView::MouseMoved(where, transit, dragMessage);
return;
}
PositionMarkerAt(where);
Invoke();
}
void
ColorField::SetModeAndValue(SelectedColorMode mode, float fixedValue)
{
float R(0), G(0), B(0);
float H(0), S(0), V(0);
float width = Width();
float height = Height();
switch (fMode) {
case R_SELECTED: {
R = fFixedValue * 255;
G = round(fMarkerPosition.x / width * 255.0);
B = round(255.0 - fMarkerPosition.y / height * 255.0);
}; break;
case G_SELECTED: {
R = round(fMarkerPosition.x / width * 255.0);
G = fFixedValue * 255;
B = round(255.0 - fMarkerPosition.y / height * 255.0);
}; break;
case B_SELECTED: {
R = round(fMarkerPosition.x / width * 255.0);
G = round(255.0 - fMarkerPosition.y / height * 255.0);
B = fFixedValue * 255;
}; break;
case H_SELECTED: {
H = fFixedValue;
S = fMarkerPosition.x / width;
V = 1.0 - fMarkerPosition.y / height;
}; break;
case S_SELECTED: {
H = fMarkerPosition.x / width * 6.0;
S = fFixedValue;
V = 1.0 - fMarkerPosition.y / height;
}; break;
case V_SELECTED: {
H = fMarkerPosition.x / width * 6.0;
S = 1.0 - fMarkerPosition.y / height;
V = fFixedValue;
}; break;
}
if (fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) {
HSV_to_RGB(H, S, V, R, G, B);
R *= 255.0; G *= 255.0; B *= 255.0;
}
rgb_color color = { (uint8)round(R), (uint8)round(G), (uint8)round(B),
255 };
if (fFixedValue != fixedValue || fMode != mode) {
fFixedValue = fixedValue;
fMode = mode;
_Update();
}
SetMarkerToColor(color);
}
void
ColorField::SetFixedValue(float fixedValue)
{
if (fFixedValue != fixedValue) {
fFixedValue = fixedValue;
_Update();
}
}
void
ColorField::SetMarkerToColor(rgb_color color)
{
float h, s, v;
RGB_to_HSV(color.red / 255.0, color.green / 255.0, color.blue / 255.0,
h, s, v );
fLastMarkerPosition = fMarkerPosition;
float width = Width();
float height = Height();
switch (fMode) {
case R_SELECTED:
fMarkerPosition = BPoint(color.green / 255.0 * width,
(255.0 - color.blue) / 255.0 * height);
break;
case G_SELECTED:
fMarkerPosition = BPoint(color.red / 255.0 * width,
(255.0 - color.blue) / 255.0 * height);
break;
case B_SELECTED:
fMarkerPosition = BPoint(color.red / 255.0 * width,
(255.0 - color.green) / 255.0 * height);
break;
case H_SELECTED:
if (fOrientation == B_VERTICAL)
fMarkerPosition = BPoint(s * width, height - v * height);
else
fMarkerPosition = BPoint(width - v * width, s * height);
break;
case S_SELECTED:
fMarkerPosition = BPoint(h / 6.0 * width, height - v * height);
break;
case V_SELECTED:
fMarkerPosition = BPoint( h / 6.0 * width, height - s * height);
break;
}
Invalidate();
}
void
ColorField::PositionMarkerAt(BPoint where)
{
BRect rect = _BitmapRect();
where.ConstrainTo(rect);
where -= rect.LeftTop();
fLastMarkerPosition = fMarkerPosition;
fMarkerPosition = where;
Invalidate();
}
float
ColorField::Width() const
{
return _BitmapRect().IntegerWidth() + 1;
}
float
ColorField::Height() const
{
return _BitmapRect().IntegerHeight() + 1;
}
static inline void
set_bits(uint8* bits, uint8 r, uint8 g, uint8 b)
{
bits[0] = b;
bits[1] = g;
bits[2] = r;
bits[3] = 255;
}
void
ColorField::_Init(SelectedColorMode mode, float fixedValue,
orientation orient, border_style border)
{
fMode = mode;
fFixedValue = fixedValue;
fOrientation = orient;
fBorderStyle = border;
fMarkerPosition = BPoint(0.0, 0.0);
fLastMarkerPosition = BPoint(-1.0, -1.0);
fMouseDown = false;
fBitmap = NULL;
fBitmapDirty = true;
SetViewColor(B_TRANSPARENT_COLOR);
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
void
ColorField::_AllocBitmap(int32 width, int32 height)
{
if (width < 2 || height < 2)
return;
delete fBitmap;
fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32);
fBitmapDirty = true;
}
void
ColorField::_Update()
{
fBitmapDirty = true;
Invalidate();
}
BRect
ColorField::_BitmapRect() const
{
BRect r = Bounds();
if (fBorderStyle == B_FANCY_BORDER)
r.InsetBy(2, 2);
return r;
}
void
ColorField::_FillBitmap(BBitmap* bitmap, SelectedColorMode mode,
float fixedValue, orientation orient) const
{
int32 width = bitmap->Bounds().IntegerWidth();
int32 height = bitmap->Bounds().IntegerHeight();
uint32 bpr = bitmap->BytesPerRow();
uint8* bits = (uint8*)bitmap->Bits();
float r = 0;
float g = 0;
float b = 0;
float h;
float s;
float v;
switch (mode) {
case R_SELECTED:
r = round(fixedValue * 255);
for (int y = height; y >= 0; y--) {
uint8* bitsHandle = bits;
b = y * 255 / height;
for (int32 x = 0; x <= width; x++) {
g = x * 255 / width;
set_bits(bitsHandle, (uint8)r, (uint8)g, (uint8)b);
bitsHandle += 4;
}
bits += bpr;
}
break;
case G_SELECTED:
g = round(fixedValue * 255);
for (int y = height; y >= 0; y--) {
uint8* bitsHandle = bits;
b = y * 255 / height;
for (int x = 0; x <= width; x++) {
r = x * 255 / width;
set_bits(bitsHandle, (uint8)r, (uint8)g, (uint8)b);
bitsHandle += 4;
}
bits += bpr;
}
break;
case B_SELECTED:
b = round(fixedValue * 255);
for (int y = height; y >= 0; y--) {
uint8* bitsHandle = bits;
g = y * 255 / height;
for (int x = 0; x <= width; ++x) {
r = x * 255 / width;
set_bits(bitsHandle, (uint8)r, (uint8)g, (uint8)b);
bitsHandle += 4;
}
bits += bpr;
}
break;
case H_SELECTED:
h = fixedValue;
if (orient == B_VERTICAL) {
for (int y = 0; y <= height; ++y) {
v = (float)(height - y) / height;
uint8* bitsHandle = bits;
for (int x = 0; x <= width; ++x) {
s = (float)x / width;
HSV_to_RGB(h, s, v, r, g, b);
set_bits(bitsHandle,
round(r * 255.0),
round(g * 255.0),
round(b * 255.0));
bitsHandle += 4;
}
bits += bpr;
}
} else {
for (int y = 0; y <= height; ++y) {
s = (float)y / height;
uint8* bitsHandle = bits;
for (int x = 0; x <= width; ++x) {
v = (float)(width - x) / width;
HSV_to_RGB(h, s, v, r, g, b);
set_bits(bitsHandle,
round(r * 255.0),
round(g * 255.0),
round(b * 255.0));
bitsHandle += 4;
}
bits += bpr;
}
}
break;
case S_SELECTED:
s = fixedValue;
for (int y = 0; y <= height; ++y) {
v = (float)(height - y) / height;
uint8* bitsHandle = bits;
for (int x = 0; x <= width; ++x) {
h = 6.0 / width * x;
HSV_to_RGB(h, s, v, r, g, b);
set_bits(bitsHandle,
round(r * 255.0),
round(g * 255.0),
round(b * 255.0));
bitsHandle += 4;
}
bits += bpr;
}
break;
case V_SELECTED:
v = fixedValue;
for (int y = 0; y <= height; ++y) {
s = (float)(height - y) / height;
uint8* bitsHandle = bits;
for (int x = 0; x <= width; ++x) {
h = 6.0 / width * x;
HSV_to_RGB(h, s, v, r, g, b);
set_bits(bitsHandle,
round(r * 255.0),
round(g * 255.0),
round(b * 255.0));
bitsHandle += 4;
}
bits += bpr;
}
break;
}
}