#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Application.h>
#include <Bitmap.h>
#include <Button.h>
#include <Message.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <View.h>
#include <Window.h>
#include "bitmap.h"
enum {
MSG_RESET = 'rset',
MSG_TICK = 'tick',
};
#define SPEED 2.0
float
random_number_between(float v1, float v2)
{
if (v1 < v2)
return v1 + fmod(rand() / 1000.0, (v2 - v1));
else if (v2 < v1)
return v2 + fmod(rand() / 1000.0, (v1 - v2));
return v1;
}
class TestView : public BView {
public:
TestView(BRect frame, const char* name,
uint32 resizeFlags, uint32 flags);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* message);
virtual void Draw(BRect updateRect);
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
private:
void _ResetRect();
void _InvalidateBitmapRect(BRect r);
void _DrawCross(BPoint where, rgb_color c);
struct point {
double x;
double y;
double direction_x;
double direction_y;
double velocity_x;
double velocity_y;
};
struct color_cycle {
uint8 value;
double direction;
};
void _FillBitmap(point* polygon);
void _InitPolygon(const BRect& b, point* polygon) const;
void _InitColor(color_cycle* color) const;
void _MorphPolygon(const BRect& b, point* polygon);
void _MorphColor(color_cycle* color);
BBitmap* fBitmap;
BView* fOffscreenView;
BMessageRunner* fTicker;
BRect fBitmapRect;
enum {
TRACKING_NONE = 0,
TRACKING_LEFT,
TRACKING_RIGHT,
TRACKING_TOP,
TRACKING_BOTTOM,
TRACKING_LEFT_TOP,
TRACKING_RIGHT_TOP,
TRACKING_LEFT_BOTTOM,
TRACKING_RIGHT_BOTTOM,
TRACKING_ALL
};
uint32 fTracking;
BPoint fLastMousePos;
point fPolygon[4];
color_cycle fColor[3];
};
TestView::TestView(BRect frame, const char* name,
uint32 resizeFlags, uint32 flags)
: BView(frame, name, resizeFlags, flags),
fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_RGB32, true)),
fOffscreenView(new BView(fBitmap->Bounds(), "Offscreen view",
B_FOLLOW_ALL, B_WILL_DRAW | B_SUBPIXEL_PRECISE)),
fTicker(NULL),
fBitmapRect(),
fTracking(TRACKING_NONE),
fLastMousePos(-1.0, -1.0)
{
SetViewColor(B_TRANSPARENT_COLOR);
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
uint32 width = fBitmap->Bounds().IntegerWidth() + 1;
uint32 height = fBitmap->Bounds().IntegerHeight() + 1;
uint32 bpr = fBitmap->BytesPerRow();
printf("width: %ld, height: %ld, bpr: %ld\n", width, height, bpr);
int32 index = 0;
for (uint32 y = 0; y < height; y++) {
uint8* h = bits;
for (uint32 x = 0; x < width; x++) {
*h = index++;
h++;
}
bits += bpr;
}
BRect a(0.0, 10.0, 20.0, 10.0);
BRect b(0.0, 10.0, 10.0, 30.0);
printf("Intersects: %d\n", a.Intersects(b));*/
if (fBitmap->Lock()) {
fBitmap->AddChild(fOffscreenView);
fOffscreenView->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
fBitmap->Unlock();
}
srand((long int)system_time());
_InitPolygon(fBitmap->Bounds(), fPolygon);
_InitColor(fColor);
_ResetRect();
}
void
TestView::AttachedToWindow()
{
BMessenger mess(this, Window());
BMessage msg(MSG_TICK);
fTicker = new BMessageRunner(mess, &msg, 40000LL);
}
void
TestView::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_RESET: {
BRect old = fBitmapRect;
_ResetRect();
_InvalidateBitmapRect(old | fBitmapRect);
break;
}
case MSG_TICK:
_MorphPolygon(fBitmap->Bounds(), fPolygon);
_MorphColor(fColor);
_FillBitmap(fPolygon);
Invalidate(fBitmapRect);
break;
default:
BView::MessageReceived(message);
break;
}
}
void
TestView::Draw(BRect updateRect)
{
SetDrawingMode(B_OP_ALPHA);
DrawBitmap(fBitmap, fBitmap->Bounds(), fBitmapRect);
SetDrawingMode(B_OP_COPY);
BRect topOfBitmap(updateRect.left, updateRect.top, updateRect.right, fBitmapRect.top - 1);
if (topOfBitmap.IsValid())
FillRect(topOfBitmap, B_SOLID_LOW);
BRect leftOfBitmap(updateRect.left, fBitmapRect.top, fBitmapRect.left - 1, fBitmapRect.bottom);
if (leftOfBitmap.IsValid())
FillRect(leftOfBitmap, B_SOLID_LOW);
BRect rightOfBitmap(fBitmapRect.right + 1, fBitmapRect.top, updateRect.right, fBitmapRect.bottom);
if (rightOfBitmap.IsValid())
FillRect(rightOfBitmap, B_SOLID_LOW);
BRect bottomOfBitmap(updateRect.left, fBitmapRect.bottom + 1, updateRect.right, updateRect.bottom);
if (bottomOfBitmap.IsValid())
FillRect(bottomOfBitmap, B_SOLID_LOW);
rgb_color red = (rgb_color){ 255, 0, 0, 255 };
_DrawCross(fBitmapRect.LeftTop() + BPoint(-1.0, -1.0), red);
_DrawCross(fBitmapRect.RightTop() + BPoint(1.0, -1.0), red);
_DrawCross(fBitmapRect.LeftBottom() + BPoint(-1.0, 1.0), red);
_DrawCross(fBitmapRect.RightBottom() + BPoint(1.0, 1.0), red);
SetDrawingMode(B_OP_ALPHA);
const char* message = "Click and drag to move and resize the bitmap!";
BPoint textPos(20.0, 30.0);
SetHighColor(255, 255, 255, 180);
DrawString(message, textPos);
SetHighColor(0, 0, 0, 180);
DrawString(message, textPos + BPoint(-1.0, -1.0));
}
bool
hit_test(BPoint where, BPoint p)
{
BRect r(p, p);
r.InsetBy(-5.0, -5.0);
return r.Contains(where);
}
bool
hit_test(BPoint where, BPoint a, BPoint b)
{
BRect r(a, b);
if (a.x == b.x)
r.InsetBy(-3.0, 0.0);
else
r.InsetBy(0.0, -3.0);
return r.Contains(where);
}
void
TestView::MouseDown(BPoint where)
{
fTracking = TRACKING_NONE;
if (hit_test(where, fBitmapRect.LeftTop()))
fTracking = TRACKING_LEFT_TOP;
else if (hit_test(where, fBitmapRect.RightTop()))
fTracking = TRACKING_RIGHT_TOP;
else if (hit_test(where, fBitmapRect.LeftBottom()))
fTracking = TRACKING_LEFT_BOTTOM;
else if (hit_test(where, fBitmapRect.RightBottom()))
fTracking = TRACKING_RIGHT_BOTTOM;
else if (hit_test(where, fBitmapRect.LeftTop(), fBitmapRect.RightTop()))
fTracking = TRACKING_TOP;
else if (hit_test(where, fBitmapRect.LeftTop(), fBitmapRect.LeftBottom()))
fTracking = TRACKING_LEFT;
else if (hit_test(where, fBitmapRect.RightTop(), fBitmapRect.RightBottom()))
fTracking = TRACKING_RIGHT;
else if (hit_test(where, fBitmapRect.LeftBottom(), fBitmapRect.RightBottom()))
fTracking = TRACKING_BOTTOM;
else if (fBitmapRect.Contains(where))
fTracking = TRACKING_ALL;
fLastMousePos = where;
}
void
TestView::MouseUp(BPoint where)
{
fTracking = TRACKING_NONE;
}
void
TestView::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (fTracking > TRACKING_NONE) {
BRect old = fBitmapRect;
BPoint offset = where - fLastMousePos;
switch (fTracking) {
case TRACKING_LEFT_TOP:
fBitmapRect.Set(fBitmapRect.left + offset.x,
fBitmapRect.top + offset.y,
fBitmapRect.right,
fBitmapRect.bottom);
break;
case TRACKING_RIGHT_BOTTOM:
fBitmapRect.Set(fBitmapRect.left,
fBitmapRect.top,
fBitmapRect.right + offset.x,
fBitmapRect.bottom + offset.y);
break;
case TRACKING_LEFT_BOTTOM:
fBitmapRect.Set(fBitmapRect.left + offset.x,
fBitmapRect.top,
fBitmapRect.right,
fBitmapRect.bottom + offset.y);
break;
case TRACKING_RIGHT_TOP:
fBitmapRect.Set(fBitmapRect.left,
fBitmapRect.top + offset.y,
fBitmapRect.right + offset.x,
fBitmapRect.bottom);
break;
case TRACKING_LEFT:
fBitmapRect.Set(fBitmapRect.left + offset.x,
fBitmapRect.top,
fBitmapRect.right,
fBitmapRect.bottom);
break;
case TRACKING_TOP:
fBitmapRect.Set(fBitmapRect.left,
fBitmapRect.top + offset.y,
fBitmapRect.right,
fBitmapRect.bottom);
break;
case TRACKING_RIGHT:
fBitmapRect.Set(fBitmapRect.left,
fBitmapRect.top,
fBitmapRect.right + offset.x,
fBitmapRect.bottom);
break;
case TRACKING_BOTTOM:
fBitmapRect.Set(fBitmapRect.left,
fBitmapRect.top,
fBitmapRect.right,
fBitmapRect.bottom + offset.y);
break;
case TRACKING_ALL:
default:
fBitmapRect.OffsetBy(offset);
break;
}
fLastMousePos = where;
if (old != fBitmapRect)
_InvalidateBitmapRect(old | fBitmapRect);
}
}
void
TestView::_ResetRect()
{
fBitmapRect = fBitmap->Bounds();
fBitmapRect.OffsetBy(floorf((Bounds().Width() - fBitmapRect.Width()) / 2.0 + 0.5),
floorf((Bounds().Height() - fBitmapRect.Height()) / 2.0 + 0.5));
}
void
TestView::_InvalidateBitmapRect(BRect r)
{
r.InsetBy(-4.0, -4.0);
Invalidate(r);
}
void
TestView::_DrawCross(BPoint where, rgb_color c)
{
BeginLineArray(4);
AddLine(BPoint(where.x, where.y - 3),
BPoint(where.x, where.y - 1), c);
AddLine(BPoint(where.x, where.y + 1),
BPoint(where.x, where.y + 3), c);
AddLine(BPoint(where.x - 3, where.y),
BPoint(where.x - 1, where.y), c);
AddLine(BPoint(where.x + 1, where.y),
BPoint(where.x + 3, where.y), c);
EndLineArray();
}
void
TestView::_FillBitmap(point* polygon)
{
if (fBitmap->Lock()) {
fOffscreenView->SetDrawingMode(B_OP_COPY);
fOffscreenView->SetHighColor(0, 0, 0, 30);
fOffscreenView->FillRect(fOffscreenView->Bounds());
fOffscreenView->SetDrawingMode(B_OP_ALPHA);
fOffscreenView->SetHighColor(fColor[0].value,
fColor[1].value,
fColor[2].value,
30);
fOffscreenView->SetPenSize(4);
fOffscreenView->SetLineMode(B_BUTT_CAP, B_ROUND_JOIN);
BPoint pointList[4];
pointList[0].x = polygon[0].x;
pointList[0].y = polygon[0].y;
pointList[1].x = polygon[1].x;
pointList[1].y = polygon[1].y;
pointList[2].x = polygon[2].x;
pointList[2].y = polygon[2].y;
pointList[3].x = polygon[3].x;
pointList[3].y = polygon[3].y;
fOffscreenView->StrokePolygon(pointList, 4);
fOffscreenView->Sync();
fBitmap->Unlock();
}
}
void
TestView::_InitPolygon(const BRect& b, point* polygon) const
{
polygon[0].x = b.left;
polygon[0].y = b.top;
polygon[0].direction_x = random_number_between(-SPEED, SPEED);
polygon[0].direction_y = random_number_between(-SPEED, SPEED);
polygon[0].velocity_x = 0.0;
polygon[0].velocity_y = 0.0;
polygon[1].x = b.right;
polygon[1].y = b.top;
polygon[1].direction_x = random_number_between(-SPEED, SPEED);
polygon[1].direction_y = random_number_between(-SPEED, SPEED);
polygon[1].velocity_x = 0.0;
polygon[1].velocity_y = 0.0;
polygon[2].x = b.right;
polygon[2].y = b.bottom;
polygon[2].direction_x = random_number_between(-SPEED, SPEED);
polygon[2].direction_y = random_number_between(-SPEED, SPEED);
polygon[2].velocity_x = 0.0;
polygon[2].velocity_y = 0.0;
polygon[3].x = b.left;
polygon[3].y = b.bottom;
polygon[3].direction_x = random_number_between(-SPEED, SPEED);
polygon[3].direction_y = random_number_between(-SPEED, SPEED);
polygon[3].velocity_x = 0.0;
polygon[3].velocity_y = 0.0;
}
void
TestView::_InitColor(color_cycle* color) const
{
color[0].value = 0;
color[0].direction = random_number_between(-SPEED * 4, SPEED * 4);
color[1].value = 0;
color[1].direction = random_number_between(-SPEED * 4, SPEED * 4);
color[2].value = 0;
color[2].direction = random_number_between(-SPEED * 4, SPEED * 4);
}
inline void
morph(double* value, double* direction, double* velocity, double min, double max)
{
*value += *velocity;
if (*value < min && *direction < 0.0) {
*direction = -*direction;
} else if (*value > max && *direction > 0.0) {
*direction = -*direction;
}
if (*direction < 0.0) {
if (*velocity > *direction)
*velocity += *direction / 10.0;
if (*velocity < *direction)
*velocity = *direction;
} else {
if (*velocity < *direction)
*velocity += *direction / 10.0;
if (*velocity > *direction)
*velocity = *direction;
}
}
inline void
morph(uint8* value, double* direction)
{
int32 v = (int32)(*value + *direction);
if (v < 0) {
v = 0;
*direction = -*direction;
} else if (v > 255) {
v = 255;
*direction = -*direction;
}
*value = (uint8)v;
}
void
TestView::_MorphPolygon(const BRect& b, point* polygon)
{
morph(&polygon[0].x, &polygon[0].direction_x, &polygon[0].velocity_x, b.left, b.right);
morph(&polygon[1].x, &polygon[1].direction_x, &polygon[1].velocity_x, b.left, b.right);
morph(&polygon[2].x, &polygon[2].direction_x, &polygon[2].velocity_x, b.left, b.right);
morph(&polygon[3].x, &polygon[3].direction_x, &polygon[3].velocity_x, b.left, b.right);
morph(&polygon[0].y, &polygon[0].direction_y, &polygon[0].velocity_y, b.top, b.bottom);
morph(&polygon[1].y, &polygon[1].direction_y, &polygon[1].velocity_y, b.top, b.bottom);
morph(&polygon[2].y, &polygon[2].direction_y, &polygon[2].velocity_y, b.top, b.bottom);
morph(&polygon[3].y, &polygon[3].direction_y, &polygon[3].velocity_y, b.top, b.bottom);
}
void
TestView::_MorphColor(color_cycle* color)
{
morph(&color[0].value, &color[0].direction);
morph(&color[1].value, &color[1].direction);
morph(&color[2].value, &color[2].direction);
}
void
show_window(BRect frame, const char* name)
{
BWindow* window = new BWindow(frame, name,
B_TITLED_WINDOW,
B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE);
BView* view = new TestView(window->Bounds(), "test", B_FOLLOW_ALL,
B_WILL_DRAW);
window->AddChild(view);
BRect b(0.0, 0.0, 60.0, 15.0);
b.OffsetTo(5.0, view->Bounds().bottom - (b.Height() + 15.0));
BButton* control = new BButton(b, "button", "Reset", new BMessage(MSG_RESET),
B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
view->AddChild(control);
control->SetTarget(view);
control->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
window->Show();
}
int
main(int argc, char** argv)
{
BApplication* app = new BApplication("application/x.vnd-Haiku.BitmapDrawing");
BRect frame(10.0, 30.0, 330.0, 220.0);
show_window(frame, "BitmapDrawing");
app->Run();
delete app;
return 0;
}