// main.cpp #include #include #include #include #include #include #include #include #include #include #include #include #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; }