From f67876a0c5b91684c175f3dc7989537c35c23f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Wed, 21 Jun 2006 10:43:19 +0000 Subject: [PATCH] some more "putting things together" for testing: * add a VectorPath to Document->Icon()->Paths() * add a Style to the global StyleManager * actually add a Shape to the Document->Icon()->Shapes() which uses the style and the path from above fleshing out of CanvasView: * use an offscreen bitmap/view to avoid flickering * use an IconRenderer to render the set Icon * implement support for zooming PathManipulator: * use the conversion from/to Canvas space and View space in CanvasView (I would have liked to solve this in a more generic way, so that PathManipulator didn't have to know CanvasView, but I was not very creative...) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17891 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/icon-o-matic/CanvasView.cpp | 217 +++++++++++++++++- src/apps/icon-o-matic/CanvasView.h | 38 +++ src/apps/icon-o-matic/MainWindow.cpp | 21 +- .../icon-o-matic/shape/PathManipulator.cpp | 69 +++--- src/apps/icon-o-matic/shape/PathManipulator.h | 4 +- 5 files changed, 312 insertions(+), 37 deletions(-) diff --git a/src/apps/icon-o-matic/CanvasView.cpp b/src/apps/icon-o-matic/CanvasView.cpp index 8343a2d59c..ff50c913d9 100644 --- a/src/apps/icon-o-matic/CanvasView.cpp +++ b/src/apps/icon-o-matic/CanvasView.cpp @@ -8,22 +8,151 @@ #include "CanvasView.h" -#include "CommandStack.h" +#include +#include +#include "ui_defines.h" + +#include "CommandStack.h" +#include "IconRenderer.h" + +// constructor CanvasView::CanvasView(BRect frame) - : StateView(frame, "canvas view", B_FOLLOW_ALL, B_WILL_DRAW) + : StateView(frame, "canvas view", B_FOLLOW_ALL, + B_WILL_DRAW | B_FRAME_EVENTS), + fBitmap(new BBitmap(BRect(0, 0, 63, 63), 0, B_RGB32)), + fRenderer(new IconRenderer(fBitmap)), + + fCanvasOrigin(50.0, 50.0), + fZoomLevel(8.0), + + fOffsreenBitmap(NULL), + fOffsreenView(NULL) { -#if __HAIKU__ + #if __HAIKU__ SetFlags(Flags() | B_SUBPIXEL_PRECISE); -#endif // __HAIKU__ + #endif // __HAIKU__ } - +// destructor CanvasView::~CanvasView() { + delete fRenderer; + delete fBitmap; + + _FreeBackBitmap(); } +// #pragma mark - +// AttachedToWindow +void +CanvasView::AttachedToWindow() +{ + StateView::AttachedToWindow(); + + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(kStripesHigh); + SetHighColor(kStripesLow); + + _AllocBackBitmap(Bounds().Width(), Bounds().Height()); +} + +// FrameResized +void +CanvasView::FrameResized(float width, float height) +{ + _AllocBackBitmap(width, height); +} + +// Draw +void +CanvasView::Draw(BRect updateRect) +{ + if (!fOffsreenView) { + _DrawInto(this, updateRect); + } else { + BPoint boundsLeftTop = Bounds().LeftTop(); + if (fOffsreenBitmap->Lock()) { + fOffsreenView->PushState(); + + // apply scrolling offset to offscreen view + fOffsreenView->SetOrigin(-boundsLeftTop.x, -boundsLeftTop.y); + // mirror the clipping of this view + // to the offscreen view for performance + BRegion clipping; + GetClippingRegion(&clipping); + fOffsreenView->ConstrainClippingRegion(&clipping); + fOffsreenView->FillRect(updateRect, B_SOLID_LOW); + + _DrawInto(fOffsreenView, updateRect); + + fOffsreenView->PopState(); + fOffsreenView->Sync(); + + fOffsreenBitmap->Unlock(); + } + // compensate scrolling offset in BView + BRect bitmapRect = updateRect; + bitmapRect.OffsetBy(-boundsLeftTop.x, -boundsLeftTop.y); + + SetDrawingMode(B_OP_COPY); + DrawBitmap(fOffsreenBitmap, bitmapRect, updateRect); + } +} + +// #pragma mark - + +// SetIcon +void +CanvasView::SetIcon(Icon* icon) +{ + fRenderer->SetIcon(icon); +} + +// ConvertFromCanvas +void +CanvasView::ConvertFromCanvas(BPoint* point) const +{ + point->x = point->x * fZoomLevel + fCanvasOrigin.x; + point->y = point->y * fZoomLevel + fCanvasOrigin.y; +} + +// ConvertToCanvas +void +CanvasView::ConvertToCanvas(BPoint* point) const +{ + point->x = (point->x - fCanvasOrigin.x) / fZoomLevel; + point->y = (point->y - fCanvasOrigin.y) / fZoomLevel; +} + +// ConvertFromCanvas +void +CanvasView::ConvertFromCanvas(BRect* r) const +{ + r->left = r->left * fZoomLevel + fCanvasOrigin.x; + r->top = r->top * fZoomLevel + fCanvasOrigin.y; + r->right++; + r->bottom++; + r->right = r->right * fZoomLevel + fCanvasOrigin.x; + r->bottom = r->bottom * fZoomLevel + fCanvasOrigin.y; + r->right--; + r->bottom--; +} + +// ConvertToCanvas +void +CanvasView::ConvertToCanvas(BRect* r) const +{ + r->left = (r->left - fCanvasOrigin.x) / fZoomLevel; + r->top = (r->top - fCanvasOrigin.y) / fZoomLevel; + r->right = (r->right - fCanvasOrigin.x) / fZoomLevel; + r->bottom = (r->bottom - fCanvasOrigin.y) / fZoomLevel; +} + +// #pragma mark - + +// _HandleKeyDown bool CanvasView::_HandleKeyDown(uint32 key, uint32 modifiers) { @@ -37,11 +166,87 @@ CanvasView::_HandleKeyDown(uint32 key, uint32 modifiers) break; default: - return StateView::HandleKeyDown(key, modifiers); + return StateView::_HandleKeyDown(key, modifiers); } return true; } +// _CanvasRect() +BRect +CanvasView::_CanvasRect() const +{ + BRect r = fBitmap->Bounds(); + ConvertFromCanvas(&r); + return r; +} +// _AllocBackBitmap +void +CanvasView::_AllocBackBitmap(float width, float height) +{ + // sanity check + if (width <= 0.0 || height <= 0.0) + return; + + if (fOffsreenBitmap) { + // see if the bitmap needs to be expanded + BRect b = fOffsreenBitmap->Bounds(); + if (b.Width() >= width && b.Height() >= height) + return; + + // it does; clean up: + _FreeBackBitmap(); + } + + BRect b(0.0, 0.0, width, height); + fOffsreenBitmap = new (nothrow) BBitmap(b, B_RGB32, true); + if (!fOffsreenBitmap) { + fprintf(stderr, "CanvasView::_AllocBackBitmap(): failed to allocate\n"); + return; + } + if (fOffsreenBitmap->IsValid()) { + fOffsreenView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW); + BFont font; + GetFont(&font); + fOffsreenView->SetFont(&font); + fOffsreenView->SetHighColor(HighColor()); + fOffsreenView->SetLowColor(LowColor()); + fOffsreenBitmap->AddChild(fOffsreenView); + } else { + _FreeBackBitmap(); + fprintf(stderr, "CanvasView::_AllocBackBitmap(): bitmap invalid\n"); + } +} + +// _FreeBackBitmap +void +CanvasView::_FreeBackBitmap() +{ + if (fOffsreenBitmap) { + delete fOffsreenBitmap; + fOffsreenBitmap = NULL; + fOffsreenView = NULL; + } +} + +// _DrawInto +void +CanvasView::_DrawInto(BView* view, BRect updateRect) +{ + // TODO: don't render here, use some + // listener technique on the shapes instead... + fRenderer->Render(); + + // icon + BRect canvas(_CanvasRect()); + view->DrawBitmap(fBitmap, fBitmap->Bounds(), canvas); + + // outside icon + BRegion outside(Bounds() & updateRect); + outside.Exclude(canvas); + view->FillRegion(&outside, kStripes); + + StateView::Draw(view, updateRect); +} diff --git a/src/apps/icon-o-matic/CanvasView.h b/src/apps/icon-o-matic/CanvasView.h index 18bc1def46..993231ff7f 100644 --- a/src/apps/icon-o-matic/CanvasView.h +++ b/src/apps/icon-o-matic/CanvasView.h @@ -11,16 +11,54 @@ #include "StateView.h" +class BBitmap; +class Icon; +class IconRenderer; + class CanvasView : public StateView { public: CanvasView(BRect frame); virtual ~CanvasView(); + // StateView interface + virtual void AttachedToWindow(); + virtual void FrameResized(float width, float height); + virtual void Draw(BRect updateRect); + + // CanvasView + void SetIcon(Icon* icon); + + inline float ZoomLevel() const + { return fZoomLevel; } + + void ConvertFromCanvas(BPoint* point) const; + void ConvertToCanvas(BPoint* point) const; + + void ConvertFromCanvas(BRect* rect) const; + void ConvertToCanvas(BRect* rect) const; + protected: // StateView interface virtual bool _HandleKeyDown(uint32 key, uint32 modifiers); + // CanvasView + BRect _CanvasRect() const; + + void _AllocBackBitmap(float width, + float height); + void _FreeBackBitmap(); + void _DrawInto(BView* view, + BRect updateRect); + private: + BBitmap* fBitmap; + IconRenderer* fRenderer; + + BPoint fCanvasOrigin; + float fZoomLevel; + + BBitmap* fOffsreenBitmap; + BView* fOffsreenView; }; #endif // CANVAS_VIEW_H diff --git a/src/apps/icon-o-matic/MainWindow.cpp b/src/apps/icon-o-matic/MainWindow.cpp index 284fa88614..5c0c6c2bb8 100644 --- a/src/apps/icon-o-matic/MainWindow.cpp +++ b/src/apps/icon-o-matic/MainWindow.cpp @@ -17,13 +17,18 @@ #include "IconEditorApp.h" // TODO: just for testing +#include "Icon.h" #include "MultipleManipulatorState.h" #include "PathManipulator.h" +#include "Shape.h" +#include "ShapeContainer.h" +#include "Style.h" +#include "StyleManager.h" #include "VectorPath.h" // constructor MainWindow::MainWindow(IconEditorApp* app, Document* document) - : BWindow(BRect(50.0, 50.0, 689, 529), "Icon-O-Matic", + : BWindow(BRect(50, 50, 661, 661), "Icon-O-Matic", B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS), fApp(app), @@ -75,12 +80,26 @@ MainWindow::_Init() fCanvasView->SetCatchAllEvents(true); fCanvasView->SetCommandStack(fDocument->CommandStack()); // fCanvasView->SetSelection(fDocument->Selection()); + fCanvasView->SetIcon(fDocument->Icon()); // TODO: for testing only: MultipleManipulatorState* state = new MultipleManipulatorState(fCanvasView); fCanvasView->SetState(state); VectorPath* path = new VectorPath(); + + fDocument->Icon()->Paths()->AddPath(path); + + Style* style = new Style(); + style->SetColor((rgb_color){ 255, 0, 0, 255 }); + + StyleManager::Default()->AddStyle(style); + + Shape* shape = new Shape(style); + shape->Paths()->AddPath(path); + + fDocument->Icon()->Shapes()->AddShape(shape); + PathManipulator* pathManipulator = new PathManipulator(path); state->AddManipulator(pathManipulator); // ---- diff --git a/src/apps/icon-o-matic/shape/PathManipulator.cpp b/src/apps/icon-o-matic/shape/PathManipulator.cpp index 549e5b6fcc..bbedd03e0b 100644 --- a/src/apps/icon-o-matic/shape/PathManipulator.cpp +++ b/src/apps/icon-o-matic/shape/PathManipulator.cpp @@ -18,7 +18,7 @@ #include "cursors.h" #include "support.h" -#include "StateView.h" +#include "CanvasView.h" #include "VectorPath.h" #include "AddPointCommand.h" @@ -237,8 +237,10 @@ PathManipulator::~PathManipulator() class StrokePathIterator : public VectorPath::Iterator { public: - StrokePathIterator(BView* drawingView) - : fDrawingView(drawingView) + StrokePathIterator(CanvasView* canvasView, + BView* drawingView) + : fCanvasView(canvasView), + fDrawingView(drawingView) { fDrawingView->SetHighColor(0, 0, 0, 255); fDrawingView->SetDrawingMode(B_OP_OVER); @@ -249,26 +251,34 @@ class StrokePathIterator : public VectorPath::Iterator { virtual void MoveTo(BPoint point) { fBlack = true; + fSkip = false; fDrawingView->SetHighColor(0, 0, 0, 255); -// fCanvasView->ConvertFromCanvas(point); + fCanvasView->ConvertFromCanvas(&point); fDrawingView->MovePenTo(point); } virtual void LineTo(BPoint point) { - if (fBlack) - fDrawingView->SetHighColor(255, 255, 255, 255); - else - fDrawingView->SetHighColor(0, 0, 0, 255); - fBlack = !fBlack; - -// fCanvasView->ConvertFromCanvas(point); - fDrawingView->StrokeLine(point); + fCanvasView->ConvertFromCanvas(&point); + if (!fSkip) { + if (fBlack) + fDrawingView->SetHighColor(255, 255, 255, 255); + else + fDrawingView->SetHighColor(0, 0, 0, 255); + fBlack = !fBlack; + + fDrawingView->StrokeLine(point); + } else { + fDrawingView->MovePenTo(point); + } + fSkip = !fSkip; } private: + CanvasView* fCanvasView; BView* fDrawingView; bool fBlack; + bool fSkip; }; // Draw @@ -278,8 +288,8 @@ PathManipulator::Draw(BView* into, BRect updateRect) // draw the Bezier curve, but only if editing // if not "editing", the path is actually on top all other modifiers // TODO: make this customizable in the GUI - StrokePathIterator iterator(into); - fPath->Iterate(&iterator, 1.0/*fCanvasView->ZoomLevel()*/); + StrokePathIterator iterator(fCanvasView, into); + fPath->Iterate(&iterator, fCanvasView->ZoomLevel()); into->SetLowColor(0, 0, 0, 255); BPoint point; @@ -294,9 +304,9 @@ PathManipulator::Draw(BView* into, BRect updateRect) into->SetLowColor(normal); into->SetHighColor(255, 255, 255, 255); // convert to view coordinate space -// fCanvasView->ConvertFromCanvas(point); -// fCanvasView->ConvertFromCanvas(pointIn); -// fCanvasView->ConvertFromCanvas(pointOut); + fCanvasView->ConvertFromCanvas(&point); + fCanvasView->ConvertFromCanvas(&pointIn); + fCanvasView->ConvertFromCanvas(&pointOut); // connect the points belonging to one control point into->SetDrawingMode(B_OP_INVERT); into->StrokeLine(point, pointIn); @@ -369,8 +379,8 @@ PathManipulator::MouseDown(BPoint where) fMode = TRANSLATE_POINTS; } - // TODO: translate from BView to canvas space BPoint canvasWhere = where; + fCanvasView->ConvertToCanvas(&canvasWhere); // maybe we're changing some point, so we construct the // "ChangePointCommand" here so that the point is remembered @@ -465,8 +475,8 @@ PathManipulator::MouseDown(BPoint where) // remember the subpixel position // so that MouseMoved() will work even before // the integer position becomes different -// fCanvasView->ConvertToCanvas(where); fLastCanvasPos = where; + fCanvasView->ConvertToCanvas(&fLastCanvasPos); // the reason to exclude the select mode // is that the BView rect tracking does not @@ -484,8 +494,8 @@ PathManipulator::MouseDown(BPoint where) void PathManipulator::MouseMoved(BPoint where) { - // TODO: translate from BView to canvas space BPoint canvasWhere = where; + fCanvasView->ConvertToCanvas(&canvasWhere); // since the tablet is generating mouse moved messages // even if only the pressure changes (and not the actual mouse position) @@ -626,8 +636,8 @@ PathManipulator::MouseUp() bool PathManipulator::MouseOver(BPoint where) { - // TODO: translate from BView to canvas space BPoint canvasWhere = where; + fCanvasView->ConvertToCanvas(&canvasWhere); // since the tablet is generating mouse moved messages // even if only the pressure changes (and not the actual mouse position) @@ -639,7 +649,7 @@ PathManipulator::MouseOver(BPoint where) // hit testing // (use a subpixel mouse pos) -// fCanvasView->ConvertToCanvas(where); + fCanvasView->ConvertToCanvas(&where); _SetModeForMousePos(where); // TODO: always true? @@ -657,7 +667,9 @@ PathManipulator::DoubleClicked(BPoint where) BRect PathManipulator::Bounds() { - return _ControlPointRect(); + BRect r = _ControlPointRect(); + fCanvasView->ConvertFromCanvas(&r); + return r; } // TrackingBounds @@ -736,8 +748,8 @@ PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command) bool result = true; float nudgeDist = 1.0; -// if (modifiers & B_SHIFT_KEY) -// nudgeDist /= fCanvasView->ZoomLevel(); + if (modifiers & B_SHIFT_KEY) + nudgeDist /= fCanvasView->ZoomLevel(); switch (key) { // commit @@ -859,7 +871,7 @@ PathManipulator::UpdateCursor() void PathManipulator::AttachedToView(BView* view) { - fCanvasView = dynamic_cast(view); + fCanvasView = dynamic_cast(view); } // DetachedFromView @@ -1199,7 +1211,8 @@ PathManipulator::_IsSelected(int32 index) const void PathManipulator::_InvalidateCanvas(BRect rect) const { - // TODO: convert from canvas to view space + // convert from canvas to view space + fCanvasView->ConvertFromCanvas(&rect); fCanvasView->Invalidate(rect); } @@ -1283,7 +1296,7 @@ PathManipulator::_SetModeForMousePos(BPoint where) uint32 mode = UNDEFINED; int32 index = -1; - float zoomLevel = 1.0;//fCanvasView->ZoomLevel(); + float zoomLevel = fCanvasView->ZoomLevel(); // see if we're close enough at a control point BPoint point; diff --git a/src/apps/icon-o-matic/shape/PathManipulator.h b/src/apps/icon-o-matic/shape/PathManipulator.h index 1113e479a2..b33705183f 100644 --- a/src/apps/icon-o-matic/shape/PathManipulator.h +++ b/src/apps/icon-o-matic/shape/PathManipulator.h @@ -12,11 +12,11 @@ #include "Manipulator.h" class AddPointCommand; +class CanvasView; class ChangePointCommand; class UndoStack; class InsertPointCommand; class Selection; -class StateView; class VectorPath; //class PathSelection { @@ -122,7 +122,7 @@ class PathManipulator : public Manipulator { void _Nudge(BPoint direction); void _FinishNudging(); - StateView* fCanvasView; + CanvasView* fCanvasView; bool fCommandDown; bool fOptionDown;