root/src/tests/servers/app/bitmap_drawing/main.cpp
// main.cpp

#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

// random_number_between
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;
}

// TestView
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];
};

// constructor
TestView::TestView(BRect frame, const char* name,
                                   uint32 resizeFlags, uint32 flags)
        : BView(frame, name, resizeFlags, flags),
//        fBitmap(new BBitmap(BRect(0, 0, kBitmapWidth - 1, kBitmapHeight -1), 0, kBitmapFormat)),
//        fBitmap(new BBitmap(BRect(0, 0, 32 - 1, 8 - 1), 0, B_CMAP8)),
//        fBitmap(new BBitmap(BRect(0, 0, 32 - 1, 8 - 1), 0, B_GRAY8)),
          fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_RGB32, true)),
//        fBitmap(new BBitmap(BRect(0, 0, 639, 479), B_RGB32, true)),
//        fBitmap(new BBitmap(BRect(0, 0, 639, 479), B_CMAP8, true)),
//        fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_CMAP8, true)),
//        fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_GRAY8, 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 size = min_c((uint32)fBitmap->BitsLength(), sizeof(kBitmapBits));
//      memcpy(fBitmap->Bits(), kBitmapBits, size);
/*      uint8* bits = (uint8*)fBitmap->Bits();
        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();
}

// AttachedToWindow
void
TestView::AttachedToWindow()
{
        BMessenger mess(this, Window());
        BMessage msg(MSG_TICK);
        fTicker = new BMessageRunner(mess, &msg, 40000LL);
}

// MessageReceived
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;
        }
}

// Draw
void
TestView::Draw(BRect updateRect)
{
        SetDrawingMode(B_OP_ALPHA);
        DrawBitmap(fBitmap, fBitmap->Bounds(), fBitmapRect);

        SetDrawingMode(B_OP_COPY);
        // background arround bitmap
        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);

        // indicate the frame to see any errors in the drawing code
        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);

        // text
        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));
}

// hit_test
bool
hit_test(BPoint where, BPoint p)
{
        BRect r(p, p);
        r.InsetBy(-5.0, -5.0);
        return r.Contains(where);
}

// hit_test
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);
}

// MouseDown
void
TestView::MouseDown(BPoint where)
{
        fTracking = TRACKING_NONE;

        // check if we hit a corner
        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;
        // check if we hit a side
        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;
        // check if we hit inside the rect
        else if (fBitmapRect.Contains(where))
                fTracking = TRACKING_ALL;

        fLastMousePos = where;
}

// MouseUp
void
TestView::MouseUp(BPoint where)
{
        fTracking = TRACKING_NONE;
}

// MouseMoved
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);
        }
}

// _ResetRect
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));
}

// _InvalidateBitmapRect
void
TestView::_InvalidateBitmapRect(BRect r)
{
        r.InsetBy(-4.0, -4.0);
        Invalidate(r);
}

// _DrawCross
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();
}

// _FillBitmap
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();
        }
}

// _InitPolygon
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;
}

// _InitColor
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);
}

// morph
inline void
morph(double* value, double* direction, double* velocity, double min, double max)
{
        *value += *velocity;

        // flip direction if necessary
        if (*value < min && *direction < 0.0) {
                *direction = -*direction;
        } else if (*value > max && *direction > 0.0) {
                *direction = -*direction;
        }

        // accelerate velocity
        if (*direction < 0.0) {
                if (*velocity > *direction)
                        *velocity += *direction / 10.0;
                // truncate velocity
                if (*velocity < *direction)
                        *velocity = *direction;
        } else {
                if (*velocity < *direction)
                        *velocity += *direction / 10.0;
                // truncate velocity
                if (*velocity > *direction)
                        *velocity = *direction;
        }
}

// morph
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;
}

// _MorphPolygon
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);
}

// _MorphColor
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);
}

// show_window
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/* | B_FULL_UPDATE_ON_RESIZE*/);

        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();
}

// main
int
main(int argc, char** argv)
{
        BApplication* app = new BApplication("application/x.vnd-Haiku.BitmapDrawing");

//      BRect frame(10.0, 30.0, 790.0, 590.0);
        BRect frame(10.0, 30.0, 330.0, 220.0);
        show_window(frame, "BitmapDrawing");

        app->Run();

        delete app;
        return 0;
}