From ab1bd2fd07b535209dd57ea00df4dc5794f9827f Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sun, 4 Jan 2015 16:33:25 +0100 Subject: [PATCH 01/29] app_server: rename DrawingContext to Canvas * Better reflects the purpose of the class: an interface for things in which we can draw (e.g. a View) * Accordingly rename OffscreenContext to OffscreenCanvas --- .../app/{DrawingContext.cpp => Canvas.cpp} | 57 ++-- .../app/{DrawingContext.h => Canvas.h} | 25 +- src/servers/app/Jamfile | 10 +- src/servers/app/ServerPicture.cpp | 286 +++++++++--------- src/servers/app/ServerPicture.h | 4 +- src/servers/app/View.h | 4 +- src/servers/app/drawing/AlphaMask.cpp | 16 +- src/tests/servers/app/Jamfile | 6 +- 8 files changed, 205 insertions(+), 203 deletions(-) rename src/servers/app/{DrawingContext.cpp => Canvas.cpp} (80%) rename src/servers/app/{DrawingContext.h => Canvas.h} (90%) diff --git a/src/servers/app/DrawingContext.cpp b/src/servers/app/Canvas.cpp similarity index 80% rename from src/servers/app/DrawingContext.cpp rename to src/servers/app/Canvas.cpp index 8a61f9b00a..715c6a1f05 100644 --- a/src/servers/app/DrawingContext.cpp +++ b/src/servers/app/Canvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014, Haiku, Inc. + * Copyright (c) 2001-2015, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: @@ -9,10 +9,11 @@ * Stephan Aßmus * Marcus Overhagen * Adrien Destugues */ -#include "DrawingContext.h" +#include "Canvas.h" #include @@ -27,27 +28,27 @@ #include "DrawState.h" -DrawingContext::DrawingContext() +Canvas::Canvas() : fDrawState(new(std::nothrow) DrawState()) { } -DrawingContext::DrawingContext(const DrawState& state) +Canvas::Canvas(const DrawState& state) : fDrawState(new(std::nothrow) DrawState(state)) { } -DrawingContext::~DrawingContext() +Canvas::~Canvas() { } status_t -DrawingContext::InitCheck() const +Canvas::InitCheck() const { if (fDrawState == NULL) return B_NO_MEMORY; @@ -57,7 +58,7 @@ DrawingContext::InitCheck() const void -DrawingContext::PushState() +Canvas::PushState() { DrawState* newState = fDrawState->PushState(); if (newState) @@ -66,7 +67,7 @@ DrawingContext::PushState() void -DrawingContext::PopState() +Canvas::PopState() { if (fDrawState->PreviousState() == NULL) return; @@ -83,7 +84,7 @@ DrawingContext::PopState() void -DrawingContext::SetDrawingOrigin(BPoint origin) +Canvas::SetDrawingOrigin(BPoint origin) { fDrawState->SetOrigin(origin); @@ -94,7 +95,7 @@ DrawingContext::SetDrawingOrigin(BPoint origin) BPoint -DrawingContext::DrawingOrigin() const +Canvas::DrawingOrigin() const { BPoint origin(fDrawState->Origin()); float scale = Scale(); @@ -107,7 +108,7 @@ DrawingContext::DrawingOrigin() const void -DrawingContext::SetScale(float scale) +Canvas::SetScale(float scale) { fDrawState->SetScale(scale); @@ -118,31 +119,31 @@ DrawingContext::SetScale(float scale) float -DrawingContext::Scale() const +Canvas::Scale() const { return fDrawState->Scale(); } void -DrawingContext::SetUserClipping(const BRegion* region) +Canvas::SetUserClipping(const BRegion* region) { fDrawState->SetClippingRegion(region); - // rebuild clipping (for just this context) + // rebuild clipping (for just this canvas) RebuildClipping(false); } void -DrawingContext::SetAlphaMask(AlphaMask* mask) +Canvas::SetAlphaMask(AlphaMask* mask) { fDrawState->SetAlphaMask(mask); } AlphaMask* -DrawingContext::GetAlphaMask() const +Canvas::GetAlphaMask() const { return fDrawState->GetAlphaMask(); } @@ -150,7 +151,7 @@ DrawingContext::GetAlphaMask() const //! converts a point from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BPoint* point) const +Canvas::ConvertToScreenForDrawing(BPoint* point) const { fDrawState->Transform(point); // NOTE: from here on, don't use the @@ -161,7 +162,7 @@ DrawingContext::ConvertToScreenForDrawing(BPoint* point) const //! converts a rect from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BRect* rect) const +Canvas::ConvertToScreenForDrawing(BRect* rect) const { fDrawState->Transform(rect); // NOTE: from here on, don't use the @@ -172,7 +173,7 @@ DrawingContext::ConvertToScreenForDrawing(BRect* rect) const //! converts a region from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BRegion* region) const +Canvas::ConvertToScreenForDrawing(BRegion* region) const { fDrawState->Transform(region); // NOTE: from here on, don't use the @@ -183,7 +184,7 @@ DrawingContext::ConvertToScreenForDrawing(BRegion* region) const //! converts a gradient from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BGradient* gradient) const +Canvas::ConvertToScreenForDrawing(BGradient* gradient) const { switch (gradient->GetType()) { case BGradient::TYPE_LINEAR: @@ -266,7 +267,7 @@ DrawingContext::ConvertToScreenForDrawing(BGradient* gradient) const //! converts points from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 num) const +Canvas::ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 num) const { // TODO: optimize this, it should be smarter while (num--) { @@ -283,7 +284,7 @@ DrawingContext::ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 //! converts rects from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 num) const +Canvas::ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 num) const { // TODO: optimize this, it should be smarter while (num--) { @@ -300,7 +301,7 @@ DrawingContext::ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 nu //! converts regions from local *drawing* to screen coordinate system void -DrawingContext::ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int32 num) const +Canvas::ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int32 num) const { // TODO: optimize this, it should be smarter while (num--) { @@ -317,20 +318,20 @@ DrawingContext::ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int3 //! converts a point from screen to local coordinate system void -DrawingContext::ConvertFromScreenForDrawing(BPoint* point) const +Canvas::ConvertFromScreenForDrawing(BPoint* point) const { ConvertFromScreen(point); fDrawState->InverseTransform(point); } -// #pragma mark - OffscreenContext +// #pragma mark - OffscreenCanvas -OffscreenContext::OffscreenContext(DrawingEngine* engine, +OffscreenCanvas::OffscreenCanvas(DrawingEngine* engine, const DrawState& state) : - DrawingContext(state), + Canvas(state), fDrawingEngine(engine) { ResyncDrawState(); @@ -338,7 +339,7 @@ OffscreenContext::OffscreenContext(DrawingEngine* engine, void -OffscreenContext::ResyncDrawState() +OffscreenCanvas::ResyncDrawState() { fDrawingEngine->SetDrawState(fDrawState); } diff --git a/src/servers/app/DrawingContext.h b/src/servers/app/Canvas.h similarity index 90% rename from src/servers/app/DrawingContext.h rename to src/servers/app/Canvas.h index 621ed0a920..c8efc4c612 100644 --- a/src/servers/app/DrawingContext.h +++ b/src/servers/app/Canvas.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014, Haiku, Inc. + * Copyright (c) 2001-2015, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: @@ -9,9 +9,10 @@ * Stephan Aßmus * Marcus Overhagen * Adrien Destugues + * Julian Harnath */ -#ifndef DRAWING_CONTEXT_H -#define DRAWING_CONTEXT_H +#ifndef CANVAS_H +#define CANVAS_H #include @@ -27,11 +28,11 @@ class IntRect; class ServerPicture; -class DrawingContext { +class Canvas { public: - DrawingContext(); - DrawingContext(const DrawState& state); - virtual ~DrawingContext(); + Canvas(); + Canvas(const DrawState& state); + virtual ~Canvas(); status_t InitCheck() const; @@ -47,10 +48,10 @@ public: void SetUserClipping(const BRegion* region); // region is expected in view coordinates - + void SetAlphaMask(AlphaMask* mask); AlphaMask* GetAlphaMask() const; - + void ConvertToScreenForDrawing(BPoint* point) const; void ConvertToScreenForDrawing(BRect* rect) const; void ConvertToScreenForDrawing(BRegion* region) const; @@ -82,9 +83,9 @@ protected: }; -class OffscreenContext: public DrawingContext { +class OffscreenCanvas : public Canvas { public: - OffscreenContext(DrawingEngine* engine, + OffscreenCanvas(DrawingEngine* engine, const DrawState& state); // Screen and View coordinates are the same for us. @@ -108,4 +109,4 @@ private: }; -#endif +#endif // CANVAS_H diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 024088b061..aebc6c8580 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -32,10 +32,10 @@ local font_src = ; UseBuildFeatureHeaders freetype ; -Includes [ FGristFiles AppServer.cpp BitmapManager.cpp +Includes [ FGristFiles AppServer.cpp BitmapManager.cpp Canvas.cpp ClientMemoryAllocator.cpp Desktop.cpp DesktopSettings.cpp - DrawState.cpp DrawingContext.cpp DrawingEngine.cpp ServerApp.cpp - ServerBitmap.cpp ServerCursor.cpp ServerFont.cpp ServerPicture.cpp + DrawState.cpp DrawingEngine.cpp ServerApp.cpp + ServerBitmap.cpp ServerCursor.cpp ServerFont.cpp ServerPicture.cpp ServerWindow.cpp View.cpp Window.cpp WorkspacesView.cpp $(decorator_src) $(font_src) ] : [ BuildFeatureAttribute freetype : headers ] ; @@ -51,6 +51,7 @@ Server app_server : AppServer.cpp #BitfieldRegion.cpp BitmapManager.cpp + Canvas.cpp ClientMemoryAllocator.cpp CursorData.cpp CursorManager.cpp @@ -59,7 +60,6 @@ Server app_server : DesktopListener.cpp DesktopSettings.cpp DirectWindowInfo.cpp - DrawingContext.cpp DrawState.cpp EventDispatcher.cpp EventStream.cpp @@ -97,7 +97,7 @@ Server app_server : # libraries : libtranslation.so libbe.so libbnetapi.so - libaslocal.a $(BROKEN_64)libasremote.a $(BROKEN_64)libashtml5.a + libaslocal.a $(BROKEN_64)libasremote.a $(BROKEN_64)libashtml5.a libasdrawing.a libpainter.a libagg.a [ BuildFeatureAttribute freetype : library ] libstackandtile.a liblinprog.a libtextencoding.so libshared.a diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index 1c115dcf6c..6f09cb0d70 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -44,7 +44,7 @@ using std::stack; class ShapePainter : public BShapeIterator { public: - ShapePainter(DrawingContext* context); + ShapePainter(Canvas* canvas); virtual ~ShapePainter(); status_t Iterate(const BShape* shape); @@ -59,15 +59,15 @@ public: void Draw(BRect frame, bool filled); private: - DrawingContext* fContext; + Canvas* fCanvas; stack fOpStack; stack fPtStack; }; -ShapePainter::ShapePainter(DrawingContext* context) +ShapePainter::ShapePainter(Canvas* canvas) : - fContext(context) + fCanvas(canvas) { } @@ -203,10 +203,10 @@ ShapePainter::Draw(BRect frame, bool filled) fPtStack.pop(); } - BPoint offset(fContext->CurrentState()->PenLocation()); - fContext->ConvertToScreenForDrawing(&offset); - fContext->GetDrawingEngine()->DrawShape(frame, opCount, opList, - ptCount, ptList, filled, offset, fContext->Scale()); + BPoint offset(fCanvas->CurrentState()->PenLocation()); + fCanvas->ConvertToScreenForDrawing(&offset); + fCanvas->GetDrawingEngine()->DrawShape(frame, opCount, opList, + ptCount, ptList, filled, offset, fCanvas->Scale()); delete[] opList; delete[] ptList; @@ -253,23 +253,23 @@ nop() static void -move_pen_by(DrawingContext* context, BPoint delta) +move_pen_by(Canvas* canvas, BPoint delta) { - context->CurrentState()->SetPenLocation( - context->CurrentState()->PenLocation() + delta); + canvas->CurrentState()->SetPenLocation( + canvas->CurrentState()->PenLocation() + delta); } static void -stroke_line(DrawingContext* context, BPoint start, BPoint end) +stroke_line(Canvas* canvas, BPoint start, BPoint end) { BPoint penPos = end; - context->ConvertToScreenForDrawing(&start); - context->ConvertToScreenForDrawing(&end); - context->GetDrawingEngine()->StrokeLine(start, end); + canvas->ConvertToScreenForDrawing(&start); + canvas->ConvertToScreenForDrawing(&end); + canvas->GetDrawingEngine()->StrokeLine(start, end); - context->CurrentState()->SetPenLocation(penPos); + canvas->CurrentState()->SetPenLocation(penPos); // the DrawingEngine/Painter does not need to be updated, since this // effects only the view->screen coord conversion, which is handled // by the view only @@ -277,109 +277,109 @@ stroke_line(DrawingContext* context, BPoint start, BPoint end) static void -stroke_rect(DrawingContext* context, BRect rect) +stroke_rect(Canvas* canvas, BRect rect) { - context->ConvertToScreenForDrawing(&rect); - context->GetDrawingEngine()->StrokeRect(rect); + canvas->ConvertToScreenForDrawing(&rect); + canvas->GetDrawingEngine()->StrokeRect(rect); } static void -fill_rect(DrawingContext* context, BRect rect) +fill_rect(Canvas* canvas, BRect rect) { - context->ConvertToScreenForDrawing(&rect); - context->GetDrawingEngine()->FillRect(rect); + canvas->ConvertToScreenForDrawing(&rect); + canvas->GetDrawingEngine()->FillRect(rect); } static void -draw_round_rect(DrawingContext* context, BRect rect, BPoint radii, bool fill) +draw_round_rect(Canvas* canvas, BRect rect, BPoint radii, bool fill) { - context->ConvertToScreenForDrawing(&rect); - float scale = context->CurrentState()->CombinedScale(); - context->GetDrawingEngine()->DrawRoundRect(rect, radii.x * scale, + canvas->ConvertToScreenForDrawing(&rect); + float scale = canvas->CurrentState()->CombinedScale(); + canvas->GetDrawingEngine()->DrawRoundRect(rect, radii.x * scale, radii.y * scale, fill); } static void -stroke_round_rect(DrawingContext* context, BRect rect, BPoint radii) +stroke_round_rect(Canvas* canvas, BRect rect, BPoint radii) { - draw_round_rect(context, rect, radii, false); + draw_round_rect(canvas, rect, radii, false); } static void -fill_round_rect(DrawingContext* context, BRect rect, BPoint radii) +fill_round_rect(Canvas* canvas, BRect rect, BPoint radii) { - draw_round_rect(context, rect, radii, true); + draw_round_rect(canvas, rect, radii, true); } static void -stroke_bezier(DrawingContext* context, const BPoint* viewPoints) +stroke_bezier(Canvas* canvas, const BPoint* viewPoints) { BPoint points[4]; - context->ConvertToScreenForDrawing(points, viewPoints, 4); + canvas->ConvertToScreenForDrawing(points, viewPoints, 4); - context->GetDrawingEngine()->DrawBezier(points, false); + canvas->GetDrawingEngine()->DrawBezier(points, false); } static void -fill_bezier(DrawingContext* context, const BPoint* viewPoints) +fill_bezier(Canvas* canvas, const BPoint* viewPoints) { BPoint points[4]; - context->ConvertToScreenForDrawing(points, viewPoints, 4); + canvas->ConvertToScreenForDrawing(points, viewPoints, 4); - context->GetDrawingEngine()->DrawBezier(points, true); + canvas->GetDrawingEngine()->DrawBezier(points, true); } static void -stroke_arc(DrawingContext* context, BPoint center, BPoint radii, +stroke_arc(Canvas* canvas, BPoint center, BPoint radii, float startTheta, float arcTheta) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - context->ConvertToScreenForDrawing(&rect); - context->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, false); + canvas->ConvertToScreenForDrawing(&rect); + canvas->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, false); } static void -fill_arc(DrawingContext* context, BPoint center, BPoint radii, +fill_arc(Canvas* canvas, BPoint center, BPoint radii, float startTheta, float arcTheta) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - context->ConvertToScreenForDrawing(&rect); - context->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, true); + canvas->ConvertToScreenForDrawing(&rect); + canvas->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, true); } static void -stroke_ellipse(DrawingContext* context, BPoint center, BPoint radii) +stroke_ellipse(Canvas* canvas, BPoint center, BPoint radii) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - context->ConvertToScreenForDrawing(&rect); - context->GetDrawingEngine()->DrawEllipse(rect, false); + canvas->ConvertToScreenForDrawing(&rect); + canvas->GetDrawingEngine()->DrawEllipse(rect, false); } static void -fill_ellipse(DrawingContext* context, BPoint center, BPoint radii) +fill_ellipse(Canvas* canvas, BPoint center, BPoint radii) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - context->ConvertToScreenForDrawing(&rect); - context->GetDrawingEngine()->DrawEllipse(rect, true); + canvas->ConvertToScreenForDrawing(&rect); + canvas->GetDrawingEngine()->DrawEllipse(rect, true); } static void -stroke_polygon(DrawingContext* context, int32 numPoints, +stroke_polygon(Canvas* canvas, int32 numPoints, const BPoint* viewPoints, bool isClosed) { if (numPoints <= 0) @@ -391,12 +391,12 @@ stroke_polygon(DrawingContext* context, int32 numPoints, char data[200 * sizeof(BPoint)]; BPoint* points = (BPoint*)data; - context->ConvertToScreenForDrawing(points, viewPoints, numPoints); + canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); - context->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, + canvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, false, isClosed && numPoints > 2); } else { // avoid constructor/destructor calls by @@ -405,12 +405,12 @@ stroke_polygon(DrawingContext* context, int32 numPoints, if (points == NULL) return; - context->ConvertToScreenForDrawing(points, viewPoints, numPoints); + canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); - context->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, + canvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, false, isClosed && numPoints > 2); free(points); } @@ -418,7 +418,7 @@ stroke_polygon(DrawingContext* context, int32 numPoints, static void -fill_polygon(DrawingContext* context, int32 numPoints, +fill_polygon(Canvas* canvas, int32 numPoints, const BPoint* viewPoints) { if (numPoints <= 0) @@ -430,12 +430,12 @@ fill_polygon(DrawingContext* context, int32 numPoints, char data[200 * sizeof(BPoint)]; BPoint* points = (BPoint*)data; - context->ConvertToScreenForDrawing(points, viewPoints, numPoints); + canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); - context->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, + canvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, true, true); } else { // avoid constructor/destructor calls by @@ -444,12 +444,12 @@ fill_polygon(DrawingContext* context, int32 numPoints, if (points == NULL) return; - context->ConvertToScreenForDrawing(points, viewPoints, numPoints); + canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); - context->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, + canvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, true, true); free(points); } @@ -457,9 +457,9 @@ fill_polygon(DrawingContext* context, int32 numPoints, static void -stroke_shape(DrawingContext* context, const BShape* shape) +stroke_shape(Canvas* canvas, const BShape* shape) { - ShapePainter drawShape(context); + ShapePainter drawShape(canvas); drawShape.Iterate(shape); drawShape.Draw(shape->Bounds(), false); @@ -467,9 +467,9 @@ stroke_shape(DrawingContext* context, const BShape* shape) static void -fill_shape(DrawingContext* context, const BShape* shape) +fill_shape(Canvas* canvas, const BShape* shape) { - ShapePainter drawShape(context); + ShapePainter drawShape(canvas); drawShape.Iterate(shape); drawShape.Draw(shape->Bounds(), true); @@ -477,21 +477,21 @@ fill_shape(DrawingContext* context, const BShape* shape) static void -draw_string(DrawingContext* context, const char* string, float deltaSpace, +draw_string(Canvas* canvas, const char* string, float deltaSpace, float deltaNonSpace) { // NOTE: the picture data was recorded with a "set pen location" // command inserted before the "draw string" command, so we can // use PenLocation() - BPoint location = context->CurrentState()->PenLocation(); + BPoint location = canvas->CurrentState()->PenLocation(); escapement_delta delta = { deltaSpace, deltaNonSpace }; - context->ConvertToScreenForDrawing(&location); - location = context->GetDrawingEngine()->DrawString(string, strlen(string), + canvas->ConvertToScreenForDrawing(&location); + location = canvas->GetDrawingEngine()->DrawString(string, strlen(string), location, &delta); - context->ConvertFromScreenForDrawing(&location); - context->CurrentState()->SetPenLocation(location); + canvas->ConvertFromScreenForDrawing(&location); + canvas->CurrentState()->SetPenLocation(location); // the DrawingEngine/Painter does not need to be updated, since this // effects only the view->screen coord conversion, which is handled // by the view only @@ -499,7 +499,7 @@ draw_string(DrawingContext* context, const char* string, float deltaSpace, static void -draw_pixels(DrawingContext* context, BRect src, BRect dest, int32 width, +draw_pixels(Canvas* canvas, BRect src, BRect dest, int32 width, int32 height, int32 bytesPerRow, int32 pixelFormat, int32 options, const void* data) { @@ -511,31 +511,31 @@ draw_pixels(DrawingContext* context, BRect src, BRect dest, int32 width, memcpy(bitmap.Bits(), data, height * bytesPerRow); - context->ConvertToScreenForDrawing(&dest); - context->GetDrawingEngine()->DrawBitmap(&bitmap, src, dest, options); + canvas->ConvertToScreenForDrawing(&dest); + canvas->GetDrawingEngine()->DrawBitmap(&bitmap, src, dest, options); } static void -draw_picture(DrawingContext* context, BPoint where, int32 token) +draw_picture(Canvas* canvas, BPoint where, int32 token) { - ServerPicture* picture = context->GetPicture(token); + ServerPicture* picture = canvas->GetPicture(token); if (picture != NULL) { - context->PushState(); - context->SetDrawingOrigin(where); + canvas->PushState(); + canvas->SetDrawingOrigin(where); - context->PushState(); - picture->Play(context); - context->PopState(); + canvas->PushState(); + picture->Play(canvas); + canvas->PopState(); - context->PopState(); + canvas->PopState(); picture->ReleaseReference(); } } static void -set_clipping_rects(DrawingContext* context, const BRect* rects, +set_clipping_rects(Canvas* canvas, const BRect* rects, uint32 numRects) { // TODO: This might be too slow, we should copy the rects @@ -543,13 +543,13 @@ set_clipping_rects(DrawingContext* context, const BRect* rects, BRegion region; for (uint32 c = 0; c < numRects; c++) region.Include(rects[c]); - context->SetUserClipping(®ion); - context->UpdateCurrentDrawingRegion(); + canvas->SetUserClipping(®ion); + canvas->UpdateCurrentDrawingRegion(); } static void -clip_to_picture(DrawingContext* context, BPicture* picture, BPoint pt, +clip_to_picture(Canvas* canvas, BPicture* picture, BPoint pt, bool clipToInverse) { printf("ClipToPicture(picture, BPoint(%.2f, %.2f), %s)\n", @@ -558,20 +558,20 @@ clip_to_picture(DrawingContext* context, BPicture* picture, BPoint pt, static void -push_state(DrawingContext* context) +push_state(Canvas* canvas) { - context->PushState(); + canvas->PushState(); } static void -pop_state(DrawingContext* context) +pop_state(Canvas* canvas) { - context->PopState(); + canvas->PopState(); BPoint p(0, 0); - context->ConvertToScreenForDrawing(&p); - context->GetDrawingEngine()->SetDrawState(context->CurrentState(), + canvas->ConvertToScreenForDrawing(&p); + canvas->GetDrawingEngine()->SetDrawState(canvas->CurrentState(), (int32)p.x, (int32)p.y); } @@ -579,42 +579,42 @@ pop_state(DrawingContext* context) // TODO: Be smart and actually take advantage of these methods: // only apply state changes when they are called static void -enter_state_change(DrawingContext* context) +enter_state_change(Canvas* canvas) { } static void -exit_state_change(DrawingContext* context) +exit_state_change(Canvas* canvas) { - context->ResyncDrawState(); + canvas->ResyncDrawState(); } static void -enter_font_state(DrawingContext* context) +enter_font_state(Canvas* canvas) { } static void -exit_font_state(DrawingContext* context) +exit_font_state(Canvas* canvas) { - context->GetDrawingEngine()->SetFont(context->CurrentState()->Font()); + canvas->GetDrawingEngine()->SetFont(canvas->CurrentState()->Font()); } static void -set_origin(DrawingContext* context, BPoint pt) +set_origin(Canvas* canvas, BPoint pt) { - context->CurrentState()->SetOrigin(pt); + canvas->CurrentState()->SetOrigin(pt); } static void -set_pen_location(DrawingContext* context, BPoint pt) +set_pen_location(Canvas* canvas, BPoint pt) { - context->CurrentState()->SetPenLocation(pt); + canvas->CurrentState()->SetPenLocation(pt); // the DrawingEngine/Painter does not need to be updated, since this // effects only the view->screen coord conversion, which is handled // by the view only @@ -622,65 +622,65 @@ set_pen_location(DrawingContext* context, BPoint pt) static void -set_drawing_mode(DrawingContext* context, drawing_mode mode) +set_drawing_mode(Canvas* canvas, drawing_mode mode) { - context->CurrentState()->SetDrawingMode(mode); - context->GetDrawingEngine()->SetDrawingMode(mode); + canvas->CurrentState()->SetDrawingMode(mode); + canvas->GetDrawingEngine()->SetDrawingMode(mode); } static void -set_line_mode(DrawingContext* context, cap_mode capMode, join_mode joinMode, +set_line_mode(Canvas* canvas, cap_mode capMode, join_mode joinMode, float miterLimit) { - DrawState* state = context->CurrentState(); + DrawState* state = canvas->CurrentState(); state->SetLineCapMode(capMode); state->SetLineJoinMode(joinMode); state->SetMiterLimit(miterLimit); - context->GetDrawingEngine()->SetStrokeMode(capMode, joinMode, miterLimit); + canvas->GetDrawingEngine()->SetStrokeMode(capMode, joinMode, miterLimit); } static void -set_pen_size(DrawingContext* context, float size) +set_pen_size(Canvas* canvas, float size) { - context->CurrentState()->SetPenSize(size); - context->GetDrawingEngine()->SetPenSize( - context->CurrentState()->PenSize()); + canvas->CurrentState()->SetPenSize(size); + canvas->GetDrawingEngine()->SetPenSize( + canvas->CurrentState()->PenSize()); // DrawState::PenSize() returns the scaled pen size, so we // need to use that value to set the drawing engine pen size. } static void -set_fore_color(DrawingContext* context, rgb_color color) +set_fore_color(Canvas* canvas, rgb_color color) { - context->CurrentState()->SetHighColor(color); - context->GetDrawingEngine()->SetHighColor(color); + canvas->CurrentState()->SetHighColor(color); + canvas->GetDrawingEngine()->SetHighColor(color); } static void -set_back_color(DrawingContext* context, rgb_color color) +set_back_color(Canvas* canvas, rgb_color color) { - context->CurrentState()->SetLowColor(color); - context->GetDrawingEngine()->SetLowColor(color); + canvas->CurrentState()->SetLowColor(color); + canvas->GetDrawingEngine()->SetLowColor(color); } static void -set_stipple_pattern(DrawingContext* context, pattern p) +set_stipple_pattern(Canvas* canvas, pattern p) { - context->CurrentState()->SetPattern(Pattern(p)); - context->GetDrawingEngine()->SetPattern(p); + canvas->CurrentState()->SetPattern(Pattern(p)); + canvas->GetDrawingEngine()->SetPattern(p); } static void -set_scale(DrawingContext* context, float scale) +set_scale(Canvas* canvas, float scale) { - context->CurrentState()->SetScale(scale); - context->ResyncDrawState(); + canvas->CurrentState()->SetScale(scale); + canvas->ResyncDrawState(); // Update the drawing engine draw state, since some stuff // (for example the pen size) needs to be recalculated. @@ -688,94 +688,94 @@ set_scale(DrawingContext* context, float scale) static void -set_font_family(DrawingContext* context, const char* family) +set_font_family(Canvas* canvas, const char* family) { FontStyle* fontStyle = gFontManager->GetStyleByIndex(family, 0); ServerFont font; font.SetStyle(fontStyle); - context->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); + canvas->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); } static void -set_font_style(DrawingContext* context, const char* style) +set_font_style(Canvas* canvas, const char* style) { - ServerFont font(context->CurrentState()->Font()); + ServerFont font(canvas->CurrentState()->Font()); FontStyle* fontStyle = gFontManager->GetStyle(font.Family(), style); font.SetStyle(fontStyle); - context->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); + canvas->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); } static void -set_font_spacing(DrawingContext* context, int32 spacing) +set_font_spacing(Canvas* canvas, int32 spacing) { ServerFont font; font.SetSpacing(spacing); - context->CurrentState()->SetFont(font, B_FONT_SPACING); + canvas->CurrentState()->SetFont(font, B_FONT_SPACING); } static void -set_font_size(DrawingContext* context, float size) +set_font_size(Canvas* canvas, float size) { ServerFont font; font.SetSize(size); - context->CurrentState()->SetFont(font, B_FONT_SIZE); + canvas->CurrentState()->SetFont(font, B_FONT_SIZE); } static void -set_font_rotate(DrawingContext* context, float rotation) +set_font_rotate(Canvas* canvas, float rotation) { ServerFont font; font.SetRotation(rotation); - context->CurrentState()->SetFont(font, B_FONT_ROTATION); + canvas->CurrentState()->SetFont(font, B_FONT_ROTATION); } static void -set_font_encoding(DrawingContext* context, int32 encoding) +set_font_encoding(Canvas* canvas, int32 encoding) { ServerFont font; font.SetEncoding(encoding); - context->CurrentState()->SetFont(font, B_FONT_ENCODING); + canvas->CurrentState()->SetFont(font, B_FONT_ENCODING); } static void -set_font_flags(DrawingContext* context, int32 flags) +set_font_flags(Canvas* canvas, int32 flags) { ServerFont font; font.SetFlags(flags); - context->CurrentState()->SetFont(font, B_FONT_FLAGS); + canvas->CurrentState()->SetFont(font, B_FONT_FLAGS); } static void -set_font_shear(DrawingContext* context, float shear) +set_font_shear(Canvas* canvas, float shear) { ServerFont font; font.SetShear(shear); - context->CurrentState()->SetFont(font, B_FONT_SHEAR); + canvas->CurrentState()->SetFont(font, B_FONT_SHEAR); } static void -set_font_face(DrawingContext* context, int32 face) +set_font_face(Canvas* canvas, int32 face) { ServerFont font; font.SetFace(face); - context->CurrentState()->SetFont(font, B_FONT_FACE); + canvas->CurrentState()->SetFont(font, B_FONT_FACE); } static void -set_blending_mode(DrawingContext* context, int16 alphaSrcMode, int16 alphaFncMode) +set_blending_mode(Canvas* canvas, int16 alphaSrcMode, int16 alphaFncMode) { - context->CurrentState()->SetBlendingMode((source_alpha)alphaSrcMode, + canvas->CurrentState()->SetBlendingMode((source_alpha)alphaSrcMode, (alpha_function)alphaFncMode); } @@ -1068,7 +1068,7 @@ ServerPicture::SetFontFromLink(BPrivate::LinkReceiver& link) void -ServerPicture::Play(DrawingContext* target) +ServerPicture::Play(Canvas* target) { // TODO: for now: then change PicturePlayer // to accept a BPositionIO object diff --git a/src/servers/app/ServerPicture.h b/src/servers/app/ServerPicture.h index 6f27d38c2a..f559b45b59 100644 --- a/src/servers/app/ServerPicture.h +++ b/src/servers/app/ServerPicture.h @@ -18,7 +18,7 @@ class BFile; -class DrawingContext; +class Canvas; class ServerApp; class View; @@ -49,7 +49,7 @@ public: void SyncState(View* view); void SetFontFromLink(BPrivate::LinkReceiver& link); - void Play(DrawingContext* target); + void Play(Canvas* target); void PushPicture(ServerPicture* picture); ServerPicture* PopPicture(); diff --git a/src/servers/app/View.h b/src/servers/app/View.h index b2291afe47..7bdc31a78d 100644 --- a/src/servers/app/View.h +++ b/src/servers/app/View.h @@ -14,7 +14,7 @@ #define VIEW_H -#include "DrawingContext.h" +#include "Canvas.h" #include "IntRect.h" #include @@ -37,7 +37,7 @@ class ServerCursor; class ServerPicture; class BGradient; -class View: public DrawingContext { +class View: public Canvas { public: View(IntRect frame, IntPoint scrollingOffset, const char* name, int32 token, diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 2a595fca86..2b744cd548 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -12,7 +12,7 @@ #include "BitmapHWInterface.h" #include "BitmapManager.h" -#include "DrawingContext.h" +#include "Canvas.h" #include "DrawingEngine.h" #include "ServerBitmap.h" #include "ServerPicture.h" @@ -56,7 +56,7 @@ AlphaMask::Update(BRect bounds, BPoint offset) { fViewBounds = bounds; fViewOffset = offset; - + if (fPreviousMask != NULL) fPreviousMask->Update(bounds, offset); } @@ -98,7 +98,7 @@ AlphaMask::Generate() delete[] fCachedBitmap; fCachedBitmap = new(std::nothrow) uint8[width * height]; } - + // If rendering the picture fails, we will draw without any clipping. ServerBitmap* bitmap = _RenderPicture(); if (bitmap == NULL || fCachedBitmap == NULL) { @@ -140,7 +140,7 @@ AlphaMask::Generate() transferBitmap = false; } } - + if (transferBitmap) { for (uint32 y = 0; y < height; y++) { for (uint32 x = 0; x < width; x++) { @@ -194,8 +194,8 @@ AlphaMask::_RenderPicture() const return NULL; } - OffscreenContext context(engine, fDrawState); - context.PushState(); + OffscreenCanvas canvas(engine, fDrawState); + canvas.PushState(); if (engine->LockParallelAccess()) { // FIXME ConstrainClippingRegion docs says passing NULL disables @@ -203,11 +203,11 @@ AlphaMask::_RenderPicture() const BRegion clipping; clipping.Include(fViewBounds); engine->ConstrainClippingRegion(&clipping); - fPicture->Play(&context); + fPicture->Play(&canvas); engine->UnlockParallelAccess(); } - context.PopState(); + canvas.PopState(); delete engine; return bitmap; diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index 376d56291d..0a5daf047e 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -152,8 +152,8 @@ SharedLibrary libtestappserver.so : AlphaMask.cpp BitmapHWInterface.cpp + Canvas.cpp DesktopSettings.cpp - DrawingContext.cpp OffscreenServerWindow.cpp OffscreenWindow.cpp RegionPool.cpp @@ -173,9 +173,9 @@ SharedLibrary libtestappserver.so : [ BuildFeatureAttribute freetype : library ] ; -Includes [ FGristFiles AppServer.cpp BitmapManager.cpp +Includes [ FGristFiles AppServer.cpp BitmapManager.cpp Canvas.cpp ClientMemoryAllocator.cpp Desktop.cpp DesktopSettings.cpp - DrawState.cpp DrawingContext.cpp DrawingEngine.cpp ServerApp.cpp + DrawState.cpp DrawingEngine.cpp ServerApp.cpp ServerBitmap.cpp ServerCursor.cpp ServerFont.cpp ServerPicture.cpp ServerWindow.cpp View.cpp Window.cpp WorkspacesView.cpp $(decorator_src) $(font_src) ] From 6f2a446e2eb8362b92dd6c12c8ed7274d110a9b1 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 6 Apr 2015 14:43:24 +0200 Subject: [PATCH 02/29] app_server: extract coordinate conversion class * Move coordinate conversion into a new class SimpleTransform. It supports scaling and translation which is sufficient for conversion between screen, local and pen (drawing) coordinates. * Because all the overloaded methods for converting BPoint/BRect/BRegion/etc are now within the single SimpleTransform class, the interfaces of Canvas, View, DrawState, etc. are slimmed down. These classes have too many responsibilities, so some will be factored out into separate classes, this being the first. --- src/servers/app/Canvas.cpp | 205 +++++------------------- src/servers/app/Canvas.h | 46 ++---- src/servers/app/DrawState.cpp | 90 ++--------- src/servers/app/DrawState.h | 14 +- src/servers/app/ServerPicture.cpp | 47 +++--- src/servers/app/ServerWindow.cpp | 130 +++++++++------ src/servers/app/SimpleTransform.h | 243 +++++++++++++++++++++++++++++ src/servers/app/View.cpp | 230 ++++----------------------- src/servers/app/View.h | 31 +--- src/servers/app/Window.cpp | 2 +- src/servers/app/WorkspacesView.cpp | 6 +- 11 files changed, 462 insertions(+), 582 deletions(-) create mode 100644 src/servers/app/SimpleTransform.h diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index 715c6a1f05..4fd87a0322 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -17,17 +17,22 @@ #include -#include -#include -#include -#include -#include #include #include "DrawingEngine.h" #include "DrawState.h" +#if __GNUC__ >= 3 +# define GCC_2_NRV(x) + // GCC >= 3.1 doesn't need it anymore +#else +# define GCC_2_NRV(x) return x; + // GCC 2 named return value syntax + // see http://gcc.gnu.org/onlinedocs/gcc-2.95.2/gcc_5.html#SEC106 +#endif + + Canvas::Canvas() : fDrawState(new(std::nothrow) DrawState()) @@ -149,179 +154,49 @@ Canvas::GetAlphaMask() const } -//! converts a point from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BPoint* point) const +SimpleTransform +Canvas::LocalToScreenTransform() const GCC_2_NRV(transform) { - fDrawState->Transform(point); - // NOTE: from here on, don't use the - // "*ForDrawing()" versions of the parent! - ConvertToScreen(point); +#if __GNUC__ >= 3 + SimpleTransform transform; +#endif + _LocalToScreenTransform(transform); + return transform; } -//! converts a rect from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BRect* rect) const +SimpleTransform +Canvas::ScreenToLocalTransform() const GCC_2_NRV(transform) { - fDrawState->Transform(rect); - // NOTE: from here on, don't use the - // "*ForDrawing()" versions of the parent! - ConvertToScreen(rect); +#if __GNUC__ >= 3 + SimpleTransform transform; +#endif + _ScreenToLocalTransform(transform); + return transform; } -//! converts a region from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BRegion* region) const +SimpleTransform +Canvas::PenToScreenTransform() const GCC_2_NRV(transform) { - fDrawState->Transform(region); - // NOTE: from here on, don't use the - // "*ForDrawing()" versions of the parent! - ConvertToScreen(region); +#if __GNUC__ >= 3 + SimpleTransform transform; +#endif + fDrawState->Transform(transform); + _LocalToScreenTransform(transform); + return transform; } -//! converts a gradient from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BGradient* gradient) const +SimpleTransform +Canvas::ScreenToPenTransform() const GCC_2_NRV(transform) { - switch (gradient->GetType()) { - case BGradient::TYPE_LINEAR: - { - BGradientLinear* linear = (BGradientLinear*) gradient; - BPoint start = linear->Start(); - BPoint end = linear->End(); - fDrawState->Transform(&start); - ConvertToScreen(&start); - fDrawState->Transform(&end); - ConvertToScreen(&end); - linear->SetStart(start); - linear->SetEnd(end); - break; - } - case BGradient::TYPE_RADIAL: - { - BGradientRadial* radial = (BGradientRadial*) gradient; - BPoint center = radial->Center(); - fDrawState->Transform(¢er); - ConvertToScreen(¢er); - radial->SetCenter(center); - break; - } - case BGradient::TYPE_RADIAL_FOCUS: - { - BGradientRadialFocus* radialFocus = (BGradientRadialFocus*) gradient; - BPoint center = radialFocus->Center(); - BPoint focal = radialFocus->Focal(); - fDrawState->Transform(¢er); - ConvertToScreen(¢er); - fDrawState->Transform(&focal); - ConvertToScreen(&focal); - radialFocus->SetCenter(center); - radialFocus->SetFocal(focal); - break; - } - case BGradient::TYPE_DIAMOND: - { - BGradientDiamond* diamond = (BGradientDiamond*) gradient; - BPoint center = diamond->Center(); - fDrawState->Transform(¢er); - ConvertToScreen(¢er); - diamond->SetCenter(center); - break; - } - case BGradient::TYPE_CONIC: - { - BGradientConic* conic = (BGradientConic*) gradient; - BPoint center = conic->Center(); - fDrawState->Transform(¢er); - ConvertToScreen(¢er); - conic->SetCenter(center); - break; - } - case BGradient::TYPE_NONE: - { - break; - } - } - - // Make sure the gradient is fully padded so that out of bounds access - // get the correct colors - gradient->SortColorStopsByOffset(); - - BGradient::ColorStop* end = gradient->ColorStopAtFast( - gradient->CountColorStops() - 1); - - if (end->offset != 255) - gradient->AddColor(end->color, 255); - - BGradient::ColorStop* start = gradient->ColorStopAtFast(0); - - if (start->offset != 0) - gradient->AddColor(start->color, 0); - - gradient->SortColorStopsByOffset(); -} - - -//! converts points from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 num) const -{ - // TODO: optimize this, it should be smarter - while (num--) { - *dst = *src; - fDrawState->Transform(dst); - // NOTE: from here on, don't use the - // "*ForDrawing()" versions of the parent! - ConvertToScreen(dst); - src++; - dst++; - } -} - - -//! converts rects from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 num) const -{ - // TODO: optimize this, it should be smarter - while (num--) { - *dst = *src; - fDrawState->Transform(dst); - // NOTE: from here on, don't use the - // "*ForDrawing()" versions of the parent! - ConvertToScreen(dst); - src++; - dst++; - } -} - - -//! converts regions from local *drawing* to screen coordinate system -void -Canvas::ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int32 num) const -{ - // TODO: optimize this, it should be smarter - while (num--) { - *dst = *src; - fDrawState->Transform(dst); - // NOTE: from here on, don't use the - // "*ForDrawing()" versions of the parent! - ConvertToScreen(dst); - src++; - dst++; - } -} - - -//! converts a point from screen to local coordinate system -void -Canvas::ConvertFromScreenForDrawing(BPoint* point) const -{ - ConvertFromScreen(point); - fDrawState->InverseTransform(point); +#if __GNUC__ >= 3 + SimpleTransform transform; +#endif + _ScreenToLocalTransform(transform); + fDrawState->InverseTransform(transform); + return transform; } diff --git a/src/servers/app/Canvas.h b/src/servers/app/Canvas.h index c8efc4c612..acb52d09e5 100644 --- a/src/servers/app/Canvas.h +++ b/src/servers/app/Canvas.h @@ -17,6 +17,8 @@ #include +#include "SimpleTransform.h" + class AlphaMask; class BGradient; @@ -52,25 +54,10 @@ public: void SetAlphaMask(AlphaMask* mask); AlphaMask* GetAlphaMask() const; - void ConvertToScreenForDrawing(BPoint* point) const; - void ConvertToScreenForDrawing(BRect* rect) const; - void ConvertToScreenForDrawing(BRegion* region) const; - void ConvertToScreenForDrawing(BGradient* gradient) const; - - void ConvertToScreenForDrawing(BPoint* dst, const BPoint* src, int32 num) const; - void ConvertToScreenForDrawing(BRect* dst, const BRect* src, int32 num) const; - void ConvertToScreenForDrawing(BRegion* dst, const BRegion* src, int32 num) const; - - void ConvertFromScreenForDrawing(BPoint* point) const; - // used when updating the pen position - - virtual void ConvertToScreen(BPoint* point) const = 0; - virtual void ConvertToScreen(IntPoint* point) const = 0; - virtual void ConvertToScreen(BRect* rect) const = 0; - virtual void ConvertToScreen(IntRect* rect) const = 0; - virtual void ConvertToScreen(BRegion* region) const = 0; - - virtual void ConvertFromScreen(BPoint* point) const = 0; + SimpleTransform LocalToScreenTransform() const; + SimpleTransform ScreenToLocalTransform() const; + SimpleTransform PenToScreenTransform() const; + SimpleTransform ScreenToPenTransform() const; virtual DrawingEngine* GetDrawingEngine() const = 0; virtual ServerPicture* GetPicture(int32 token) const = 0; @@ -78,6 +65,12 @@ public: virtual void ResyncDrawState() {}; virtual void UpdateCurrentDrawingRegion() {}; +protected: + virtual void _LocalToScreenTransform( + SimpleTransform& transform) const = 0; + virtual void _ScreenToLocalTransform( + SimpleTransform& transform) const = 0; + protected: DrawState* fDrawState; }; @@ -88,22 +81,17 @@ public: OffscreenCanvas(DrawingEngine* engine, const DrawState& state); - // Screen and View coordinates are the same for us. - // DrawState already takes care of World<>View - // conversions. - virtual void ConvertToScreen(BPoint*) const {} - virtual void ConvertToScreen(IntPoint*) const {} - virtual void ConvertToScreen(BRect*) const {} - virtual void ConvertToScreen(IntRect*) const {} - virtual void ConvertToScreen(BRegion*) const {} - virtual void ConvertFromScreen(BPoint*) const {} - virtual DrawingEngine* GetDrawingEngine() const { return fDrawingEngine; } virtual void RebuildClipping(bool deep) { /* TODO */ } virtual void ResyncDrawState(); virtual ServerPicture* GetPicture(int32 token) const { /* TODO */ return NULL; } + +protected: + virtual void _LocalToScreenTransform(SimpleTransform&) const {} + virtual void _ScreenToLocalTransform(SimpleTransform&) const {} + private: DrawingEngine* fDrawingEngine; }; diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index 23c358fe6e..d7f88bc922 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008, Haiku. + * Copyright 2001-2015, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -8,6 +8,7 @@ * Stephan Aßmus * Axel Dörfler, axeld@pinc-software.de * Michael Pfeiffer + * Julian Harnath */ //! Data classes for working with BView states and draw parameters @@ -211,7 +212,7 @@ DrawState::ReadFromLink(BPrivate::LinkReceiver& link) ViewSetStateInfo info; link.Read(&info); - + fPenLocation = info.penLocation; fPenSize = info.penSize; fHighColor = info.highColor; @@ -407,8 +408,9 @@ DrawState::GetCombinedClippingRegion(BRegion* region) const { if (fClippingRegion != NULL) { BRegion localTransformedClipping(*fClippingRegion); - Transform(&localTransformedClipping); - + SimpleTransform penTransform; + Transform(penTransform); + penTransform.Apply(&localTransformedClipping); if (fPreviousState != NULL && fPreviousState->GetCombinedClippingRegion(region)) { localTransformedClipping.IntersectWith(region); @@ -438,7 +440,7 @@ DrawState::SetAlphaMask(AlphaMask* mask) fAlphaMask = mask; if (fAlphaMask != NULL && fPreviousState != NULL) fAlphaMask->SetPrevious(fPreviousState->fAlphaMask); - + } @@ -453,83 +455,19 @@ DrawState::GetAlphaMask() const void -DrawState::Transform(float* x, float* y) const +DrawState::Transform(SimpleTransform& transform) const { - // scale relative to origin, therefore - // scale first then translate to - // origin - *x *= fCombinedScale; - *y *= fCombinedScale; - *x += fCombinedOrigin.x; - *y += fCombinedOrigin.y; + transform.AddOffset(fCombinedOrigin.x, fCombinedOrigin.y); + transform.SetScale(fCombinedScale); } void -DrawState::InverseTransform(float* x, float* y) const +DrawState::InverseTransform(SimpleTransform& transform) const { - *x -= fCombinedOrigin.x; - *y -= fCombinedOrigin.y; - if (fCombinedScale != 0.0) { - *x /= fCombinedScale; - *y /= fCombinedScale; - } -} - - -void -DrawState::Transform(BPoint* point) const -{ - Transform(&(point->x), &(point->y)); -} - - -void -DrawState::Transform(BRect* rect) const -{ - Transform(&(rect->left), &(rect->top)); - Transform(&(rect->right), &(rect->bottom)); -} - - -void -DrawState::Transform(BRegion* region) const -{ - if (fCombinedScale == 1.0) { - region->OffsetBy(fCombinedOrigin.x, fCombinedOrigin.y); - } else { - // TODO: optimize some more - BRegion converted; - int32 count = region->CountRects(); - for (int32 i = 0; i < count; i++) { - BRect r = region->RectAt(i); - BPoint lt(r.LeftTop()); - BPoint rb(r.RightBottom()); - // offset to bottom right corner of pixel before transformation - rb.x++; - rb.y++; - // apply transformation - Transform(<.x, <.y); - Transform(&rb.x, &rb.y); - // reset bottom right to pixel "index" - rb.x--; - rb.y--; - // add rect to converted region - // NOTE/TODO: the rect would not have to go - // through the whole intersection test process, - // it is guaranteed not to overlap with any rect - // already contained in the region - converted.Include(BRect(lt, rb)); - } - *region = converted; - } -} - - -void -DrawState::InverseTransform(BPoint* point) const -{ - InverseTransform(&(point->x), &(point->y)); + transform.AddOffset(-fCombinedOrigin.x, -fCombinedOrigin.y); + if (fCombinedScale != 0.0) + transform.SetScale(1.0 / fCombinedScale); } diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index 48508ed6cf..392549789f 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008, Haiku. + * Copyright 2001-2015, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -7,6 +7,7 @@ * Adi Oanca * Stephan Aßmus * Axel Dörfler, axeld@pinc-software.de + * Julian Harnath */ #ifndef _DRAW_STATE_H_ #define _DRAW_STATE_H_ @@ -20,6 +21,7 @@ #include "ServerFont.h" #include "PatternHandler.h" +#include "SimpleTransform.h" class AlphaMask; class BRegion; @@ -78,14 +80,8 @@ public: AlphaMask* GetAlphaMask() const; // coordinate transformations - void Transform(float* x, float* y) const; - void InverseTransform(float* x, float* y) const; - - void Transform(BPoint* point) const; - void Transform(BRect* rect) const; - void Transform(BRegion* region) const; - - void InverseTransform(BPoint* point) const; + void Transform(SimpleTransform& transform) const; + void InverseTransform(SimpleTransform& transform) const; // color void SetHighColor(rgb_color color); diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index 6f09cb0d70..d03ddb29e3 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -204,7 +204,7 @@ ShapePainter::Draw(BRect frame, bool filled) } BPoint offset(fCanvas->CurrentState()->PenLocation()); - fCanvas->ConvertToScreenForDrawing(&offset); + fCanvas->PenToScreenTransform().Apply(&offset); fCanvas->GetDrawingEngine()->DrawShape(frame, opCount, opList, ptCount, ptList, filled, offset, fCanvas->Scale()); @@ -265,8 +265,9 @@ stroke_line(Canvas* canvas, BPoint start, BPoint end) { BPoint penPos = end; - canvas->ConvertToScreenForDrawing(&start); - canvas->ConvertToScreenForDrawing(&end); + const SimpleTransform transform = canvas->PenToScreenTransform(); + transform.Apply(&start); + transform.Apply(&end); canvas->GetDrawingEngine()->StrokeLine(start, end); canvas->CurrentState()->SetPenLocation(penPos); @@ -279,7 +280,7 @@ stroke_line(Canvas* canvas, BPoint start, BPoint end) static void stroke_rect(Canvas* canvas, BRect rect) { - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->StrokeRect(rect); } @@ -287,7 +288,7 @@ stroke_rect(Canvas* canvas, BRect rect) static void fill_rect(Canvas* canvas, BRect rect) { - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->FillRect(rect); } @@ -295,7 +296,7 @@ fill_rect(Canvas* canvas, BRect rect) static void draw_round_rect(Canvas* canvas, BRect rect, BPoint radii, bool fill) { - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); float scale = canvas->CurrentState()->CombinedScale(); canvas->GetDrawingEngine()->DrawRoundRect(rect, radii.x * scale, radii.y * scale, fill); @@ -320,8 +321,7 @@ static void stroke_bezier(Canvas* canvas, const BPoint* viewPoints) { BPoint points[4]; - canvas->ConvertToScreenForDrawing(points, viewPoints, 4); - + canvas->PenToScreenTransform().Apply(points, viewPoints, 4); canvas->GetDrawingEngine()->DrawBezier(points, false); } @@ -330,8 +330,7 @@ static void fill_bezier(Canvas* canvas, const BPoint* viewPoints) { BPoint points[4]; - canvas->ConvertToScreenForDrawing(points, viewPoints, 4); - + canvas->PenToScreenTransform().Apply(points, viewPoints, 4); canvas->GetDrawingEngine()->DrawBezier(points, true); } @@ -342,7 +341,7 @@ stroke_arc(Canvas* canvas, BPoint center, BPoint radii, { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, false); } @@ -353,7 +352,7 @@ fill_arc(Canvas* canvas, BPoint center, BPoint radii, { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, true); } @@ -363,7 +362,7 @@ stroke_ellipse(Canvas* canvas, BPoint center, BPoint radii) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->DrawEllipse(rect, false); } @@ -373,7 +372,7 @@ fill_ellipse(Canvas* canvas, BPoint center, BPoint radii) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); - canvas->ConvertToScreenForDrawing(&rect); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->DrawEllipse(rect, true); } @@ -391,8 +390,7 @@ stroke_polygon(Canvas* canvas, int32 numPoints, char data[200 * sizeof(BPoint)]; BPoint* points = (BPoint*)data; - canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); - + canvas->PenToScreenTransform().Apply(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); @@ -405,8 +403,7 @@ stroke_polygon(Canvas* canvas, int32 numPoints, if (points == NULL) return; - canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); - + canvas->PenToScreenTransform().Apply(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); @@ -430,8 +427,7 @@ fill_polygon(Canvas* canvas, int32 numPoints, char data[200 * sizeof(BPoint)]; BPoint* points = (BPoint*)data; - canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); - + canvas->PenToScreenTransform().Apply(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); @@ -444,8 +440,7 @@ fill_polygon(Canvas* canvas, int32 numPoints, if (points == NULL) return; - canvas->ConvertToScreenForDrawing(points, viewPoints, numPoints); - + canvas->PenToScreenTransform().Apply(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); @@ -486,11 +481,11 @@ draw_string(Canvas* canvas, const char* string, float deltaSpace, BPoint location = canvas->CurrentState()->PenLocation(); escapement_delta delta = { deltaSpace, deltaNonSpace }; - canvas->ConvertToScreenForDrawing(&location); + canvas->PenToScreenTransform().Apply(&location); location = canvas->GetDrawingEngine()->DrawString(string, strlen(string), location, &delta); - canvas->ConvertFromScreenForDrawing(&location); + canvas->PenToScreenTransform().Apply(&location); canvas->CurrentState()->SetPenLocation(location); // the DrawingEngine/Painter does not need to be updated, since this // effects only the view->screen coord conversion, which is handled @@ -511,7 +506,7 @@ draw_pixels(Canvas* canvas, BRect src, BRect dest, int32 width, memcpy(bitmap.Bits(), data, height * bytesPerRow); - canvas->ConvertToScreenForDrawing(&dest); + canvas->PenToScreenTransform().Apply(&dest); canvas->GetDrawingEngine()->DrawBitmap(&bitmap, src, dest, options); } @@ -570,7 +565,7 @@ pop_state(Canvas* canvas) canvas->PopState(); BPoint p(0, 0); - canvas->ConvertToScreenForDrawing(&p); + canvas->PenToScreenTransform().Apply(&p); canvas->GetDrawingEngine()->SetDrawState(canvas->CurrentState(), (int32)p.x, (int32)p.y); } diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 242438faf2..81892f939c 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -351,7 +351,7 @@ ServerWindow::_Show() fDesktop->ShowWindow(fWindow); if (fDirectWindowInfo && fDirectWindowInfo->IsFullScreen()) _ResizeToFullScreen(); - + fDesktop->LockSingleWindow(); } @@ -1964,7 +1964,7 @@ fDesktop->LockSingleWindow(); } else { _UpdateCurrentDrawingRegion(); BRegion region(fCurrentDrawingRegion); - fCurrentView->ConvertFromScreen(®ion); + fCurrentView->ScreenToLocalTransform().Apply(®ion); fLink.AttachRegion(region); } fLink.Flush(); @@ -2254,8 +2254,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, info.endPoint.x, info.endPoint.y)); BPoint penPos = info.endPoint; - fCurrentView->ConvertToScreenForDrawing(&info.startPoint); - fCurrentView->ConvertToScreenForDrawing(&info.endPoint); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&info.startPoint); + transform.Apply(&info.endPoint); drawingEngine->StrokeLine(info.startPoint, info.endPoint); // We update the pen here because many DrawingEngine calls which @@ -2279,7 +2281,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); - fCurrentView->ConvertToScreenForDrawing(&rect); + fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->InvertRect(rect); break; } @@ -2294,7 +2296,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); - fCurrentView->ConvertToScreenForDrawing(&rect); + fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->StrokeRect(rect); break; } @@ -2309,7 +2311,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); - fCurrentView->ConvertToScreenForDrawing(&rect); + fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->FillRect(rect); break; } @@ -2326,8 +2328,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); - fCurrentView->ConvertToScreenForDrawing(&rect); - fCurrentView->ConvertToScreenForDrawing(gradient); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&rect); + transform.Apply(gradient); drawingEngine->FillRect(rect, *gradient); delete gradient; break; @@ -2356,7 +2360,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, info.viewRect.left, info.viewRect.top, info.viewRect.right, info.viewRect.bottom)); - fCurrentView->ConvertToScreenForDrawing(&info.viewRect); + fCurrentView->PenToScreenTransform().Apply(&info.viewRect); // TODO: Unbreak... // if ((info.options & B_WAIT_FOR_RETRACE) != 0) @@ -2382,7 +2386,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, if (link.Read(&span) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&r); + fCurrentView->PenToScreenTransform().Apply(&r); drawingEngine->DrawArc(r, angle, span, code == AS_FILL_ARC); break; } @@ -2399,8 +2403,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&r); - fCurrentView->ConvertToScreenForDrawing(gradient); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&r); + transform.Apply(gradient); drawingEngine->FillArc(r, angle, span, *gradient); delete gradient; break; @@ -2411,11 +2417,13 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_BEZIER\n", Title())); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); BPoint pts[4]; status_t status; for (int32 i = 0; i < 4; i++) { status = link.Read(&(pts[i])); - fCurrentView->ConvertToScreenForDrawing(&pts[i]); + transform.Apply(&pts[i]); } if (status != B_OK) break; @@ -2428,15 +2436,17 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, GTRACE(("ServerWindow %s: Message AS_FILL_BEZIER_GRADIENT\n", Title())); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); BPoint pts[4]; for (int32 i = 0; i < 4; i++) { link.Read(&(pts[i])); - fCurrentView->ConvertToScreenForDrawing(&pts[i]); + transform.Apply(&pts[i]); } BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(gradient); + transform.Apply(gradient); drawingEngine->FillBezier(pts, *gradient); delete gradient; break; @@ -2451,7 +2461,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, if (link.Read(&rect) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&rect); + fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->DrawEllipse(rect, code == AS_FILL_ELLIPSE); break; } @@ -2465,8 +2475,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&rect); - fCurrentView->ConvertToScreenForDrawing(gradient); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&rect); + transform.Apply(gradient); drawingEngine->FillEllipse(rect, *gradient); delete gradient; break; @@ -2485,7 +2497,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, if (link.Read(&yRadius) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&rect); + fCurrentView->PenToScreenTransform().Apply(&rect); float scale = fCurrentView->CurrentState()->CombinedScale(); drawingEngine->DrawRoundRect(rect, xRadius * scale, yRadius * scale, code == AS_FILL_ROUNDRECT); @@ -2504,8 +2516,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&rect); - fCurrentView->ConvertToScreenForDrawing(gradient); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&rect); + transform.Apply(gradient); drawingEngine->FillRoundRect(rect, xrad, yrad, *gradient); delete gradient; break; @@ -2516,18 +2530,20 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_TRIANGLE\n", Title())); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); BPoint pts[3]; BRect rect; for (int32 i = 0; i < 3; i++) { link.Read(&(pts[i])); - fCurrentView->ConvertToScreenForDrawing(&pts[i]); + transform.Apply(&pts[i]); } if (link.Read(&rect) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&rect); + transform.Apply(&rect); drawingEngine->DrawTriangle(pts, rect, code == AS_FILL_TRIANGLE); break; } @@ -2536,18 +2552,20 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, DTRACE(("ServerWindow %s: Message AS_FILL_TRIANGLE_GRADIENT\n", Title())); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); BPoint pts[3]; BRect rect; for (int32 i = 0; i < 3; i++) { link.Read(&(pts[i])); - fCurrentView->ConvertToScreenForDrawing(&pts[i]); + transform.Apply(&pts[i]); } link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(&rect); - fCurrentView->ConvertToScreenForDrawing(gradient); + transform.Apply(&rect); + transform.Apply(gradient); drawingEngine->FillTriangle(pts, rect, *gradient); delete gradient; break; @@ -2567,11 +2585,13 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, link.Read(&isClosed); link.Read(&pointCount); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); BPoint* pointList = new(nothrow) BPoint[pointCount]; if (link.Read(pointList, pointCount * sizeof(BPoint)) >= B_OK) { for (int32 i = 0; i < pointCount; i++) - fCurrentView->ConvertToScreenForDrawing(&pointList[i]); - fCurrentView->ConvertToScreenForDrawing(&polyFrame); + transform.Apply(&pointList[i]); + transform.Apply(&polyFrame); drawingEngine->DrawPolygon(pointList, pointCount, polyFrame, code == AS_FILL_POLYGON, isClosed && pointCount > 2); @@ -2590,14 +2610,16 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, link.Read(&polyFrame); link.Read(&pointCount); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); BPoint* pointList = new(nothrow) BPoint[pointCount]; BGradient* gradient; if (link.Read(pointList, pointCount * sizeof(BPoint)) == B_OK && link.ReadGradient(&gradient) == B_OK) { for (int32 i = 0; i < pointCount; i++) - fCurrentView->ConvertToScreenForDrawing(&pointList[i]); - fCurrentView->ConvertToScreenForDrawing(&polyFrame); - fCurrentView->ConvertToScreenForDrawing(gradient); + transform.Apply(&pointList[i]); + transform.Apply(&polyFrame); + transform.Apply(gradient); drawingEngine->FillPolygon(pointList, pointCount, polyFrame, *gradient, isClosed && pointCount > 2); @@ -2631,8 +2653,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, = fCurrentView->CurrentState()->PenLocation(); shapeFrame.OffsetBy(screenOffset); - fCurrentView->ConvertToScreenForDrawing(&screenOffset); - fCurrentView->ConvertToScreenForDrawing(&shapeFrame); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&screenOffset); + transform.Apply(&shapeFrame); drawingEngine->DrawShape(shapeFrame, opCount, opList, ptCount, ptList, code == AS_FILL_SHAPE, screenOffset, @@ -2669,9 +2693,11 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, = fCurrentView->CurrentState()->PenLocation(); shapeFrame.OffsetBy(screenOffset); - fCurrentView->ConvertToScreenForDrawing(&screenOffset); - fCurrentView->ConvertToScreenForDrawing(&shapeFrame); - fCurrentView->ConvertToScreenForDrawing(gradient); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(&screenOffset); + transform.Apply(&shapeFrame); + transform.Apply(gradient); drawingEngine->FillShape(shapeFrame, opCount, opList, ptCount, ptList, *gradient, screenOffset, fCurrentView->Scale()); @@ -2690,7 +2716,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, if (link.ReadRegion(®ion) < B_OK) break; - fCurrentView->ConvertToScreenForDrawing(®ion); + fCurrentView->PenToScreenTransform().Apply(®ion); drawingEngine->FillRegion(region); break; @@ -2707,8 +2733,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, if (link.ReadGradient(&gradient) != B_OK) break; - fCurrentView->ConvertToScreenForDrawing(®ion); - fCurrentView->ConvertToScreenForDrawing(gradient); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); + transform.Apply(®ion); + transform.Apply(gradient); drawingEngine->FillRegion(region, *gradient); delete gradient; break; @@ -2747,11 +2775,11 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, } // Convert to screen coords and draw + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); for (int32 i = 0; i < lineCount; i++) { - fCurrentView->ConvertToScreenForDrawing( - &lineData[i].startPoint); - fCurrentView->ConvertToScreenForDrawing( - &lineData[i].endPoint); + transform.Apply(&lineData[i].startPoint); + transform.Apply(&lineData[i].endPoint); } drawingEngine->StrokeLineArray(lineCount, lineData); @@ -2796,11 +2824,11 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, DTRACE(("ServerWindow %s: Message AS_DRAW_STRING, View: %s " "-> %s\n", Title(), fCurrentView->Name(), string)); - fCurrentView->ConvertToScreenForDrawing(&info.location); + fCurrentView->PenToScreenTransform().Apply(&info.location); BPoint penLocation = drawingEngine->DrawString(string, info.stringLength, info.location, delta); - fCurrentView->ConvertFromScreenForDrawing(&penLocation); + fCurrentView->ScreenToPenTransform().Apply(&penLocation); fCurrentView->CurrentState()->SetPenLocation(penLocation); if (string != stackString) @@ -2853,13 +2881,15 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, DTRACE(("ServerWindow %s: Message AS_DRAW_STRING_WITH_OFFSETS, View: %s " "-> %s\n", Title(), fCurrentView->Name(), string)); + const SimpleTransform transform = + fCurrentView->PenToScreenTransform(); for (int32 i = 0; i < glyphCount; i++) - fCurrentView->ConvertToScreenForDrawing(&locations[i]); + transform.Apply(&locations[i]); BPoint penLocation = drawingEngine->DrawString(string, stringLength, locations); - fCurrentView->ConvertFromScreenForDrawing(&penLocation); + fCurrentView->ScreenToPenTransform().Apply(&penLocation); fCurrentView->CurrentState()->SetPenLocation(penLocation); break; @@ -3703,11 +3733,11 @@ ServerWindow::_UpdateDrawState(View* view) if (view != NULL && drawingEngine != NULL) { BPoint leftTop(0, 0); if (view->GetAlphaMask() != NULL) { - view->ConvertToScreen(&leftTop); + view->LocalToScreenTransform().Apply(&leftTop); view->GetAlphaMask()->Update(view->Bounds(), leftTop); leftTop = BPoint(0, 0); } - view->ConvertToScreenForDrawing(&leftTop); + view->PenToScreenTransform().Apply(&leftTop); drawingEngine->SetDrawState(view->CurrentState(), leftTop.x, leftTop.y); } } diff --git a/src/servers/app/SimpleTransform.h b/src/servers/app/SimpleTransform.h new file mode 100644 index 0000000000..00c8f458a5 --- /dev/null +++ b/src/servers/app/SimpleTransform.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2001-2015, Haiku, Inc. + * Distributed under the terms of the MIT license. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + * Stephan Aßmus + * Adrien Destugues + * Julian Harnath + */ + +#ifndef SIMPLE_TRANSFORM_H +#define SIMPLE_TRANSFORM_H + +#include +#include +#include +#include +#include +#include +#include + +#include "IntPoint.h" +#include "IntRect.h" + + +class SimpleTransform { +public: + SimpleTransform() + : + fScale(1.0) + { + } + + void AddOffset(float x, float y) + { + fOffset.x += x; + fOffset.y += y; + } + + void SetScale(float scale) + { + fScale = scale; + } + + void Apply(BPoint* point) const + { + _Apply(point->x, point->y); + } + + void Apply(IntPoint* point) const + { + _Apply(point->x, point->y); + } + + void Apply(BRect* rect) const + { + if (fScale == 1.0) { + rect->OffsetBy(fOffset.x, fOffset.y); + } else { + _Apply(rect->left, rect->top); + _Apply(rect->right, rect->bottom); + } + } + + void Apply(IntRect* rect) const + { + if (fScale == 1.0) { + rect->OffsetBy(fOffset.x, fOffset.y); + } else { + _Apply(rect->left, rect->top); + _Apply(rect->right, rect->bottom); + } + } + + void Apply(BRegion* region) const + { + if (fScale == 1.0) { + region->OffsetBy(fOffset.x, fOffset.y); + } else { + // TODO: optimize some more + BRegion converted; + int32 count = region->CountRects(); + for (int32 i = 0; i < count; i++) { + BRect r = region->RectAt(i); + BPoint lt(r.LeftTop()); + BPoint rb(r.RightBottom()); + // offset to bottom right corner of pixel before transformation + rb.x++; + rb.y++; + // apply transformation + _Apply(lt.x, lt.y); + _Apply(rb.x, rb.y); + // reset bottom right to pixel "index" + rb.x--; + rb.y--; + // add rect to converted region + // NOTE/TODO: the rect would not have to go + // through the whole intersection test process, + // it is guaranteed not to overlap with any rect + // already contained in the region + converted.Include(BRect(lt, rb)); + } + *region = converted; + } + } + + void Apply(BGradient* gradient) const + { + switch (gradient->GetType()) { + case BGradient::TYPE_LINEAR: + { + BGradientLinear* linear = (BGradientLinear*) gradient; + BPoint start = linear->Start(); + BPoint end = linear->End(); + Apply(&start); + Apply(&end); + linear->SetStart(start); + linear->SetEnd(end); + break; + } + + case BGradient::TYPE_RADIAL: + { + BGradientRadial* radial = (BGradientRadial*) gradient; + BPoint center = radial->Center(); + Apply(¢er); + radial->SetCenter(center); + break; + } + + case BGradient::TYPE_RADIAL_FOCUS: + { + BGradientRadialFocus* radialFocus = + (BGradientRadialFocus*)gradient; + BPoint center = radialFocus->Center(); + BPoint focal = radialFocus->Focal(); + Apply(¢er); + Apply(&focal); + radialFocus->SetCenter(center); + radialFocus->SetFocal(focal); + break; + } + + case BGradient::TYPE_DIAMOND: + { + BGradientDiamond* diamond = (BGradientDiamond*) gradient; + BPoint center = diamond->Center(); + Apply(¢er); + diamond->SetCenter(center); + break; + } + + case BGradient::TYPE_CONIC: + { + BGradientConic* conic = (BGradientConic*) gradient; + BPoint center = conic->Center(); + Apply(¢er); + conic->SetCenter(center); + break; + } + + case BGradient::TYPE_NONE: + { + break; + } + } + + // Make sure the gradient is fully padded so that out of bounds access + // get the correct colors + gradient->SortColorStopsByOffset(); + + BGradient::ColorStop* end = gradient->ColorStopAtFast( + gradient->CountColorStops() - 1); + + if (end->offset != 255) + gradient->AddColor(end->color, 255); + + BGradient::ColorStop* start = gradient->ColorStopAtFast(0); + + if (start->offset != 0) + gradient->AddColor(start->color, 0); + + gradient->SortColorStopsByOffset(); + } + + void Apply(BPoint* destination, const BPoint* source, int32 count) const + { + // TODO: optimize this, it should be smarter + while (count--) { + *destination = *source; + Apply(destination); + source++; + destination++; + } + } + + void Apply(BRect* destination, const BRect* source, int32 count) const + { + // TODO: optimize this, it should be smarter + while (count--) { + *destination = *source; + Apply(destination); + source++; + destination++; + } + } + + void Apply(BRegion* destination, const BRegion* source, int32 count) const + { + // TODO: optimize this, it should be smarter + while (count--) { + *destination = *source; + Apply(destination); + source++; + destination++; + } + } + +private: + void _Apply(int32& x, int32& y) const + { + x *= (int32)fScale; + y *= (int32)fScale; + x += (int32)fOffset.x; + y += (int32)fOffset.y; + } + + void _Apply(float& x, float& y) const + { + x *= fScale; + y *= fScale; + x += fOffset.x; + y += fOffset.y; + } + +private: + BPoint fOffset; + float fScale; +}; + + +#endif // SIMPLE_TRANSFORM_H diff --git a/src/servers/app/View.cpp b/src/servers/app/View.cpp index 7831b45cfb..b1375327a3 100644 --- a/src/servers/app/View.cpp +++ b/src/servers/app/View.cpp @@ -165,7 +165,8 @@ View::ConvertToVisibleInTopView(IntRect* bounds) const { *bounds = *bounds & Bounds(); // NOTE: this step is necessary even if we don't have a parent! - ConvertToParent(bounds); + bounds->OffsetBy(fFrame.left - fScrollingOffset.x, + fFrame.top - fScrollingOffset.y); if (fParent) fParent->ConvertToVisibleInTopView(bounds); @@ -440,7 +441,7 @@ View::ViewAt(const BPoint& where) IntRect frame = Frame(); if (Parent() != NULL) - Parent()->ConvertToScreen(&frame); + Parent()->LocalToScreenTransform().Apply(&frame); if (!frame.Contains(where)) return NULL; @@ -524,7 +525,7 @@ View::_UpdateOverlayView() const return; IntRect destination = fBitmapDestination; - ConvertToScreen(&destination); + LocalToScreenTransform().Apply(&destination); overlay->Configure(fBitmapSource, destination); } @@ -556,201 +557,34 @@ View::UpdateOverlay() void -View::ConvertToParent(BPoint* point) const +View::_LocalToScreenTransform(SimpleTransform& transform) const { - // remove scrolling offset and convert to parent coordinate space - point->x += fFrame.left - fScrollingOffset.x; - point->y += fFrame.top - fScrollingOffset.y; + const View* view = this; + int32 offsetX = 0; + int32 offsetY = 0; + do { + offsetX += view->fFrame.left - view->fScrollingOffset.x; + offsetY += view->fFrame.top - view->fScrollingOffset.y; + view = view->fParent; + } while (view != NULL); + + transform.AddOffset(offsetX, offsetY); } void -View::ConvertToParent(IntPoint* point) const +View::_ScreenToLocalTransform(SimpleTransform& transform) const { - // remove scrolling offset and convert to parent coordinate space - point->x += fFrame.left - fScrollingOffset.x; - point->y += fFrame.top - fScrollingOffset.y; -} + const View* view = this; + int32 offsetX = 0; + int32 offsetY = 0; + do { + offsetX += view->fScrollingOffset.x - view->fFrame.left; + offsetY += view->fScrollingOffset.y - view->fFrame.top; + view = view->fParent; + } while (view != NULL); - -void -View::ConvertToParent(BRect* rect) const -{ - // remove scrolling offset and convert to parent coordinate space - rect->OffsetBy(fFrame.left - fScrollingOffset.x, - fFrame.top - fScrollingOffset.y); -} - - -void -View::ConvertToParent(IntRect* rect) const -{ - // remove scrolling offset and convert to parent coordinate space - rect->OffsetBy(fFrame.left - fScrollingOffset.x, - fFrame.top - fScrollingOffset.y); -} - - -void -View::ConvertToParent(BRegion* region) const -{ - // remove scrolling offset and convert to parent coordinate space - region->OffsetBy(fFrame.left - fScrollingOffset.x, - fFrame.top - fScrollingOffset.y); -} - - -void -View::ConvertFromParent(BPoint* point) const -{ - // convert from parent coordinate space amd add scrolling offset - point->x += fScrollingOffset.x - fFrame.left; - point->y += fScrollingOffset.y - fFrame.top; -} - - -void -View::ConvertFromParent(IntPoint* point) const -{ - // convert from parent coordinate space amd add scrolling offset - point->x += fScrollingOffset.x - fFrame.left; - point->y += fScrollingOffset.y - fFrame.top; -} - - -void -View::ConvertFromParent(BRect* rect) const -{ - // convert from parent coordinate space amd add scrolling offset - rect->OffsetBy(fScrollingOffset.x - fFrame.left, - fScrollingOffset.y - fFrame.top); -} - - -void -View::ConvertFromParent(IntRect* rect) const -{ - // convert from parent coordinate space amd add scrolling offset - rect->OffsetBy(fScrollingOffset.x - fFrame.left, - fScrollingOffset.y - fFrame.top); -} - - -void -View::ConvertFromParent(BRegion* region) const -{ - // convert from parent coordinate space amd add scrolling offset - region->OffsetBy(fScrollingOffset.x - fFrame.left, - fScrollingOffset.y - fFrame.top); -} - -//! converts a point from local to screen coordinate system -void -View::ConvertToScreen(BPoint* pt) const -{ - ConvertToParent(pt); - - if (fParent) - fParent->ConvertToScreen(pt); -} - - -//! converts a point from local to screen coordinate system -void -View::ConvertToScreen(IntPoint* pt) const -{ - ConvertToParent(pt); - - if (fParent) - fParent->ConvertToScreen(pt); -} - - -//! converts a rect from local to screen coordinate system -void -View::ConvertToScreen(BRect* rect) const -{ - BPoint offset(0.0, 0.0); - ConvertToScreen(&offset); - - rect->OffsetBy(offset); -} - - -//! converts a rect from local to screen coordinate system -void -View::ConvertToScreen(IntRect* rect) const -{ - BPoint offset(0.0, 0.0); - ConvertToScreen(&offset); - - rect->OffsetBy(offset); -} - - -//! converts a region from local to screen coordinate system -void -View::ConvertToScreen(BRegion* region) const -{ - BPoint offset(0.0, 0.0); - ConvertToScreen(&offset); - - region->OffsetBy((int)offset.x, (int)offset.y); -} - - -//! converts a point from screen to local coordinate system -void -View::ConvertFromScreen(BPoint* pt) const -{ - ConvertFromParent(pt); - - if (fParent) - fParent->ConvertFromScreen(pt); -} - - -//! converts a point from screen to local coordinate system -void -View::ConvertFromScreen(IntPoint* pt) const -{ - ConvertFromParent(pt); - - if (fParent) - fParent->ConvertFromScreen(pt); -} - - -//! converts a rect from screen to local coordinate system -void -View::ConvertFromScreen(BRect* rect) const -{ - BPoint offset(0.0, 0.0); - ConvertFromScreen(&offset); - - rect->OffsetBy(offset.x, offset.y); -} - - -//! converts a rect from screen to local coordinate system -void -View::ConvertFromScreen(IntRect* rect) const -{ - BPoint offset(0.0, 0.0); - ConvertFromScreen(&offset); - - rect->OffsetBy((int)offset.x, (int)offset.y); -} - - -//! converts a region from screen to local coordinate system -void -View::ConvertFromScreen(BRegion* region) const -{ - BPoint offset(0.0, 0.0); - ConvertFromScreen(&offset); - - region->OffsetBy((int)offset.x, (int)offset.y); + transform.AddOffset(offsetX, offsetY); } @@ -775,7 +609,7 @@ View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion) // local clipping to see which parts need invalidation IntRect oldVisibleBounds(newVisibleBounds); oldVisibleBounds.OffsetBy(-x, -y); - ConvertToScreen(&oldVisibleBounds); + LocalToScreenTransform().Apply(&oldVisibleBounds); ConvertToVisibleInTopView(&newVisibleBounds); @@ -788,7 +622,7 @@ View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion) IntRect oldVisibleBounds(Bounds()); IntRect newVisibleBounds(oldVisibleBounds); oldVisibleBounds.OffsetBy(-x, -y); - ConvertToScreen(&oldVisibleBounds); + LocalToScreenTransform().Apply(&oldVisibleBounds); // NOTE: using ConvertToVisibleInTopView() // instead of ConvertToScreen()! see below @@ -875,7 +709,7 @@ View::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) } } - ConvertToScreen(dirty); + LocalToScreenTransform().Apply(dirty); dirtyRegion->Include(dirty); } fWindow->RecycleRegion(dirty); @@ -1171,7 +1005,7 @@ View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, // draw view bitmap // TODO: support other options! BRect rect = fBitmapDestination; - ConvertToScreenForDrawing(&rect); + PenToScreenTransform().Apply(&rect); align_rect_to_pixels(&rect); @@ -1406,7 +1240,7 @@ View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region, // This check will prevent descending the view hierarchy // any further than necessary IntRect screenBounds(Bounds()); - ConvertToScreen(&screenBounds); + LocalToScreenTransform().Apply(&screenBounds); if (!region.Intersects((clipping_rect)screenBounds)) return; @@ -1537,7 +1371,7 @@ View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const if (fScreenAndUserClipping == NULL) return fScreenClipping; - ConvertToScreen(fScreenAndUserClipping); + LocalToScreenTransform().Apply(fScreenAndUserClipping); fScreenAndUserClipping->IntersectWith( &_ScreenClipping(windowContentClipping, force)); return *fScreenAndUserClipping; @@ -1578,7 +1412,7 @@ View::_ScreenClipping(BRegion* windowContentClipping, bool force) const { if (!fScreenClippingValid || force) { fScreenClipping = fLocalClipping; - ConvertToScreen(&fScreenClipping); + LocalToScreenTransform().Apply(&fScreenClipping); // see if parts of our bounds are hidden underneath // the parent, the local clipping does not account for this diff --git a/src/servers/app/View.h b/src/servers/app/View.h index 7bdc31a78d..5550fcb961 100644 --- a/src/servers/app/View.h +++ b/src/servers/app/View.h @@ -111,31 +111,7 @@ public: View* ViewAt(const BPoint& where); - // coordinate conversion - void ConvertToParent(BPoint* point) const; - void ConvertToParent(IntPoint* point) const; - void ConvertToParent(BRect* rect) const; - void ConvertToParent(IntRect* rect) const; - void ConvertToParent(BRegion* region) const; - - void ConvertFromParent(BPoint* point) const; - void ConvertFromParent(IntPoint* point) const; - void ConvertFromParent(BRect* rect) const; - void ConvertFromParent(IntRect* rect) const; - void ConvertFromParent(BRegion* region) const; - - void ConvertToScreen(BPoint* point) const; - void ConvertToScreen(IntPoint* point) const; - void ConvertToScreen(BRect* rect) const; - void ConvertToScreen(IntRect* rect) const; - void ConvertToScreen(BRegion* region) const; - - void ConvertFromScreen(BPoint* point) const; - void ConvertFromScreen(IntPoint* point) const; - void ConvertFromScreen(BRect* rect) const; - void ConvertFromScreen(IntRect* rect) const; - void ConvertFromScreen(BRegion* region) const; - +public: void MoveBy(int32 dx, int32 dy, BRegion* dirtyRegion); @@ -235,6 +211,11 @@ public: #endif protected: + virtual void _LocalToScreenTransform( + SimpleTransform& transform) const; + virtual void _ScreenToLocalTransform( + SimpleTransform& transform) const; + BRegion& _ScreenClipping(BRegion* windowContentClipping, bool force = false) const; void _MoveScreenClipping(int32 x, int32 y, diff --git a/src/servers/app/Window.cpp b/src/servers/app/Window.cpp index 117e67a564..8af42bd2bd 100644 --- a/src/servers/app/Window.cpp +++ b/src/servers/app/Window.cpp @@ -817,7 +817,7 @@ Window::InvalidateView(View* view, BRegion& viewRegion) if (!fContentRegionValid) _UpdateContentRegion(); - view->ConvertToScreen(&viewRegion); + view->LocalToScreenTransform().Apply(&viewRegion); viewRegion.IntersectWith(&VisibleContentRegion()); if (viewRegion.CountRects() > 0) { viewRegion.IntersectWith( diff --git a/src/servers/app/WorkspacesView.cpp b/src/servers/app/WorkspacesView.cpp index be53497cec..2e4aabac75 100644 --- a/src/servers/app/WorkspacesView.cpp +++ b/src/servers/app/WorkspacesView.cpp @@ -90,7 +90,7 @@ WorkspacesView::_WorkspaceAt(int32 i) _GetGrid(columns, rows); BRect frame = Bounds(); - ConvertToScreen(&frame); + LocalToScreenTransform().Apply(&frame); int32 width = frame.IntegerWidth() / columns; int32 height = frame.IntegerHeight() / rows; @@ -356,7 +356,7 @@ void WorkspacesView::_Invalidate() const { BRect frame = Bounds(); - ConvertToScreen(&frame); + LocalToScreenTransform().Apply(&frame); BRegion region(frame); Window()->MarkContentDirty(region); @@ -385,7 +385,7 @@ WorkspacesView::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, drawingEngine->ConstrainClippingRegion(&gridRegion); BRect frame = Bounds(); - ConvertToScreen(&frame); + LocalToScreenTransform().Apply(&frame); // horizontal lines From ad53a0d999135776e664131cba0321bd1ce9e729 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 6 Apr 2015 14:44:11 +0200 Subject: [PATCH 03/29] app_server: add unit test add-on, test for SimpleTransform * app_server currently does not have any real unit tests, making changes more difficult and riskier. A new directory unit_tests with a test add-on is added in app_server's tetsts directory to hold future unit tests. * Add test for SimpleTransform class --- src/tests/servers/app/Jamfile | 1 + .../app/unit_tests/AppServerUnitTestAddOn.cpp | 20 ++ src/tests/servers/app/unit_tests/Jamfile | 15 + .../app/unit_tests/SimpleTransformTest.cpp | 269 ++++++++++++++++++ .../app/unit_tests/SimpleTransformTest.h | 36 +++ 5 files changed, 341 insertions(+) create mode 100644 src/tests/servers/app/unit_tests/AppServerUnitTestAddOn.cpp create mode 100644 src/tests/servers/app/unit_tests/Jamfile create mode 100644 src/tests/servers/app/unit_tests/SimpleTransformTest.cpp create mode 100644 src/tests/servers/app/unit_tests/SimpleTransformTest.h diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index 0a5daf047e..f3e3ac340d 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -249,6 +249,7 @@ SubInclude HAIKU_TOP src tests servers app statusbar ; SubInclude HAIKU_TOP src tests servers app stress_test ; SubInclude HAIKU_TOP src tests servers app textview ; SubInclude HAIKU_TOP src tests servers app transformation ; +SubInclude HAIKU_TOP src tests servers app unit_tests ; SubInclude HAIKU_TOP src tests servers app view_state ; SubInclude HAIKU_TOP src tests servers app view_transit ; SubInclude HAIKU_TOP src tests servers app window_creation ; diff --git a/src/tests/servers/app/unit_tests/AppServerUnitTestAddOn.cpp b/src/tests/servers/app/unit_tests/AppServerUnitTestAddOn.cpp new file mode 100644 index 0000000000..2044db51de --- /dev/null +++ b/src/tests/servers/app/unit_tests/AppServerUnitTestAddOn.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include +#include + +#include "SimpleTransformTest.h" + + +BTestSuite* +getTestSuite() +{ + BTestSuite* suite = new BTestSuite("AppServerUnitTests"); + + SimpleTransformTest::AddTests(*suite); + + return suite; +} diff --git a/src/tests/servers/app/unit_tests/Jamfile b/src/tests/servers/app/unit_tests/Jamfile new file mode 100644 index 0000000000..89fbc1b0fd --- /dev/null +++ b/src/tests/servers/app/unit_tests/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src tests servers app unit_tests ; + +UseHeaders [ FDirName $(HAIKU_TOP) src servers app ] : true ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers app ] ; + +UnitTestLib app_server_unit_tests.so : + AppServerUnitTestAddOn.cpp + + IntPoint.cpp + IntRect.cpp + SimpleTransformTest.cpp + + : be [ TargetLibstdc++ ] + ; diff --git a/src/tests/servers/app/unit_tests/SimpleTransformTest.cpp b/src/tests/servers/app/unit_tests/SimpleTransformTest.cpp new file mode 100644 index 0000000000..db4872b9f3 --- /dev/null +++ b/src/tests/servers/app/unit_tests/SimpleTransformTest.cpp @@ -0,0 +1,269 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "SimpleTransformTest.h" + +#include "IntPoint.h" +#include "IntRect.h" + +#include +#include + + +void +SimpleTransformTest::TransformPoint() +{ + BPoint point(1.0, 1.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 10.0); + specimen.Apply(&point); + CPPUNIT_ASSERT(point == BPoint(11.0, 11.0)); + + specimen.AddOffset(10.0, 10.0); + point.Set(0.0, 0.0); + specimen.Apply(&point); + CPPUNIT_ASSERT(point == BPoint(20.0, 20.0)); +} + + +void +SimpleTransformTest::TransformIntPoint() +{ + IntPoint point(1, 1); + SimpleTransform specimen; + specimen.AddOffset(10.0, 10.0); + specimen.Apply(&point); + CPPUNIT_ASSERT(point == BPoint(11.0, 11.0)); +} + + +void +SimpleTransformTest::TransformRect() +{ + BRect rect(5.0, 10.0, 15.0, 20.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 10.0); + specimen.Apply(&rect); + CPPUNIT_ASSERT(rect == BRect(15.0, 20.0, 25.0, 30.0)); + + specimen.SetScale(2.5); + specimen.Apply(&rect); + CPPUNIT_ASSERT(rect == BRect(47.5, 60.0, 72.5, 85.0)); +} + + +void +SimpleTransformTest::TransformIntRect() +{ + IntRect rect(5, 10, 15, 20); + SimpleTransform specimen; + specimen.AddOffset(10.0, 10.0); + specimen.Apply(&rect); + CPPUNIT_ASSERT(rect == IntRect(15, 20, 25, 30)); + + specimen.SetScale(2); + specimen.Apply(&rect); + CPPUNIT_ASSERT(rect == BRect(40, 50, 60, 70)); +} + + +void +SimpleTransformTest::TransformRegion() +{ + BRegion region; + region.Include(BRect( 5.0, 5.0, 20.0, 20.0)); + region.Include(BRect(10.0, 10.0, 30.0, 30.0)); + region.Exclude(BRect(10.0, 20.0, 20.0, 25.0)); + + BRegion reference1 = region; + reference1.OffsetBy(10, 20); + + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.Apply(®ion); + CPPUNIT_ASSERT(region == reference1); + + specimen.SetScale(2.5); + + BRegion reference2; + reference2.Include(BRect(47.0, 82.0, 87.0, 122.0)); + reference2.Include(BRect(60.0, 95.0, 112.0, 147.0)); + reference2.Exclude(BRect(60.0, 120.0, 86.0, 134.0)); + + specimen.Apply(®ion); + CPPUNIT_ASSERT(region == reference2); +} + + +void +SimpleTransformTest::TransformGradientLinear() +{ + BGradientLinear gradient(10.0, 20.0, 30.0, 40.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&gradient); + CPPUNIT_ASSERT(gradient.Start() == BPoint(35.0, 70.0)); + CPPUNIT_ASSERT(gradient.End() == BPoint(85.0, 120.0)); +} + + +void +SimpleTransformTest::TransformGradientRadial() +{ + BGradientRadial gradient(10.0, 20.0, 10.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&gradient); + CPPUNIT_ASSERT(gradient.Center() == BPoint(35.0, 70.0)); +} + + +void +SimpleTransformTest::TransformGradientRadialFocus() +{ + BGradientRadialFocus gradient(10.0, 20.0, 10.0, 30.0, 40.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&gradient); + CPPUNIT_ASSERT(gradient.Center() == BPoint(35.0, 70.0)); + CPPUNIT_ASSERT(gradient.Focal() == BPoint(85.0, 120.0)); +} + + +void +SimpleTransformTest::TransformGradientDiamond() +{ + BGradientDiamond gradient(10.0, 20.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&gradient); + CPPUNIT_ASSERT(gradient.Center() == BPoint(35.0, 70.0)); +} + + +void +SimpleTransformTest::TransformGradientConic() +{ + BGradientConic gradient(10.0, 20.0, 10.0); + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&gradient); + CPPUNIT_ASSERT(gradient.Center() == BPoint(35.0, 70.0)); +} + + +void +SimpleTransformTest::TransformPointArray() +{ + BPoint points[3]; + points[0].Set(10.0, 20.0); + points[1].Set(30.0, 40.0); + points[2].Set(50.0, 60.0); + BPoint transformedPoints[3]; + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.Apply(&transformedPoints[0], &points[0], 3); + CPPUNIT_ASSERT(transformedPoints[0] == BPoint(20.0, 40.0)); + CPPUNIT_ASSERT(transformedPoints[1] == BPoint(40.0, 60.0)); + CPPUNIT_ASSERT(transformedPoints[2] == BPoint(60.0, 80.0)); +} + + +void +SimpleTransformTest::TransformRectArray() +{ + BRect rects[3]; + rects[0].Set( 5.0, 10.0, 15.0, 20.0); + rects[1].Set(15.0, 20.0, 25.0, 30.0); + rects[2].Set(25.0, 30.0, 35.0, 40.0); + BRect transformedRects[3]; + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&transformedRects[0], &rects[0], 3); + CPPUNIT_ASSERT(transformedRects[0] == BRect(22.5, 45.0, 47.5, 70.0)); + CPPUNIT_ASSERT(transformedRects[1] == BRect(47.5, 70.0, 72.5, 95.0)); + CPPUNIT_ASSERT(transformedRects[2] == BRect(72.5, 95.0, 97.5, 120.0)); +} + + +void +SimpleTransformTest::TransformRegionArray() +{ + BRegion regions[2]; + regions[0].Include(BRect( 5.0, 5.0, 20.0, 20.0)); + regions[0].Include(BRect(10.0, 10.0, 30.0, 30.0)); + regions[0].Exclude(BRect(10.0, 20.0, 20.0, 25.0)); + regions[1].Include(BRect( 5.0, 5.0, 20.0, 20.0)); + regions[1].Include(BRect(10.0, 10.0, 30.0, 30.0)); + regions[1].Exclude(BRect(10.0, 20.0, 20.0, 25.0)); + BRegion transformedRegions[3]; + SimpleTransform specimen; + specimen.AddOffset(10.0, 20.0); + specimen.SetScale(2.5); + specimen.Apply(&transformedRegions[0], ®ions[0], 2); + BRegion reference; + reference.Include(BRect(22.0, 32.0, 62.0, 72.0)); + reference.Include(BRect(35.0, 45.0, 87.0, 97.0)); + reference.Exclude(BRect(35.0, 70.0, 61.0, 84.0)); + + CPPUNIT_ASSERT(transformedRegions[0] == reference); + CPPUNIT_ASSERT(transformedRegions[1] == reference); +} + + +/* static */ void +SimpleTransformTest::AddTests(BTestSuite& parent) +{ + CppUnit::TestSuite* const suite = new CppUnit::TestSuite( + "SimpleTransformTest"); + + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformPoint", + &SimpleTransformTest::TransformPoint)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformIntPoint", + &SimpleTransformTest::TransformIntPoint)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformRect", + &SimpleTransformTest::TransformRect)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformIntRect", + &SimpleTransformTest::TransformIntRect)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformRegion", + &SimpleTransformTest::TransformRegion)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformGradientLinear", + &SimpleTransformTest::TransformGradientLinear)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformGradientRadial", + &SimpleTransformTest::TransformGradientRadial)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformGradientRadialFocus", + &SimpleTransformTest::TransformGradientRadialFocus)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformGradientDiamond", + &SimpleTransformTest::TransformGradientDiamond)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformGradientConic", + &SimpleTransformTest::TransformGradientConic)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformPointArray", + &SimpleTransformTest::TransformPointArray)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformRectArray", + &SimpleTransformTest::TransformRectArray)); + suite->addTest(new CppUnit::TestCaller( + "SimpleTransformTest::TransformRegionArray", + &SimpleTransformTest::TransformRegionArray)); + + parent.addTest("SimpleTransformTest", suite); +} diff --git a/src/tests/servers/app/unit_tests/SimpleTransformTest.h b/src/tests/servers/app/unit_tests/SimpleTransformTest.h new file mode 100644 index 0000000000..89a889fd46 --- /dev/null +++ b/src/tests/servers/app/unit_tests/SimpleTransformTest.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef SIMPLE_TRANSFORM_TEST_H +#define SIMPLE_TRANSFORM_TEST_H + +#include +#include + +#include "SimpleTransform.h" + + +class SimpleTransformTest : public BTestCase { +public: + static void AddTests(BTestSuite& parent); + + void TransformPoint(); + void TransformIntPoint(); + void TransformRect(); + void TransformIntRect(); + void TransformRegion(); + + void TransformGradientLinear(); + void TransformGradientRadial(); + void TransformGradientRadialFocus(); + void TransformGradientDiamond(); + void TransformGradientConic(); + + void TransformPointArray(); + void TransformRectArray(); + void TransformRegionArray(); +}; + + +#endif // SIMPLE_TRANSFORM_TEST_H From ccaee9e818cc4ef94f313aa47a0b6c18ff580f57 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 8 Jul 2015 20:07:51 +0200 Subject: [PATCH 04/29] app_server: add Squash method in DrawState * New method DrawState::Squash() uses the combined scale, origin and transform as the state's own scale, origin, transform. This can be used when making copies of DrawStates which have previous states below them in the state stack. The top of stack DrawState can be copied, squashed and then used just like the original one with the whole stack below it (except of course when trying to pop off any earlier state). --- src/servers/app/DrawState.cpp | 13 +++++++++++++ src/servers/app/DrawState.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index d7f88bc922..9621cfa68e 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -370,6 +370,19 @@ DrawState::SetTransform(BAffineTransform transform) } +DrawState* +DrawState::Squash() +{ + DrawState* const squashedState = new DrawState(*this); + + squashedState->fOrigin = fCombinedOrigin; + squashedState->fScale = fCombinedScale; + squashedState->fTransform = fCombinedTransform; + + return squashedState; +} + + void DrawState::SetClippingRegion(const BRegion* region) { diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index 392549789f..48a6f371c6 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -69,6 +69,8 @@ public: BAffineTransform CombinedTransform() const { return fCombinedTransform; } + DrawState* Squash(); + // additional clipping as requested by client void SetClippingRegion(const BRegion* region); From 1b4dba929dd35c7aeaa92673efd63dbfc665c8e0 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 8 Jul 2015 20:17:04 +0200 Subject: [PATCH 05/29] app_server: add picture player for determining bounding box * Add PictureBoundingBoxPlayer, a new player for BPictures. Instead of drawing the picture, it determines an approximate bounding box of its contained drawing operations. * To increase performance, the resulting bounding box is an approximation: it guarantees to always enclose all pixels of the picture, however not necessarily tightly. * PictureBoundingBoxPlayer::Play() gets a DrawState which is the initial state used when playing the picture. The player does not modify this state (it uses a copy internally), so the method is idempotent. --- src/servers/app/Jamfile | 1 + src/servers/app/PictureBoundingBoxPlayer.cpp | 778 +++++++++++++++++++ src/servers/app/PictureBoundingBoxPlayer.h | 28 + 3 files changed, 807 insertions(+) create mode 100644 src/servers/app/PictureBoundingBoxPlayer.cpp create mode 100644 src/servers/app/PictureBoundingBoxPlayer.h diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index aebc6c8580..fdaea83c7e 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -71,6 +71,7 @@ Server app_server : MultiLocker.cpp OffscreenServerWindow.cpp OffscreenWindow.cpp + PictureBoundingBoxPlayer.cpp ProfileMessageSupport.cpp RGBColor.cpp RegionPool.cpp diff --git a/src/servers/app/PictureBoundingBoxPlayer.cpp b/src/servers/app/PictureBoundingBoxPlayer.cpp new file mode 100644 index 0000000000..08ff0f7f0d --- /dev/null +++ b/src/servers/app/PictureBoundingBoxPlayer.cpp @@ -0,0 +1,778 @@ +/* + * Copyright 2001-2015, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marc Flerackers (mflerackers@androme.be) + * Stefano Ceccherini (stefano.ceccherini@gmail.com) + * Marcus Overhagen + * Julian Harnath + */ + +#include "PictureBoundingBoxPlayer.h" + +#include +#include + +#include "DrawState.h" +#include "FontManager.h" +#include "ServerApp.h" +#include "ServerBitmap.h" +#include "ServerFont.h" +#include "ServerPicture.h" +#include "ServerTokenSpace.h" +#include "View.h" +#include "Window.h" + +#include +#include +#include +#include +#include +#include +#include + + +//#define DEBUG_TRACE_BB +#ifdef DEBUG_TRACE_BB +# define TRACE_BB(text, ...) debug_printf("PBBP: " text, ##__VA_ARGS__) +#else +# define TRACE_BB(text, ...) +#endif + + +typedef PictureBoundingBoxPlayer::State BoundingBoxState; + + +// #pragma mark - PictureBoundingBoxPlayer::State + + +class PictureBoundingBoxPlayer::State { +public: + State(DrawState* drawState, BRect* boundingBox) + : + fDrawState(drawState->Squash()), + fBoundingBox(boundingBox) + { + fBoundingBox->Set(INT_MAX, INT_MAX, 0, 0); + } + + ~State() + { + delete fDrawState; + } + + DrawState* GetDrawState() + { + return fDrawState; + } + + void PushDrawState() + { + DrawState* nextState = fDrawState->PushState(); + if (nextState != NULL) + fDrawState = nextState; + } + + void PopDrawState() + { + if (fDrawState->PreviousState() != NULL) + fDrawState = fDrawState->PopState(); + } + + SimpleTransform PenToLocalTransform() const + { + SimpleTransform transform; + fDrawState->Transform(transform); + return transform; + } + + void IncludeRect(BRect& rect) + { + _AffineTransformRect(rect); + *fBoundingBox = (*fBoundingBox) | rect; + } + +private: + void _AffineTransformRect(BRect& rect) + { + BAffineTransform transform = fDrawState->Transform(); + if (transform.IsIdentity()) + return; + + BPoint transformedShape[4]; + transformedShape[0] = rect.LeftTop(); + transformedShape[1] = rect.LeftBottom(); + transformedShape[2] = rect.RightTop(); + transformedShape[3] = rect.RightBottom(); + + transform.Apply(&transformedShape[0], 4); + + float minX = INT_MAX; + float minY = INT_MAX; + float maxX = 0; + float maxY = 0; + + for (uint32 i = 0; i < 4; i++) { + if (transformedShape[i].x < minX) + minX = transformedShape[i].x; + else if (transformedShape[i].x > maxX) + maxX = transformedShape[i].x; + if (transformedShape[i].y < minY) + minY = transformedShape[i].y; + else if (transformedShape[i].y > maxY) + maxY = transformedShape[i].y; + } + + rect.Set(minX, minY, maxX, maxY); + } + + +private: + DrawState* fDrawState; + BRect* fBoundingBox; +}; + + +// #pragma mark - Picture playback hooks + + +static void +get_polygon_frame(const BPoint* points, int32 numPoints, BRect* frame) +{ + ASSERT(numPoints > 0); + + float left = points->x; + float top = points->y; + float right = left; + float bottom = top; + + points++; + numPoints--; + + while (numPoints--) { + if (points->x < left) + left = points->x; + if (points->x > right) + right = points->x; + if (points->y < top) + top = points->y; + if (points->y > bottom) + bottom = points->y; + points++; + } + + frame->Set(left, top, right, bottom); +} + + +template +static void +expand_rect_for_pen_size(BoundingBoxState* state, RectType& rect) +{ + float penInset = -((state->GetDrawState()->PenSize() / 2.0f) + 1.0f); + rect.InsetBy(penInset, penInset); +} + + +static void +nop() +{ +} + + +static void +move_pen_by(BoundingBoxState* state, BPoint delta) +{ + TRACE_BB("%p move pen by %.2f %.2f\n", state, delta.x, delta.y); + + state->GetDrawState()->SetPenLocation( + state->GetDrawState()->PenLocation() + delta); +} + + +static void +determine_bounds_stroke_line(BoundingBoxState* state, BPoint start, BPoint end) +{ + TRACE_BB("%p stroke line %.2f %.2f -> %.2f %.2f\n", state, + start.x, start.y, end.x, end.y); + + BPoint penPos = end; + + const SimpleTransform transform = state->PenToLocalTransform(); + transform.Apply(&start); + transform.Apply(&end); + + BRect rect; + if (start.x <= end.x) { + rect.left = start.x; + rect.right = end.x; + } else { + rect.left = end.x; + rect.right = start.x; + } + if (start.y <= end.y) { + rect.top = start.y; + rect.bottom = end.y; + } else { + rect.top = end.y; + rect.bottom = start.y; + } + + expand_rect_for_pen_size(state, rect); + state->IncludeRect(rect); + + state->GetDrawState()->SetPenLocation(penPos); +} + + +static void +determine_bounds_stroke_rect(BoundingBoxState* state, BRect rect) +{ + TRACE_BB("%p stroke rect %.2f %.2f %.2f %.2f\n", state, + rect.left, rect.top, rect.right, rect.bottom); + + state->PenToLocalTransform().Apply(&rect); + expand_rect_for_pen_size(state, rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_fill_rect(BoundingBoxState* state, BRect rect) +{ + TRACE_BB("%p fill rect %.2f %.2f %.2f %.2f\n", state, + rect.left, rect.top, rect.right, rect.bottom); + + state->PenToLocalTransform().Apply(&rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_bezier(BoundingBoxState* state, const BPoint* viewPoints, + BRect& outRect) +{ + // Note: this is an approximation which results in a rectangle which + // encloses all four control points. That will always enclose the curve, + // although not necessarily tightly, but it's good enough for the purpose. + // The exact bounding box of a bezier curve is not trivial to determine, + // (need to calculate derivative of the curve) and we're going for + // performance here. + BPoint points[4]; + state->PenToLocalTransform().Apply(points, viewPoints, 4); + BPoint topLeft = points[0]; + BPoint bottomRight = points[0]; + for (uint32 index = 1; index < 4; index++) { + if (points[index].x < topLeft.x || points[index].y < topLeft.y) + topLeft = points[index]; + if (points[index].x > topLeft.x || points[index].y > topLeft.y) + bottomRight = points[index]; + } + outRect.SetLeftTop(topLeft); + outRect.SetRightBottom(bottomRight); +} + + +static void +determine_bounds_stroke_bezier(BoundingBoxState* state, const BPoint* viewPoints) +{ + TRACE_BB("%p stroke bezier (%.2f %.2f) (%.2f %.2f) (%.2f %.2f) (%.2f %.2f)\n", + state, + viewPoints[0].x, viewPoints[0].y, + viewPoints[1].x, viewPoints[1].y, + viewPoints[2].x, viewPoints[2].y, + viewPoints[3].x, viewPoints[3].y); + + BRect rect; + determine_bounds_bezier(state, viewPoints, rect); + expand_rect_for_pen_size(state, rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_fill_bezier(BoundingBoxState* state, const BPoint* viewPoints) +{ + TRACE_BB("%p fill bezier (%.2f %.2f) (%.2f %.2f) (%.2f %.2f) (%.2f %.2f)\n", + state, + viewPoints[0].x, viewPoints[0].y, + viewPoints[1].x, viewPoints[1].y, + viewPoints[2].x, viewPoints[2].y, + viewPoints[3].x, viewPoints[3].y); + + BRect rect; + determine_bounds_bezier(state, viewPoints, rect); + state->IncludeRect(rect); +} + + + +static void +determine_bounds_stroke_ellipse(BoundingBoxState* state, BPoint center, BPoint radii) +{ + TRACE_BB("%p stroke ellipse (%.2f %.2f) (%.2f %.2f)\n", state, + center.x, center.y, radii.x, radii.y); + + BRect rect(center.x - radii.x, center.y - radii.y, + center.x + radii.x - 1, center.y + radii.y - 1); + state->PenToLocalTransform().Apply(&rect); + expand_rect_for_pen_size(state, rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_fill_ellipse(BoundingBoxState* state, BPoint center, BPoint radii) +{ + TRACE_BB("%p fill ellipse (%.2f %.2f) (%.2f %.2f)\n", state, + center.x, center.y, radii.x, radii.y); + + BRect rect(center.x - radii.x, center.y - radii.y, + center.x + radii.x - 1, center.y + radii.y - 1); + + TRACE_BB(" --> (%.2f %.2f %.2f %.2f)\n", + rect.left, rect.top, rect.right, rect.bottom); + + state->PenToLocalTransform().Apply(&rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_polygon(BoundingBoxState* state, int32 numPoints, + const BPoint* viewPoints, BRect& outRect) +{ + if (numPoints <= 0) + return; + + if (numPoints <= 200) { + // fast path: no malloc/free, also avoid + // constructor/destructor calls + char data[200 * sizeof(BPoint)]; + BPoint* points = (BPoint*)data; + + state->PenToLocalTransform().Apply(points, viewPoints, numPoints); + get_polygon_frame(points, numPoints, &outRect); + + } else { + // avoid constructor/destructor calls by + // using malloc instead of new [] + BPoint* points = (BPoint*)malloc(numPoints * sizeof(BPoint)); + if (points == NULL) + return; + + state->PenToLocalTransform().Apply(points, viewPoints, numPoints); + get_polygon_frame(points, numPoints, &outRect); + + free(points); + } +} + + +void +determine_bounds_stroke_polygon(BoundingBoxState* state, int32 numPoints, + const BPoint* viewPoints, bool) +{ + TRACE_BB("%p stroke polygon (%ld points)\n", state, numPoints); + + BRect rect; + determine_bounds_polygon(state, numPoints, viewPoints, rect); + expand_rect_for_pen_size(state, rect); + state->IncludeRect(rect); +} + + +void +determine_bounds_fill_polygon(BoundingBoxState* state, int32 numPoints, + const BPoint* viewPoints, bool) +{ + TRACE_BB("%p fill polygon (%ld points)\n", state, numPoints); + + BRect rect; + determine_bounds_polygon(state, numPoints, viewPoints, rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_stroke_shape(BoundingBoxState* state, const BShape* shape) +{ + BRect rect = shape->Bounds(); + + TRACE_BB("%p stroke shape (bounds %.2f %.2f %.2f %.2f)\n", state, + rect.left, rect.top, rect.right, rect.bottom); + + state->PenToLocalTransform().Apply(&rect); + expand_rect_for_pen_size(state, rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_fill_shape(BoundingBoxState* state, const BShape* shape) +{ + BRect rect = shape->Bounds(); + + TRACE_BB("%p fill shape (bounds %.2f %.2f %.2f %.2f)\n", state, + rect.left, rect.top, rect.right, rect.bottom); + + state->PenToLocalTransform().Apply(&rect); + state->IncludeRect(rect); +} + + +static void +determine_bounds_string(BoundingBoxState* state, const char* string, float deltaSpace, + float deltaNonSpace) +{ + TRACE_BB("%p string '%s'\n", state, string); + + ServerFont font = state->GetDrawState()->Font(); + + escapement_delta delta = { deltaSpace, deltaNonSpace }; + BRect rect; + int32 length = strlen(string); + font.GetBoundingBoxesForStrings((char**)&string, &length, 1, &rect, + B_SCREEN_METRIC, &delta); + + BPoint location = state->GetDrawState()->PenLocation(); + + state->PenToLocalTransform().Apply(&location); + rect.OffsetBy(location); + state->IncludeRect(rect); + + state->PenToLocalTransform().Apply(&location); + state->GetDrawState()->SetPenLocation(location); +} + + +static void +determine_bounds_pixels(BoundingBoxState* state, BRect, BRect dest, int32 w, int32 h, + int32 bpr, int32 pf, int32, const void*) +{ + TRACE_BB("%p pixels (dest %.2f %.2f %.2f %.2f) w=%ld h=%ld bpr=%ld pf=%ld\n", state, + dest.left, dest.top, dest.right, dest.bottom, + w, h, bpr, pf); + // TODO remove params + + state->PenToLocalTransform().Apply(&dest); + state->IncludeRect(dest); +} + + +static void +draw_picture(BoundingBoxState* state, BPoint where, int32 token) +{ + TRACE_BB("%p picture (unimplemented)\n", state); + + // TODO + (void)state; + (void)where; + (void)token; +} + + +static void +set_clipping_rects(BoundingBoxState* state, const BRect* rects, + uint32 numRects) +{ + TRACE_BB("%p cliping rects (%ld rects)\n", state, numRects); + + // TODO + (void)state; + (void)rects; + (void)numRects; +} + + +static void +clip_to_picture(BoundingBoxState* state, BPicture* picture, BPoint pt, + bool clipToInverse) +{ + TRACE_BB("%p clip to picture (unimplemented)\n", state); + + // TODO + printf("ClipToPicture(picture, BPoint(%.2f, %.2f), %s)\n", + pt.x, pt.y, clipToInverse ? "inverse" : ""); +} + + +static void +push_state(BoundingBoxState* state) +{ + TRACE_BB("%p push state\n", state); + state->PushDrawState(); +} + + +static void +pop_state(BoundingBoxState* state) +{ + TRACE_BB("%p pop state\n", state); + state->PopDrawState(); +} + + +static void +enter_state_change(BoundingBoxState*) +{ +} + + +static void +exit_state_change(BoundingBoxState*) +{ +} + + +static void +enter_font_state(BoundingBoxState*) +{ +} + + +static void +exit_font_state(BoundingBoxState*) +{ +} + + +static void +set_origin(BoundingBoxState* state, BPoint pt) +{ + TRACE_BB("%p set origin %.2f %.2f\n", state, pt.x, pt.y); + state->GetDrawState()->SetOrigin(pt); +} + + +static void +set_pen_location(BoundingBoxState* state, BPoint pt) +{ + TRACE_BB("%p set pen location %.2f %.2f\n", state, pt.x, pt.y); + state->GetDrawState()->SetPenLocation(pt); +} + + +static void +set_drawing_mode(BoundingBoxState*, drawing_mode) +{ +} + + +static void +set_line_mode(BoundingBoxState* state, cap_mode capMode, join_mode joinMode, + float miterLimit) +{ + DrawState* drawState = state->GetDrawState(); + drawState->SetLineCapMode(capMode); + drawState->SetLineJoinMode(joinMode); + drawState->SetMiterLimit(miterLimit); +} + + +static void +set_pen_size(BoundingBoxState* state, float size) +{ + TRACE_BB("%p set pen size %.2f\n", state, size); + state->GetDrawState()->SetPenSize(size); +} + + +static void +set_fore_color(BoundingBoxState* state, rgb_color color) +{ + state->GetDrawState()->SetHighColor(color); +} + + +static void +set_back_color(BoundingBoxState* state, rgb_color color) +{ + state->GetDrawState()->SetLowColor(color); +} + + +static void +set_stipple_pattern(BoundingBoxState* state, pattern p) +{ + state->GetDrawState()->SetPattern(Pattern(p)); +} + + +static void +set_scale(BoundingBoxState* state, float scale) +{ + state->GetDrawState()->SetScale(scale); +} + + +static void +set_font_family(BoundingBoxState* state, const char* family) +{ + FontStyle* fontStyle = gFontManager->GetStyleByIndex(family, 0); + ServerFont font; + font.SetStyle(fontStyle); + state->GetDrawState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); +} + + +static void +set_font_style(BoundingBoxState* state, const char* style) +{ + ServerFont font(state->GetDrawState()->Font()); + FontStyle* fontStyle = gFontManager->GetStyle(font.Family(), style); + font.SetStyle(fontStyle); + state->GetDrawState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); +} + + +static void +set_font_spacing(BoundingBoxState* state, int32 spacing) +{ + ServerFont font; + font.SetSpacing(spacing); + state->GetDrawState()->SetFont(font, B_FONT_SPACING); +} + + +static void +set_font_size(BoundingBoxState* state, float size) +{ + ServerFont font; + font.SetSize(size); + state->GetDrawState()->SetFont(font, B_FONT_SIZE); +} + + +static void +set_font_rotate(BoundingBoxState* state, float rotation) +{ + ServerFont font; + font.SetRotation(rotation); + state->GetDrawState()->SetFont(font, B_FONT_ROTATION); +} + + +static void +set_font_encoding(BoundingBoxState* state, int32 encoding) +{ + ServerFont font; + font.SetEncoding(encoding); + state->GetDrawState()->SetFont(font, B_FONT_ENCODING); +} + + +static void +set_font_flags(BoundingBoxState* state, int32 flags) +{ + ServerFont font; + font.SetFlags(flags); + state->GetDrawState()->SetFont(font, B_FONT_FLAGS); +} + + +static void +set_font_shear(BoundingBoxState* state, float shear) +{ + ServerFont font; + font.SetShear(shear); + state->GetDrawState()->SetFont(font, B_FONT_SHEAR); +} + + +static void +set_font_face(BoundingBoxState* state, int32 face) +{ + ServerFont font; + font.SetFace(face); + state->GetDrawState()->SetFont(font, B_FONT_FACE); +} + + +static void +set_blending_mode(BoundingBoxState*, int16, int16) +{ +} + + +static void +set_transform(BoundingBoxState* state, BAffineTransform transform) +{ + TRACE_BB("%p transform\n", state); + state->GetDrawState()->SetTransform(transform); +} + + +const static void* kTableEntries[] = { + (const void*)nop, // 0 + (const void*)move_pen_by, + (const void*)determine_bounds_stroke_line, + (const void*)determine_bounds_stroke_rect, + (const void*)determine_bounds_fill_rect, + (const void*)determine_bounds_stroke_rect, // 5 + (const void*)determine_bounds_fill_rect, + (const void*)determine_bounds_stroke_bezier, + (const void*)determine_bounds_fill_bezier, + (const void*)determine_bounds_stroke_ellipse, + (const void*)determine_bounds_fill_ellipse, // 10 + (const void*)determine_bounds_stroke_ellipse, + (const void*)determine_bounds_fill_ellipse, + (const void*)determine_bounds_stroke_polygon, + (const void*)determine_bounds_fill_polygon, + (const void*)determine_bounds_stroke_shape, // 15 + (const void*)determine_bounds_fill_shape, + (const void*)determine_bounds_string, + (const void*)determine_bounds_pixels, + (const void*)draw_picture, + (const void*)set_clipping_rects, // 20 + (const void*)clip_to_picture, + (const void*)push_state, + (const void*)pop_state, + (const void*)enter_state_change, + (const void*)exit_state_change, // 25 + (const void*)enter_font_state, + (const void*)exit_font_state, + (const void*)set_origin, + (const void*)set_pen_location, + (const void*)set_drawing_mode, // 30 + (const void*)set_line_mode, + (const void*)set_pen_size, + (const void*)set_fore_color, + (const void*)set_back_color, + (const void*)set_stipple_pattern, // 35 + (const void*)set_scale, + (const void*)set_font_family, + (const void*)set_font_style, + (const void*)set_font_spacing, + (const void*)set_font_size, // 40 + (const void*)set_font_rotate, + (const void*)set_font_encoding, + (const void*)set_font_flags, + (const void*)set_font_shear, + (const void*)nop, // 45 + (const void*)set_font_face, + (const void*)set_blending_mode, + (const void*)set_transform // 48 +}; + + +// #pragma mark - PictureBoundingBoxPlayer + + +/* static */ void +PictureBoundingBoxPlayer::Play(ServerPicture* picture, + DrawState* drawState, BRect* outBoundingBox) +{ + State state(drawState, outBoundingBox); + + BMallocIO* mallocIO = dynamic_cast(picture->fData); + if (mallocIO == NULL) + return; + + BPrivate::PicturePlayer player(mallocIO->Buffer(), + mallocIO->BufferLength(), ServerPicture::PictureList::Private( + picture->fPictures).AsBList()); + player.Play(const_cast(kTableEntries), + sizeof(kTableEntries) / sizeof(void*), &state); +} diff --git a/src/servers/app/PictureBoundingBoxPlayer.h b/src/servers/app/PictureBoundingBoxPlayer.h new file mode 100644 index 0000000000..37ae88e2c3 --- /dev/null +++ b/src/servers/app/PictureBoundingBoxPlayer.h @@ -0,0 +1,28 @@ +/* + * Copyright 2015, Haiku. + * Distributed under the terms of the MIT license. + * + * Authors: + * Julian Harnath + */ +#ifndef PICTURE_BOUNDING_BOX_H +#define PICTURE_BOUNDING_BOX_H + + +class BRect; +class DrawState; +class ServerPicture; + + +class PictureBoundingBoxPlayer { +public: + class State; + +public: + static void Play(ServerPicture* picture, + DrawState* drawState, + BRect* outBoundingBox); +}; + + +#endif // PICTURE_BOUNDING_BOX_H From 8511f6ac9b4db63e490538fa7d0248ff0e0d5924 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 8 Jul 2015 20:54:56 +0200 Subject: [PATCH 06/29] app_server: fix ServerPicture::SyncState pen size * Should use the unscaled pen size here because we also write down the current scale, and we don't want to scale the pen twice. --- src/servers/app/ServerPicture.cpp | 2 +- src/servers/app/ServerPicture.h | 2 ++ src/tests/servers/app/Jamfile | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index d03ddb29e3..b59a61701a 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -978,7 +978,7 @@ ServerPicture::SyncState(View* view) WriteSetOrigin(view->CurrentState()->Origin()); WriteSetPenLocation(view->CurrentState()->PenLocation()); - WriteSetPenSize(view->CurrentState()->PenSize()); + WriteSetPenSize(view->CurrentState()->UnscaledPenSize()); WriteSetScale(view->CurrentState()->Scale()); WriteSetLineMode(view->CurrentState()->LineCapMode(), view->CurrentState()->LineJoinMode(), diff --git a/src/servers/app/ServerPicture.h b/src/servers/app/ServerPicture.h index f559b45b59..5b008e22d9 100644 --- a/src/servers/app/ServerPicture.h +++ b/src/servers/app/ServerPicture.h @@ -63,6 +63,8 @@ public: status_t ExportData(BPrivate::PortLink& link); private: + friend class PictureBoundingBoxPlayer; + typedef BObjectList PictureList; int32 fToken; diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index f3e3ac340d..73d8b98009 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -156,6 +156,7 @@ SharedLibrary libtestappserver.so : DesktopSettings.cpp OffscreenServerWindow.cpp OffscreenWindow.cpp + PictureBoundingBoxPlayer.cpp RegionPool.cpp Screen.cpp ScreenConfigurations.cpp From ae0468762f2ff1f11bb575f40184fdb90d398dd9 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 8 Jul 2015 21:16:30 +0200 Subject: [PATCH 07/29] app_server: add Canvas::PenToLocalTransform --- src/servers/app/Canvas.cpp | 12 +++++++++++- src/servers/app/Canvas.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index 4fd87a0322..729a160ff4 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -188,6 +188,17 @@ Canvas::PenToScreenTransform() const GCC_2_NRV(transform) } +SimpleTransform +Canvas::PenToLocalTransform() const GCC_2_NRV(transform) +{ +#if __GNUC__ >= 3 + SimpleTransform transform; +#endif + fDrawState->Transform(transform); + return transform; +} + + SimpleTransform Canvas::ScreenToPenTransform() const GCC_2_NRV(transform) { @@ -218,4 +229,3 @@ OffscreenCanvas::ResyncDrawState() { fDrawingEngine->SetDrawState(fDrawState); } - diff --git a/src/servers/app/Canvas.h b/src/servers/app/Canvas.h index acb52d09e5..8bc799aae3 100644 --- a/src/servers/app/Canvas.h +++ b/src/servers/app/Canvas.h @@ -57,6 +57,7 @@ public: SimpleTransform LocalToScreenTransform() const; SimpleTransform ScreenToLocalTransform() const; SimpleTransform PenToScreenTransform() const; + SimpleTransform PenToLocalTransform() const; SimpleTransform ScreenToPenTransform() const; virtual DrawingEngine* GetDrawingEngine() const = 0; From c34cbf2878cb39d46bad3de00d17e60bf502d750 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 8 Jul 2015 21:22:25 +0200 Subject: [PATCH 08/29] app_server: fix PicturePlayer dummy function table * With the support for BAffineTransform, it needs to have 49 entries --- src/kits/interface/PicturePlayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kits/interface/PicturePlayer.cpp b/src/kits/interface/PicturePlayer.cpp index f337bc1896..ed2f13c546 100644 --- a/src/kits/interface/PicturePlayer.cpp +++ b/src/kits/interface/PicturePlayer.cpp @@ -159,7 +159,8 @@ PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData) (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop + (void *)nop, (void *)nop, (void *)nop, (void *)nop, + (void *)nop }; if ((uint32)tableEntries < kOpsTableSize) { From b5c7f936de1af0ce11e8257c4bba56c4a4e12f11 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 8 Jul 2015 21:27:24 +0200 Subject: [PATCH 09/29] app_server: allow replacing the DrawState in a Canvas * Needed for layers support. The previously set DrawState and its stack predecessors are not freed, so take care to not leak memory when using this. --- src/servers/app/Canvas.cpp | 7 +++++++ src/servers/app/Canvas.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index 729a160ff4..89b100f9d6 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -88,6 +88,13 @@ Canvas::PopState() } +void +Canvas::SetDrawState(DrawState* newState) +{ + fDrawState = newState; +} + + void Canvas::SetDrawingOrigin(BPoint origin) { diff --git a/src/servers/app/Canvas.h b/src/servers/app/Canvas.h index 8bc799aae3..3744435187 100644 --- a/src/servers/app/Canvas.h +++ b/src/servers/app/Canvas.h @@ -41,6 +41,7 @@ public: virtual void PushState(); virtual void PopState(); DrawState* CurrentState() const { return fDrawState; } + void SetDrawState(DrawState* newState); void SetDrawingOrigin(BPoint origin); BPoint DrawingOrigin() const; From 65a54d2892d46ff6fb72d76390555de0d5d6dd96 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Tue, 21 Jul 2015 22:46:43 +0200 Subject: [PATCH 10/29] app_server: implement setting BAffineTransforms in BPicture * Add a simple callback for the picture command --- src/servers/app/ServerPicture.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index b59a61701a..f7f2babaad 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -1,11 +1,12 @@ /* - * Copyright 2001-2010, Haiku. + * Copyright 2001-2015, Haiku. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers (mflerackers@androme.be) * Stefano Ceccherini (stefano.ceccherini@gmail.com) * Marcus Overhagen + * Julian Harnath */ #include "ServerPicture.h" @@ -372,6 +373,7 @@ fill_ellipse(Canvas* canvas, BPoint center, BPoint radii) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); + canvas->PenToScreenTransform().Apply(&rect); canvas->GetDrawingEngine()->DrawEllipse(rect, true); } @@ -775,6 +777,13 @@ set_blending_mode(Canvas* canvas, int16 alphaSrcMode, int16 alphaFncMode) } +static void +set_transform(Canvas* canvas, BAffineTransform transform) +{ + canvas->CurrentState()->SetTransform(transform); +} + + static void reserved() { @@ -787,7 +796,7 @@ const static void* kTableEntries[] = { (const void*)stroke_line, (const void*)stroke_rect, (const void*)fill_rect, - (const void*)stroke_round_rect, // 5 + (const void*)stroke_round_rect, // 5 (const void*)fill_round_rect, (const void*)stroke_bezier, (const void*)fill_bezier, @@ -807,7 +816,7 @@ const static void* kTableEntries[] = { (const void*)push_state, (const void*)pop_state, (const void*)enter_state_change, - (const void*)exit_state_change, // 25 + (const void*)exit_state_change, // 25 (const void*)enter_font_state, (const void*)exit_font_state, (const void*)set_origin, @@ -822,14 +831,15 @@ const static void* kTableEntries[] = { (const void*)set_font_family, (const void*)set_font_style, (const void*)set_font_spacing, - (const void*)set_font_size, // 40 + (const void*)set_font_size, // 40 (const void*)set_font_rotate, (const void*)set_font_encoding, (const void*)set_font_flags, (const void*)set_font_shear, (const void*)reserved, // 45 (const void*)set_font_face, - (const void*)set_blending_mode // 47 + (const void*)set_blending_mode, + (const void*)set_transform // 48 }; From edc0b5e9db34f77086d510fa7159d2daf22488aa Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 22 Jul 2015 21:01:19 +0200 Subject: [PATCH 11/29] app_server: allow disabling affine transforms in DrawState * New method DrawState::SetTransformEnabled allows to temporarily disable all BAffineTransforms in the state stack (up to 'this'). Later, the same method can be used to reenable the transforms. Needed for layers support: when drawing the finished layer bitmap onto the view, the affine transforms must not be applied again -- they have already been applied while drawing the bitmap's contents. --- src/servers/app/DrawState.cpp | 16 ++++++++++++++++ src/servers/app/DrawState.h | 1 + 2 files changed, 17 insertions(+) diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index 9621cfa68e..2eaa39f5de 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -370,6 +370,22 @@ DrawState::SetTransform(BAffineTransform transform) } +/* Can be used to temporarily disable all BAffineTransforms in the state + stack, and later reenable them. +*/ +void +DrawState::SetTransformEnabled(bool enabled) +{ + if (enabled) { + BAffineTransform temp = fTransform; + SetTransform(BAffineTransform()); + SetTransform(temp); + } + else + fCombinedTransform = BAffineTransform(); +} + + DrawState* DrawState::Squash() { diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index 48a6f371c6..e67e1ed2af 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -68,6 +68,7 @@ public: { return fTransform; } BAffineTransform CombinedTransform() const { return fCombinedTransform; } + void SetTransformEnabled(bool enabled); DrawState* Squash(); From 6ac468ef24f664699f674e5dd9b27d41be906d95 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 22 Jul 2015 21:26:49 +0200 Subject: [PATCH 12/29] app_server: add support for uniform opacity alpha masks * Another constructor for AlphaMask allows creating a mask with no picture, it will simply be a single uniform alpha value over the whole mask. * No need to even allocate a buffer in this case, we can just use the feature of clipped_alpha_mask to define an opacity for outside the mask, and set the buffer size to zero. --- src/servers/app/drawing/AlphaMask.cpp | 55 +++++++++++++++++++++++---- src/servers/app/drawing/AlphaMask.h | 12 +++++- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 2b744cd548..391c929466 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -1,10 +1,11 @@ /* - * Copyright 2014, Haiku, Inc. + * Copyright 2014-2015, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * Adrien Destugues * Stephan Aßmus + * Julian Harnath */ @@ -26,6 +27,7 @@ AlphaMask::AlphaMask(ServerPicture* picture, bool inverse, BPoint origin, fPicture(picture), fInverse(inverse), fOrigin(origin), + fBackgroundOpacity(0), fDrawState(drawState), fViewBounds(), @@ -43,9 +45,34 @@ AlphaMask::AlphaMask(ServerPicture* picture, bool inverse, BPoint origin, } +AlphaMask::AlphaMask(uint8 backgroundOpacity) + : + fPreviousMask(NULL), + + fPicture(NULL), + fInverse(false), + fOrigin(0, 0), + fBackgroundOpacity(backgroundOpacity), + fDrawState(), + + fViewBounds(), + fViewOffset(), + + fCachedBitmap(NULL), + fCachedBounds(), + fCachedOffset(), + + fBuffer(), + fCachedMask(), + fScanline(fCachedMask) +{ +} + + AlphaMask::~AlphaMask() { - fPicture->ReleaseReference(); + if (fPicture != NULL) + fPicture->ReleaseReference(); delete[] fCachedBitmap; SetPrevious(NULL); } @@ -81,7 +108,13 @@ AlphaMask::SetPrevious(AlphaMask* mask) scanline_unpacked_masked_type* AlphaMask::Generate() { - if (fPicture == NULL || !fViewBounds.IsValid()) + if (fPicture == NULL) { + fBuffer.attach(NULL, 0, 0, 0); + _AttachMaskToBuffer(); + return &fScanline; + } + + if (!fViewBounds.IsValid()) return NULL; // See if a cached bitmap can be used. Don't use it when the view offset @@ -162,9 +195,7 @@ AlphaMask::Generate() fCachedOffset = fViewOffset; fBuffer.attach(fCachedBitmap, width, height, width); - - fCachedMask.attach(fBuffer, fViewOffset.x + fOrigin.x, - fViewOffset.y + fOrigin.y, fInverse ? 255 : 0); + _AttachMaskToBuffer(); return &fScanline; } @@ -184,7 +215,7 @@ AlphaMask::_RenderPicture() const } // Clear the bitmap with the transparent color - memset(bitmap->Bits(), 0, bitmap->BitsLength()); + memset(bitmap->Bits(), fBackgroundOpacity, bitmap->BitsLength()); // Render the picture to the bitmap BitmapHWInterface interface(bitmap); @@ -213,3 +244,13 @@ AlphaMask::_RenderPicture() const return bitmap; } + +void +AlphaMask::_AttachMaskToBuffer() +{ + uint8 outsideOpacity = fInverse ? 255 - fBackgroundOpacity + : fBackgroundOpacity; + + fCachedMask.attach(fBuffer, fViewOffset.x + fOrigin.x, + fViewOffset.y + fOrigin.y, outsideOpacity); +} diff --git a/src/servers/app/drawing/AlphaMask.h b/src/servers/app/drawing/AlphaMask.h index 661ec7768e..83ceb7fd2f 100644 --- a/src/servers/app/drawing/AlphaMask.h +++ b/src/servers/app/drawing/AlphaMask.h @@ -1,5 +1,5 @@ /* - * Copyright 2014, Haiku, Inc. + * Copyright 2014-2015, Haiku, Inc. * Distributed under the terms of the MIT License. */ @@ -23,6 +23,7 @@ class AlphaMask : public BReferenceable { public: AlphaMask(ServerPicture* mask, bool inverse, BPoint origin, const DrawState& drawState); + AlphaMask(uint8 backgroundOpacity); ~AlphaMask(); void Update(BRect bounds, BPoint offset); @@ -33,6 +34,7 @@ public: private: ServerBitmap* _RenderPicture() const; + void _AttachMaskToBuffer(); private: @@ -41,10 +43,18 @@ private: ServerPicture* fPicture; const bool fInverse; BPoint fOrigin; + // position of this mask, relative to + // either its parent mask, or (if there + // is none) the canvas + uint8 fBackgroundOpacity; DrawState fDrawState; + // draw state used for drawing fPicture BRect fViewBounds; + // determines alpha mask size BPoint fViewOffset; + // position of alpha mask in screen + // coordinates uint8* fCachedBitmap; BRect fCachedBounds; From cd621b9585b4dd2dd629283976130c13afeb087e Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 22 Jul 2015 21:31:42 +0200 Subject: [PATCH 13/29] app_server: add method to shift alpha masks * Allow shifting the offset of alpha masks without changing the size. Ideally, we only need to reattach the buffer in the shifted position, saving the work of reallocating and redrawing the mask picture. Needed for layers support. --- src/servers/app/drawing/AlphaMask.cpp | 19 +++++++++++++++++++ src/servers/app/drawing/AlphaMask.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 391c929466..49636c6296 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -89,6 +89,25 @@ AlphaMask::Update(BRect bounds, BPoint offset) } +BPoint +AlphaMask::Update(BPoint offset) +{ + BPoint oldOffset = fViewOffset; + fViewOffset = offset; + + if (oldOffset == fCachedOffset && fCachedBitmap != NULL) { + // No need to redraw the picture when only the offset is shifted + fCachedOffset = offset; + _AttachMaskToBuffer(); + } + + if (fPreviousMask != NULL) + fPreviousMask->Update(offset); + + return oldOffset; +} + + void AlphaMask::SetPrevious(AlphaMask* mask) { diff --git a/src/servers/app/drawing/AlphaMask.h b/src/servers/app/drawing/AlphaMask.h index 83ceb7fd2f..cb1969af7a 100644 --- a/src/servers/app/drawing/AlphaMask.h +++ b/src/servers/app/drawing/AlphaMask.h @@ -27,6 +27,7 @@ public: ~AlphaMask(); void Update(BRect bounds, BPoint offset); + BPoint Update(BPoint offset); void SetPrevious(AlphaMask* mask); From d56beda4d8fa72f264b64c8e649ae4d8a0430a43 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Wed, 22 Jul 2015 21:36:10 +0200 Subject: [PATCH 14/29] app_server: optional coordinate shifting in renderer_region * agg::renderer_region gets an extra feature which allows to optionally shift the coordinates by a specified offset. * This allows to shift coordinates at the lowest level, even below the transformations done by BAffineTransform (which happen in the painter, right before rasterization). Needed for layers support: shifts the origin of the layer bitmaps to their position in the view while keeping all transformations (BView origin/scale transforms as well as BAffineTransforms) intact. The offset for the layer bitmaps within their parent view is determined by the bounding box and is then fixed, it must not be altered while the layer's BPicture is played into the bitmap. If this offset were added to the BView origin or as translation in the BAffineTransform, it would be further transformed by the BView scale or the other affine transform parameters. Thus, we need another low-level offset mechanism which is even below BAffineTransform's transformations. --- src/servers/app/drawing/DrawingEngine.cpp | 12 +- src/servers/app/drawing/DrawingEngine.h | 5 +- src/servers/app/drawing/Painter/Painter.cpp | 12 +- src/servers/app/drawing/Painter/Painter.h | 3 + .../app/drawing/Painter/agg_renderer_region.h | 138 ++++++++++++++++-- 5 files changed, 156 insertions(+), 14 deletions(-) diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index b5c425a97a..147a359b18 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -1,9 +1,10 @@ /* - * Copyright 2001-2009, Haiku, Inc. + * Copyright 2001-2015, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus + * Julian Harnath */ @@ -1070,7 +1071,7 @@ DrawingEngine::FillRegion(BRegion& r) doInSoftware = false; } } - + if (doInSoftware && (fAvailableHWAccleration & HW_ACC_INVERT_REGION) != 0 && fPainter->Pattern() == B_SOLID_HIGH @@ -1555,6 +1556,13 @@ DrawingEngine::CopyRect(BRect src, int32 xOffset, int32 yOffset) const } +void +DrawingEngine::SetRendererOffset(int32 offsetX, int32 offsetY) +{ + fPainter->SetRendererOffset(offsetX, offsetY); +} + + void DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, uint32 bytesPerRow, int32 xOffset, int32 yOffset) const diff --git a/src/servers/app/drawing/DrawingEngine.h b/src/servers/app/drawing/DrawingEngine.h index dd15db25c3..d9a0896614 100644 --- a/src/servers/app/drawing/DrawingEngine.h +++ b/src/servers/app/drawing/DrawingEngine.h @@ -1,11 +1,12 @@ /* - * Copyright 2001-2009, Haiku, Inc. + * Copyright 2001-2015, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Gabe Yoder * Stephan Aßmus + * Julian Harnath */ #ifndef DRAWING_ENGINE_H_ #define DRAWING_ENGINE_H_ @@ -187,6 +188,8 @@ public: virtual BRect CopyRect(BRect rect, int32 xOffset, int32 yOffset) const; + void SetRendererOffset(int32 offsetX, int32 offsetY); + private: void _CopyRect(uint8* bits, uint32 width, uint32 height, uint32 bytesPerRow, diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 9ac646eb6c..1ff042ceba 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -2,6 +2,7 @@ * Copyright 2009, Christian Packmann. * Copyright 2008, Andrej Spielmann . * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath * All rights reserved. Distributed under the terms of the MIT License. */ @@ -667,7 +668,7 @@ Painter::DrawPolygon(BPoint* p, int32 numPts, bool filled, bool closed) const bool centerOffset = !filled && fIdentityTransform && fmodf(fPenSize, 2.0) != 0.0; - + fPath.remove_all(); _Align(p, centerOffset); @@ -1508,6 +1509,13 @@ Painter::InvertRect(const BRect& r) const } +void +Painter::SetRendererOffset(int32 offsetX, int32 offsetY) +{ + fBaseRenderer.set_offset(offsetX, offsetY); +} + + // #pragma mark - private @@ -2939,7 +2947,7 @@ Painter::_RasterizePath(VertexSource& path, const BGradient& gradient) const _RasterizePath(path, gradient, gradientFunction, gradientTransform); break; } - + default: case BGradient::TYPE_NONE: GTRACE(("Painter::_FillPathGradient> type == TYPE_NONE/unkown\n")); diff --git a/src/servers/app/drawing/Painter/Painter.h b/src/servers/app/drawing/Painter/Painter.h index 166de5b8b1..4402113438 100644 --- a/src/servers/app/drawing/Painter/Painter.h +++ b/src/servers/app/drawing/Painter/Painter.h @@ -1,6 +1,7 @@ /* * Copyright 2005-2007, Stephan Aßmus . * Copyright 2008, Andrej Spielmann . + * Copyright 2015, Julian Harnath * All rights reserved. Distributed under the terms of the MIT License. * * API to the Anti-Grain Geometry based "Painter" drawing backend. Manages @@ -243,6 +244,8 @@ public: inline BRect AlignAndClipRect(BRect rect) const; inline BRect AlignRect(BRect rect) const; + void SetRendererOffset(int32 offsetX, + int32 offsetY); private: float _Align(float coord, bool round, diff --git a/src/servers/app/drawing/Painter/agg_renderer_region.h b/src/servers/app/drawing/Painter/agg_renderer_region.h index e761129744..faf0664fc2 100644 --- a/src/servers/app/drawing/Painter/agg_renderer_region.h +++ b/src/servers/app/drawing/Painter/agg_renderer_region.h @@ -1,6 +1,7 @@ /* * Copyright 2005-2006, Stephan Aßmus . * Copyright 2008, Andrej Spielmann . + * Copyright 2015, Julian Harnath * All rights reserved. Distributed under the terms of the MIT License. * * Copyright 2002-2004 Maxim Shemanarev (http://www.antigrain.com) @@ -36,7 +37,9 @@ namespace agg m_ren(ren), m_region(NULL), m_curr_cb(0), - m_bounds(m_ren.xmin(), m_ren.ymin(), m_ren.xmax(), m_ren.ymax()) + m_bounds(m_ren.xmin(), m_ren.ymin(), m_ren.xmax(), m_ren.ymax()), + m_offset_x(0), + m_offset_y(0) { } @@ -49,11 +52,15 @@ namespace agg unsigned height() const { return m_ren.height(); } //-------------------------------------------------------------------- - const rect_i& clip_box() const { return m_ren.clip_box(); } - int xmin() const { return m_ren.xmin(); } - int ymin() const { return m_ren.ymin(); } - int xmax() const { return m_ren.xmax(); } - int ymax() const { return m_ren.ymax(); } + const rect_i& clip_box() const { return m_bounds; } + int xmin() const { return translate_from_base_ren_x( + m_ren.xmin()); } + int ymin() const { return translate_from_base_ren_y( + m_ren.ymin()); } + int xmax() const { return translate_from_base_ren_x( + m_ren.xmax()); } + int ymax() const { return translate_from_base_ren_y( + m_ren.ymax()); } //-------------------------------------------------------------------- const rect_i& bounding_clip_box() const { return m_bounds; } @@ -69,7 +76,12 @@ namespace agg if(m_region && m_region->CountRects() > 0) { clipping_rect cb = m_region->RectAtInt(0); - m_ren.clip_box_naked(cb.left, cb.top, cb.right, cb.bottom); + translate_to_base_ren(cb); + m_ren.clip_box_naked( + cb.left, + cb.top, + cb.right, + cb.bottom); } else m_ren.clip_box_naked(0, 0, -1, -1); @@ -81,7 +93,12 @@ namespace agg if(m_region && (int)(++m_curr_cb) < m_region->CountRects()) { clipping_rect cb = m_region->RectAtInt(m_curr_cb); - m_ren.clip_box_naked(cb.left, cb.top, cb.right, cb.bottom); + translate_to_base_ren(cb); + m_ren.clip_box_naked( + cb.left, + cb.top, + cb.right, + cb.bottom); return true; } return false; @@ -94,6 +111,7 @@ namespace agg m_region = NULL; m_curr_cb = 0; m_bounds = m_ren.clip_box(); + translate_from_base_ren(m_bounds); } //-------------------------------------------------------------------- @@ -117,6 +135,68 @@ namespace agg } } + //-------------------------------------------------------------------- + void set_offset(int offset_x, int offset_y) + { + m_offset_x = offset_x; + m_offset_y = offset_y; + + if (m_region == NULL) { + m_bounds = m_ren.clip_box(); + translate_from_base_ren(m_bounds); + } + } + + //-------------------------------------------------------------------- + void translate_to_base_ren_x(int& x) + { + x -= m_offset_x; + } + + void translate_to_base_ren_y(int& y) + { + y -= m_offset_y; + } + + void translate_to_base_ren(int& x, int&y) + { + x -= m_offset_x; + y -= m_offset_y; + } + + void translate_to_base_ren(clipping_rect& clip) + { + clip.left -= m_offset_x; + clip.right -= m_offset_x; + clip.top -= m_offset_y; + clip.bottom -= m_offset_y; + } + + //-------------------------------------------------------------------- + int translate_from_base_ren_x(int x) const + { + return x + m_offset_x; + } + + int translate_from_base_ren_y(int y) const + { + return y + m_offset_y; + } + + void translate_from_base_ren(int& x, int& y) + { + x += m_offset_x; + y += m_offset_y; + } + + void translate_from_base_ren(rect_i& rect) + { + rect.x1 += m_offset_x; + rect.x2 += m_offset_x; + rect.y1 += m_offset_y; + rect.y2 += m_offset_y; + } + //-------------------------------------------------------------------- void clear(const color_type& c) { @@ -126,6 +206,8 @@ namespace agg //-------------------------------------------------------------------- void copy_pixel(int x, int y, const color_type& c) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -141,6 +223,8 @@ namespace agg //-------------------------------------------------------------------- void blend_pixel(int x, int y, const color_type& c, cover_type cover) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -156,6 +240,8 @@ namespace agg //-------------------------------------------------------------------- color_type pixel(int x, int y) const { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -171,6 +257,9 @@ namespace agg //-------------------------------------------------------------------- void copy_hline(int x1, int y, int x2, const color_type& c) { + translate_to_base_ren(x1, y); + translate_to_base_ren_x(x2); + first_clip_box(); do { @@ -182,6 +271,9 @@ namespace agg //-------------------------------------------------------------------- void copy_vline(int x, int y1, int y2, const color_type& c) { + translate_to_base_ren(x, y1); + translate_to_base_ren_y(y2); + first_clip_box(); do { @@ -194,6 +286,9 @@ namespace agg void blend_hline(int x1, int y, int x2, const color_type& c, cover_type cover) { + translate_to_base_ren(x1, y); + translate_to_base_ren_x(x2); + first_clip_box(); do { @@ -201,11 +296,14 @@ namespace agg } while(next_clip_box()); } - + //-------------------------------------------------------------------- void blend_vline(int x, int y1, int y2, const color_type& c, cover_type cover) { + translate_to_base_ren(x, y1); + translate_to_base_ren_y(y2); + first_clip_box(); do { @@ -217,6 +315,9 @@ namespace agg //-------------------------------------------------------------------- void copy_bar(int x1, int y1, int x2, int y2, const color_type& c) { + translate_to_base_ren(x1, y1); + translate_to_base_ren(x2, y2); + first_clip_box(); do { @@ -229,6 +330,9 @@ namespace agg void blend_bar(int x1, int y1, int x2, int y2, const color_type& c, cover_type cover) { + translate_to_base_ren(x1, y1); + translate_to_base_ren(x2, y2); + first_clip_box(); do { @@ -242,6 +346,8 @@ namespace agg void blend_solid_hspan(int x, int y, int len, const color_type& c, const cover_type* covers) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -254,6 +360,8 @@ namespace agg void blend_solid_hspan_subpix(int x, int y, int len, const color_type& c, const cover_type* covers) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -266,6 +374,8 @@ namespace agg void blend_solid_vspan(int x, int y, int len, const color_type& c, const cover_type* covers) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -280,6 +390,8 @@ namespace agg const cover_type* covers, cover_type cover = cover_full) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -294,6 +406,8 @@ namespace agg const cover_type* covers, cover_type cover = cover_full) { + translate_to_base_ren(x, y); + first_clip_box(); do { @@ -308,6 +422,7 @@ namespace agg const cover_type* covers, cover_type cover = cover_full) { + translate_to_base_ren(x, y); m_ren.blend_color_hspan_no_clip(x, y, len, colors, covers, cover); } @@ -317,6 +432,7 @@ namespace agg const cover_type* covers, cover_type cover = cover_full) { + translate_to_base_ren(x, y); m_ren.blend_color_vspan_no_clip(x, y, len, colors, covers, cover); } @@ -326,6 +442,7 @@ namespace agg int x_to=0, int y_to=0) { + translate_to_base_ren(x_to, y_to); first_clip_box(); do { @@ -343,6 +460,9 @@ namespace agg BRegion* m_region; unsigned m_curr_cb; rect_i m_bounds; + + int m_offset_x; + int m_offset_y; }; From 551438b9be6bb3c4c52b8276718d56eccf969dff Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 25 Jul 2015 15:44:23 +0200 Subject: [PATCH 15/29] app_server: add new BView layers API * Add new methods BView::BeginLayer(uint8 opacity) BView::EndLayer() * All drawing between begin and end of a layer is redirected onto an intermediate bitmap. When ending the layer, this bitmap is composited onto the view with the opacity given when the layer was started. * Layers can be nested arbitrarily and will be blended onto each other in order. There can also be any arbitrary interleaving of layer begin/end and drawing operations. * Internally, drawing commands are redirected into a BPicture between BeginLayer and EndLayer (but client code need not know or care about this). Client code can also start/end other BPictures while inside a layer. * Uses the PictureBoundingBoxPlayer to determine the size of the layer bitmap before allocating and drawing into it, so it does not allocate more memory than necessary and -- more importantly -- it will not alpha-composite more pixels than necessary. * Drawing mode is always set to B_OP_ALPHA, blend mode to (B_PIXEL_ALPHA, B_ALPHA_COMPOSITE) while inside layers. This is necessary for (a) correct compositing output and (b) for redirection of drawing into the intermediate bitmap, which uses the renderer_region offset (in B_OP_COPY, the Painter does not use the AGG renderer methods, it directly accesses the pixel data. This would access out-of-bounds without the offset, so B_OP_COPY cannot be allowed.) To ensure these modes aren't changed, BView::SetDrawingMode() and BView::SetBlendingMode() are ignored while inside a layer. * The main motivation behind this new API is WebKit, which internally expects such a layers functionality to be present. A performant and reusable implementation of this functionality can only be done server-side in app_server. --- headers/os/interface/View.h | 3 + headers/private/app/ServerProtocol.h | 3 + headers/private/interface/PictureDataWriter.h | 6 +- headers/private/interface/PictureProtocol.h | 3 +- src/kits/interface/PictureDataWriter.cpp | 18 +- src/kits/interface/PicturePlayer.cpp | 16 +- src/kits/interface/View.cpp | 26 ++- src/servers/app/Canvas.cpp | 42 ++++ src/servers/app/Canvas.h | 3 + src/servers/app/Jamfile | 1 + src/servers/app/Layer.cpp | 216 ++++++++++++++++++ src/servers/app/Layer.h | 42 ++++ src/servers/app/PictureBoundingBoxPlayer.cpp | 16 +- src/servers/app/ServerPicture.cpp | 11 +- src/servers/app/ServerPicture.h | 5 +- src/servers/app/ServerWindow.cpp | 69 +++++- src/servers/app/View.cpp | 17 +- src/servers/app/View.h | 5 +- src/tests/servers/app/Jamfile | 1 + 19 files changed, 489 insertions(+), 14 deletions(-) create mode 100644 src/servers/app/Layer.cpp create mode 100644 src/servers/app/Layer.h diff --git a/headers/os/interface/View.h b/headers/os/interface/View.h index e744375fee..29a3fefc12 100644 --- a/headers/os/interface/View.h +++ b/headers/os/interface/View.h @@ -505,6 +505,9 @@ public: void DrawPictureAsync(const char* filename, long offset, BPoint where); + void BeginLayer(uint8 opacity); + void EndLayer(); + status_t SetEventMask(uint32 mask, uint32 options = 0); uint32 EventMask(); status_t SetMouseEventMask(uint32 mask, diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index 952a6111dd..897baa18e2 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -7,6 +7,7 @@ * Jérôme Duval, jerome.duval@free.fr * Axel Dörfler, axeld@pinc-software.de * Andrej Spielmann, + * Julian Harnath, */ #ifndef APP_SERVER_PROTOCOL_H #define APP_SERVER_PROTOCOL_H @@ -319,6 +320,8 @@ enum { AS_VIEW_SET_VIEW_BITMAP, AS_VIEW_SET_PATTERN, AS_SET_CURRENT_VIEW, + AS_VIEW_BEGIN_LAYER, + AS_VIEW_END_LAYER, // BDirectWindow/BWindowScreen codes AS_DIRECT_WINDOW_GET_SYNC_DATA, diff --git a/headers/private/interface/PictureDataWriter.h b/headers/private/interface/PictureDataWriter.h index 4af9187f24..a0f7904ee8 100644 --- a/headers/private/interface/PictureDataWriter.h +++ b/headers/private/interface/PictureDataWriter.h @@ -1,9 +1,10 @@ /* - * Copyright 2006-2007 Haiku, Inc. All rights reserved. + * Copyright 2006-2015 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Stefano Ceccherini, burton666@libero.it + * Julian Harnath, */ #ifndef _PICTURE_DATA_WRITER_H #define _PICTURE_DATA_WRITER_H @@ -17,6 +18,7 @@ #include +class Layer; class BPositionIO; class BRegion; @@ -91,6 +93,8 @@ public: status_t WriteDrawPicture(const BPoint& where, const int32& token); + status_t WriteBlendLayer(Layer* layer); + protected: // throw a status_t on error void BeginOp(const int16& op); diff --git a/headers/private/interface/PictureProtocol.h b/headers/private/interface/PictureProtocol.h index f83b53d86c..f33abddaa6 100644 --- a/headers/private/interface/PictureProtocol.h +++ b/headers/private/interface/PictureProtocol.h @@ -52,10 +52,11 @@ enum { B_PIC_SET_FONT_BPP = 0x0388, B_PIC_SET_FONT_FACE = 0x0389, B_PIC_SET_TRANSFORM = 0x0390, + B_PIC_BLEND_LAYER = 0x0391 }; -const static uint32 kOpsTableSize = 49; +const static uint32 kOpsTableSize = 50; #endif diff --git a/src/kits/interface/PictureDataWriter.cpp b/src/kits/interface/PictureDataWriter.cpp index 3f9f5c911c..e2ae828fe3 100644 --- a/src/kits/interface/PictureDataWriter.cpp +++ b/src/kits/interface/PictureDataWriter.cpp @@ -1,9 +1,10 @@ /* - * Copyright 2006-2009 Haiku, Inc. All rights reserved. + * Copyright 2006-2015 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Stefano Ceccherini, stefano.ceccherini@gmail.com + * Julian Harnath, */ #include @@ -641,6 +642,21 @@ PictureDataWriter::WritePopState() } +status_t +PictureDataWriter::WriteBlendLayer(Layer* layer) +{ + try { + BeginOp(B_PIC_BLEND_LAYER); + Write(layer); + EndOp(); + } catch (status_t& status) { + return status; + } + + return B_OK; +} + + // private void PictureDataWriter::BeginOp(const int16& op) diff --git a/src/kits/interface/PicturePlayer.cpp b/src/kits/interface/PicturePlayer.cpp index ed2f13c546..c56cfd66c0 100644 --- a/src/kits/interface/PicturePlayer.cpp +++ b/src/kits/interface/PicturePlayer.cpp @@ -1,11 +1,12 @@ /* - * Copyright 2001-2007, Haiku Inc. + * Copyright 2001-2015, Haiku Inc. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers (mflerackers@androme.be) * Stefano Ceccherini (stefano.ceccherini@gmail.com) * Marcus Overhagen (marcus@overhagen.de) + * Julian Harnath (julian.harnath@rwth-aachen.de) */ /** PicturePlayer is used to play picture data. */ @@ -23,6 +24,9 @@ using BPrivate::PicturePlayer; +class Layer; + + typedef void (*fnc)(void*); typedef void (*fnc_BPoint)(void*, BPoint); typedef void (*fnc_BPointBPoint)(void*, BPoint, BPoint); @@ -47,6 +51,7 @@ typedef void (*fnc_DrawPixels)(void *, BRect, BRect, int32, int32, int32, typedef void (*fnc_DrawPicture)(void *, BPoint, int32); typedef void (*fnc_BShape)(void*, BShape*); typedef void (*fnc_BAffineTransform)(void*, BAffineTransform); +typedef void (*fnc_Layer)(void*, const Layer*); static void @@ -160,7 +165,7 @@ PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData) (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop + (void *)nop, (void *)nop }; if ((uint32)tableEntries < kOpsTableSize) { @@ -546,6 +551,13 @@ PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData) break; } + case B_PIC_BLEND_LAYER: + { + ((fnc_Layer)functionTable[49])(userData, + *reinterpret_cast(data)); + break; + } + default: break; } diff --git a/src/kits/interface/View.cpp b/src/kits/interface/View.cpp index eb07f0f64b..703320cdb8 100644 --- a/src/kits/interface/View.cpp +++ b/src/kits/interface/View.cpp @@ -7,6 +7,7 @@ * Axel Dörfler, axeld@pinc-software.de * Adrian Oanca, adioanca@cotty.iren.ro * Ingo Weinhold. ingo_weinhold@gmx.de + * Julian Harnath, julian.harnath@rwth-aachen.de */ @@ -3933,6 +3934,27 @@ BView::DrawPictureAsync(const char* filename, long offset, BPoint where) } +void +BView::BeginLayer(uint8 opacity) +{ + if (_CheckOwnerLockAndSwitchCurrent()) { + fOwner->fLink->StartMessage(AS_VIEW_BEGIN_LAYER); + fOwner->fLink->Attach(opacity); + _FlushIfNotInTransaction(); + } +} + + +void +BView::EndLayer() +{ + if (_CheckOwnerLockAndSwitchCurrent()) { + fOwner->fLink->StartMessage(AS_VIEW_END_LAYER); + _FlushIfNotInTransaction(); + } +} + + void BView::Invalidate(BRect invalRect) { @@ -5225,7 +5247,7 @@ BView::_ClipToPicture(BPicture* picture, BPoint where, bool invert, bool sync) if (picture == NULL) { fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_PICTURE); fOwner->fLink->Attach(-1); - + // NOTE: No need to sync here, since the -1 token cannot // become invalid on the server. } else { @@ -5240,7 +5262,7 @@ BView::_ClipToPicture(BPicture* picture, BPoint where, bool invert, bool sync) // the client creates BPictures on the stack, these BPictures may // have issued a AS_DELETE_PICTURE command to the ServerApp when Draw() // goes out of scope, and the command is processed earlier in the - // ServerApp thread than the AS_VIEW_CLIP_TO_PICTURE command in the + // ServerApp thread than the AS_VIEW_CLIP_TO_PICTURE command in the // ServerWindow thread, which will then have the result that no // ServerPicture is found of the token. if (sync) diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index 89b100f9d6..e79b1527a2 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -19,8 +19,10 @@ #include +#include "AlphaMask.h" #include "DrawingEngine.h" #include "DrawState.h" +#include "Layer.h" #if __GNUC__ >= 3 @@ -218,6 +220,46 @@ Canvas::ScreenToPenTransform() const GCC_2_NRV(transform) } +void +Canvas::BlendLayer(Layer* layer) +{ + UtilityBitmap* layerBitmap = layer->RenderToBitmap(this); + if (layerBitmap == NULL) + return; + + BRect destination = layerBitmap->Bounds(); + destination.OffsetBy(layer->LeftTopOffset()); + LocalToScreenTransform().Apply(&destination); + + PushState(); + + fDrawState->SetDrawingMode(B_OP_ALPHA); + fDrawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + fDrawState->SetTransformEnabled(false); + + AlphaMask* mask = new AlphaMask(layer->Opacity()); + if (mask == NULL) { + layerBitmap->ReleaseReference(); + return; + } + + SetAlphaMask(mask); + mask->ReleaseReference(); + ResyncDrawState(); + + GetDrawingEngine()->DrawBitmap(layerBitmap, layerBitmap->Bounds(), + destination, 0); + + fDrawState->SetTransformEnabled(true); + + PopState(); + ResyncDrawState(); + + layerBitmap->ReleaseReference(); + layer->ReleaseReference(); +} + + // #pragma mark - OffscreenCanvas diff --git a/src/servers/app/Canvas.h b/src/servers/app/Canvas.h index 3744435187..aaad41b9c6 100644 --- a/src/servers/app/Canvas.h +++ b/src/servers/app/Canvas.h @@ -27,6 +27,7 @@ class DrawingEngine; class DrawState; class IntPoint; class IntRect; +class Layer; class ServerPicture; @@ -61,6 +62,8 @@ public: SimpleTransform PenToLocalTransform() const; SimpleTransform ScreenToPenTransform() const; + void BlendLayer(Layer* layer); + virtual DrawingEngine* GetDrawingEngine() const = 0; virtual ServerPicture* GetPicture(int32 token) const = 0; virtual void RebuildClipping(bool deep) = 0; diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index fdaea83c7e..7c96133439 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -67,6 +67,7 @@ Server app_server : InputManager.cpp IntPoint.cpp IntRect.cpp + Layer.cpp MessageLooper.cpp MultiLocker.cpp OffscreenServerWindow.cpp diff --git a/src/servers/app/Layer.cpp b/src/servers/app/Layer.cpp new file mode 100644 index 0000000000..1c80ad736b --- /dev/null +++ b/src/servers/app/Layer.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ +#include "Layer.h" + +#include "AlphaMask.h" +#include "BitmapHWInterface.h" +#include "DrawingEngine.h" +#include "DrawState.h" +#include "IntRect.h" +#include "PictureBoundingBoxPlayer.h" +#include "ServerBitmap.h" +#include "View.h" + + +class LayerCanvas : public Canvas { +public: + LayerCanvas(DrawingEngine* drawingEngine, DrawState* drawState) + : + Canvas(), + fDrawingEngine(drawingEngine) + { + delete fDrawState; + fDrawState = drawState; + } + + virtual DrawingEngine* GetDrawingEngine() const + { + return fDrawingEngine; + } + + virtual ServerPicture* GetPicture(int32 token) const + { + return NULL; + } + + virtual void RebuildClipping(bool) + { + } + + virtual void ResyncDrawState() + { + fDrawingEngine->SetDrawState(fDrawState); + } + +protected: + virtual void _LocalToScreenTransform(SimpleTransform&) const + { + } + + virtual void _ScreenToLocalTransform(SimpleTransform&) const + { + } + +private: + DrawingEngine* fDrawingEngine; +}; + + +Layer::Layer(uint8 opacity) + : + fOpacity(opacity), + fLeftTopOffset(0, 0) +{ +} + + +Layer::~Layer() +{ +} + + +void +Layer::PushLayer(Layer* layer) +{ + PushPicture(layer); +} + + +Layer* +Layer::PopLayer() +{ + Layer* const previousLayer = static_cast(PopPicture()); + if (previousLayer != NULL) + previousLayer->ReleaseReference(); + return previousLayer; +} + + +UtilityBitmap* +Layer::RenderToBitmap(Canvas* canvas) +{ + BRect boundingBox = _DetermineBoundingBox(canvas); + if (!boundingBox.IsValid()) + return NULL; + + fLeftTopOffset = boundingBox.LeftTop(); + + UtilityBitmap* const layerBitmap = _AllocateBitmap(boundingBox); + if (layerBitmap == NULL) + return NULL; + + BitmapHWInterface layerInterface(layerBitmap); + DrawingEngine* const layerEngine = layerInterface.CreateDrawingEngine(); + if (layerEngine == NULL) { + layerBitmap->ReleaseReference(); + return NULL; + } + layerEngine->SetRendererOffset(boundingBox.left, boundingBox.top); + // Drawing commands of the layer's picture use coordinates in the + // coordinate space of the underlying canvas. The coordinate origin + // of the layer bitmap is at boundingBox.LeftTop(). So all the drawing + // from the picture needs to be offset to be moved into the bitmap. + // We use a low-level offsetting via the AGG renderer here because the + // offset needs to be processed independently, after all other + // transforms, even after the BAffineTransforms (which are processed in + // Painter), to prevent this origin from being further transformed by + // e.g. scaling. + + LayerCanvas layerCanvas(layerEngine, canvas->CurrentState()); + + AlphaMask* const mask = layerCanvas.GetAlphaMask(); + BPoint oldOffset; + if (mask != NULL) { + // Move alpha mask to bitmap origin + oldOffset = mask->Update(BPoint(0, 0)); + } + + canvas->CurrentState()->SetDrawingMode(B_OP_ALPHA); + canvas->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + + layerCanvas.ResyncDrawState(); + // Apply state to the new drawing engine of the layer canvas + + if (layerEngine->LockParallelAccess()) { + const BRegion region(boundingBox); + layerEngine->ConstrainClippingRegion(®ion); + + // Draw recorded picture into bitmap + Play(&layerCanvas); + layerEngine->UnlockParallelAccess(); + } + + if (mask != NULL) { + // Move alpha mask back to its old position + // Note: this needs to be adapted if setting alpha masks is + // implemented as BPicture command (the mask now might be a different + // one than before). + mask->Update(oldOffset); + layerCanvas.ResyncDrawState(); + } + + canvas->SetDrawState(layerCanvas.CurrentState()); + // Update state in canvas (the top-of-stack state could be a different + // state instance now, if the picture commands contained push/pop + // commands) + + delete layerEngine; + + return layerBitmap; +} + + +IntPoint +Layer::LeftTopOffset() const +{ + return fLeftTopOffset; +} + + +uint8 +Layer::Opacity() const +{ + return fOpacity; +} + + +BRect +Layer::_DetermineBoundingBox(Canvas* canvas) +{ + BRect boundingBox; + PictureBoundingBoxPlayer::Play(this, canvas->CurrentState(), &boundingBox); + + if (!boundingBox.IsValid()) + return boundingBox; + + // Round up and add an additional 2 pixels on the bottom/right to + // compensate for the various types of rounding used in Painter. + boundingBox.left = floorf(boundingBox.left); + boundingBox.right = ceilf(boundingBox.right) + 2; + boundingBox.top = floorf(boundingBox.top); + boundingBox.bottom = ceilf(boundingBox.bottom) + 2; + + // TODO: for optimization, crop the bounding box to the underlying + // view bounds here + + return boundingBox; +} + + +UtilityBitmap* +Layer::_AllocateBitmap(const BRect& bounds) +{ + UtilityBitmap* const layerBitmap = new(std::nothrow) UtilityBitmap(bounds, + B_RGBA32, 0); + if (layerBitmap == NULL) + return NULL; + if (!layerBitmap->IsValid()) { + delete layerBitmap; + return NULL; + } + memset(layerBitmap->Bits(), 0, layerBitmap->BitsLength()); + + return layerBitmap; +} diff --git a/src/servers/app/Layer.h b/src/servers/app/Layer.h new file mode 100644 index 0000000000..7a9f969cd4 --- /dev/null +++ b/src/servers/app/Layer.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef LAYER_H +#define LAYER_H + + +#include "ServerPicture.h" + +#include "IntPoint.h" + + +class AlphaMask; +class Canvas; +class UtilityBitmap; + + +class Layer : public ServerPicture { +public: + Layer(uint8 opacity); + virtual ~Layer(); + + void PushLayer(Layer* layer); + Layer* PopLayer(); + + UtilityBitmap* RenderToBitmap(Canvas* canvas); + + IntPoint LeftTopOffset() const; + uint8 Opacity() const; + +private: + BRect _DetermineBoundingBox(Canvas* canvas); + UtilityBitmap* _AllocateBitmap(const BRect& bounds); + +private: + uint8 fOpacity; + IntPoint fLeftTopOffset; +}; + + +#endif // LAYER_H diff --git a/src/servers/app/PictureBoundingBoxPlayer.cpp b/src/servers/app/PictureBoundingBoxPlayer.cpp index 08ff0f7f0d..b330a78265 100644 --- a/src/servers/app/PictureBoundingBoxPlayer.cpp +++ b/src/servers/app/PictureBoundingBoxPlayer.cpp @@ -16,6 +16,7 @@ #include "DrawState.h" #include "FontManager.h" +#include "Layer.h" #include "ServerApp.h" #include "ServerBitmap.h" #include "ServerFont.h" @@ -704,6 +705,18 @@ set_transform(BoundingBoxState* state, BAffineTransform transform) } +static void +determine_bounds_nested_layer(BoundingBoxState* state, Layer* layer) +{ + TRACE_BB("%p nested layer\n", state); + + BRect boundingBox; + PictureBoundingBoxPlayer::Play(layer, state->GetDrawState(), &boundingBox); + if (boundingBox.IsValid()) + state->IncludeRect(boundingBox); +} + + const static void* kTableEntries[] = { (const void*)nop, // 0 (const void*)move_pen_by, @@ -753,7 +766,8 @@ const static void* kTableEntries[] = { (const void*)nop, // 45 (const void*)set_font_face, (const void*)set_blending_mode, - (const void*)set_transform // 48 + (const void*)set_transform, + (const void*)determine_bounds_nested_layer // 49 }; diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index f7f2babaad..9e060d2ac6 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -18,6 +18,7 @@ #include "DrawingEngine.h" #include "DrawState.h" #include "FontManager.h" +#include "Layer.h" #include "ServerApp.h" #include "ServerBitmap.h" #include "ServerFont.h" @@ -784,6 +785,13 @@ set_transform(Canvas* canvas, BAffineTransform transform) } +static void +blend_layer(Canvas* canvas, Layer* layer) +{ + canvas->BlendLayer(layer); +} + + static void reserved() { @@ -839,7 +847,8 @@ const static void* kTableEntries[] = { (const void*)reserved, // 45 (const void*)set_font_face, (const void*)set_blending_mode, - (const void*)set_transform // 48 + (const void*)set_transform, + (const void*)blend_layer // 49 }; diff --git a/src/servers/app/ServerPicture.h b/src/servers/app/ServerPicture.h index 5b008e22d9..d44d2a5282 100644 --- a/src/servers/app/ServerPicture.h +++ b/src/servers/app/ServerPicture.h @@ -1,10 +1,11 @@ /* - * Copyright 2001-2010, Haiku. + * Copyright 2001-2015, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Stefano Ceccherini + * Julian Harnath */ #ifndef SERVER_PICTURE_H #define SERVER_PICTURE_H @@ -35,7 +36,7 @@ public: ServerPicture(const ServerPicture& other); ServerPicture(const char* fileName, int32 offset); - ~ServerPicture(); + virtual ~ServerPicture(); int32 Token() { return fToken; } bool SetOwner(ServerApp* owner); diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 81892f939c..d0b1d01476 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku. + * Copyright 2001-2015, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -11,6 +11,7 @@ * Artur Wyszynski * Philippe Saint-Pierre * Brecht Machiels + * Julian Harnath */ @@ -61,6 +62,7 @@ #include "DrawingEngine.h" #include "DrawState.h" #include "HWInterface.h" +#include "Layer.h" #include "Overlay.h" #include "ProfileMessageSupport.h" #include "RenderingBuffer.h" @@ -2184,6 +2186,21 @@ fDesktop->LockSingleWindow(); break; } + case AS_VIEW_BEGIN_LAYER: + { + DTRACE(("ServerWindow %s: Message AS_VIEW_BEGIN_LAYER\n", + Title())); + + uint8 opacity; + link.Read(&opacity); + + Layer* layer = new(std::nothrow) Layer(opacity); + if (layer == NULL) + break; + fCurrentView->SetPicture(layer); + break; + } + default: _DispatchViewDrawingMessage(code, link); break; @@ -2922,6 +2939,15 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, break; } + case AS_VIEW_END_LAYER: + { + DTRACE(("ServerWindow %s: Message AS_VIEW_END_LAYER\n", + Title())); + fCurrentView->BlendAllLayers(); + fCurrentView->SetPicture(NULL); + break; + } + default: BString codeString; string_for_message_code(code, codeString); @@ -2983,6 +3009,11 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) int8 drawingMode; link.Read(&drawingMode); + if (dynamic_cast(picture) != NULL) { + // drawing mode changes not allowed in layers + break; + } + picture->WriteSetDrawingMode((drawing_mode)drawingMode); fCurrentView->CurrentState()->SetDrawingMode( @@ -3416,6 +3447,42 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) fLink.Flush(); return true; } + + case AS_VIEW_BEGIN_LAYER: + { + uint8 opacity; + link.Read(&opacity); + + Layer* layer = dynamic_cast(picture); + if (layer == NULL) + break; + + Layer* nextLayer = new(std::nothrow) Layer(opacity); + if (nextLayer == NULL) + break; + + nextLayer->PushLayer(layer); + fCurrentView->SetPicture(nextLayer); + break; + } + + case AS_VIEW_END_LAYER: + { + Layer* layer = dynamic_cast(picture); + if (layer == NULL) + break; + + Layer* previousLayer = layer->PopLayer(); + if (previousLayer == NULL) { + // End last layer + return false; + } + fCurrentView->SetPicture(previousLayer); + + previousLayer->WriteBlendLayer(layer); + break; + } + /* case AS_VIEW_SET_BLENDING_MODE: { diff --git a/src/servers/app/View.cpp b/src/servers/app/View.cpp index b1375327a3..eaf80412fc 100644 --- a/src/servers/app/View.cpp +++ b/src/servers/app/View.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014, Haiku, Inc. + * Copyright (c) 2001-2015, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: @@ -9,6 +9,7 @@ * Stephan Aßmus * Marcus Overhagen * Adrien Destugues */ #include "View.h" @@ -19,6 +20,7 @@ #include "Desktop.h" #include "DrawingEngine.h" #include "DrawState.h" +#include "Layer.h" #include "Overlay.h" #include "ServerApp.h" #include "ServerBitmap.h" @@ -27,6 +29,7 @@ #include "ServerWindow.h" #include "Window.h" +#include "BitmapHWInterface.h" #include "drawing_support.h" #include @@ -972,6 +975,18 @@ View::SetPicture(ServerPicture* picture) } +void +View::BlendAllLayers() +{ + if (fPicture == NULL) + return; + Layer* layer = dynamic_cast(fPicture); + if (layer == NULL) + return; + BlendLayer(layer); +} + + void View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, BRegion* windowContentClipping, bool deep) diff --git a/src/servers/app/View.h b/src/servers/app/View.h index 5550fcb961..fbf66409b9 100644 --- a/src/servers/app/View.h +++ b/src/servers/app/View.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014, Haiku, Inc. + * Copyright (c) 2001-2015, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: @@ -9,6 +9,7 @@ * Stephan Aßmus * Marcus Overhagen * Adrien Destugues + * Julian Harnath */ #ifndef VIEW_H #define VIEW_H @@ -156,6 +157,8 @@ public: ServerPicture* Picture() const { return fPicture; } + void BlendAllLayers(); + // for background clearing virtual void Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index 73d8b98009..de8d379f46 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -154,6 +154,7 @@ SharedLibrary libtestappserver.so : BitmapHWInterface.cpp Canvas.cpp DesktopSettings.cpp + Layer.cpp OffscreenServerWindow.cpp OffscreenWindow.cpp PictureBoundingBoxPlayer.cpp From d727d0743b2f8bebaf895dc2a661a97fbf3f226a Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Fri, 31 Jul 2015 21:01:12 +0200 Subject: [PATCH 16/29] app_server: fix clearing user clipping from BPicture * Clipping was set to an empty region instead of being cleared when inside a BPicture --- src/servers/app/ServerPicture.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index 9e060d2ac6..bec689149f 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -538,10 +538,14 @@ set_clipping_rects(Canvas* canvas, const BRect* rects, { // TODO: This might be too slow, we should copy the rects // directly to BRegion's internal data - BRegion region; - for (uint32 c = 0; c < numRects; c++) - region.Include(rects[c]); - canvas->SetUserClipping(®ion); + if (numRects == 0) + canvas->SetUserClipping(NULL); + else { + BRegion region; + for (uint32 c = 0; c < numRects; c++) + region.Include(rects[c]); + canvas->SetUserClipping(®ion); + } canvas->UpdateCurrentDrawingRegion(); } From 3d12d3a832462309cca084b87a89d008ae50d5e9 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 3 Aug 2015 18:50:58 +0200 Subject: [PATCH 17/29] app_server: special handling for opaque/invisible layers * Opaque layers (opacity = 255) don't need to use an intermediate bitmap (and everything that comes with it) for drawing at all, they can just directly draw onto the underlying canvas or layer. (WebKit likes to use plenty of opaque layers) * Invisible layers (opacity = 0) can simply be ignored. --- src/servers/app/Canvas.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index e79b1527a2..3c53676e62 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -223,6 +223,15 @@ Canvas::ScreenToPenTransform() const GCC_2_NRV(transform) void Canvas::BlendLayer(Layer* layer) { + if (layer->Opacity() == 255) { + layer->Play(this); + layer->ReleaseReference(); + return; + } else if (layer->Opacity() == 0) { + layer->ReleaseReference(); + return; + } + UtilityBitmap* layerBitmap = layer->RenderToBitmap(this); if (layerBitmap == NULL) return; From c77b945acdc5bdad234c2d5cd35ffb158e1ae9d8 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 3 Aug 2015 18:53:20 +0200 Subject: [PATCH 18/29] app_server: allow drawing mode changes in opaque layers * For better performance, we allow doing drawing mode changes (and thus, B_OP_COPY) again when inside an opaque layer which has only other opaque layers below it in the layer stack. As soon as the first non-opaque layer turns up in the stack, the drawing mode will be locked to alpha composite mode, until this layer stack is ended entirely. This allows using B_OP_COPY in many cases as used by WebKit. * In the long term it would be nice to get rid of the drawing-mode lock altogether, however that would need some larger refactoring work in Painter (i.e. remove the offsetting from renderer_region again and instead implement an "exit-level transform" (support for offsets is enough) in Painter which is applied after all other transforms). --- src/servers/app/DrawState.cpp | 19 ++++++++++++++++--- src/servers/app/DrawState.h | 3 +++ src/servers/app/ServerWindow.cpp | 19 ++++++++++++++----- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index 2eaa39f5de..de5c81173e 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -47,6 +47,7 @@ DrawState::DrawState() fDrawingMode(B_OP_COPY), fAlphaSrcMode(B_PIXEL_ALPHA), fAlphaFncMode(B_ALPHA_OVERLAY), + fDrawingModeLocked(false), fPenLocation(0.0f, 0.0f), fPenSize(1.0f), @@ -81,6 +82,7 @@ DrawState::DrawState(const DrawState& other) fDrawingMode(other.fDrawingMode), fAlphaSrcMode(other.fAlphaSrcMode), fAlphaFncMode(other.fAlphaFncMode), + fDrawingModeLocked(other.fDrawingModeLocked), fPenLocation(other.fPenLocation), fPenSize(other.fPenSize), @@ -527,18 +529,29 @@ DrawState::SetPattern(const Pattern& pattern) void DrawState::SetDrawingMode(drawing_mode mode) { - fDrawingMode = mode; + if (!fDrawingModeLocked) + fDrawingMode = mode; } void DrawState::SetBlendingMode(source_alpha srcMode, alpha_function fncMode) { - fAlphaSrcMode = srcMode; - fAlphaFncMode = fncMode; + if (!fDrawingModeLocked) { + fAlphaSrcMode = srcMode; + fAlphaFncMode = fncMode; + } } +void +DrawState::SetDrawingModeLocked(bool locked) +{ + fDrawingModeLocked = locked; +} + + + void DrawState::SetPenLocation(BPoint location) { diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index e67e1ed2af..7e7274cdf3 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -111,6 +111,8 @@ public: alpha_function AlphaFncMode() const { return fAlphaFncMode; } + void SetDrawingModeLocked(bool locked); + // pen void SetPenLocation(BPoint location); BPoint PenLocation() const; @@ -173,6 +175,7 @@ protected: drawing_mode fDrawingMode; source_alpha fAlphaSrcMode; alpha_function fAlphaFncMode; + bool fDrawingModeLocked; BPoint fPenLocation; float fPenSize; diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index d0b1d01476..369e13454a 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -2197,6 +2197,13 @@ fDesktop->LockSingleWindow(); Layer* layer = new(std::nothrow) Layer(opacity); if (layer == NULL) break; + + if (opacity != 255) { + fCurrentView->CurrentState()->SetDrawingMode(B_OP_ALPHA); + fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + fCurrentView->CurrentState()->SetDrawingModeLocked(true); + } + fCurrentView->SetPicture(layer); break; } @@ -2945,6 +2952,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, Title())); fCurrentView->BlendAllLayers(); fCurrentView->SetPicture(NULL); + fCurrentView->CurrentState()->SetDrawingModeLocked(false); break; } @@ -3009,11 +3017,6 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) int8 drawingMode; link.Read(&drawingMode); - if (dynamic_cast(picture) != NULL) { - // drawing mode changes not allowed in layers - break; - } - picture->WriteSetDrawingMode((drawing_mode)drawingMode); fCurrentView->CurrentState()->SetDrawingMode( @@ -3461,6 +3464,12 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) if (nextLayer == NULL) break; + if (opacity != 255) { + fCurrentView->CurrentState()->SetDrawingMode(B_OP_ALPHA); + fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + fCurrentView->CurrentState()->SetDrawingModeLocked(true); + } + nextLayer->PushLayer(layer); fCurrentView->SetPicture(nextLayer); break; From e353fe396a362ab9c10a7c058e76007939370f66 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Mon, 3 Aug 2015 23:32:07 +0200 Subject: [PATCH 19/29] app_server Painter: refactoring, extract bitmap drawing * Extract bitmap drawing from Painter into separate class Painter::BitmapPainter. This will allow to add new optimized drawing modes without making Painter larger. * BitmapPainter itself is further decomposed into separate (method object) structs per drawing mode (currently, those are: generic, no scale, nearest neighbor, bilinear). New optimized implementations can be added by writing additional method objects and calling them from BitmapPainter. * DrawBitmapNoScale and DrawBitmapBilinear are implemented using CRTP. This removes the function pointer in the 'no scale' version, which was previously used to select the row copy type. In the bilinear version it untangles the three variants (default, low filter ratio, SIMD) into separate methods. * While BitmapPainter is a nested class in Painter, the specialized method objects are not. Instead, the AGG-specific data fields from Painter are moved into a new struct PainterAggInterface. This struct is passed to the method objects and allows them to access the Painter's AGG renderer/rasterizer/scanline containers/etc. Alternatives would be to make all the involved structs friends of Painter, or nesting them all, or exposing all of Painter's internals via getter methods -- all of these would be quite messy. The details of the bitmap painting implementations are intentionally hidden from Painter: there is no need for it to know about their internals -- it does not even know their type names. (Nesting or making them friend would expose their type names to Painter.) Furthermore, there is another level of information hiding between BitmapPainter and the DrawBitmap[...] method objects. BitmapPainter itself only needs to decide that it uses e.g. the bilinear version. It has no knowledge that DrawBitmapBilinear is internally made out of several structs implementing specially optimized versions. * Refactoring only, no functional change intended. Performance should be unaffected. --- src/servers/app/drawing/Painter/Jamfile | 4 + src/servers/app/drawing/Painter/Painter.cpp | 1055 +---------------- src/servers/app/drawing/Painter/Painter.h | 73 +- .../app/drawing/Painter/PainterAggInterface.h | 67 ++ .../Painter/bitmap_painter/BitmapPainter.cpp | 314 +++++ .../Painter/bitmap_painter/BitmapPainter.h | 60 + .../bitmap_painter/DrawBitmapBilinear.h | 520 ++++++++ .../bitmap_painter/DrawBitmapGeneric.h | 112 ++ .../DrawBitmapNearestNeighbor.h | 143 +++ .../bitmap_painter/DrawBitmapNoScale.h | 169 +++ .../painter_bilinear_scale.nasm | 0 11 files changed, 1425 insertions(+), 1092 deletions(-) create mode 100644 src/servers/app/drawing/Painter/PainterAggInterface.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h create mode 100644 src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h rename src/servers/app/drawing/Painter/{ => bitmap_painter}/painter_bilinear_scale.nasm (100%) diff --git a/src/servers/app/drawing/Painter/Jamfile b/src/servers/app/drawing/Painter/Jamfile index 47703c8b28..61bf424b85 100644 --- a/src/servers/app/drawing/Painter/Jamfile +++ b/src/servers/app/drawing/Painter/Jamfile @@ -12,6 +12,7 @@ UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter drawing_modes UseBuildFeatureHeaders freetype ; SEARCH_SOURCE += [ FDirName $(SUBDIR) drawing_modes ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) bitmap_painter ] ; local PAINTER_ARCH_SOURCES ; if $(TARGET_ARCH) = x86 { @@ -29,6 +30,9 @@ StaticLibrary libpainter.a : # drawing_modes PixelFormat.cpp + # bitmap_painter + BitmapPainter.cpp + AGGTextRenderer.cpp $(PAINTER_ARCH_SOURCES) diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 1ff042ceba..45cd532cc8 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -54,6 +54,7 @@ #include #include "AlphaMask.h" +#include "BitmapPainter.h" #include "DrawingMode.h" #include "GlobalSubpixelSettings.h" #include "PatternHandler.h" @@ -86,19 +87,28 @@ using std::nothrow; #define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1); #define CHECK_CLIPPING_NO_RETURN if (!fValidClipping) return; -// Defines for SIMD support. -#define APPSERVER_SIMD_MMX (1 << 0) -#define APPSERVER_SIMD_SSE (1 << 1) -// Prototypes for assembler routines -extern "C" { - void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, - void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR); -} +// Shortcuts for accessing internal data +#define fBuffer fInternal.fBuffer +#define fPixelFormat fInternal.fPixelFormat +#define fBaseRenderer fInternal.fBaseRenderer +#define fUnpackedScanline fInternal.fUnpackedScanline +#define fPackedScanline fInternal.fPackedScanline +#define fRasterizer fInternal.fRasterizer +#define fRenderer fInternal.fRenderer +#define fRendererBin fInternal.fRendererBin +#define fSubpixPackedScanline fInternal.fSubpixPackedScanline +#define fSubpixUnpackedScanline fInternal.fSubpixUnpackedScanline +#define fSubpixRasterizer fInternal.fSubpixRasterizer +#define fSubpixRenderer fInternal.fSubpixRenderer +#define fMaskedUnpackedScanline fInternal.fMaskedUnpackedScanline +#define fPath fInternal.fPath +#define fCurve fInternal.fCurve + static uint32 detect_simd(); -static uint32 sSIMDFlags = detect_simd(); +uint32 gSIMDFlags = detect_simd(); /*! Detect SIMD flags for use in AppServer. Checks all CPUs in the system @@ -170,23 +180,7 @@ detect_simd() Painter::Painter() : - fBuffer(), - fPixelFormat(fBuffer, &fPatternHandler), - fBaseRenderer(fPixelFormat), - fUnpackedScanline(), - fPackedScanline(), - fRasterizer(), - fRenderer(fBaseRenderer), - fRendererBin(fBaseRenderer), - fSubpixPackedScanline(), - fSubpixUnpackedScanline(), - fSubpixRasterizer(), - fSubpixRenderer(fBaseRenderer), - fMaskedUnpackedScanline(NULL), - - fPath(), - fCurve(fPath), - + fInternal(fPatternHandler), fSubpixelPrecise(false), fValidClipping(false), fDrawingText(false), @@ -1431,27 +1425,11 @@ Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, BRect touched = TransformAlignAndClipRect(viewRect); - if (bitmap && bitmap->IsValid() && touched.IsValid()) { - // the native bitmap coordinate system - BRect actualBitmapRect(bitmap->Bounds()); - - TRACE("Painter::DrawBitmap()\n"); - TRACE(" actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - actualBitmapRect.left, actualBitmapRect.top, - actualBitmapRect.right, actualBitmapRect.bottom); - TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - bitmapRect.left, bitmapRect.top, bitmapRect.right, - bitmapRect.bottom); - TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); - - agg::rendering_buffer srcBuffer; - srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), - bitmap->BytesPerRow()); - - _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, - bitmapRect, viewRect, options); + if (touched.IsValid()) { + BitmapPainter bitmapPainter(this, bitmap, options); + bitmapPainter.Draw(bitmapRect, viewRect); } + return touched; } @@ -1642,61 +1620,6 @@ Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const } -// copy_bitmap_row_cmap8_copy -static inline void -copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - const uint8* s = src; - while (numPixels--) { - const rgb_color c = colorMap[*s++]; - *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); - } -} - - -// copy_bitmap_row_cmap8_over -static inline void -copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - const uint8* s = src; - while (numPixels--) { - const rgb_color c = colorMap[*s++]; - if (c.alpha) - *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); - d++; - } -} - - -// copy_bitmap_row_bgr32_copy -static inline void -copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - memcpy(dst, src, numPixels * 4); -} - - -// copy_bitmap_row_bgr32_over -static inline void -copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - uint32* s = (uint32*)src; - while (numPixels--) { - if (*s != B_TRANSPARENT_MAGIC_RGBA32) - *(uint32*)d = *(uint32*)s; - d++; - s++; - } -} - - void Painter::_IterateShapeData(const int32& opCount, const uint32* opList, const int32& ptCount, const BPoint* points, @@ -1764,935 +1687,6 @@ Painter::_IterateShapeData(const int32& opCount, const uint32* opList, } -// copy_bitmap_row_bgr32_alpha -static inline void -copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) -{ - uint32* d = (uint32*)dst; - int32 bytes = numPixels * 4; - uint8 buffer[bytes]; - uint8* b = buffer; - while (numPixels--) { - if (src[3] == 255) { - *(uint32*)b = *(uint32*)src; - } else { - *(uint32*)b = *d; - b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; - b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; - b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; - } - d++; - b += 4; - src += 4; - } - memcpy(dst, buffer, bytes); -} - - -// _TransparentMagicToAlpha -template -void -Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width, - uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic, - BBitmap* output) const -{ - uint8* sourceRow = (uint8*)buffer; - uint8* destRow = (uint8*)output->Bits(); - uint32 destBytesPerRow = output->BytesPerRow(); - - for (uint32 y = 0; y < height; y++) { - sourcePixel* pixel = (sourcePixel*)sourceRow; - uint32* destPixel = (uint32*)destRow; - for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { - if (*pixel == transparentMagic) - *destPixel &= 0x00ffffff; - } - - sourceRow += sourceBytesPerRow; - destRow += destBytesPerRow; - } -} - - -// _DrawBitmap -void -Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, - BRect actualBitmapRect, BRect bitmapRect, BRect viewRect, - uint32 options) const -{ - if (!fValidClipping - || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) - || !viewRect.IsValid()) { - return; - } - - if (!fSubpixelPrecise) { - align_rect_to_pixels(&bitmapRect); - align_rect_to_pixels(&viewRect); - } - - TRACE("Painter::_DrawBitmap()\n"); - TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom); - TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", - viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); - - double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); - double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); - - if (xScale == 0.0 || yScale == 0.0) - return; - - // compensate for the lefttop offset the actualBitmapRect might have - // actualBitmapRect has the right size, but put it at B_ORIGIN - // bitmapRect is already in good coordinates - actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); - - // constrain rect to passed bitmap bounds - // and transfer the changes to the viewRect with the right scale - if (bitmapRect.left < actualBitmapRect.left) { - float diff = actualBitmapRect.left - bitmapRect.left; - viewRect.left += diff * xScale; - bitmapRect.left = actualBitmapRect.left; - } - if (bitmapRect.top < actualBitmapRect.top) { - float diff = actualBitmapRect.top - bitmapRect.top; - viewRect.top += diff * yScale; - bitmapRect.top = actualBitmapRect.top; - } - if (bitmapRect.right > actualBitmapRect.right) { - float diff = bitmapRect.right - actualBitmapRect.right; - viewRect.right -= diff * xScale; - bitmapRect.right = actualBitmapRect.right; - } - if (bitmapRect.bottom > actualBitmapRect.bottom) { - float diff = bitmapRect.bottom - actualBitmapRect.bottom; - viewRect.bottom -= diff * yScale; - bitmapRect.bottom = actualBitmapRect.bottom; - } - - double xOffset = viewRect.left - bitmapRect.left; - double yOffset = viewRect.top - bitmapRect.top; - - // optimized code path for B_CMAP8 and no scale - if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform - && fMaskedUnpackedScanline == NULL) { - if (format == B_CMAP8) { - if (fDrawingMode == B_OP_COPY) { - _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, - srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); - return; - } - if (fDrawingMode == B_OP_OVER) { - _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, - srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); - return; - } - } else if (format == B_RGB32) { - if (fDrawingMode == B_OP_OVER) { - _DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4, - srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); - return; - } - } - } - - BBitmap* temp = NULL; - ObjectDeleter tempDeleter; - - if ((format != B_RGBA32 && format != B_RGB32) - || (format == B_RGB32 && fDrawingMode != B_OP_COPY -#if 1 -// Enabling this would make the behavior compatible to BeOS, which -// treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in -// all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. -// B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually -// use this color, unless the alpha channel contains 255 for all other -// pixels, which is inconsistent. - && fDrawingMode != B_OP_ALPHA -#endif - )) { - temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, - B_RGBA32); - if (temp == NULL) { - fprintf(stderr, "Painter::_DrawBitmap() - " - "out of memory for creating temporary conversion bitmap\n"); - return; - } - - tempDeleter.SetTo(temp); - - status_t err = temp->ImportBits(srcBuffer.buf(), - srcBuffer.height() * srcBuffer.stride(), - srcBuffer.stride(), 0, format); - if (err < B_OK) { - fprintf(stderr, "Painter::_DrawBitmap() - " - "colorspace conversion failed: %s\n", strerror(err)); - return; - } - - // the original bitmap might have had some of the - // transaparent magic colors set that we now need to - // make transparent in our RGBA32 bitmap again. - switch (format) { - case B_RGB32: - _TransparentMagicToAlpha((uint32 *)srcBuffer.buf(), - srcBuffer.width(), srcBuffer.height(), - srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32, - temp); - break; - - // TODO: not sure if this applies to B_RGBA15 too. It - // should not because B_RGBA15 actually has an alpha - // channel itself and it should have been preserved - // when importing the bitmap. Maybe it applies to - // B_RGB16 though? - case B_RGB15: - _TransparentMagicToAlpha((uint16 *)srcBuffer.buf(), - srcBuffer.width(), srcBuffer.height(), - srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15, - temp); - break; - - default: - break; - } - - srcBuffer.attach((uint8*)temp->Bits(), - (uint32)actualBitmapRect.IntegerWidth() + 1, - (uint32)actualBitmapRect.IntegerHeight() + 1, - temp->BytesPerRow()); - } - - // maybe we can use an optimized version if there is no scale - if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform - && fMaskedUnpackedScanline == NULL) { - if (fDrawingMode == B_OP_COPY) { - _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer, - (int32)xOffset, (int32)yOffset, viewRect); - return; - } - if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA - && fAlphaSrcMode == B_PIXEL_ALPHA - && fAlphaFncMode == B_ALPHA_OVERLAY)) { - _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer, - (int32)xOffset, (int32)yOffset, viewRect); - return; - } - } - - if (fDrawingMode == B_OP_COPY && fIdentityTransform - && fMaskedUnpackedScanline == NULL) { - if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { - _DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, - yScale, viewRect); - } else { - _DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset, - xScale, yScale, viewRect); - } - return; - } - - // for all other cases (non-optimized drawing mode or scaled drawing) - _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect, - options); -} - - -#define DEBUG_DRAW_BITMAP 0 - - -// _DrawBitmapNoScale32 -template -void -Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, - agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset, - BRect viewRect) const -{ - // NOTE: this would crash if viewRect was large enough to read outside the - // bitmap, so make sure this is not the case before calling this function! - uint8* dst = fBuffer.row_ptr(0); - uint32 dstBPR = fBuffer.stride(); - - const uint8* src = srcBuffer.row_ptr(0); - uint32 srcBPR = srcBuffer.stride(); - - int32 left = (int32)viewRect.left; - int32 top = (int32)viewRect.top; - int32 right = (int32)viewRect.right; - int32 bottom = (int32)viewRect.bottom; - -#if DEBUG_DRAW_BITMAP -if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || - right - xOffset >= (int32)srcBuffer.width() || - top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || - bottom - yOffset >= (int32)srcBuffer.height()) { - - char message[256]; - sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " - "(%d, %d) (%ld, %ld)", - left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, - srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); - debugger(message); -} -#endif - - const rgb_color* colorMap = SystemPalette(); - - // copy rects, iterate over clipping boxes - fBaseRenderer.first_clip_box(); - do { - int32 x1 = max_c(fBaseRenderer.xmin(), left); - int32 x2 = min_c(fBaseRenderer.xmax(), right); - if (x1 <= x2) { - int32 y1 = max_c(fBaseRenderer.ymin(), top); - int32 y2 = min_c(fBaseRenderer.ymax(), bottom); - if (y1 <= y2) { - uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; - const uint8* srcHandle = src + (y1 - yOffset) * srcBPR - + (x1 - xOffset) * bytesPerSourcePixel; - - for (; y1 <= y2; y1++) { - copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap); - - dstHandle += dstBPR; - srcHandle += srcBPR; - } - } - } - } while (fBaseRenderer.next_clip_box()); -} - - -// _DrawBitmapNearestNeighborCopy32 -void -Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, double xScale, double yScale, - BRect viewRect) const -{ - //bigtime_t now = system_time(); - uint32 dstWidth = viewRect.IntegerWidth() + 1; - uint32 dstHeight = viewRect.IntegerHeight() + 1; - uint32 srcWidth = srcBuffer.width(); - uint32 srcHeight = srcBuffer.height(); - - // Do not calculate more filter weights than necessary and also - // keep the stack based allocations reasonably sized - if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) - dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; - if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) - dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; - - // When calculating less filter weights than specified by viewRect, - // we need to compensate the offset. - uint32 filterWeightXIndexOffset = 0; - uint32 filterWeightYIndexOffset = 0; - if (fClippingRegion->Frame().left > viewRect.left) { - filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left - - viewRect.left); - } - if (fClippingRegion->Frame().top > viewRect.top) { - filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top - - viewRect.top); - } - - // should not pose a problem with stack overflows - // (needs around 6Kb for 1920x1200) - uint16 xIndices[dstWidth]; - uint16 yIndices[dstHeight]; - - // Extract the cropping information for the source bitmap, - // If only a part of the source bitmap is to be drawn with scale, - // the offset will be different from the viewRect left top corner. - int32 xBitmapShift = (int32)(viewRect.left - xOffset); - int32 yBitmapShift = (int32)(viewRect.top - yOffset); - - for (uint32 i = 0; i < dstWidth; i++) { - // index into source - uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth - / (srcWidth * xScale)); - // round down to get the left pixel - xIndices[i] = index; - // handle cropped source bitmap - xIndices[i] += xBitmapShift; - // precompute index for 32 bit pixels - xIndices[i] *= 4; - } - - for (uint32 i = 0; i < dstHeight; i++) { - // index into source - uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight - / (srcHeight * yScale)); - // round down to get the top pixel - yIndices[i] = index; - // handle cropped source bitmap - yIndices[i] += yBitmapShift; - } -//printf("X: %d ... %d, %d (%ld or %f)\n", -// xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth, -// srcWidth * xScale); -//printf("Y: %d ... %d, %d (%ld or %f)\n", -// yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight, -// srcHeight * yScale); - - const int32 left = (int32)viewRect.left; - const int32 top = (int32)viewRect.top; - const int32 right = (int32)viewRect.right; - const int32 bottom = (int32)viewRect.bottom; - - const uint32 dstBPR = fBuffer.stride(); - - // iterate over clipping boxes - fBaseRenderer.first_clip_box(); - do { - const int32 x1 = max_c(fBaseRenderer.xmin(), left); - const int32 x2 = min_c(fBaseRenderer.xmax(), right); - if (x1 > x2) - continue; - - int32 y1 = max_c(fBaseRenderer.ymin(), top); - int32 y2 = min_c(fBaseRenderer.ymax(), bottom); - if (y1 > y2) - continue; - - // buffer offset into destination - uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; - - // x and y are needed as indeces into the wheight arrays, so the - // offset into the target buffer needs to be compensated - const int32 xIndexL = x1 - left - filterWeightXIndexOffset; - const int32 xIndexR = x2 - left - filterWeightXIndexOffset; - y1 -= top + filterWeightYIndexOffset; - y2 -= top + filterWeightYIndexOffset; - -//printf("x: %ld - %ld\n", xIndexL, xIndexR); -//printf("y: %ld - %ld\n", y1, y2); - - for (; y1 <= y2; y1++) { - // buffer offset into source (top row) - register const uint8* src = srcBuffer.row_ptr(yIndices[y1]); - // buffer handle for destination to be incremented per pixel - register uint32* d = (uint32*)dst; - - for (int32 x = xIndexL; x <= xIndexR; x++) { - *d = *(uint32*)(src + xIndices[x]); - d++; - } - dst += dstBPR; - } - } while (fBaseRenderer.next_clip_box()); - -//printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); -} - - -// _DrawBitmapBilinearCopy32 -void -Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, double xScale, double yScale, - BRect viewRect) const -{ - //bigtime_t now = system_time(); - uint32 dstWidth = viewRect.IntegerWidth() + 1; - uint32 dstHeight = viewRect.IntegerHeight() + 1; - uint32 srcWidth = srcBuffer.width(); - uint32 srcHeight = srcBuffer.height(); - - // Do not calculate more filter weights than necessary and also - // keep the stack based allocations reasonably sized - if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) - dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; - if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) - dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; - - // When calculating less filter weights than specified by viewRect, - // we need to compensate the offset. - uint32 filterWeightXIndexOffset = 0; - uint32 filterWeightYIndexOffset = 0; - if (fClippingRegion->Frame().left > viewRect.left) { - filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left - - viewRect.left); - } - if (fClippingRegion->Frame().top > viewRect.top) { - filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top - - viewRect.top); - } - - struct FilterInfo { - uint16 index; // index into source bitmap row/column - uint16 weight; // weight of the pixel at index [0..255] - }; - -//#define FILTER_INFOS_ON_HEAP -#ifdef FILTER_INFOS_ON_HEAP - FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth]; - FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight]; - if (xWeights == NULL || yWeights == NULL) { - delete[] xWeights; - delete[] yWeights; - return; - } -#else - // stack based saves about 200µs on 1.85 GHz Core 2 Duo - // should not pose a problem with stack overflows - // (needs around 12Kb for 1920x1200) - FilterInfo xWeights[dstWidth]; - FilterInfo yWeights[dstHeight]; -#endif - - // Extract the cropping information for the source bitmap, - // If only a part of the source bitmap is to be drawn with scale, - // the offset will be different from the viewRect left top corner. - int32 xBitmapShift = (int32)(viewRect.left - xOffset); - int32 yBitmapShift = (int32)(viewRect.top - yOffset); - - for (uint32 i = 0; i < dstWidth; i++) { - // fractional index into source - // NOTE: It is very important to calculate the fractional index - // into the source pixel grid like this to prevent out of bounds - // access! It will result in the rightmost pixel of the destination - // to access the rightmost pixel of the source with a weighting - // of 255. This in turn will trigger an optimization in the loop - // that also prevents out of bounds access. - float index = (i + filterWeightXIndexOffset) * (srcWidth - 1) - / (srcWidth * xScale - 1); - // round down to get the left pixel - xWeights[i].index = (uint16)index; - xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255); - // handle cropped source bitmap - xWeights[i].index += xBitmapShift; - // precompute index for 32 bit pixels - xWeights[i].index *= 4; - } - - for (uint32 i = 0; i < dstHeight; i++) { - // fractional index into source - // NOTE: It is very important to calculate the fractional index - // into the source pixel grid like this to prevent out of bounds - // access! It will result in the bottommost pixel of the destination - // to access the bottommost pixel of the source with a weighting - // of 255. This in turn will trigger an optimization in the loop - // that also prevents out of bounds access. - float index = (i + filterWeightYIndexOffset) * (srcHeight - 1) - / (srcHeight * yScale - 1); - // round down to get the top pixel - yWeights[i].index = (uint16)index; - yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255); - // handle cropped source bitmap - yWeights[i].index += yBitmapShift; - } -//printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", -// xWeights[0].index, xWeights[0].weight, -// xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, -// xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, -// dstWidth); -//printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", -// yWeights[0].index, yWeights[0].weight, -// yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, -// yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, -// dstHeight); - - const int32 left = (int32)viewRect.left; - const int32 top = (int32)viewRect.top; - const int32 right = (int32)viewRect.right; - const int32 bottom = (int32)viewRect.bottom; - - const uint32 dstBPR = fBuffer.stride(); - const uint32 srcBPR = srcBuffer.stride(); - - // Figure out which version of the code we want to use... - enum { - kOptimizeForLowFilterRatio = 0, - kUseDefaultVersion, - kUseSIMDVersion - }; - - int codeSelect = kUseDefaultVersion; - - uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; - if ((sSIMDFlags & neededSIMDFlags) == neededSIMDFlags) - codeSelect = kUseSIMDVersion; - else { - if (xScale == yScale && (xScale == 1.5 || xScale == 2.0 - || xScale == 2.5 || xScale == 3.0)) { - codeSelect = kOptimizeForLowFilterRatio; - } - } - - // iterate over clipping boxes - fBaseRenderer.first_clip_box(); - do { - const int32 x1 = max_c(fBaseRenderer.xmin(), left); - const int32 x2 = min_c(fBaseRenderer.xmax(), right); - if (x1 > x2) - continue; - - int32 y1 = max_c(fBaseRenderer.ymin(), top); - int32 y2 = min_c(fBaseRenderer.ymax(), bottom); - if (y1 > y2) - continue; - - // buffer offset into destination - uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; - - // x and y are needed as indeces into the wheight arrays, so the - // offset into the target buffer needs to be compensated - const int32 xIndexL = x1 - left - filterWeightXIndexOffset; - const int32 xIndexR = x2 - left - filterWeightXIndexOffset; - y1 -= top + filterWeightYIndexOffset; - y2 -= top + filterWeightYIndexOffset; - -//printf("x: %ld - %ld\n", xIndexL, xIndexR); -//printf("y: %ld - %ld\n", y1, y2); - - switch (codeSelect) { - case kOptimizeForLowFilterRatio: - { - // In this mode, we anticipate to hit many destination pixels - // that map directly to a source pixel, we have more branches - // in the inner loop but save time because of the special - // cases. If there are too few direct hit pixels, the branches - // only waste time. - for (; y1 <= y2; y1++) { - // cache the weight of the top and bottom row - const uint16 wTop = yWeights[y1].weight; - const uint16 wBottom = 255 - yWeights[y1].weight; - - // buffer offset into source (top row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y1].index); - // buffer handle for destination to be incremented per - // pixel - register uint8* d = dst; - - if (wTop == 255) { - for (int32 x = xIndexL; x <= xIndexR; x++) { - const uint8* s = src + xWeights[x].index; - // This case is important to prevent out - // of bounds access at bottom edge of the source - // bitmap. If the scale is low and integer, it will - // also help the speed. - if (xWeights[x].weight == 255) { - // As above, but to prevent out of bounds - // on the right edge. - *(uint32*)d = *(uint32*)s; - } else { - // Only the left and right pixels are - // interpolated, since the top row has 100% - // weight. - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - } - d += 4; - } - } else { - for (int32 x = xIndexL; x <= xIndexR; x++) { - const uint8* s = src + xWeights[x].index; - if (xWeights[x].weight == 255) { - // Prevent out of bounds access on the right - // edge or simply speed up. - const uint8* sBottom = s + srcBPR; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) - >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) - >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) - >> 8; - } else { - // calculate the weighted sum of all four - // interpolated pixels - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - // left and right of top row - uint32 t0 = (s[0] * wLeft + s[4] * wRight) - * wTop; - uint32 t1 = (s[1] * wLeft + s[5] * wRight) - * wTop; - uint32 t2 = (s[2] * wLeft + s[6] * wRight) - * wTop; - - // left and right of bottom row - s += srcBPR; - t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; - t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; - t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; - - d[0] = t0 >> 16; - d[1] = t1 >> 16; - d[2] = t2 >> 16; - } - d += 4; - } - } - dst += dstBPR; - } - break; - } - - case kUseDefaultVersion: - { - // In this mode we anticipate many pixels wich need filtering, - // there are no special cases for direct hit pixels except for - // the last column/row and the right/bottom corner pixel. - - // The last column/row handling does not need to be performed - // for all clipping rects! - int32 yMax = y2; - if (yWeights[yMax].weight == 255) - yMax--; - int32 xIndexMax = xIndexR; - if (xWeights[xIndexMax].weight == 255) - xIndexMax--; - - for (; y1 <= yMax; y1++) { - // cache the weight of the top and bottom row - const uint16 wTop = yWeights[y1].weight; - const uint16 wBottom = 255 - yWeights[y1].weight; - - // buffer offset into source (top row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y1].index); - // buffer handle for destination to be incremented per - // pixel - register uint8* d = dst; - - for (int32 x = xIndexL; x <= xIndexMax; x++) { - const uint8* s = src + xWeights[x].index; - // calculate the weighted sum of all four - // interpolated pixels - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - // left and right of top row - uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; - uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; - uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; - - // left and right of bottom row - s += srcBPR; - t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; - t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; - t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; - d[0] = t0 >> 16; - d[1] = t1 >> 16; - d[2] = t2 >> 16; - d += 4; - } - // last column of pixels if necessary - if (xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - const uint8* sBottom = s + srcBPR; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; - } - - dst += dstBPR; - } - - // last row of pixels if necessary - // buffer offset into source (bottom row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y2].index); - // buffer handle for destination to be incremented per pixel - register uint8* d = dst; - - if (yMax < y2) { - for (int32 x = xIndexL; x <= xIndexMax; x++) { - const uint8* s = src + xWeights[x].index; - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - d += 4; - } - } - - // pixel in bottom right corner if necessary - if (yMax < y2 && xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - *(uint32*)d = *(uint32*)s; - } - break; - } - -#ifdef __INTEL__ - case kUseSIMDVersion: - { - // Basically the same as the "standard" mode, but we use SIMD - // routines for the processing of the single display lines. - - // The last column/row handling does not need to be performed - // for all clipping rects! - int32 yMax = y2; - if (yWeights[yMax].weight == 255) - yMax--; - int32 xIndexMax = xIndexR; - if (xWeights[xIndexMax].weight == 255) - xIndexMax--; - - for (; y1 <= yMax; y1++) { - // cache the weight of the top and bottom row - const uint16 wTop = yWeights[y1].weight; - const uint16 wBottom = 255 - yWeights[y1].weight; - - // buffer offset into source (top row) - const uint8* src = srcBuffer.row_ptr(yWeights[y1].index); - // buffer handle for destination to be incremented per - // pixel - uint8* d = dst; - bilinear_scale_xloop_mmxsse(src, dst, xWeights, xIndexL, - xIndexMax, wTop, srcBPR); - // increase pointer by processed pixels - d += (xIndexMax - xIndexL + 1) * 4; - - // last column of pixels if necessary - if (xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - const uint8* sBottom = s + srcBPR; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; - } - - dst += dstBPR; - } - - // last row of pixels if necessary - // buffer offset into source (bottom row) - register const uint8* src - = srcBuffer.row_ptr(yWeights[y2].index); - // buffer handle for destination to be incremented per pixel - register uint8* d = dst; - - if (yMax < y2) { - for (int32 x = xIndexL; x <= xIndexMax; x++) { - const uint8* s = src + xWeights[x].index; - const uint16 wLeft = xWeights[x].weight; - const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - d += 4; - } - } - - // pixel in bottom right corner if necessary - if (yMax < y2 && xIndexMax < xIndexR) { - const uint8* s = src + xWeights[xIndexR].index; - *(uint32*)d = *(uint32*)s; - } - break; - } -#endif // __INTEL__ - } - } while (fBaseRenderer.next_clip_box()); - -#ifdef FILTER_INFOS_ON_HEAP - delete[] xWeights; - delete[] yWeights; -#endif -//printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); -} - - -// _DrawBitmapGeneric32 -void -Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, double xScale, double yScale, - BRect viewRect, uint32 options) const -{ - TRACE("Painter::_DrawBitmapGeneric32()\n"); - TRACE(" offset: %.1f, %.1f\n", xOffset, yOffset); - TRACE(" scale: %.3f, %.3f\n", xScale, yScale); - TRACE(" viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n", - viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); - // AGG pipeline - - // pixel format attached to bitmap - typedef agg::pixfmt_bgra32 pixfmt_image; - pixfmt_image pixf_img(srcBuffer); - - agg::trans_affine srcMatrix; - // NOTE: R5 seems to ignore this offset when drawing bitmaps - // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, - // -actualBitmapRect.top); - srcMatrix *= fTransform; - - agg::trans_affine imgMatrix; - imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left, - yOffset - viewRect.top); - imgMatrix *= agg::trans_affine_scaling(xScale, yScale); - imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top); - imgMatrix *= fTransform; - imgMatrix.invert(); - - // image interpolator - typedef agg::span_interpolator_linear<> interpolator_type; - interpolator_type interpolator(imgMatrix); - - // scanline allocator - agg::span_allocator spanAllocator; - - // image accessor attached to pixel format of bitmap - typedef agg::image_accessor_clone source_type; - source_type source(pixf_img); - - // clip to the current clipping region's frame - if (fIdentityTransform) - viewRect = viewRect & fClippingRegion->Frame(); - // convert to pixel coords (versus pixel indices) - viewRect.right++; - viewRect.bottom++; - - // path enclosing the bitmap - fPath.remove_all(); - fPath.move_to(viewRect.left, viewRect.top); - fPath.line_to(viewRect.right, viewRect.top); - fPath.line_to(viewRect.right, viewRect.bottom); - fPath.line_to(viewRect.left, viewRect.bottom); - fPath.close_polygon(); - - agg::conv_transform transformedPath(fPath, srcMatrix); - fRasterizer.reset(); - fRasterizer.add_path(transformedPath); - - if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { - // image filter (bilinear) - typedef agg::span_image_filter_rgba_bilinear< - source_type, interpolator_type> span_gen_type; - span_gen_type spanGenerator(source, interpolator); - - // render the path with the bitmap as scanline fill - if (fMaskedUnpackedScanline != NULL) { - agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } else { - agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } - } else { - // image filter (nearest neighbor) - typedef agg::span_image_filter_rgba_nn< - source_type, interpolator_type> span_gen_type; - span_gen_type spanGenerator(source, interpolator); - - // render the path with the bitmap as scanline fill - if (fMaskedUnpackedScanline != NULL) { - agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } else { - agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, - fBaseRenderer, spanAllocator, spanGenerator); - } - } -} - - // _InvertRect32 void Painter::_InvertRect32(BRect r) const @@ -3132,4 +2126,3 @@ Painter::_RasterizePath(VertexSource& path, const BGradient& gradient, gradientRenderer); } } - diff --git a/src/servers/app/drawing/Painter/Painter.h b/src/servers/app/drawing/Painter/Painter.h index 4402113438..fc22b08193 100644 --- a/src/servers/app/drawing/Painter/Painter.h +++ b/src/servers/app/drawing/Painter/Painter.h @@ -14,6 +14,7 @@ #include "AGGTextRenderer.h" #include "FontManager.h" +#include "PainterAggInterface.h" #include "PatternHandler.h" #include "ServerFont.h" #include "Transformable.h" @@ -21,7 +22,6 @@ #include "defines.h" #include -#include #include #include @@ -43,6 +43,11 @@ class ServerBitmap; class ServerFont; +// Defines for SIMD support. +#define APPSERVER_SIMD_MMX (1 << 0) +#define APPSERVER_SIMD_SSE (1 << 1) + + class Painter { public: Painter(); @@ -68,6 +73,8 @@ public: inline bool IsIdentityTransform() const { return fIdentityTransform; } + const Transformable& Transform() const + { return fTransform; } void SetHighColor(const rgb_color& color); inline rgb_color HighColor() const @@ -273,41 +280,6 @@ private: const BPoint& viewToScreenOffset, float viewScale) const; - template - void _TransparentMagicToAlpha(sourcePixel *buffer, - uint32 width, uint32 height, - uint32 sourceBytesPerRow, - sourcePixel transparentMagic, - BBitmap *output) const; - - void _DrawBitmap(agg::rendering_buffer& srcBuffer, - color_space format, - BRect actualBitmapRect, - BRect bitmapRect, BRect viewRect, - uint32 bitmapFlags) const; - template - void _DrawBitmapNoScale32( F copyRowFunction, - uint32 bytesPerSourcePixel, - agg::rendering_buffer& srcBuffer, - int32 xOffset, int32 yOffset, - BRect viewRect) const; - void _DrawBitmapNearestNeighborCopy32( - agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, - double xScale, double yScale, - BRect viewRect) const; - void _DrawBitmapBilinearCopy32( - agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, - double xScale, double yScale, - BRect viewRect) const; - void _DrawBitmapGeneric32( - agg::rendering_buffer& srcBuffer, - double xOffset, double yOffset, - double xScale, double yScale, - BRect viewRect, - uint32 bitmapFlags) const; - void _InvertRect32(BRect r) const; void _BlendRect32(const BRect& r, const rgb_color& c) const; @@ -356,33 +328,12 @@ private: int gradientStop = 100) const; private: - mutable agg::rendering_buffer fBuffer; + class BitmapPainter; - // AGG rendering and rasterization classes - pixfmt fPixelFormat; - mutable renderer_base fBaseRenderer; + friend class BitmapPainter; // needed only for gcc2 - // Regular drawing mode: pixel-aligned, no alpha masking - mutable scanline_unpacked_type fUnpackedScanline; - mutable scanline_packed_type fPackedScanline; - mutable rasterizer_type fRasterizer; - mutable renderer_type fRenderer; - - // Fast mode: no antialiasing needed (horizontal/vertical lines, ...) - mutable renderer_bin_type fRendererBin; - - // Subpixel mode - mutable scanline_packed_subpix_type fSubpixPackedScanline; - mutable scanline_unpacked_subpix_type fSubpixUnpackedScanline; - mutable rasterizer_subpix_type fSubpixRasterizer; - mutable renderer_subpix_type fSubpixRenderer; - - // Alpha-Masked mode: for ClipToPicture - // (this uses the standard rasterizer and renderer) - mutable scanline_unpacked_masked_type* fMaskedUnpackedScanline; - - mutable agg::path_storage fPath; - mutable agg::conv_curve fCurve; +private: + mutable PainterAggInterface fInternal; // for internal coordinate rounding/transformation bool fSubpixelPrecise : 1; diff --git a/src/servers/app/drawing/Painter/PainterAggInterface.h b/src/servers/app/drawing/Painter/PainterAggInterface.h new file mode 100644 index 0000000000..57e9c6fa7e --- /dev/null +++ b/src/servers/app/drawing/Painter/PainterAggInterface.h @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2007, Stephan Aßmus . + * Copyright 2008, Andrej Spielmann . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef PAINTER_DATA_H +#define PAINTER_DATA_H + + +#include "defines.h" + +#include + + +struct PainterAggInterface { + PainterAggInterface(PatternHandler& patternHandler) + : + fBuffer(), + fPixelFormat(fBuffer, &patternHandler), + fBaseRenderer(fPixelFormat), + fUnpackedScanline(), + fPackedScanline(), + fRasterizer(), + fRenderer(fBaseRenderer), + fRendererBin(fBaseRenderer), + fSubpixPackedScanline(), + fSubpixUnpackedScanline(), + fSubpixRasterizer(), + fSubpixRenderer(fBaseRenderer), + fMaskedUnpackedScanline(NULL), + fPath(), + fCurve(fPath) + { + } + + agg::rendering_buffer fBuffer; + + // AGG rendering and rasterization classes + pixfmt fPixelFormat; + renderer_base fBaseRenderer; + + // Regular drawing mode: pixel-aligned, no alpha masking + scanline_unpacked_type fUnpackedScanline; + scanline_packed_type fPackedScanline; + rasterizer_type fRasterizer; + renderer_type fRenderer; + + // Fast mode: no antialiasing needed (horizontal/vertical lines, ...) + renderer_bin_type fRendererBin; + + // Subpixel mode + scanline_packed_subpix_type fSubpixPackedScanline; + scanline_unpacked_subpix_type fSubpixUnpackedScanline; + rasterizer_subpix_type fSubpixRasterizer; + renderer_subpix_type fSubpixRenderer; + + // Alpha-Masked mode: for ClipToPicture + // (this uses the standard rasterizer and renderer) + scanline_unpacked_masked_type* fMaskedUnpackedScanline; + + agg::path_storage fPath; + agg::conv_curve fCurve; +}; + + +#endif // PAINTER_DATA_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp new file mode 100644 index 0000000000..a56635630d --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp @@ -0,0 +1,314 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "BitmapPainter.h" + +#include + +#include +#include +#include + +#include "DrawBitmapBilinear.h" +#include "DrawBitmapGeneric.h" +#include "DrawBitmapNearestNeighbor.h" +#include "DrawBitmapNoScale.h" +#include "drawing_support.h" +#include "ServerBitmap.h" +#include "SystemPalette.h" + + +// #define TRACE_BITMAP_PAINTER +#ifdef TRACE_BITMAP_PAINTER +# define TRACE(x...) printf(x) +#else +# define TRACE(x...) +#endif + + +Painter::BitmapPainter::BitmapPainter(const Painter* painter, + const ServerBitmap* bitmap, uint32 options) + : + fPainter(painter), + fStatus(B_NO_INIT), + fOptions(options) +{ + if (bitmap == NULL || !bitmap->IsValid()) + return; + + fBitmapBounds = bitmap->Bounds(); + fBitmapBounds.OffsetBy(-fBitmapBounds.left, -fBitmapBounds.top); + // Compensate for the lefttop offset the bitmap bounds might have + // It has the right size, but put it at B_ORIGIN + + fColorSpace = bitmap->ColorSpace(); + + fBitmap.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), + bitmap->BytesPerRow()); + + fStatus = B_OK; +} + + +void +Painter::BitmapPainter::Draw(const BRect& sourceRect, + const BRect& destinationRect) +{ + using namespace BitmapPainterPrivate; + + if (fStatus != B_OK) + return; + + TRACE("BitmapPainter::Draw()\n"); + TRACE(" bitmapBounds = (%.1f, %.1f) - (%.1f, %.1f)\n", + bitmapBounds.left, bitmapBounds.top, + bitmapBounds.right, bitmapBounds.bottom); + TRACE(" sourceRect = (%.1f, %.1f) - (%.1f, %.1f)\n", + sourceRect.left, sourceRect.top, + sourceRect.right, sourceRect.bottom); + TRACE(" destinationRect = (%.1f, %.1f) - (%.1f, %.1f)\n", + destinationRect.left, destinationRect.top, + destinationRect.right, destinationRect.bottom); + + bool success = _DetermineTransform(sourceRect, destinationRect); + if (!success) + return; + + // optimized version for no scale in CMAP8 or RGB32 OP_OVER + if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) { + if (fColorSpace == B_CMAP8) { + if (fPainter->fDrawingMode == B_OP_COPY) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 1, fOffset, fDestinationRect); + return; + } + if (fPainter->fDrawingMode == B_OP_OVER) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 1, fOffset, fDestinationRect); + return; + } + } else if (fColorSpace == B_RGB32) { + if (fPainter->fDrawingMode == B_OP_OVER) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 4, fOffset, fDestinationRect); + return; + } + } + } + + ObjectDeleter convertedBitmapDeleter; + _ConvertColorSpace(convertedBitmapDeleter); + + // optimized version if there is no scale + if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) { + if (fPainter->fDrawingMode == B_OP_COPY) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 4, fOffset, fDestinationRect); + return; + } + if (fPainter->fDrawingMode == B_OP_OVER + || (fPainter->fDrawingMode == B_OP_ALPHA + && fPainter->fAlphaSrcMode == B_PIXEL_ALPHA + && fPainter->fAlphaFncMode == B_ALPHA_OVERLAY)) { + DrawBitmapNoScale::Draw(fPainter->fInternal, + fBitmap, 4, fOffset, fDestinationRect); + return; + } + } + + // bilinear and nearest-neighbor scaled, OP_COPY only + if (fPainter->fDrawingMode == B_OP_COPY + && !_HasAffineTransform() && !_HasAlphaMask()) { + if ((fOptions & B_FILTER_BITMAP_BILINEAR) != 0) { + DrawBitmapBilinearCopy drawBilinear; + drawBilinear.Draw(fPainter, fPainter->fInternal, + fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); + } + else { + DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal, + fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); + } + return; + } + + // for all other cases (non-optimized drawing mode or scaled drawing) + DrawBitmapGeneric::Draw(fPainter, fPainter->fInternal, fBitmap, fOffset, + fScaleX, fScaleY, fDestinationRect, fOptions); +} + + +bool +Painter::BitmapPainter::_DetermineTransform(BRect sourceRect, + const BRect& destinationRect) +{ + if (!fPainter->fValidClipping + || !sourceRect.IsValid() + || !sourceRect.Intersects(fBitmapBounds) + || !destinationRect.IsValid()) { + return false; + } + + fDestinationRect = destinationRect; + + if (!fPainter->fSubpixelPrecise) { + align_rect_to_pixels(&sourceRect); + align_rect_to_pixels(&fDestinationRect); + } + + fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1); + fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1); + + if (fScaleX == 0.0 || fScaleY == 0.0) + return false; + + // constrain source rect to bitmap bounds and transfer the changes to + // the destination rect with the right scale + if (sourceRect.left < fBitmapBounds.left) { + float diff = fBitmapBounds.left - sourceRect.left; + fDestinationRect.left += diff * fScaleX; + sourceRect.left = fBitmapBounds.left; + } + if (sourceRect.top < fBitmapBounds.top) { + float diff = fBitmapBounds.top - sourceRect.top; + fDestinationRect.top += diff * fScaleY; + sourceRect.top = fBitmapBounds.top; + } + if (sourceRect.right > fBitmapBounds.right) { + float diff = sourceRect.right - fBitmapBounds.right; + fDestinationRect.right -= diff * fScaleX; + sourceRect.right = fBitmapBounds.right; + } + if (sourceRect.bottom > fBitmapBounds.bottom) { + float diff = sourceRect.bottom - fBitmapBounds.bottom; + fDestinationRect.bottom -= diff * fScaleY; + sourceRect.bottom = fBitmapBounds.bottom; + } + + fOffset.x = fDestinationRect.left - sourceRect.left; + fOffset.y = fDestinationRect.top - sourceRect.top; + + return true; +} + + +bool +Painter::BitmapPainter::_HasScale() +{ + return fScaleX != 1.0 || fScaleY != 1.0; +} + + +bool +Painter::BitmapPainter::_HasAffineTransform() +{ + return !fPainter->fIdentityTransform; +} + + +bool +Painter::BitmapPainter::_HasAlphaMask() +{ + return fPainter->fInternal.fMaskedUnpackedScanline != NULL; +} + + +void +Painter::BitmapPainter::_ConvertColorSpace( + ObjectDeleter& convertedBitmapDeleter) +{ + if (fColorSpace == B_RGBA32) + return; + + if (fColorSpace == B_RGB32 + && (fPainter->fDrawingMode == B_OP_COPY +#if 1 +// Enabling this would make the behavior compatible to BeOS, which +// treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in +// all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. +// B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually +// use this color, unless the alpha channel contains 255 for all other +// pixels, which is inconsistent. + || fPainter->fDrawingMode == B_OP_ALPHA +#endif + )) { + return; + } + + BBitmap* conversionBitmap = new(nothrow) BBitmap(fBitmapBounds, + B_BITMAP_NO_SERVER_LINK, B_RGBA32); + if (conversionBitmap == NULL) { + fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - " + "out of memory for creating temporary conversion bitmap\n"); + return; + } + convertedBitmapDeleter.SetTo(conversionBitmap); + + status_t err = conversionBitmap->ImportBits(fBitmap.buf(), + fBitmap.height() * fBitmap.stride(), + fBitmap.stride(), 0, fColorSpace); + if (err < B_OK) { + fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - " + "colorspace conversion failed: %s\n", strerror(err)); + return; + } + + // the original bitmap might have had some of the + // transaparent magic colors set that we now need to + // make transparent in our RGBA32 bitmap again. + switch (fColorSpace) { + case B_RGB32: + _TransparentMagicToAlpha((uint32 *)fBitmap.buf(), + fBitmap.width(), fBitmap.height(), + fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA32, + conversionBitmap); + break; + + // TODO: not sure if this applies to B_RGBA15 too. It + // should not because B_RGBA15 actually has an alpha + // channel itself and it should have been preserved + // when importing the bitmap. Maybe it applies to + // B_RGB16 though? + case B_RGB15: + _TransparentMagicToAlpha((uint16 *)fBitmap.buf(), + fBitmap.width(), fBitmap.height(), + fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA15, + conversionBitmap); + break; + + default: + break; + } + + fBitmap.attach((uint8*)conversionBitmap->Bits(), + (uint32)fBitmapBounds.IntegerWidth() + 1, + (uint32)fBitmapBounds.IntegerHeight() + 1, + conversionBitmap->BytesPerRow()); +} + + +template +void +Painter::BitmapPainter::_TransparentMagicToAlpha(sourcePixel* buffer, + uint32 width, uint32 height, uint32 sourceBytesPerRow, + sourcePixel transparentMagic, BBitmap* output) +{ + uint8* sourceRow = (uint8*)buffer; + uint8* destRow = (uint8*)output->Bits(); + uint32 destBytesPerRow = output->BytesPerRow(); + + for (uint32 y = 0; y < height; y++) { + sourcePixel* pixel = (sourcePixel*)sourceRow; + uint32* destPixel = (uint32*)destRow; + for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { + if (*pixel == transparentMagic) + *destPixel &= 0x00ffffff; + } + + sourceRow += sourceBytesPerRow; + destRow += destBytesPerRow; + } +} diff --git a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h new file mode 100644 index 0000000000..4afaa50645 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.h @@ -0,0 +1,60 @@ +/* + * Copyright 2005-2007, Stephan Aßmus . + * Copyright 2008, Andrej Spielmann . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef BITMAP_PAINTER_H +#define BITMAP_PAINTER_H + +#include + +#include "Painter.h" + + +class Painter::BitmapPainter { +public: + +public: + BitmapPainter(const Painter* painter, + const ServerBitmap* bitmap, + uint32 options); + + void Draw(const BRect& sourceRect, + const BRect& destinationRect); + +private: + bool _DetermineTransform( + BRect sourceRect, + const BRect& destinationRect); + + bool _HasScale(); + bool _HasAffineTransform(); + bool _HasAlphaMask(); + + void _ConvertColorSpace(ObjectDeleter& + convertedBitmapDeleter); + + template + void _TransparentMagicToAlpha(sourcePixel *buffer, + uint32 width, uint32 height, + uint32 sourceBytesPerRow, + sourcePixel transparentMagic, + BBitmap *output); + +private: + const Painter* fPainter; + status_t fStatus; + agg::rendering_buffer fBitmap; + BRect fBitmapBounds; + color_space fColorSpace; + uint32 fOptions; + + BRect fDestinationRect; + double fScaleX; + double fScaleY; + BPoint fOffset; +}; + + +#endif // BITMAP_PAINTER_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h new file mode 100644 index 0000000000..5dc75257a1 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h @@ -0,0 +1,520 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_BILINEAR_H +#define DRAW_BITMAP_BILINEAR_H + +#include "Painter.h" + + +// Prototypes for assembler routines +extern "C" { + void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, + void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR); +} + + +extern uint32 gSIMDFlags; + + +namespace BitmapPainterPrivate { + + +struct FilterInfo { + uint16 index; // index into source bitmap row/column + uint16 weight; // weight of the pixel at index [0..255] +}; + + +struct FilterData { + FilterInfo* fWeightsX; + FilterInfo* fWeightsY; + uint32 fIndexOffsetX; + uint32 fIndexOffsetY; +}; + + +template +struct DrawBitmapBilinearOptimized { + void Draw(PainterAggInterface& aggInterface, const BRect& destinationRect, + agg::rendering_buffer* bitmap, const FilterData& filterData) + { + fSource = bitmap; + fSourceBytesPerRow = bitmap->stride(); + fDestination = NULL; + fDestinationBytesPerRow = aggInterface.fBuffer.stride(); + fWeightsX = filterData.fWeightsX; + fWeightsY = filterData.fWeightsY; + + const int32 left = (int32)destinationRect.left; + const int32 top = (int32)destinationRect.top; + const int32 right = (int32)destinationRect.right; + const int32 bottom = (int32)destinationRect.bottom; + + renderer_base& baseRenderer = aggInterface.fBaseRenderer; + + // iterate over clipping boxes + baseRenderer.first_clip_box(); + do { + const int32 x1 = max_c(baseRenderer.xmin(), left); + const int32 x2 = min_c(baseRenderer.xmax(), right); + if (x1 > x2) + continue; + + int32 y1 = max_c(baseRenderer.ymin(), top); + int32 y2 = min_c(baseRenderer.ymax(), bottom); + if (y1 > y2) + continue; + + // buffer offset into destination + fDestination = aggInterface.fBuffer.row_ptr(y1) + x1 * 4; + + // x and y are needed as indices into the weight arrays, so the + // offset into the target buffer needs to be compensated + const int32 xIndexL = x1 - left - filterData.fIndexOffsetX; + const int32 xIndexR = x2 - left - filterData.fIndexOffsetX; + y1 -= top + filterData.fIndexOffsetY; + y2 -= top + filterData.fIndexOffsetY; + + //printf("x: %ld - %ld\n", xIndexL, xIndexR); + //printf("y: %ld - %ld\n", y1, y2); + + OptimizedVersion optimizedVersion; + optimizedVersion.DrawToClipRect(xIndexL, xIndexR, y1, y2); + + } while (baseRenderer.next_clip_box()); + } + +protected: + agg::rendering_buffer* fSource; + uint32 fSourceBytesPerRow; + uint8* fDestination; + uint32 fDestinationBytesPerRow; + FilterInfo* fWeightsX; + FilterInfo* fWeightsY; +}; + + +struct BilinearDefault : DrawBitmapBilinearOptimized { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) + { + // In this mode we anticipate many pixels wich need filtering, + // there are no special cases for direct hit pixels except for + // the last column/row and the right/bottom corner pixel. + + // The last column/row handling does not need to be performed + // for all clipping rects! + int32 yMax = y2; + if (fWeightsY[yMax].weight == 255) + yMax--; + int32 xIndexMax = xIndexR; + if (fWeightsX[xIndexMax].weight == 255) + xIndexMax--; + + for (; y1 <= yMax; y1++) { + // cache the weight of the top and bottom row + const uint16 wTop = fWeightsY[y1].weight; + const uint16 wBottom = 255 - fWeightsY[y1].weight; + + // buffer offset into source (top row) + register const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per + // pixel + register uint8* d = fDestination; + + for (int32 x = xIndexL; x <= xIndexMax; x++) { + const uint8* s = src + fWeightsX[x].index; + // calculate the weighted sum of all four + // interpolated pixels + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + // left and right of top row + uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; + uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; + uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; + + // left and right of bottom row + s += fSourceBytesPerRow; + t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; + t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; + t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; + d[0] = t0 >> 16; + d[1] = t1 >> 16; + d[2] = t2 >> 16; + d += 4; + } + // last column of pixels if necessary + if (xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + const uint8* sBottom = s + fSourceBytesPerRow; + d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; + d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; + d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + } + + fDestination += fDestinationBytesPerRow; + } + + // last row of pixels if necessary + // buffer offset into source (bottom row) + register const uint8* src + = fSource->row_ptr(fWeightsY[y2].index); + // buffer handle for destination to be incremented per pixel + register uint8* d = fDestination; + + if (yMax < y2) { + for (int32 x = xIndexL; x <= xIndexMax; x++) { + const uint8* s = src + fWeightsX[x].index; + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + d += 4; + } + } + + // pixel in bottom right corner if necessary + if (yMax < y2 && xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + *(uint32*)d = *(uint32*)s; + } + } +}; + + +struct BilinearLowFilterRatio : + DrawBitmapBilinearOptimized { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) + { + // In this mode, we anticipate to hit many destination pixels + // that map directly to a source pixel, we have more branches + // in the inner loop but save time because of the special + // cases. If there are too few direct hit pixels, the branches + // only waste time. + + for (; y1 <= y2; y1++) { + // cache the weight of the top and bottom row + const uint16 wTop = fWeightsY[y1].weight; + const uint16 wBottom = 255 - fWeightsY[y1].weight; + + // buffer offset into source (top row) + register const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per + // pixel + register uint8* d = fDestination; + + if (wTop == 255) { + for (int32 x = xIndexL; x <= xIndexR; x++) { + const uint8* s = src + fWeightsX[x].index; + // This case is important to prevent out + // of bounds access at bottom edge of the source + // bitmap. If the scale is low and integer, it will + // also help the speed. + if (fWeightsX[x].weight == 255) { + // As above, but to prevent out of bounds + // on the right edge. + *(uint32*)d = *(uint32*)s; + } else { + // Only the left and right pixels are + // interpolated, since the top row has 100% + // weight. + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + } + d += 4; + } + } else { + for (int32 x = xIndexL; x <= xIndexR; x++) { + const uint8* s = src + fWeightsX[x].index; + if (fWeightsX[x].weight == 255) { + // Prevent out of bounds access on the right + // edge or simply speed up. + const uint8* sBottom = s + fSourceBytesPerRow; + d[0] = (s[0] * wTop + sBottom[0] * wBottom) + >> 8; + d[1] = (s[1] * wTop + sBottom[1] * wBottom) + >> 8; + d[2] = (s[2] * wTop + sBottom[2] * wBottom) + >> 8; + } else { + // calculate the weighted sum of all four + // interpolated pixels + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + // left and right of top row + uint32 t0 = (s[0] * wLeft + s[4] * wRight) + * wTop; + uint32 t1 = (s[1] * wLeft + s[5] * wRight) + * wTop; + uint32 t2 = (s[2] * wLeft + s[6] * wRight) + * wTop; + + // left and right of bottom row + s += fSourceBytesPerRow; + t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; + t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; + t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; + + d[0] = t0 >> 16; + d[1] = t1 >> 16; + d[2] = t2 >> 16; + } + d += 4; + } + } + fDestination += fDestinationBytesPerRow; + } + } +}; + + +#ifdef __INTEL__ + +struct BilinearSimd : DrawBitmapBilinearOptimized { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) + { + // Basically the same as the "standard" mode, but we use SIMD + // routines for the processing of the single display lines. + + // The last column/row handling does not need to be performed + // for all clipping rects! + int32 yMax = y2; + if (fWeightsY[yMax].weight == 255) + yMax--; + int32 xIndexMax = xIndexR; + if (fWeightsX[xIndexMax].weight == 255) + xIndexMax--; + + for (; y1 <= yMax; y1++) { + // cache the weight of the top and bottom row + const uint16 wTop = fWeightsY[y1].weight; + const uint16 wBottom = 255 - fWeightsY[y1].weight; + + // buffer offset into source (top row) + const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per + // pixel + uint8* d = fDestination; + bilinear_scale_xloop_mmxsse(src, fDestination, fWeightsX, xIndexL, + xIndexMax, wTop, fSourceBytesPerRow); + // increase pointer by processed pixels + d += (xIndexMax - xIndexL + 1) * 4; + + // last column of pixels if necessary + if (xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + const uint8* sBottom = s + fSourceBytesPerRow; + d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; + d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; + d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + } + + fDestination += fDestinationBytesPerRow; + } + + // last row of pixels if necessary + // buffer offset into source (bottom row) + register const uint8* src = fSource->row_ptr(fWeightsY[y2].index); + // buffer handle for destination to be incremented per pixel + register uint8* d = fDestination; + + if (yMax < y2) { + for (int32 x = xIndexL; x <= xIndexMax; x++) { + const uint8* s = src + fWeightsX[x].index; + const uint16 wLeft = fWeightsX[x].weight; + const uint16 wRight = 255 - wLeft; + d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + d += 4; + } + } + + // pixel in bottom right corner if necessary + if (yMax < y2 && xIndexMax < xIndexR) { + const uint8* s = src + fWeightsX[xIndexR].index; + *(uint32*)d = *(uint32*)s; + } + } +}; + +#endif // __INTEL__ + + +struct DrawBitmapBilinearCopy { + void + Draw(const Painter* painter, PainterAggInterface& aggInterface, + agg::rendering_buffer& bitmap, BPoint offset, + double scaleX, double scaleY, BRect destinationRect) + { + //bigtime_t now = system_time(); + uint32 dstWidth = destinationRect.IntegerWidth() + 1; + uint32 dstHeight = destinationRect.IntegerHeight() + 1; + uint32 srcWidth = bitmap.width(); + uint32 srcHeight = bitmap.height(); + + // Do not calculate more filter weights than necessary and also + // keep the stack based allocations reasonably sized + const BRegion& clippingRegion = *painter->ClippingRegion(); + if (clippingRegion.Frame().IntegerWidth() + 1 < (int32)dstWidth) + dstWidth = clippingRegion.Frame().IntegerWidth() + 1; + if (clippingRegion.Frame().IntegerHeight() + 1 < (int32)dstHeight) + dstHeight = clippingRegion.Frame().IntegerHeight() + 1; + + // When calculating less filter weights than specified by + // destinationRect, we need to compensate the offset. + FilterData filterData; + filterData.fIndexOffsetX = 0; + filterData.fIndexOffsetY = 0; + if (clippingRegion.Frame().left > destinationRect.left) { + filterData.fIndexOffsetX = (int32)(clippingRegion.Frame().left + - destinationRect.left); + } + if (clippingRegion.Frame().top > destinationRect.top) { + filterData.fIndexOffsetY = (int32)(clippingRegion.Frame().top + - destinationRect.top); + } + +//#define FILTER_INFOS_ON_HEAP +#ifdef FILTER_INFOS_ON_HEAP + filterData.fWeightsX = new (nothrow) FilterInfo[dstWidth]; + filterData.fWeightsY = new (nothrow) FilterInfo[dstHeight]; + if (filterData.fWeightsX == NULL || filterData.fWeightsY == NULL) { + delete[] filterData.fWeightsX; + delete[] filterData.fWeightsY; + return; + } +#else + // stack based saves about 200µs on 1.85 GHz Core 2 Duo + // should not pose a problem with stack overflows + // (needs around 12Kb for 1920x1200) + FilterInfo xWeights[dstWidth]; + FilterInfo yWeights[dstHeight]; + filterData.fWeightsX = &xWeights[0]; + filterData.fWeightsY = &yWeights[0]; +#endif + + // Extract the cropping information for the source bitmap, + // If only a part of the source bitmap is to be drawn with scale, + // the offset will be different from the destinationRect left top + // corner. + const int32 xBitmapShift = (int32)(destinationRect.left - offset.x); + const int32 yBitmapShift = (int32)(destinationRect.top - offset.y); + + for (uint32 i = 0; i < dstWidth; i++) { + // fractional index into source + // NOTE: It is very important to calculate the fractional index + // into the source pixel grid like this to prevent out of bounds + // access! It will result in the rightmost pixel of the destination + // to access the rightmost pixel of the source with a weighting + // of 255. This in turn will trigger an optimization in the loop + // that also prevents out of bounds access. + float index = (i + filterData.fIndexOffsetX) * (srcWidth - 1) + / (srcWidth * scaleX - 1); + // round down to get the left pixel + filterData.fWeightsX[i].index = (uint16)index; + filterData.fWeightsX[i].weight = + 255 - (uint16)((index - filterData.fWeightsX[i].index) * 255); + // handle cropped source bitmap + filterData.fWeightsX[i].index += xBitmapShift; + // precompute index for 32 bit pixels + filterData.fWeightsX[i].index *= 4; + } + + for (uint32 i = 0; i < dstHeight; i++) { + // fractional index into source + // NOTE: It is very important to calculate the fractional index + // into the source pixel grid like this to prevent out of bounds + // access! It will result in the bottommost pixel of the + // destination to access the bottommost pixel of the source with a + // weighting of 255. This in turn will trigger an optimization in + // the loop that also prevents out of bounds access. + float index = (i + filterData.fIndexOffsetY) * (srcHeight - 1) + / (srcHeight * scaleY - 1); + // round down to get the top pixel + filterData.fWeightsY[i].index = (uint16)index; + filterData.fWeightsY[i].weight = + 255 - (uint16)((index - filterData.fWeightsY[i].index) * 255); + // handle cropped source bitmap + filterData.fWeightsY[i].index += yBitmapShift; + } + //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", + // xWeights[0].index, xWeights[0].weight, + // xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, + // xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, + // dstWidth); + //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", + // yWeights[0].index, yWeights[0].weight, + // yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, + // yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, + // dstHeight); + + // Figure out which version of the code we want to use... + enum { + kOptimizeForLowFilterRatio = 0, + kUseDefaultVersion, + kUseSIMDVersion + }; + + int codeSelect = kUseDefaultVersion; + + uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; + if ((gSIMDFlags & neededSIMDFlags) == neededSIMDFlags) + codeSelect = kUseSIMDVersion; + else { + if (scaleX == scaleY && (scaleX == 1.5 || scaleX == 2.0 + || scaleX == 2.5 || scaleX == 3.0)) { + codeSelect = kOptimizeForLowFilterRatio; + } + } + + switch (codeSelect) { + case kUseDefaultVersion: + { + BilinearDefault bilinearPainter; + bilinearPainter.Draw(aggInterface, destinationRect, &bitmap, + filterData); + break; + } + + case kOptimizeForLowFilterRatio: + { + BilinearLowFilterRatio bilinearPainter; + bilinearPainter.Draw(aggInterface, destinationRect, + &bitmap, filterData); + break; + } + +#ifdef __INTEL__ + case kUseSIMDVersion: + { + BilinearSimd bilinearPainter; + bilinearPainter.Draw(aggInterface, destinationRect, &bitmap, + filterData); + break; + } +#endif // __INTEL__ + } + +#ifdef FILTER_INFOS_ON_HEAP + delete[] filterData.fWeightsX; + delete[] filterData.fWeightsY; +#endif + //printf("draw bitmap %.5fx%.5f: %lld\n", scaleX, scaleY, + // system_time() - now); + } +}; + + +} // namespace BitmapPainterPrivate + + +#endif // DRAW_BITMAP_BILINEAR_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h new file mode 100644 index 0000000000..f148583f81 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapGeneric.h @@ -0,0 +1,112 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_GENERIC_H +#define DRAW_BITMAP_GENERIC_H + +#include "Painter.h" + + +struct DrawBitmapGeneric { + static void + Draw(const Painter* painter, PainterAggInterface& aggInterface, + agg::rendering_buffer& bitmap, BPoint offset, + double scaleX, double scaleY, BRect destinationRect, uint32 options) + { + // pixel format attached to bitmap + typedef agg::pixfmt_bgra32 pixfmt_image; + pixfmt_image pixf_img(bitmap); + + agg::trans_affine srcMatrix; + // NOTE: R5 seems to ignore this offset when drawing bitmaps + // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, + // -actualBitmapRect.top); + srcMatrix *= painter->Transform(); + + agg::trans_affine imgMatrix; + imgMatrix *= agg::trans_affine_translation( + offset.x - destinationRect.left, offset.y - destinationRect.top); + imgMatrix *= agg::trans_affine_scaling(scaleX, scaleY); + imgMatrix *= agg::trans_affine_translation(destinationRect.left, + destinationRect.top); + imgMatrix *= painter->Transform(); + imgMatrix.invert(); + + // image interpolator + typedef agg::span_interpolator_linear<> interpolator_type; + interpolator_type interpolator(imgMatrix); + + // scanline allocator + agg::span_allocator spanAllocator; + + // image accessor attached to pixel format of bitmap + typedef agg::image_accessor_clone source_type; + source_type source(pixf_img); + + // clip to the current clipping region's frame + if (painter->IsIdentityTransform()) { + destinationRect = destinationRect + & painter->ClippingRegion()->Frame(); + } + // convert to pixel coords (versus pixel indices) + destinationRect.right++; + destinationRect.bottom++; + + // path enclosing the bitmap + agg::path_storage& path = aggInterface.fPath; + rasterizer_type& rasterizer = aggInterface.fRasterizer; + + path.remove_all(); + path.move_to(destinationRect.left, destinationRect.top); + path.line_to(destinationRect.right, destinationRect.top); + path.line_to(destinationRect.right, destinationRect.bottom); + path.line_to(destinationRect.left, destinationRect.bottom); + path.close_polygon(); + + agg::conv_transform transformedPath(path, + srcMatrix); + rasterizer.reset(); + rasterizer.add_path(transformedPath); + + if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { + // image filter (bilinear) + typedef agg::span_image_filter_rgba_bilinear< + source_type, interpolator_type> span_gen_type; + span_gen_type spanGenerator(source, interpolator); + + // render the path with the bitmap as scanline fill + if (aggInterface.fMaskedUnpackedScanline != NULL) { + agg::render_scanlines_aa(rasterizer, + *aggInterface.fMaskedUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } else { + agg::render_scanlines_aa(rasterizer, + aggInterface.fUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } + } else { + // image filter (nearest neighbor) + typedef agg::span_image_filter_rgba_nn< + source_type, interpolator_type> span_gen_type; + span_gen_type spanGenerator(source, interpolator); + + // render the path with the bitmap as scanline fill + if (aggInterface.fMaskedUnpackedScanline != NULL) { + agg::render_scanlines_aa(rasterizer, + *aggInterface.fMaskedUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } else { + agg::render_scanlines_aa(rasterizer, + aggInterface.fUnpackedScanline, + aggInterface.fBaseRenderer, spanAllocator, spanGenerator); + } + } + } +}; + + +#endif // DRAW_BITMAP_GENERIC_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h new file mode 100644 index 0000000000..59fc68b097 --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h @@ -0,0 +1,143 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_NEAREST_NEIGHBOR_H +#define DRAW_BITMAP_NEAREST_NEIGHBOR_H + +#include "Painter.h" + + +struct DrawBitmapNearestNeighborCopy { + static void + Draw(const Painter* painter, PainterAggInterface& aggInterface, + agg::rendering_buffer& bitmap, BPoint offset, + double scaleX, double scaleY, BRect destinationRect) + { + //bigtime_t now = system_time(); + uint32 dstWidth = destinationRect.IntegerWidth() + 1; + uint32 dstHeight = destinationRect.IntegerHeight() + 1; + uint32 srcWidth = bitmap.width(); + uint32 srcHeight = bitmap.height(); + + // Do not calculate more filter weights than necessary and also + // keep the stack based allocations reasonably sized + const BRegion& clippingRegion = *painter->ClippingRegion(); + if (clippingRegion.Frame().IntegerWidth() + 1 < (int32)dstWidth) + dstWidth = clippingRegion.Frame().IntegerWidth() + 1; + if (clippingRegion.Frame().IntegerHeight() + 1 < (int32)dstHeight) + dstHeight = clippingRegion.Frame().IntegerHeight() + 1; + + // When calculating less filter weights than specified by + // destinationRect, we need to compensate the offset. + uint32 filterWeightXIndexOffset = 0; + uint32 filterWeightYIndexOffset = 0; + if (clippingRegion.Frame().left > destinationRect.left) { + filterWeightXIndexOffset = (int32)(clippingRegion.Frame().left + - destinationRect.left); + } + if (clippingRegion.Frame().top > destinationRect.top) { + filterWeightYIndexOffset = (int32)(clippingRegion.Frame().top + - destinationRect.top); + } + + // should not pose a problem with stack overflows + // (needs around 6Kb for 1920x1200) + uint16 xIndices[dstWidth]; + uint16 yIndices[dstHeight]; + + // Extract the cropping information for the source bitmap, + // If only a part of the source bitmap is to be drawn with scale, + // the offset will be different from the destinationRect left top + // corner. + const int32 xBitmapShift = (int32)(destinationRect.left - offset.x); + const int32 yBitmapShift = (int32)(destinationRect.top - offset.y); + + for (uint32 i = 0; i < dstWidth; i++) { + // index into source + uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth + / (srcWidth * scaleX)); + // round down to get the left pixel + xIndices[i] = index; + // handle cropped source bitmap + xIndices[i] += xBitmapShift; + // precompute index for 32 bit pixels + xIndices[i] *= 4; + } + + for (uint32 i = 0; i < dstHeight; i++) { + // index into source + uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight + / (srcHeight * scaleY)); + // round down to get the top pixel + yIndices[i] = index; + // handle cropped source bitmap + yIndices[i] += yBitmapShift; + } + //printf("X: %d ... %d, %d (%ld or %f)\n", + // xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], + // dstWidth, srcWidth * scaleX); + //printf("Y: %d ... %d, %d (%ld or %f)\n", + // yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], + // dstHeight, srcHeight * scaleY); + + const int32 left = (int32)destinationRect.left; + const int32 top = (int32)destinationRect.top; + const int32 right = (int32)destinationRect.right; + const int32 bottom = (int32)destinationRect.bottom; + + const uint32 dstBPR = aggInterface.fBuffer.stride(); + + renderer_base& baseRenderer = aggInterface.fBaseRenderer; + + // iterate over clipping boxes + baseRenderer.first_clip_box(); + do { + const int32 x1 = max_c(baseRenderer.xmin(), left); + const int32 x2 = min_c(baseRenderer.xmax(), right); + if (x1 > x2) + continue; + + int32 y1 = max_c(baseRenderer.ymin(), top); + int32 y2 = min_c(baseRenderer.ymax(), bottom); + if (y1 > y2) + continue; + + // buffer offset into destination + uint8* dst = aggInterface.fBuffer.row_ptr(y1) + x1 * 4; + + // x and y are needed as indeces into the wheight arrays, so the + // offset into the target buffer needs to be compensated + const int32 xIndexL = x1 - left - filterWeightXIndexOffset; + const int32 xIndexR = x2 - left - filterWeightXIndexOffset; + y1 -= top + filterWeightYIndexOffset; + y2 -= top + filterWeightYIndexOffset; + + //printf("x: %ld - %ld\n", xIndexL, xIndexR); + //printf("y: %ld - %ld\n", y1, y2); + + for (; y1 <= y2; y1++) { + // buffer offset into source (top row) + register const uint8* src = bitmap.row_ptr(yIndices[y1]); + // buffer handle for destination to be incremented per pixel + register uint32* d = (uint32*)dst; + + for (int32 x = xIndexL; x <= xIndexR; x++) { + *d = *(uint32*)(src + xIndices[x]); + d++; + } + dst += dstBPR; + } + } while (baseRenderer.next_clip_box()); + + //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, + // system_time() - now); + } +}; + + + +#endif // DRAW_BITMAP_NEAREST_NEIGHBOR_H diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h new file mode 100644 index 0000000000..61be834b4f --- /dev/null +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h @@ -0,0 +1,169 @@ +/* + * Copyright 2009, Christian Packmann. + * Copyright 2008, Andrej Spielmann . + * Copyright 2005-2014, Stephan Aßmus . + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef DRAW_BITMAP_NO_SCALE_H +#define DRAW_BITMAP_NO_SCALE_H + +#include "IntPoint.h" +#include "Painter.h" + + +template +struct DrawBitmapNoScale { + static void + Draw(PainterAggInterface& aggInterface, agg::rendering_buffer& bitmap, + uint32 bytesPerSourcePixel, IntPoint offset, BRect destinationRect) + { + // NOTE: this would crash if destinationRect was large enough to read + // outside the bitmap, so make sure this is not the case before calling + // this function! + uint8* dst = aggInterface.fBuffer.row_ptr(0); + const uint32 dstBPR = aggInterface.fBuffer.stride(); + + const uint8* src = bitmap.row_ptr(0); + const uint32 srcBPR = bitmap.stride(); + + const int32 left = (int32)destinationRect.left; + const int32 top = (int32)destinationRect.top; + const int32 right = (int32)destinationRect.right; + const int32 bottom = (int32)destinationRect.bottom; + +#if DEBUG_DRAW_BITMAP + if (left - offset.x < 0 + || left - offset.x >= (int32)bitmap.width() + || right - offset.x >= (int32)srcBuffer.width() + || top - offset.y < 0 + || top - offset.y >= (int32)bitmap.height() + || bottom - offset.y >= (int32)bitmap.height()) { + char message[256]; + sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " + "(%d, %d) (%ld, %ld)", + left - offset.x, top - offset.y, + right - offset.x, bottom - offset.y, + bitmap.width(), bitmap.height(), offset.x, offset.y); + debugger(message); + } +#endif + + const rgb_color* colorMap = SystemPalette(); + renderer_base& baseRenderer = aggInterface.fBaseRenderer; + + // copy rects, iterate over clipping boxes + baseRenderer.first_clip_box(); + do { + int32 x1 = max_c(baseRenderer.xmin(), left); + int32 x2 = min_c(baseRenderer.xmax(), right); + if (x1 <= x2) { + int32 y1 = max_c(baseRenderer.ymin(), top); + int32 y2 = min_c(baseRenderer.ymax(), bottom); + if (y1 <= y2) { + uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; + const uint8* srcHandle = src + (y1 - offset.y) * srcBPR + + (x1 - offset.x) * bytesPerSourcePixel; + + for (; y1 <= y2; y1++) { + BlendType::BlendRow(dstHandle, srcHandle, + x2 - x1 + 1, colorMap); + + dstHandle += dstBPR; + srcHandle += srcBPR; + } + } + } + } while (baseRenderer.next_clip_box()); + } +}; + + +struct CMap8Copy : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color* colorMap) + { + uint32* d = (uint32*)dst; + const uint8* s = src; + while (numPixels--) { + const rgb_color c = colorMap[*s++]; + *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); + } + } +}; + + +struct CMap8Over : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color* colorMap) + { + uint32* d = (uint32*)dst; + const uint8* s = src; + while (numPixels--) { + const rgb_color c = colorMap[*s++]; + if (c.alpha) + *d = (c.alpha << 24) | (c.red << 16) + | (c.green << 8) | (c.blue); + d++; + } + } +}; + + +struct Bgr32Copy : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color*) + { + memcpy(dst, src, numPixels * 4); + } +}; + + +struct Bgr32Over : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color*) + { + uint32* d = (uint32*)dst; + uint32* s = (uint32*)src; + while (numPixels--) { + if (*s != B_TRANSPARENT_MAGIC_RGBA32) + *(uint32*)d = *(uint32*)s; + d++; + s++; + } + } +}; + + +struct Bgr32Alpha : public DrawBitmapNoScale +{ + static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, + const rgb_color*) + { + uint32* d = (uint32*)dst; + int32 bytes = numPixels * 4; + uint8 buffer[bytes]; + uint8* b = buffer; + while (numPixels--) { + if (src[3] == 255) { + *(uint32*)b = *(uint32*)src; + } else { + *(uint32*)b = *d; + b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; + b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; + b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; + } + d++; + b += 4; + src += 4; + } + memcpy(dst, buffer, bytes); + } +}; + + +#endif // DRAW_BITMAP_NO_SCALE_H diff --git a/src/servers/app/drawing/Painter/painter_bilinear_scale.nasm b/src/servers/app/drawing/Painter/bitmap_painter/painter_bilinear_scale.nasm similarity index 100% rename from src/servers/app/drawing/Painter/painter_bilinear_scale.nasm rename to src/servers/app/drawing/Painter/bitmap_painter/painter_bilinear_scale.nasm From f4f05935dbe4fa5c5b1fda880e23d81a18b11da2 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 11:14:42 +0200 Subject: [PATCH 20/29] app_server: give Painter direct access to alpha masks * To use alpha masks in optimized drawing code outside of the AGG renderer pipeline, we need to allow access to the alpha mask's underlying buffer: - AlphaMask gets another method which returns its clipped_alpha_mask - clipped_alpha_mask gets a get_hspan() method which returns a span of alpha values without combining it with anything - Painter/PainterAggInterface store a pointer to the clipped_alpha_mask (in addition to the AlphaMask's scanline container) --- src/servers/app/drawing/AlphaMask.cpp | 7 ++ src/servers/app/drawing/AlphaMask.h | 1 + src/servers/app/drawing/Painter/Painter.cpp | 8 +- .../app/drawing/Painter/PainterAggInterface.h | 2 + .../drawing/Painter/agg_clipped_alpha_mask.h | 100 +++++++++++------- 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 49636c6296..865425a391 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -220,6 +220,13 @@ AlphaMask::Generate() } +agg::clipped_alpha_mask* +AlphaMask::Mask() +{ + return &fCachedMask; +} + + ServerBitmap* AlphaMask::_RenderPicture() const { diff --git a/src/servers/app/drawing/AlphaMask.h b/src/servers/app/drawing/AlphaMask.h index cb1969af7a..81503c0874 100644 --- a/src/servers/app/drawing/AlphaMask.h +++ b/src/servers/app/drawing/AlphaMask.h @@ -32,6 +32,7 @@ public: void SetPrevious(AlphaMask* mask); scanline_unpacked_masked_type* Generate(); + agg::clipped_alpha_mask* Mask(); private: ServerBitmap* _RenderPicture() const; diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 45cd532cc8..c8e83756c2 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -102,6 +102,7 @@ using std::nothrow; #define fSubpixRasterizer fInternal.fSubpixRasterizer #define fSubpixRenderer fInternal.fSubpixRenderer #define fMaskedUnpackedScanline fInternal.fMaskedUnpackedScanline +#define fClippedAlphaMask fInternal.fClippedAlphaMask #define fPath fInternal.fPath #define fCurve fInternal.fCurve @@ -287,10 +288,13 @@ Painter::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset) fSubpixelPrecise = state->SubPixelPrecise(); - if (state->GetAlphaMask() != NULL) + if (state->GetAlphaMask() != NULL) { fMaskedUnpackedScanline = state->GetAlphaMask()->Generate(); - else + fClippedAlphaMask = state->GetAlphaMask()->Mask(); + } else { fMaskedUnpackedScanline = NULL; + fClippedAlphaMask = NULL; + } // any of these conditions means we need to use a different drawing // mode instance diff --git a/src/servers/app/drawing/Painter/PainterAggInterface.h b/src/servers/app/drawing/Painter/PainterAggInterface.h index 57e9c6fa7e..2f4e6ea16e 100644 --- a/src/servers/app/drawing/Painter/PainterAggInterface.h +++ b/src/servers/app/drawing/Painter/PainterAggInterface.h @@ -29,6 +29,7 @@ struct PainterAggInterface { fSubpixRasterizer(), fSubpixRenderer(fBaseRenderer), fMaskedUnpackedScanline(NULL), + fClippedAlphaMask(NULL), fPath(), fCurve(fPath) { @@ -58,6 +59,7 @@ struct PainterAggInterface { // Alpha-Masked mode: for ClipToPicture // (this uses the standard rasterizer and renderer) scanline_unpacked_masked_type* fMaskedUnpackedScanline; + agg::clipped_alpha_mask* fClippedAlphaMask; agg::path_storage fPath; agg::conv_curve fCurve; diff --git a/src/servers/app/drawing/Painter/agg_clipped_alpha_mask.h b/src/servers/app/drawing/Painter/agg_clipped_alpha_mask.h index 59c77740e9..08e34a251c 100644 --- a/src/servers/app/drawing/Painter/agg_clipped_alpha_mask.h +++ b/src/servers/app/drawing/Painter/agg_clipped_alpha_mask.h @@ -23,7 +23,7 @@ namespace agg public: typedef int8u cover_type; enum cover_scale_e - { + { cover_shift = 8, cover_none = 0, cover_full = 255 @@ -44,46 +44,13 @@ namespace agg void combine_hspan(int x, int y, cover_type* dst, int num_pix) const { - x -= m_xOffset; - y -= m_yOffset; - - int xmax = m_rbuf->width() - 1; - int ymax = m_rbuf->height() - 1; - int count = num_pix; cover_type* covers = dst; - if(y < 0 || y > ymax) - { - memset(dst, m_outside, num_pix * sizeof(cover_type)); + bool has_inside = _set_outside(x, y, covers, count); + if (!has_inside) return; - } - if(x < 0) - { - count += x; - if(count <= 0) - { - memset(dst, m_outside, num_pix * sizeof(cover_type)); - return; - } - memset(covers, m_outside, -x * sizeof(cover_type)); - covers -= x; - x = 0; - } - - if(x + count > xmax) - { - int rest = x + count - xmax - 1; - count -= rest; - if(count <= 0) - { - memset(dst, m_outside, num_pix * sizeof(cover_type)); - return; - } - memset(covers + count, m_outside, rest * sizeof(cover_type)); - } - const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; do { @@ -95,6 +62,67 @@ namespace agg while(--count); } + void get_hspan(int x, int y, cover_type* dst, int num_pix) const + { + int count = num_pix; + cover_type* covers = dst; + + bool has_inside = _set_outside(x, y, covers, count); + if (!has_inside) + return; + + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + memcpy(covers, mask, count); + } + + private: + bool _set_outside(int& x, int& y, cover_type*& covers, + int& count) const + { + x -= m_xOffset; + y -= m_yOffset; + + int xmax = m_rbuf->width() - 1; + int ymax = m_rbuf->height() - 1; + + int num_pix = count; + cover_type* dst = covers; + + if(y < 0 || y > ymax) + { + memset(dst, m_outside, num_pix * sizeof(cover_type)); + return false; + } + + if(x < 0) + { + count += x; + if(count <= 0) + { + memset(dst, m_outside, num_pix * sizeof(cover_type)); + return false; + } + memset(covers, m_outside, -x * sizeof(cover_type)); + covers -= x; + x = 0; + } + + if(x + count > xmax) + { + int rest = x + count - xmax - 1; + count -= rest; + if(count <= 0) + { + memset(dst, m_outside, num_pix * sizeof(cover_type)); + return false; + } + memset(covers + count, m_outside, rest * sizeof(cover_type)); + } + + return true; + } + + private: int m_xOffset; int m_yOffset; From 79a483ebbfdbf0bdf39c99b12c128a4fd088aa1d Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 11:23:51 +0200 Subject: [PATCH 21/29] app_server: add alpha masked mode to DrawBitmapNoScale * Add another mode to DrawBitmapNoScale for drawing bitmaps using B_OP_COPY with alpha masks. It behaves like the definition for ClipToPicture from the BeBook: pixels with alpha = 0 are ignored, pixels with any alpha > 0 are copied. Before, this fell back to the slower generic AGG-pipeline-based version. * Some light refactoring --- .../Painter/bitmap_painter/BitmapPainter.cpp | 34 +++++--- .../bitmap_painter/DrawBitmapNoScale.h | 79 +++++++++++++------ 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp index a56635630d..813f291e90 100644 --- a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp +++ b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp @@ -82,19 +82,22 @@ Painter::BitmapPainter::Draw(const BRect& sourceRect, if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) { if (fColorSpace == B_CMAP8) { if (fPainter->fDrawingMode == B_OP_COPY) { - DrawBitmapNoScale::Draw(fPainter->fInternal, - fBitmap, 1, fOffset, fDestinationRect); + DrawBitmapNoScale drawNoScale; + drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset, + fDestinationRect); return; } if (fPainter->fDrawingMode == B_OP_OVER) { - DrawBitmapNoScale::Draw(fPainter->fInternal, - fBitmap, 1, fOffset, fDestinationRect); + DrawBitmapNoScale drawNoScale; + drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset, + fDestinationRect); return; } } else if (fColorSpace == B_RGB32) { if (fPainter->fDrawingMode == B_OP_OVER) { - DrawBitmapNoScale::Draw(fPainter->fInternal, - fBitmap, 4, fOffset, fDestinationRect); + DrawBitmapNoScale drawNoScale; + drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset, + fDestinationRect); return; } } @@ -106,16 +109,27 @@ Painter::BitmapPainter::Draw(const BRect& sourceRect, // optimized version if there is no scale if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) { if (fPainter->fDrawingMode == B_OP_COPY) { - DrawBitmapNoScale::Draw(fPainter->fInternal, - fBitmap, 4, fOffset, fDestinationRect); + DrawBitmapNoScale drawNoScale; + drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset, + fDestinationRect); return; } if (fPainter->fDrawingMode == B_OP_OVER || (fPainter->fDrawingMode == B_OP_ALPHA && fPainter->fAlphaSrcMode == B_PIXEL_ALPHA && fPainter->fAlphaFncMode == B_ALPHA_OVERLAY)) { - DrawBitmapNoScale::Draw(fPainter->fInternal, - fBitmap, 4, fOffset, fDestinationRect); + DrawBitmapNoScale drawNoScale; + drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset, + fDestinationRect); + return; + } + } + + if (!_HasScale() && !_HasAffineTransform() && _HasAlphaMask()) { + if (fPainter->fDrawingMode == B_OP_COPY) { + DrawBitmapNoScale drawNoScale; + drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset, + fDestinationRect); return; } } diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h index 61be834b4f..d42371552d 100644 --- a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNoScale.h @@ -9,12 +9,14 @@ #define DRAW_BITMAP_NO_SCALE_H #include "IntPoint.h" +#include "IntRect.h" #include "Painter.h" template struct DrawBitmapNoScale { - static void +public: + void Draw(PainterAggInterface& aggInterface, agg::rendering_buffer& bitmap, uint32 bytesPerSourcePixel, IntPoint offset, BRect destinationRect) { @@ -49,25 +51,28 @@ struct DrawBitmapNoScale { } #endif - const rgb_color* colorMap = SystemPalette(); + fColorMap = SystemPalette(); + fAlphaMask = aggInterface.fClippedAlphaMask; renderer_base& baseRenderer = aggInterface.fBaseRenderer; // copy rects, iterate over clipping boxes baseRenderer.first_clip_box(); do { - int32 x1 = max_c(baseRenderer.xmin(), left); - int32 x2 = min_c(baseRenderer.xmax(), right); - if (x1 <= x2) { - int32 y1 = max_c(baseRenderer.ymin(), top); - int32 y2 = min_c(baseRenderer.ymax(), bottom); - if (y1 <= y2) { - uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; - const uint8* srcHandle = src + (y1 - offset.y) * srcBPR - + (x1 - offset.x) * bytesPerSourcePixel; + fRect.left = max_c(baseRenderer.xmin(), left); + fRect.right = min_c(baseRenderer.xmax(), right); + if (fRect.left <= fRect.right) { + fRect.top = max_c(baseRenderer.ymin(), top); + fRect.bottom = min_c(baseRenderer.ymax(), bottom); + if (fRect.top <= fRect.bottom) { + uint8* dstHandle = dst + fRect.top * dstBPR + + fRect.left * 4; + const uint8* srcHandle = src + + (fRect.top - offset.y) * srcBPR + + (fRect.left - offset.x) * bytesPerSourcePixel; - for (; y1 <= y2; y1++) { - BlendType::BlendRow(dstHandle, srcHandle, - x2 - x1 + 1, colorMap); + for (; fRect.top <= fRect.bottom; fRect.top++) { + static_cast(this)->BlendRow(dstHandle, + srcHandle, fRect.right - fRect.left + 1); dstHandle += dstBPR; srcHandle += srcBPR; @@ -76,18 +81,22 @@ struct DrawBitmapNoScale { } } while (baseRenderer.next_clip_box()); } + +protected: + IntRect fRect; + const rgb_color* fColorMap; + const agg::clipped_alpha_mask* fAlphaMask; }; struct CMap8Copy : public DrawBitmapNoScale { - static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) + void BlendRow(uint8* dst, const uint8* src, int32 numPixels) { uint32* d = (uint32*)dst; const uint8* s = src; while (numPixels--) { - const rgb_color c = colorMap[*s++]; + const rgb_color c = fColorMap[*s++]; *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); } } @@ -96,13 +105,12 @@ struct CMap8Copy : public DrawBitmapNoScale struct CMap8Over : public DrawBitmapNoScale { - static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color* colorMap) + void BlendRow(uint8* dst, const uint8* src, int32 numPixels) { uint32* d = (uint32*)dst; const uint8* s = src; while (numPixels--) { - const rgb_color c = colorMap[*s++]; + const rgb_color c = fColorMap[*s++]; if (c.alpha) *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); @@ -114,8 +122,7 @@ struct CMap8Over : public DrawBitmapNoScale struct Bgr32Copy : public DrawBitmapNoScale { - static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color*) + void BlendRow(uint8* dst, const uint8* src, int32 numPixels) { memcpy(dst, src, numPixels * 4); } @@ -124,8 +131,7 @@ struct Bgr32Copy : public DrawBitmapNoScale struct Bgr32Over : public DrawBitmapNoScale { - static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color*) + void BlendRow(uint8* dst, const uint8* src, int32 numPixels) { uint32* d = (uint32*)dst; uint32* s = (uint32*)src; @@ -141,8 +147,7 @@ struct Bgr32Over : public DrawBitmapNoScale struct Bgr32Alpha : public DrawBitmapNoScale { - static void BlendRow(uint8* dst, const uint8* src, int32 numPixels, - const rgb_color*) + void BlendRow(uint8* dst, const uint8* src, int32 numPixels) { uint32* d = (uint32*)dst; int32 bytes = numPixels * 4; @@ -166,4 +171,26 @@ struct Bgr32Alpha : public DrawBitmapNoScale }; +struct Bgr32CopyMasked : public DrawBitmapNoScale +{ + void BlendRow(uint8* dst, const uint8* src, int32 numPixels) + { + uint8 covers[numPixels]; + fAlphaMask->get_hspan(fRect.left, fRect.top, covers, numPixels); + + uint32* destination = (uint32*)dst; + uint32* source = (uint32*)src; + uint8* mask = (uint8*)&covers[0]; + + while (numPixels--) { + if (*mask != 0) + *destination = *source; + destination++; + source++; + mask++; + } + } +}; + + #endif // DRAW_BITMAP_NO_SCALE_H From 64c6e038ebf396d2bc65fae21533458355fd6b90 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 12:50:32 +0200 Subject: [PATCH 22/29] app_server: bilinear bitmap painting: alpha overlay support * Add support for pixel alpha overlay mode in the optimized bilinear- scaled bitmap drawing code of BitmapPainter. For now, only BilinearDefault supports this. DrawBitmapBilinear gets the colour type and draw mode as template parameters to minimize code duplication and allow simple extension with further pixel formats and modes. Avoids, for this mode, fallback to the slower generic AGG-pipeline-based version. --- .../Painter/bitmap_painter/BitmapPainter.cpp | 16 +- .../bitmap_painter/DrawBitmapBilinear.h | 191 +++++++++++++++--- 2 files changed, 172 insertions(+), 35 deletions(-) diff --git a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp index 813f291e90..d22e8c0e62 100644 --- a/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp +++ b/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp @@ -138,17 +138,27 @@ Painter::BitmapPainter::Draw(const BRect& sourceRect, if (fPainter->fDrawingMode == B_OP_COPY && !_HasAffineTransform() && !_HasAlphaMask()) { if ((fOptions & B_FILTER_BITMAP_BILINEAR) != 0) { - DrawBitmapBilinearCopy drawBilinear; + DrawBitmapBilinear drawBilinear; drawBilinear.Draw(fPainter, fPainter->fInternal, fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); - } - else { + } else { DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal, fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); } return; } + if (fPainter->fDrawingMode == B_OP_ALPHA + && fPainter->fAlphaSrcMode == B_PIXEL_ALPHA + && fPainter->fAlphaFncMode == B_ALPHA_OVERLAY + && !_HasAffineTransform() && !_HasAlphaMask() + && (fOptions & B_FILTER_BITMAP_BILINEAR) != 0) { + DrawBitmapBilinear drawBilinear; + drawBilinear.Draw(fPainter, fPainter->fInternal, + fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect); + return; + } + // for all other cases (non-optimized drawing mode or scaled drawing) DrawBitmapGeneric::Draw(fPainter, fPainter->fInternal, fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect, fOptions); diff --git a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h index 5dc75257a1..5268a6b2d4 100644 --- a/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h +++ b/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapBilinear.h @@ -10,6 +10,8 @@ #include "Painter.h" +#include + // Prototypes for assembler routines extern "C" { @@ -83,8 +85,8 @@ struct DrawBitmapBilinearOptimized { //printf("x: %ld - %ld\n", xIndexL, xIndexR); //printf("y: %ld - %ld\n", y1, y2); - OptimizedVersion optimizedVersion; - optimizedVersion.DrawToClipRect(xIndexL, xIndexR, y1, y2); + static_cast(this)->DrawToClipRect( + xIndexL, xIndexR, y1, y2); } while (baseRenderer.next_clip_box()); } @@ -99,7 +101,134 @@ protected: }; -struct BilinearDefault : DrawBitmapBilinearOptimized { +struct ColorTypeRgb { + static void + Interpolate(uint32* t, const uint8* s, uint32 sourceBytesPerRow, + uint16 wLeft, uint16 wTop, uint16 wRight, uint16 wBottom) + { + // left and right of top row + t[0] = (s[0] * wLeft + s[4] * wRight) * wTop; + t[1] = (s[1] * wLeft + s[5] * wRight) * wTop; + t[2] = (s[2] * wLeft + s[6] * wRight) * wTop; + + // left and right of bottom row + s += sourceBytesPerRow; + t[0] += (s[0] * wLeft + s[4] * wRight) * wBottom; + t[1] += (s[1] * wLeft + s[5] * wRight) * wBottom; + t[2] += (s[2] * wLeft + s[6] * wRight) * wBottom; + + t[0] >>= 16; + t[1] >>= 16; + t[2] >>= 16; + } + + static void + InterpolateLastColumn(uint32* t, const uint8* s, const uint8* sBottom, + uint16 wTop, uint16 wBottom) + { + t[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; + t[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; + t[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + } + + static void + InterpolateLastRow(uint32* t, const uint8* s, uint16 wLeft, + uint16 wRight) + { + t[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + t[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + t[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + } +}; + + +struct ColorTypeRgba { + static void + Interpolate(uint32* t, const uint8* s, uint32 sourceBytesPerRow, + uint16 wLeft, uint16 wTop, uint16 wRight, uint16 wBottom) + { + // left and right of top row + t[0] = (s[0] * wLeft + s[4] * wRight) * wTop; + t[1] = (s[1] * wLeft + s[5] * wRight) * wTop; + t[2] = (s[2] * wLeft + s[6] * wRight) * wTop; + t[3] = (s[3] * wLeft + s[7] * wRight) * wTop; + + // left and right of bottom row + s += sourceBytesPerRow; + + t[0] += (s[0] * wLeft + s[4] * wRight) * wBottom; + t[1] += (s[1] * wLeft + s[5] * wRight) * wBottom; + t[2] += (s[2] * wLeft + s[6] * wRight) * wBottom; + t[3] += (s[3] * wLeft + s[7] * wRight) * wBottom; + + t[0] >>= 16; + t[1] >>= 16; + t[2] >>= 16; + t[3] >>= 16; + } + + static void + InterpolateLastColumn(uint32* t, const uint8* s, const uint8* sBottom, + uint16 wTop, uint16 wBottom) + { + t[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; + t[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; + t[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + t[3] = (s[3] * wTop + sBottom[3] * wBottom) >> 8; + } + + static void + InterpolateLastRow(uint32* t, const uint8* s, uint16 wLeft, + uint16 wRight) + { + t[0] = (s[0] * wLeft + s[4] * wRight) >> 8; + t[1] = (s[1] * wLeft + s[5] * wRight) >> 8; + t[2] = (s[2] * wLeft + s[6] * wRight) >> 8; + t[3] = (s[3] * wLeft + s[7] * wRight) >> 8; + } +}; + + +struct DrawModeCopy { + static void + Blend(uint8*& d, uint32* t) + { + d[0] = t[0]; + d[1] = t[1]; + d[2] = t[2]; + d += 4; + } +}; + + +struct DrawModeAlphaOverlay { + static void + Blend(uint8*& d, uint32* t) + { + uint8 t0 = t[0]; + uint8 t1 = t[1]; + uint8 t2 = t[2]; + uint8 t3 = t[3]; + + if (t3 == 255) { + d[0] = t0; + d[1] = t1; + d[2] = t2; + } else { + d[0] = ((t0 - d[0]) * t3 + (d[0] << 8)) >> 8; + d[1] = ((t1 - d[1]) * t3 + (d[1] << 8)) >> 8; + d[2] = ((t2 - d[2]) * t3 + (d[2] << 8)) >> 8; + } + + d += 4; + } +}; + + +template +struct BilinearDefault : + DrawBitmapBilinearOptimized > { + void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2) { // In this mode we anticipate many pixels wich need filtering, @@ -122,38 +251,33 @@ struct BilinearDefault : DrawBitmapBilinearOptimized { // buffer offset into source (top row) register const uint8* src = fSource->row_ptr(fWeightsY[y1].index); + // buffer handle for destination to be incremented per // pixel register uint8* d = fDestination; for (int32 x = xIndexL; x <= xIndexMax; x++) { const uint8* s = src + fWeightsX[x].index; + // calculate the weighted sum of all four // interpolated pixels const uint16 wLeft = fWeightsX[x].weight; const uint16 wRight = 255 - wLeft; - // left and right of top row - uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; - uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; - uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; - // left and right of bottom row - s += fSourceBytesPerRow; - t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; - t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; - t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; - d[0] = t0 >> 16; - d[1] = t1 >> 16; - d[2] = t2 >> 16; - d += 4; + uint32 t[4]; + ColorType::Interpolate(&t[0], s, fSourceBytesPerRow, + wLeft, wTop, wRight, wBottom); + DrawMode::Blend(d, &t[0]); } // last column of pixels if necessary if (xIndexMax < xIndexR) { const uint8* s = src + fWeightsX[xIndexR].index; const uint8* sBottom = s + fSourceBytesPerRow; - d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; - d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; - d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; + + uint32 t[4]; + ColorType::InterpolateLastColumn(&t[0], s, sBottom, wTop, + wBottom); + DrawMode::Blend(d, &t[0]); } fDestination += fDestinationBytesPerRow; @@ -171,10 +295,9 @@ struct BilinearDefault : DrawBitmapBilinearOptimized { const uint8* s = src + fWeightsX[x].index; const uint16 wLeft = fWeightsX[x].weight; const uint16 wRight = 255 - wLeft; - d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; - d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; - d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; - d += 4; + uint32 t[4]; + ColorType::InterpolateLastRow(&t[0], s, wLeft, wRight); + DrawMode::Blend(d, &t[0]); } } @@ -349,7 +472,8 @@ struct BilinearSimd : DrawBitmapBilinearOptimized { #endif // __INTEL__ -struct DrawBitmapBilinearCopy { +template +struct DrawBitmapBilinear { void Draw(const Painter* painter, PainterAggInterface& aggInterface, agg::rendering_buffer& bitmap, BPoint offset, @@ -466,20 +590,23 @@ struct DrawBitmapBilinearCopy { int codeSelect = kUseDefaultVersion; - uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; - if ((gSIMDFlags & neededSIMDFlags) == neededSIMDFlags) - codeSelect = kUseSIMDVersion; - else { - if (scaleX == scaleY && (scaleX == 1.5 || scaleX == 2.0 - || scaleX == 2.5 || scaleX == 3.0)) { - codeSelect = kOptimizeForLowFilterRatio; + if (typeid(ColorType) == typeid(ColorTypeRgb) + && typeid(DrawMode) == typeid(DrawModeCopy)) { + uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; + if ((gSIMDFlags & neededSIMDFlags) == neededSIMDFlags) + codeSelect = kUseSIMDVersion; + else { + if (scaleX == scaleY && (scaleX == 1.5 || scaleX == 2.0 + || scaleX == 2.5 || scaleX == 3.0)) { + codeSelect = kOptimizeForLowFilterRatio; + } } } switch (codeSelect) { case kUseDefaultVersion: { - BilinearDefault bilinearPainter; + BilinearDefault bilinearPainter; bilinearPainter.Draw(aggInterface, destinationRect, &bitmap, filterData); break; From 01c730420420627cf60877c5c018a8b180fa6ceb Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 12:53:50 +0200 Subject: [PATCH 23/29] app_server: fix comment header in pixel alpha overlay modes --- .../Painter/drawing_modes/DrawingModeAlphaPO.h | 12 ++++++------ .../Painter/drawing_modes/DrawingModeAlphaPOSUBPIX.h | 2 +- .../Painter/drawing_modes/DrawingModeAlphaPOSolid.h | 8 ++++---- .../drawing_modes/DrawingModeAlphaPOSolidSUBPIX.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPO.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPO.h index bd82411c34..1d2d27f002 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPO.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPO.h @@ -2,7 +2,7 @@ * Copyright 2005, Stephan Aßmus . All rights reserved. * Distributed under the terms of the MIT License. * - * DrawingMode implementing B_OP_ALPHA in "Constant Overlay" mode on B_RGBA32. + * DrawingMode implementing B_OP_ALPHA in "Pixel Overlay" mode on B_RGBA32. * */ @@ -43,7 +43,7 @@ blend_pixel_alpha_po(int x, int y, const color_type& c, uint8 cover, // blend_hline_alpha_po void -blend_hline_alpha_po(int x, int y, unsigned len, +blend_hline_alpha_po(int x, int y, unsigned len, const color_type& c, uint8 cover, agg_buffer* buffer, const PatternHandler* pattern) { @@ -65,7 +65,7 @@ blend_hline_alpha_po(int x, int y, unsigned len, // blend_solid_hspan_alpha_po void -blend_solid_hspan_alpha_po(int x, int y, unsigned len, +blend_solid_hspan_alpha_po(int x, int y, unsigned len, const color_type& c, const uint8* covers, agg_buffer* buffer, const PatternHandler* pattern) { @@ -90,7 +90,7 @@ blend_solid_hspan_alpha_po(int x, int y, unsigned len, // blend_solid_vspan_alpha_po void -blend_solid_vspan_alpha_po(int x, int y, unsigned len, +blend_solid_vspan_alpha_po(int x, int y, unsigned len, const color_type& c, const uint8* covers, agg_buffer* buffer, const PatternHandler* pattern) { @@ -114,8 +114,8 @@ blend_solid_vspan_alpha_po(int x, int y, unsigned len, // blend_color_hspan_alpha_po void -blend_color_hspan_alpha_po(int x, int y, unsigned len, - const color_type* colors, +blend_color_hspan_alpha_po(int x, int y, unsigned len, + const color_type* colors, const uint8* covers, uint8 cover, agg_buffer* buffer, const PatternHandler* pattern) { diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSUBPIX.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSUBPIX.h index 7cc0b1c509..1d4227e8f3 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSUBPIX.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSUBPIX.h @@ -3,7 +3,7 @@ * Copyright 2008, Andrej Spielmann . * All rights reserved. Distributed under the terms of the MIT License. * - * DrawingMode implementing B_OP_ALPHA in "Constant Overlay" mode on B_RGBA32. + * DrawingMode implementing B_OP_ALPHA in "Pixel Overlay" mode on B_RGBA32. * */ diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolid.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolid.h index 53ea222f5b..fa850be0a8 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolid.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolid.h @@ -2,7 +2,7 @@ * Copyright 2005, Stephan Aßmus . All rights reserved. * Distributed under the terms of the MIT License. * - * DrawingMode implementing B_OP_ALPHA in "Constant Overlay" mode on B_RGBA32. + * DrawingMode implementing B_OP_ALPHA in "Pixel Overlay" mode on B_RGBA32. * */ @@ -27,7 +27,7 @@ blend_pixel_alpha_po_solid(int x, int y, const color_type& c, uint8 cover, // blend_hline_alpha_po_solid void -blend_hline_alpha_po_solid(int x, int y, unsigned len, +blend_hline_alpha_po_solid(int x, int y, unsigned len, const color_type& c, uint8 cover, agg_buffer* buffer, const PatternHandler* pattern) { @@ -64,7 +64,7 @@ blend_hline_alpha_po_solid(int x, int y, unsigned len, // blend_solid_hspan_alpha_po_solid void -blend_solid_hspan_alpha_po_solid(int x, int y, unsigned len, +blend_solid_hspan_alpha_po_solid(int x, int y, unsigned len, const color_type& c, const uint8* covers, agg_buffer* buffer, const PatternHandler* pattern) { @@ -88,7 +88,7 @@ blend_solid_hspan_alpha_po_solid(int x, int y, unsigned len, // blend_solid_vspan_alpha_po_solid void -blend_solid_vspan_alpha_po_solid(int x, int y, unsigned len, +blend_solid_vspan_alpha_po_solid(int x, int y, unsigned len, const color_type& c, const uint8* covers, agg_buffer* buffer, const PatternHandler* pattern) { diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolidSUBPIX.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolidSUBPIX.h index e52632737c..c8b1f107b7 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolidSUBPIX.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPOSolidSUBPIX.h @@ -3,7 +3,7 @@ * Copyright 2008, Andrej Spielmann . * All rights reserved. Distributed under the terms of the MIT License. * - * DrawingMode implementing B_OP_ALPHA in "Constant Overlay" mode on B_RGBA32. + * DrawingMode implementing B_OP_ALPHA in "Pixel Overlay" mode on B_RGBA32. * */ From ed7b139e08946d93fd3caf56dbc38e709b381094 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 12:54:36 +0200 Subject: [PATCH 24/29] app_server: fix direct assignment in alpha pixel composite * 'alpha' is 16 bit (alpha * cover) in blend_hline_alpha_pc(), so compare with 255 * 255 --- .../Painter/drawing_modes/DrawingModeAlphaPC.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPC.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPC.h index 0313d3a00e..07a92b8703 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPC.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPC.h @@ -43,7 +43,7 @@ blend_pixel_alpha_pc(int x, int y, const color_type& c, uint8 cover, // blend_hline_alpha_pc void -blend_hline_alpha_pc(int x, int y, unsigned len, +blend_hline_alpha_pc(int x, int y, unsigned len, const color_type& c, uint8 cover, agg_buffer* buffer, const PatternHandler* pattern) { @@ -52,7 +52,7 @@ blend_hline_alpha_pc(int x, int y, unsigned len, rgb_color color = pattern->ColorAt(x, y); uint16 alpha = color.alpha * cover; if (alpha) { - if (alpha == 255) { + if (alpha == 255 * 255) { ASSIGN_ALPHA_PC(p, color.red, color.green, color.blue); } else { BLEND_ALPHA_PC(p, color.red, color.green, color.blue, alpha); @@ -65,7 +65,7 @@ blend_hline_alpha_pc(int x, int y, unsigned len, // blend_solid_hspan_alpha_pc void -blend_solid_hspan_alpha_pc(int x, int y, unsigned len, +blend_solid_hspan_alpha_pc(int x, int y, unsigned len, const color_type& c, const uint8* covers, agg_buffer* buffer, const PatternHandler* pattern) { @@ -88,7 +88,7 @@ blend_solid_hspan_alpha_pc(int x, int y, unsigned len, // blend_solid_vspan_alpha_pc void -blend_solid_vspan_alpha_pc(int x, int y, unsigned len, +blend_solid_vspan_alpha_pc(int x, int y, unsigned len, const color_type& c, const uint8* covers, agg_buffer* buffer, const PatternHandler* pattern) { @@ -112,8 +112,8 @@ blend_solid_vspan_alpha_pc(int x, int y, unsigned len, // blend_color_hspan_alpha_pc void -blend_color_hspan_alpha_pc(int x, int y, unsigned len, - const color_type* colors, +blend_color_hspan_alpha_pc(int x, int y, unsigned len, + const color_type* colors, const uint8* covers, uint8 cover, agg_buffer* buffer, const PatternHandler* pattern) { From 801b5d2119148b22e273b9c2639b41d979dc86fa Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 12:56:37 +0200 Subject: [PATCH 25/29] app_server: add pixel alpha composite solid mode * Same concept as the previously exisiting drawing mode implementations for e.g. pixel alpha overlay mode: when pattern is solid, provide a separate mode implementation which does no unnecessary pattern pixel lookups. This provides a considerable speedup in composite mode when no stipple pattern is used. --- .../drawing_modes/DrawingModeAlphaPCSolid.h | 155 ++++++++++++++++++ .../Painter/drawing_modes/PixelFormat.cpp | 24 ++- 2 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPCSolid.h diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPCSolid.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPCSolid.h new file mode 100644 index 0000000000..da5f0de2ef --- /dev/null +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingModeAlphaPCSolid.h @@ -0,0 +1,155 @@ +/* + * Copyright 2005, Stephan Aßmus + * Copyright 2015, Julian Harnath + * All rights reserved. Distributed under the terms of the MIT License. + * + * DrawingMode implementing B_OP_ALPHA in "Pixel Composite" mode on B_RGBA32. + * + */ + +#ifndef DRAWING_MODE_ALPHA_PC_SOLID_H +#define DRAWING_MODE_ALPHA_PC_SOLID_H + +#include "DrawingMode.h" + + +#define BLEND_ALPHA_PC(d, r, g, b, a) \ +{ \ + BLEND_COMPOSITE16(d, r, g, b, a); \ +} + + +#define ASSIGN_ALPHA_PC(d, r, g, b) \ +{ \ + d[0] = (b); \ + d[1] = (g); \ + d[2] = (r); \ + d[3] = 255; \ +} + + +void +blend_pixel_alpha_pc_solid(int x, int y, const color_type& color, uint8 cover, + agg_buffer* buffer, const PatternHandler*) +{ + uint8* p = buffer->row_ptr(y) + (x << 2); + uint16 alpha = color.a * cover; + if (alpha == 255 * 255) { + ASSIGN_ALPHA_PC(p, color.r, color.g, color.b); + } else { + BLEND_ALPHA_PC(p, color.r, color.g, color.b, alpha); + } +} + + +void +blend_hline_alpha_pc_solid(int x, int y, unsigned len, + const color_type& color, uint8 cover, + agg_buffer* buffer, const PatternHandler*) +{ + uint8* p = buffer->row_ptr(y) + (x << 2); + uint16 alpha = color.a * cover; + if (alpha == 0) + return; + + if (alpha == 255 * 255) { + do { + ASSIGN_ALPHA_PC(p, color.r, color.g, color.b); + p += 4; + } while(--len); + return; + } + + do { + BLEND_ALPHA_PC(p, color.r, color.g, color.b, alpha); + p += 4; + } while(--len); +} + + +void +blend_solid_hspan_alpha_pc_solid(int x, int y, unsigned len, + const color_type& color, const uint8* covers, + agg_buffer* buffer, const PatternHandler*) +{ + uint8* p = buffer->row_ptr(y) + (x << 2); + do { + uint16 alpha = color.a * *covers; + if (alpha) { + if(alpha == 255 * 255) { + ASSIGN_ALPHA_PC(p, color.r, color.g, color.b); + } else { + BLEND_ALPHA_PC(p, color.r, color.g, color.b, alpha); + } + } + covers++; + p += 4; + } while(--len); +} + + +void +blend_solid_vspan_alpha_pc_solid(int x, int y, unsigned len, + const color_type& color, const uint8* covers, + agg_buffer* buffer, const PatternHandler*) +{ + uint8* p = buffer->row_ptr(y) + (x << 2); + do { + uint16 alpha = color.a * *covers; + if (alpha) { + if (alpha == 255 * 255) { + ASSIGN_ALPHA_PC(p, color.r, color.g, color.b); + } else { + BLEND_ALPHA_PC(p, color.r, color.g, color.b, alpha); + } + } + covers++; + p += buffer->stride(); + } while(--len); +} + + +void +blend_color_hspan_alpha_pc_solid(int x, int y, unsigned len, + const color_type* colors, + const uint8* covers, uint8 cover, + agg_buffer* buffer, const PatternHandler*) +{ + uint8* p = buffer->row_ptr(y) + (x << 2); + if (covers) { + // non-solid opacity + do { + uint16 alpha = colors->a * *covers; + if (alpha) { + if (alpha == 255 * 255) { + ASSIGN_ALPHA_PC(p, colors->r, colors->g, colors->b); + } else { + BLEND_ALPHA_PC(p, colors->r, colors->g, colors->b, alpha); + } + } + covers++; + p += 4; + ++colors; + } while(--len); + } else { + // solid full opcacity + uint16 alpha = colors->a * cover; + if (alpha == 255 * 255) { + do { + ASSIGN_ALPHA_PC(p, colors->r, colors->g, colors->b); + p += 4; + ++colors; + } while(--len); + // solid partial opacity + } else if (alpha) { + do { + BLEND_ALPHA_PC(p, colors->r, colors->g, colors->b, alpha); + p += 4; + ++colors; + } while(--len); + } + } +} + + +#endif // DRAWING_MODE_ALPHA_PC_SOLID_H diff --git a/src/servers/app/drawing/Painter/drawing_modes/PixelFormat.cpp b/src/servers/app/drawing/Painter/drawing_modes/PixelFormat.cpp index 0e4508ffe3..40ca5d3505 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/PixelFormat.cpp +++ b/src/servers/app/drawing/Painter/drawing_modes/PixelFormat.cpp @@ -20,6 +20,7 @@ #include "DrawingModeAlphaCO.h" #include "DrawingModeAlphaCOSolid.h" #include "DrawingModeAlphaPC.h" +#include "DrawingModeAlphaPCSolid.h" #include "DrawingModeAlphaPO.h" #include "DrawingModeAlphaPOSolid.h" #include "DrawingModeBlend.h" @@ -211,7 +212,7 @@ PixelFormat::SetDrawingMode(drawing_mode mode, source_alpha alphaSrcMode, case B_OP_COPY: if (text) { fBlendPixel = blend_pixel_copy_text; - fBlendHLine = blend_hline_copy_text; + fBlendHLine = blend_hline_copy_text; fBlendSolidHSpanSubpix = blend_solid_hspan_copy_text_subpix; fBlendSolidHSpan = blend_solid_hspan_copy_text; fBlendSolidVSpan = blend_solid_vspan_copy_text; @@ -327,12 +328,21 @@ PixelFormat::SetDrawingMode(drawing_mode mode, source_alpha alphaSrcMode, } fBlendColorHSpan = blend_color_hspan_alpha_po; } else if (alphaFncMode == B_ALPHA_COMPOSITE) { - fBlendPixel = blend_pixel_alpha_pc; - fBlendHLine = blend_hline_alpha_pc; - fBlendSolidHSpanSubpix = blend_solid_hspan_alpha_pc_subpix; - fBlendSolidHSpan = blend_solid_hspan_alpha_pc; - fBlendSolidVSpan = blend_solid_vspan_alpha_pc; - fBlendColorHSpan = blend_color_hspan_alpha_pc; + if (fPatternHandler->IsSolid()) { + fBlendPixel = blend_pixel_alpha_pc_solid; + fBlendHLine = blend_hline_alpha_pc_solid; + fBlendSolidHSpanSubpix = blend_solid_hspan_alpha_pc_subpix; + fBlendSolidHSpan = blend_solid_hspan_alpha_pc_solid; + fBlendSolidVSpan = blend_solid_vspan_alpha_pc_solid; + fBlendColorHSpan = blend_color_hspan_alpha_pc_solid; + } else { + fBlendPixel = blend_pixel_alpha_pc; + fBlendHLine = blend_hline_alpha_pc; + fBlendSolidHSpanSubpix = blend_solid_hspan_alpha_pc_subpix; + fBlendSolidHSpan = blend_solid_hspan_alpha_pc; + fBlendSolidVSpan = blend_solid_vspan_alpha_pc; + fBlendColorHSpan = blend_color_hspan_alpha_pc; + } } } break; From e3d73948690d37330da4349499d4974dcd328f70 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sat, 15 Aug 2015 13:02:11 +0200 Subject: [PATCH 26/29] app_server: fix test-app_server for launch_daemon changes * Make test-app_server work again in a launch_daemon environment * test_registrar gets a separate signature and port name again so the host system can distinguish it from the system registrar * AppServer is normally a BServer now, however, there can't be two BApplications in one team. A class TestServerLoopAdapter is added, which becomes the base class of AppServer instead of BServer when compiling for libbe_test. It's an adapter class which looks towards AppServer as it if was a BServer, but internally it is derived from MessageLooper (like the old AppServer before the transition to BServer). This way, AppServer can stay a BServer in normal builds and it also avoids having to use too many #ifdefs to distinguish the two versions. --- headers/private/app/RegistrarDefs.h | 9 +- headers/private/app/ServerProtocol.h | 4 + src/kits/app/AppMisc.cpp | 60 +++++++++ src/kits/app/Roster.cpp | 15 ++- src/servers/app/AppServer.cpp | 8 +- src/servers/app/AppServer.h | 14 +- src/servers/app/MessageLooper.cpp | 4 +- src/servers/app/TestServerLoopAdapter.cpp | 121 ++++++++++++++++++ src/servers/app/TestServerLoopAdapter.h | 51 ++++++++ .../app/drawing/DWindowHWInterface.cpp | 3 +- src/servers/app/drawing/ViewHWInterface.cpp | 3 +- src/servers/app/test_app_server.rdef | 2 +- src/servers/registrar/Registrar.cpp | 2 +- src/tests/servers/app/Jamfile | 1 + src/tests/servers/registrar/Jamfile | 2 +- .../servers/registrar/run_test_registrar.cpp | 2 +- .../servers/registrar/test_registrar.rdef | 96 ++++++++++++++ 17 files changed, 381 insertions(+), 16 deletions(-) create mode 100644 src/servers/app/TestServerLoopAdapter.cpp create mode 100644 src/servers/app/TestServerLoopAdapter.h create mode 100644 src/tests/servers/registrar/test_registrar.rdef diff --git a/headers/private/app/RegistrarDefs.h b/headers/private/app/RegistrarDefs.h index 736edbf361..e58216951b 100644 --- a/headers/private/app/RegistrarDefs.h +++ b/headers/private/app/RegistrarDefs.h @@ -23,7 +23,14 @@ namespace BPrivate { extern const char* kRAppLooperPortName; -#define B_REGISTRAR_SIGNATURE "application/x-vnd.haiku-registrar" +#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST +# define B_REGISTRAR_SIGNATURE "application/x-vnd.haiku-registrar" +# define B_REGISTRAR_PORT_NAME "system:roster" +#else +# define B_REGISTRAR_SIGNATURE "application/x-vnd.test-registrar" +# define B_REGISTRAR_PORT_NAME "haiku-test:roster" +#endif + #define B_REGISTRAR_AUTHENTICATION_PORT_NAME "auth" diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index 897baa18e2..8e6bc00f4f 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -16,6 +16,10 @@ #include +#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST +# define SERVER_PORT_NAME "haiku-test:app_server" +#endif + #if TEST_MODE # define SERVER_INPUT_PORT "haiku-test:input port" #endif diff --git a/src/kits/app/AppMisc.cpp b/src/kits/app/AppMisc.cpp index 1a607f785f..e996190ab8 100644 --- a/src/kits/app/AppMisc.cpp +++ b/src/kits/app/AppMisc.cpp @@ -176,6 +176,9 @@ is_app_showing_modal_window(team_id team) } +#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST + + /*! Creates a connection with the desktop. */ status_t @@ -208,4 +211,61 @@ create_desktop_connection(ServerLink* link, const char* name, int32 capacity) } +#else // HAIKU_TARGET_PLATFORM_LIBBE_TEST + + +static port_id sServerPort = -1; + + +port_id +get_app_server_port() +{ + if (sServerPort < 0) { + // No need for synchronization - in the worst case, we'll call + // find_port() twice. + sServerPort = find_port(SERVER_PORT_NAME); + } + + return sServerPort; +} + + +/*! Creates a connection with the desktop. +*/ +status_t +create_desktop_connection(ServerLink* link, const char* name, int32 capacity) +{ + port_id serverPort = get_app_server_port(); + if (serverPort < 0) + return serverPort; + + // Create the port so that the app_server knows where to send messages + port_id clientPort = create_port(capacity, name); + if (clientPort < 0) + return clientPort; + + link->SetTo(serverPort, clientPort); + + link->StartMessage(AS_GET_DESKTOP); + link->Attach(clientPort); + link->Attach(getuid()); + link->AttachString(getenv("TARGET_SCREEN")); + link->Attach(AS_PROTOCOL_VERSION); + + int32 code; + if (link->FlushWithReply(code) != B_OK || code != B_OK) { + link->SetSenderPort(-1); + return B_ERROR; + } + + link->Read(&serverPort); + link->SetSenderPort(serverPort); + + return B_OK; +} + + +#endif // HAIKU_TARGET_PLATFORM_LIBBE_TEST + + } // namespace BPrivate diff --git a/src/kits/app/Roster.cpp b/src/kits/app/Roster.cpp index dc6a914107..46433d1827 100644 --- a/src/kits/app/Roster.cpp +++ b/src/kits/app/Roster.cpp @@ -2604,9 +2604,10 @@ BRoster::_InitMessenger() DBG(OUT("BRoster::InitMessengers()\n")); // find the registrar port + +#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST BMessage data; - if (BLaunchRoster().GetData("application/x-vnd.Haiku-registrar", data) - == B_OK) { + if (BLaunchRoster().GetData(B_REGISTRAR_SIGNATURE, data) == B_OK) { port_id port = data.GetInt32("port", -1); team_id team = data.GetInt32("team", -1); if (port >= 0) { @@ -2616,6 +2617,16 @@ BRoster::_InitMessenger() B_PREFERRED_TOKEN); } } +#else + port_id rosterPort = find_port(B_REGISTRAR_PORT_NAME); + port_info info; + if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) { + DBG(OUT(" found roster port\n")); + + BMessenger::Private(fMessenger).SetTo(info.team, rosterPort, + B_PREFERRED_TOKEN); + } +#endif DBG(OUT("BRoster::InitMessengers() done\n")); } diff --git a/src/servers/app/AppServer.cpp b/src/servers/app/AppServer.cpp index 710d61c79a..9090086e72 100644 --- a/src/servers/app/AppServer.cpp +++ b/src/servers/app/AppServer.cpp @@ -48,7 +48,8 @@ uint32 gAppServerSIMDFlags = 0; */ AppServer::AppServer(status_t* status) : - BServer("application/x-vnd.Haiku-app_server", "picasso", -1, false, status), + SERVER_BASE("application/x-vnd.Haiku-app_server", "picasso", -1, false, + status), fDesktopLock("AppServerDesktopLock") { openlog("app_server", 0, LOG_DAEMON); @@ -153,7 +154,10 @@ AppServer::QuitRequested() wait_for_thread(thread, &status); } - return BServer::QuitRequested(); + delete this; + exit(0); + + return SERVER_BASE::QuitRequested(); #else return false; #endif diff --git a/src/servers/app/AppServer.h b/src/servers/app/AppServer.h index 9320edb9d1..e25beaad58 100644 --- a/src/servers/app/AppServer.h +++ b/src/servers/app/AppServer.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -23,12 +22,21 @@ #include "ServerConfig.h" +#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST +# include +# define SERVER_BASE BServer +#else +# include "TestServerLoopAdapter.h" +# define SERVER_BASE TestServerLoopAdapter +#endif + + class ServerApp; class BitmapManager; class Desktop; -class AppServer : public BServer { +class AppServer : public SERVER_BASE { public: AppServer(status_t* status); virtual ~AppServer(); @@ -39,7 +47,7 @@ public: private: Desktop* _CreateDesktop(uid_t userID, const char* targetScreen); - Desktop* _FindDesktop(uid_t userID, + virtual Desktop* _FindDesktop(uid_t userID, const char* targetScreen); void _LaunchInputServer(); diff --git a/src/servers/app/MessageLooper.cpp b/src/servers/app/MessageLooper.cpp index e801712eed..2828b8ef4d 100644 --- a/src/servers/app/MessageLooper.cpp +++ b/src/servers/app/MessageLooper.cpp @@ -154,9 +154,9 @@ MessageLooper::_MessageLooper() Lock(); - if (code == kMsgQuitLooper) { + if (code == kMsgQuitLooper) Quit(); - } else + else _DispatchMessage(code, receiver); Unlock(); diff --git a/src/servers/app/TestServerLoopAdapter.cpp b/src/servers/app/TestServerLoopAdapter.cpp new file mode 100644 index 0000000000..a12cdc86b6 --- /dev/null +++ b/src/servers/app/TestServerLoopAdapter.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2001-2015, Haiku, Inc. + * Distributed under the terms of the MIT license. + * + * Authors: + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + * Stephan Aßmus + * Christian Packmann + * Julian Harnath + */ +#include "TestServerLoopAdapter.h" + +#include "Desktop.h" +#include "ServerConfig.h" +#include "ServerProtocol.h" + +#include + +#include + +//#define DEBUG_SERVER +#ifdef DEBUG_SERVER +# include +# define STRACE(x) printf x +#else +# define STRACE(x) ; +#endif + + +TestServerLoopAdapter::TestServerLoopAdapter(const char* signature, + const char*, port_id, bool, status_t* outError) + : + MessageLooper("test-app_server"), + fMessagePort(_CreatePort()) +{ + fLink.SetReceiverPort(fMessagePort); + *outError = B_OK; +} + + +TestServerLoopAdapter::~TestServerLoopAdapter() +{ +} + + +bool +TestServerLoopAdapter::Run() +{ + rename_thread(find_thread(NULL), "picasso"); + _message_thread((void*)this); + return true; +} + + +void +TestServerLoopAdapter::_DispatchMessage(int32 code, + BPrivate::LinkReceiver& link) +{ + switch (code) { + case AS_GET_DESKTOP: + { + port_id replyPort = 0; + link.Read(&replyPort); + + int32 userID = -1; + link.Read(&userID); + + char* targetScreen = NULL; + link.ReadString(&targetScreen); + + int32 version = -1; + link.Read(&version); + + BMessage message(AS_GET_DESKTOP); + message.AddInt32("user", userID); + message.AddInt32("version", version); + message.AddString("target", targetScreen); + MessageReceived(&message); + + // AppServer will try to send a reply, we just let that fail + // since we can find out the port by getting the desktop instance + // ourselves + + free(targetScreen); + Desktop* desktop = _FindDesktop(userID, targetScreen); + + BPrivate::LinkSender reply(replyPort); + if (desktop != NULL) { + reply.StartMessage(B_OK); + reply.Attach(desktop->MessagePort()); + } else + reply.StartMessage(B_ERROR); + + reply.Flush(); + + break; + } + + case B_QUIT_REQUESTED: + { + QuitRequested(); + break; + } + + default: + STRACE(("Server::MainLoop received unexpected code %" B_PRId32 " " + "(offset %" B_PRId32 ")\n", code, code - SERVER_TRUE)); + break; + } +} + + +port_id +TestServerLoopAdapter::_CreatePort() +{ + port_id port = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME); + if (port < B_OK) + debugger("test-app_server could not create message port"); + return port; +} diff --git a/src/servers/app/TestServerLoopAdapter.h b/src/servers/app/TestServerLoopAdapter.h new file mode 100644 index 0000000000..f3d2879f6e --- /dev/null +++ b/src/servers/app/TestServerLoopAdapter.h @@ -0,0 +1,51 @@ +/* + * Copyright 2001-2015, Haiku, Inc. + * Distributed under the terms of the MIT license. + * + * Authors: + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + * Julian Harnath, + */ +#ifndef TEST_SERVER_LOOP_ADAPTER_H +#define TEST_SERVER_LOOP_ADAPTER_H + +#include "MessageLooper.h" + + +class BMessage; +class Desktop; + + +class TestServerLoopAdapter : public MessageLooper { +public: + TestServerLoopAdapter(const char* signature, + const char* looperName, port_id port, + bool initGui, status_t* outError); + virtual ~TestServerLoopAdapter(); + + // MessageLooper interface + virtual port_id MessagePort() const { return fMessagePort; } + virtual bool Run(); + + // BApplication interface + virtual void MessageReceived(BMessage* message) = 0; + virtual bool QuitRequested() { return true; } + +private: + // MessageLooper interface + virtual void _DispatchMessage(int32 code, + BPrivate::LinkReceiver &link); + + virtual Desktop* _FindDesktop(uid_t userID, + const char* targetScreen) = 0; + port_id _CreatePort(); + + + +private: + port_id fMessagePort; +}; + + +#endif // TEST_SERVER_LOOP_ADAPTER_H diff --git a/src/servers/app/drawing/DWindowHWInterface.cpp b/src/servers/app/drawing/DWindowHWInterface.cpp index a903ab5b1a..70e535c7b5 100644 --- a/src/servers/app/drawing/DWindowHWInterface.cpp +++ b/src/servers/app/drawing/DWindowHWInterface.cpp @@ -694,7 +694,8 @@ DWindowHWInterface::SetMode(const display_mode& mode) // has not been created either, but we need one to display // a real BWindow in the test environment. // be_app->Run() needs to be called in another thread - BApplication* app = new BApplication("application/x-vnd.haiku-app-server"); + BApplication* app = new BApplication( + "application/x-vnd.Haiku-test-app_server"); app->Unlock(); thread_id appThread = spawn_thread(run_app_thread, "app thread", diff --git a/src/servers/app/drawing/ViewHWInterface.cpp b/src/servers/app/drawing/ViewHWInterface.cpp index fe4ca9879c..4fec110348 100644 --- a/src/servers/app/drawing/ViewHWInterface.cpp +++ b/src/servers/app/drawing/ViewHWInterface.cpp @@ -483,7 +483,8 @@ ViewHWInterface::SetMode(const display_mode& mode) // has not been created either, but we need one to display // a real BWindow in the test environment. // be_app->Run() needs to be called in another thread - BApplication* app = new BApplication("application/x-vnd.haiku-app-server"); + BApplication* app = new BApplication( + "application/x-vnd.Haiku-test-app_server"); app->Unlock(); thread_id appThread = spawn_thread(run_app_thread, "app thread", diff --git a/src/servers/app/test_app_server.rdef b/src/servers/app/test_app_server.rdef index 29a0c4c9fe..e71bea4fbc 100644 --- a/src/servers/app/test_app_server.rdef +++ b/src/servers/app/test_app_server.rdef @@ -2,7 +2,7 @@ * app_server.rdef */ -resource app_signature "application/x-vnd.Haiku-app-server"; +resource app_signature "application/x-vnd.Haiku-test-app_server"; resource app_flags B_EXCLUSIVE_LAUNCH ; diff --git a/src/servers/registrar/Registrar.cpp b/src/servers/registrar/Registrar.cpp index 79b3d02a66..7987a1bc63 100644 --- a/src/servers/registrar/Registrar.cpp +++ b/src/servers/registrar/Registrar.cpp @@ -57,7 +57,7 @@ static const bigtime_t kRosterSanityEventInterval = 1000000LL; */ Registrar::Registrar(status_t* _error) : - BServer(B_REGISTRAR_SIGNATURE, "system:roster", -1, false, _error), + BServer(B_REGISTRAR_SIGNATURE, B_REGISTRAR_PORT_NAME, -1, false, _error), fRoster(NULL), fClipboardHandler(NULL), fMIMEManager(NULL), diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index de8d379f46..24598d765f 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -127,6 +127,7 @@ SharedLibrary libtestappserver.so : ProfileMessageSupport.cpp EventDispatcher.cpp EventStream.cpp + TestServerLoopAdapter.cpp MessageLooper.cpp # Decorator diff --git a/src/tests/servers/registrar/Jamfile b/src/tests/servers/registrar/Jamfile index 91d8c362c2..fe142624dc 100644 --- a/src/tests/servers/registrar/Jamfile +++ b/src/tests/servers/registrar/Jamfile @@ -110,7 +110,7 @@ Server test_registrar libstorage_kit_mime.a be localestub [ TargetLibstdc++ ] : - registrar.rdef + test_registrar.rdef ; if $(TARGET_PLATFORM) = libbe_test { diff --git a/src/tests/servers/registrar/run_test_registrar.cpp b/src/tests/servers/registrar/run_test_registrar.cpp index 20cf63bcb1..a32a2382a7 100644 --- a/src/tests/servers/registrar/run_test_registrar.cpp +++ b/src/tests/servers/registrar/run_test_registrar.cpp @@ -53,7 +53,7 @@ main(int argc, char* argv[]) while (get_next_thread_info(teamInfo.team, &threadCookie, &threadInfo) == B_OK) { // search for the roster thread - if (!strcmp(threadInfo.name, "_roster_thread_")) { + if (!strcmp(threadInfo.name, "roster")) { port_id port = find_port("haiku-test:roster"); port_info portInfo; if (get_port_info(port, &portInfo) == B_OK diff --git a/src/tests/servers/registrar/test_registrar.rdef b/src/tests/servers/registrar/test_registrar.rdef new file mode 100644 index 0000000000..2748c0b157 --- /dev/null +++ b/src/tests/servers/registrar/test_registrar.rdef @@ -0,0 +1,96 @@ +/* + * registrar.rdef + */ + +resource app_signature "application/x-vnd.test-registrar"; + +resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP; + /* the registrar's application flags are actually + * ignored. + * The actually used flags are set in TRoster::Init(). + */ + +resource app_version { + major = 1, + middle = 0, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "registrar", + long_info = "registrar ©2005-2008 Haiku Inc." +}; + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + +resource vector_icon array { + $"6E6369660704006B0500020006023BB8A43D8642BF898E3DBC1F4BAE5447805C" + $"00E3AD00FFBC8F05020106023D66A73B07B6B9BEF73C1A2B48379549C4DE3D76" + $"9564FF536E4402000602B898C93319083A5C163FF4CC4AB5EA4451BB00B58A00" + $"FF8565030200060236EEF83B80F5BF3E743AB74A4B669742DBD600FFEFC0FFFF" + $"DE7C05FF0D0A044D5B585B59594D530A0626484C5B4E594E32282626270A0426" + $"484C5B4C3426270A0448542A452A2C48370A042C442C2D2A2C2A450A044C5B4E" + $"594E324C340A04282626274C344E320A042A45485448512C4408023133313C08" + $"023534353E08023936394008023D373D4208022E344040090A00010010011584" + $"00040A0101011001178400040A020102000A030103000A040104000A04010500" + $"0A050106000A050107000A06050C0B0A0908100117820004" +}; + +#else + +resource large_icon array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFF00D9D9D90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFF00D98383D9D90000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFF00D900008383D9D90000FFFFFFFFFFFFFFFFFF00000000FFFFFFFFFF" + $"FFFFFFFF00D9008F00008383D9D9000000000000FFFF003FD9D9D90000FFFFFF" + $"FFFFFFFF00D9008F8F8F00008383D9D900003F3F00003FD9D9D9D9D9D90000FF" + $"FFFFFFFF00D9008F3F8F8F8F00008383D9D900003F3FAAAAD9D9D9D9D9D98300" + $"FFFFFFFF00D9003F3F8F3F8F8F8F00008383D9D900008383AAAAD9D9D983AA00" + $"FFFFFFFF00D9008F3F3F3F8F3F8F8F8F00008383D9D900003F3FAAAA83AAAA00" + $"FFFFFFFF00D9008F3F8F3F8F3F8F8F8F8F8F00008383D9D900D93F3FAAAAAA00" + $"FFFFFFFF00D9008F3F8F3F3F3F8F8F8F8F8F8F8F000083AA00D9D9D983AA0111" + $"FFFFFFFF00D9008F8F8F3F8F3F8F8F8F8F8F8F8F8F0083AA00D9D9D9D9AA0017" + $"FFFFFFFF00D9008F8F8F8F8F3F3F8F8F8F8F8F8F8F0083AA00D9D9D9D9830011" + $"FFFFFFFF00D9008F8F8F8F8F8F8F3F8F8F8F8F8F8F0083AA00D9D9D983AA0111" + $"FFFFFFFF00D900008F8F8F8F8F8F8F8F8F8F8F8F8F0083AA00D9D983AA001111" + $"FFFFFFFF0000838300008F8F8F8F8F8F8F8F8F8F8F0083AA00D983AA001111FF" + $"FFFFFFFFFF000000838300008F8F8F8F8F8F8F8F8F0083AA0083AA001111FFFF" + $"FFFFFFFF003FD9830000838300008F8F8F8F8F8F8F0083AA00AA001111FFFFFF" + $"FFFFFF003FD98383001E0000838300008F8F8F8F8F0083AA00001111FFFFFFFF" + $"FFFF003FD98383031ED9D9830000838300008F8F8F0083AA001111FFFFFFFFFF" + $"FF003FD98383003FD9D98383003F0000838300008F0083AA0011FFFFFFFFFFFF" + $"003FD98383003FD9D98383003FD9D9D900008383000083AA0011FFFFFFFFFFFF" + $"00D98383003FD9D98383003FD9D98383003F0000838383AA0011FFFFFFFFFFFF" + $"FF0000003FD9D98383003FD9D98383003FD98383000083001111FFFFFFFFFFFF" + $"FFFF003FD9D98383003FD9D98383003FD98383001111001111FFFFFFFFFFFFFF" + $"FFFF00D9838306003FD9D98383003FD98383001111FFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFF0000001000D9D98383003FD98383001111FFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFF000000003FD98300001111FFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFF000000111111FFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource mini_icon array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFF000000FFFFFFFFFFFFFFFFFFFFFF" + $"FF03D9D9D90000FFFFFFFF0000FFFFFF" + $"FF03D9B6B6D9D900000000D9D90000FF" + $"FF03D9B669B6B6D9D90000AAAAD98300" + $"FF03D9B6116868B6B6D9D9003FAAAA00" + $"FF03D9B68F681B8FB683AA00D9D9AA00" + $"FF03D9B68F181B8FB683AA003FD9AA01" + $"FF03D9B6B68F188FB683AA00D9830011" + $"FF03038383B6B68FB683AA00830011FF" + $"FF00D933048383B6B683AA000011FFFF" + $"00D98300D90303838383AA0011FFFFFF" + $"008300D98300D90303030011FFFFFFFF" + $"FF00D98300D900D9830011FFFFFFFFFF" + $"FFFF0000000000000011FFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +#endif // HAIKU_TARGET_PLATFORM_HAIKU From 1cd452ea0362086a2cdcaf93fbec292f23ff9d78 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sun, 23 Aug 2015 00:29:38 +0200 Subject: [PATCH 27/29] app_server: Drawing mode fixups * TODO: squash commit before merge into master --- src/servers/app/DrawState.cpp | 11 ++++++++--- src/servers/app/DrawState.h | 4 ++-- src/servers/app/ServerPicture.cpp | 4 ++-- src/servers/app/ServerWindow.cpp | 6 ++++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index de5c81173e..8d07c8e453 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -526,21 +526,26 @@ DrawState::SetPattern(const Pattern& pattern) } -void +bool DrawState::SetDrawingMode(drawing_mode mode) { - if (!fDrawingModeLocked) + if (!fDrawingModeLocked) { fDrawingMode = mode; + return true; + } + return false; } -void +bool DrawState::SetBlendingMode(source_alpha srcMode, alpha_function fncMode) { if (!fDrawingModeLocked) { fAlphaSrcMode = srcMode; fAlphaFncMode = fncMode; + return true; } + return false; } diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index 7e7274cdf3..6f39022b92 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -100,11 +100,11 @@ public: { return fPattern; } // drawing/blending mode - void SetDrawingMode(drawing_mode mode); + bool SetDrawingMode(drawing_mode mode); drawing_mode GetDrawingMode() const { return fDrawingMode; } - void SetBlendingMode(source_alpha srcMode, + bool SetBlendingMode(source_alpha srcMode, alpha_function fncMode); source_alpha AlphaSrcMode() const { return fAlphaSrcMode; } diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index bec689149f..20d3719c4e 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -626,8 +626,8 @@ set_pen_location(Canvas* canvas, BPoint pt) static void set_drawing_mode(Canvas* canvas, drawing_mode mode) { - canvas->CurrentState()->SetDrawingMode(mode); - canvas->GetDrawingEngine()->SetDrawingMode(mode); + if (canvas->CurrentState()->SetDrawingMode(mode)) + canvas->GetDrawingEngine()->SetDrawingMode(mode); } diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 369e13454a..b91ce89115 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -2200,7 +2200,8 @@ fDesktop->LockSingleWindow(); if (opacity != 255) { fCurrentView->CurrentState()->SetDrawingMode(B_OP_ALPHA); - fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, + B_ALPHA_COMPOSITE); fCurrentView->CurrentState()->SetDrawingModeLocked(true); } @@ -3466,7 +3467,8 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) if (opacity != 255) { fCurrentView->CurrentState()->SetDrawingMode(B_OP_ALPHA); - fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, + B_ALPHA_COMPOSITE); fCurrentView->CurrentState()->SetDrawingModeLocked(true); } From bafd2b461acb32cfb40755067d408e480b0d099e Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sun, 23 Aug 2015 00:31:10 +0200 Subject: [PATCH 28/29] app_server: PictureBoundingBoxPlayer fixups * TODO: squash commit before merge into master --- src/servers/app/PictureBoundingBoxPlayer.cpp | 12 ++++++------ src/servers/app/PictureBoundingBoxPlayer.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/servers/app/PictureBoundingBoxPlayer.cpp b/src/servers/app/PictureBoundingBoxPlayer.cpp index b330a78265..c8b931ce0c 100644 --- a/src/servers/app/PictureBoundingBoxPlayer.cpp +++ b/src/servers/app/PictureBoundingBoxPlayer.cpp @@ -50,12 +50,12 @@ typedef PictureBoundingBoxPlayer::State BoundingBoxState; class PictureBoundingBoxPlayer::State { public: - State(DrawState* drawState, BRect* boundingBox) + State(const DrawState* drawState, BRect* boundingBox) : fDrawState(drawState->Squash()), fBoundingBox(boundingBox) { - fBoundingBox->Set(INT_MAX, INT_MAX, 0, 0); + fBoundingBox->Set(INT_MAX, INT_MAX, INT_MIN, INT_MIN); } ~State() @@ -97,7 +97,7 @@ public: private: void _AffineTransformRect(BRect& rect) { - BAffineTransform transform = fDrawState->Transform(); + BAffineTransform transform = fDrawState->CombinedTransform(); if (transform.IsIdentity()) return; @@ -111,8 +111,8 @@ private: float minX = INT_MAX; float minY = INT_MAX; - float maxX = 0; - float maxY = 0; + float maxX = INT_MIN; + float maxY = INT_MIN; for (uint32 i = 0; i < 4; i++) { if (transformedShape[i].x < minX) @@ -776,7 +776,7 @@ const static void* kTableEntries[] = { /* static */ void PictureBoundingBoxPlayer::Play(ServerPicture* picture, - DrawState* drawState, BRect* outBoundingBox) + const DrawState* drawState, BRect* outBoundingBox) { State state(drawState, outBoundingBox); diff --git a/src/servers/app/PictureBoundingBoxPlayer.h b/src/servers/app/PictureBoundingBoxPlayer.h index 37ae88e2c3..115f1acbfb 100644 --- a/src/servers/app/PictureBoundingBoxPlayer.h +++ b/src/servers/app/PictureBoundingBoxPlayer.h @@ -20,7 +20,7 @@ public: public: static void Play(ServerPicture* picture, - DrawState* drawState, + const DrawState* drawState, BRect* outBoundingBox); }; From 9b417f6486256a8166b79c1f92178503e39c4408 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Sun, 23 Aug 2015 00:38:55 +0200 Subject: [PATCH 29/29] app_server: rework AlphaMask, use bounds for mask pictures * Refactor AlphaMask class to separate the mask from its source. This allows use to not just a BPicture as source for a pixel alpha mask, but also e.g. a BShape in the future (not yet implemented). * For BPicture-based masks, use the PictureBoundingBoxPlayer to determine the size of the resulting mask bitmap. The masks are now drawn into bitmaps of this size (instead of the whole view size). When alpha masks are stacked, their bounding rectangles intersect (i.e. masks further up in the stack can never be larger than masks lower in the stack). The bitmap of a mask always contains the state of itself blended with all masks in the stack below it. This also avoids frequent rerendering of the masks. They are now independent of view size. When the view origin (in screen coordinates) changes we only have to reattach the mask buffer, without having to redraw it. * The class UniformAlphaMask is used for simple masks with the same alpha value in all pixels, it uses no mask bitmap at all. Currently, it can only be used on its own and not be stacked together with other mask types. --- src/servers/app/Canvas.cpp | 2 +- src/servers/app/DrawState.cpp | 25 +- src/servers/app/DrawState.h | 5 +- src/servers/app/Layer.cpp | 6 +- src/servers/app/ServerWindow.cpp | 8 +- src/servers/app/drawing/AlphaMask.cpp | 492 ++++++++++++-------- src/servers/app/drawing/AlphaMask.h | 122 +++-- src/servers/app/drawing/Painter/Painter.cpp | 3 +- 8 files changed, 397 insertions(+), 266 deletions(-) diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index 3c53676e62..66e28ed770 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -246,7 +246,7 @@ Canvas::BlendLayer(Layer* layer) fDrawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); fDrawState->SetTransformEnabled(false); - AlphaMask* mask = new AlphaMask(layer->Opacity()); + AlphaMask* mask = new UniformAlphaMask(layer->Opacity()); if (mask == NULL) { layerBitmap->ReleaseReference(); return; diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index 8d07c8e453..8bae0e1ebb 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -110,8 +110,6 @@ DrawState::~DrawState() { delete fClippingRegion; delete fPreviousState; - if (fAlphaMask != NULL) - fAlphaMask->ReleaseReference(); } @@ -389,15 +387,10 @@ DrawState::SetTransformEnabled(bool enabled) DrawState* -DrawState::Squash() +DrawState::Squash() const { DrawState* const squashedState = new DrawState(*this); - - squashedState->fOrigin = fCombinedOrigin; - squashedState->fScale = fCombinedScale; - squashedState->fTransform = fCombinedTransform; - - return squashedState; + return squashedState->PushState(); } @@ -461,24 +454,14 @@ DrawState::SetAlphaMask(AlphaMask* mask) { // NOTE: In BeOS, it wasn't possible to clip to a BPicture and keep // regular custom clipping to a BRegion at the same time. - if (fAlphaMask == mask) - return; - - if (mask != NULL) - mask->AcquireReference(); - if (fAlphaMask != NULL) - fAlphaMask->ReleaseReference(); - fAlphaMask = mask; - if (fAlphaMask != NULL && fPreviousState != NULL) - fAlphaMask->SetPrevious(fPreviousState->fAlphaMask); - + fAlphaMask.SetTo(mask); } AlphaMask* DrawState::GetAlphaMask() const { - return fAlphaMask; + return fAlphaMask.Get(); } diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index 6f39022b92..3572874b7a 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "ServerFont.h" @@ -70,7 +71,7 @@ public: { return fCombinedTransform; } void SetTransformEnabled(bool enabled); - DrawState* Squash(); + DrawState* Squash() const; // additional clipping as requested by client void SetClippingRegion(const BRegion* region); @@ -166,7 +167,7 @@ protected: BRegion* fClippingRegion; - AlphaMask* fAlphaMask; + BReference fAlphaMask; rgb_color fHighColor; rgb_color fLowColor; diff --git a/src/servers/app/Layer.cpp b/src/servers/app/Layer.cpp index 1c80ad736b..96bf38eafd 100644 --- a/src/servers/app/Layer.cpp +++ b/src/servers/app/Layer.cpp @@ -121,10 +121,10 @@ Layer::RenderToBitmap(Canvas* canvas) LayerCanvas layerCanvas(layerEngine, canvas->CurrentState()); AlphaMask* const mask = layerCanvas.GetAlphaMask(); - BPoint oldOffset; + IntPoint oldOffset; if (mask != NULL) { // Move alpha mask to bitmap origin - oldOffset = mask->Update(BPoint(0, 0)); + oldOffset = mask->SetViewOrigin(IntPoint(0, 0)); } canvas->CurrentState()->SetDrawingMode(B_OP_ALPHA); @@ -147,7 +147,7 @@ Layer::RenderToBitmap(Canvas* canvas) // Note: this needs to be adapted if setting alpha masks is // implemented as BPicture command (the mask now might be a different // one than before). - mask->Update(oldOffset); + mask->SetViewOrigin(oldOffset); layerCanvas.ResyncDrawState(); } diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index b91ce89115..7322cd8adc 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -1942,11 +1942,13 @@ fDesktop->LockSingleWindow(); if (picture == NULL) break; - AlphaMask* mask = new(std::nothrow) AlphaMask( - picture, inverse, where, *fCurrentView->CurrentState()); + AlphaMask* const mask = new PictureAlphaMask( + fCurrentView->GetAlphaMask(), picture, + *fCurrentView->CurrentState(), where, inverse); fCurrentView->SetAlphaMask(mask); if (mask != NULL) mask->ReleaseReference(); + _UpdateDrawState(fCurrentView); picture->ReleaseReference(); @@ -3812,7 +3814,7 @@ ServerWindow::_UpdateDrawState(View* view) BPoint leftTop(0, 0); if (view->GetAlphaMask() != NULL) { view->LocalToScreenTransform().Apply(&leftTop); - view->GetAlphaMask()->Update(view->Bounds(), leftTop); + view->GetAlphaMask()->SetViewOrigin(leftTop); leftTop = BPoint(0, 0); } view->PenToScreenTransform().Apply(&leftTop); diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 865425a391..fc5e00659d 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -15,222 +15,73 @@ #include "BitmapManager.h" #include "Canvas.h" #include "DrawingEngine.h" +#include "PictureBoundingBoxPlayer.h" #include "ServerBitmap.h" #include "ServerPicture.h" +#include "Shape.h" -AlphaMask::AlphaMask(ServerPicture* picture, bool inverse, BPoint origin, - const DrawState& drawState) +// #pragma mark - AlphaMask + + +AlphaMask::AlphaMask(AlphaMask* previousMask, bool inverse) : - fPreviousMask(NULL), - - fPicture(picture), + fPreviousMask(previousMask), + fBounds(), + fViewOrigin(), fInverse(inverse), - fOrigin(origin), fBackgroundOpacity(0), - fDrawState(drawState), - - fViewBounds(), - fViewOffset(), - - fCachedBitmap(NULL), - fCachedBounds(), - fCachedOffset(), - + fBits(NULL), fBuffer(), - fCachedMask(), - fScanline(fCachedMask) + fMask(), + fScanline(fMask) { - fPicture->AcquireReference(); } AlphaMask::AlphaMask(uint8 backgroundOpacity) : - fPreviousMask(NULL), - - fPicture(NULL), + fPreviousMask(), + fBounds(), + fViewOrigin(), fInverse(false), - fOrigin(0, 0), fBackgroundOpacity(backgroundOpacity), - fDrawState(), - - fViewBounds(), - fViewOffset(), - - fCachedBitmap(NULL), - fCachedBounds(), - fCachedOffset(), - + fBits(NULL), fBuffer(), - fCachedMask(), - fScanline(fCachedMask) + fMask(), + fScanline(fMask) { } AlphaMask::~AlphaMask() { - if (fPicture != NULL) - fPicture->ReleaseReference(); - delete[] fCachedBitmap; - SetPrevious(NULL); + delete[] fBits; } -void -AlphaMask::Update(BRect bounds, BPoint offset) +IntPoint +AlphaMask::SetViewOrigin(IntPoint viewOrigin) { - fViewBounds = bounds; - fViewOffset = offset; + if (viewOrigin == fViewOrigin) + return fViewOrigin; - if (fPreviousMask != NULL) - fPreviousMask->Update(bounds, offset); -} + IntPoint oldOrigin = fViewOrigin; + fViewOrigin = viewOrigin; - -BPoint -AlphaMask::Update(BPoint offset) -{ - BPoint oldOffset = fViewOffset; - fViewOffset = offset; - - if (oldOffset == fCachedOffset && fCachedBitmap != NULL) { - // No need to redraw the picture when only the offset is shifted - fCachedOffset = offset; - _AttachMaskToBuffer(); - } - - if (fPreviousMask != NULL) - fPreviousMask->Update(offset); - - return oldOffset; -} - - -void -AlphaMask::SetPrevious(AlphaMask* mask) -{ - // Since multiple DrawStates can point to the same AlphaMask, - // don't accept ourself as the "previous" mask on the state stack. - if (mask == this || mask == fPreviousMask) - return; - - if (mask != NULL) - mask->AcquireReference(); - if (fPreviousMask != NULL) - fPreviousMask->ReleaseReference(); - fPreviousMask = mask; -} - - -scanline_unpacked_masked_type* -AlphaMask::Generate() -{ - if (fPicture == NULL) { - fBuffer.attach(NULL, 0, 0, 0); - _AttachMaskToBuffer(); - return &fScanline; - } - - if (!fViewBounds.IsValid()) - return NULL; - - // See if a cached bitmap can be used. Don't use it when the view offset - // or bounds have changed. - if (fCachedBitmap != NULL - && fViewBounds == fCachedBounds && fViewOffset == fCachedOffset) { - return &fScanline; - } - - uint32 width = fViewBounds.IntegerWidth() + 1; - uint32 height = fViewBounds.IntegerHeight() + 1; - - if (fViewBounds != fCachedBounds || fCachedBitmap == NULL) { - delete[] fCachedBitmap; - fCachedBitmap = new(std::nothrow) uint8[width * height]; - } - - // If rendering the picture fails, we will draw without any clipping. - ServerBitmap* bitmap = _RenderPicture(); - if (bitmap == NULL || fCachedBitmap == NULL) { - fBuffer.attach(NULL, 0, 0, 0); - return NULL; - } - - uint8* bits = bitmap->Bits(); - uint32 bytesPerRow = bitmap->BytesPerRow(); - uint8* row = bits; - uint8* pixel = fCachedBitmap; - - // Let any previous masks also regenerate themselves. Updating the cached - // mask bitmap is only necessary after the view size changed or the - // scrolling offset, which definitely affects any masks of lower states - // as well, so it works recursively until the bottom mask is regenerated. - bool transferBitmap = true; - if (fPreviousMask != NULL) { - fPreviousMask->Generate(); - if (fPreviousMask->fCachedBitmap != NULL) { - uint8* previousBits = fPreviousMask->fCachedBitmap; - for (uint32 y = 0; y < height; y++) { - for (uint32 x = 0; x < width; x++) { - if (previousBits[0] != 0) { - if (fInverse) - pixel[0] = 255 - row[3]; - else - pixel[0] = row[3]; - pixel[0] = pixel[0] * previousBits[0] / 255; - } else - pixel[0] = 0; - previousBits++; - pixel++; - row += 4; - } - bits += bytesPerRow; - row = bits; - } - transferBitmap = false; - } - } - - if (transferBitmap) { - for (uint32 y = 0; y < height; y++) { - for (uint32 x = 0; x < width; x++) { - if (fInverse) - pixel[0] = 255 - row[3]; - else - pixel[0] = row[3]; - pixel++; - row += 4; - } - bits += bytesPerRow; - row = bits; - } - } - - bitmap->ReleaseReference(); - - fCachedBounds = fViewBounds; - fCachedOffset = fViewOffset; - - fBuffer.attach(fCachedBitmap, width, height, width); _AttachMaskToBuffer(); - return &fScanline; -} + if (fPreviousMask != NULL) + fPreviousMask->SetViewOrigin(viewOrigin); - -agg::clipped_alpha_mask* -AlphaMask::Mask() -{ - return &fCachedMask; + return oldOrigin; } ServerBitmap* -AlphaMask::_RenderPicture() const +AlphaMask::_CreateTemporaryBitmap(BRect bounds) const { - UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(fViewBounds, + UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(bounds, B_RGBA32, 0); if (bitmap == NULL) return NULL; @@ -240,43 +91,272 @@ AlphaMask::_RenderPicture() const return NULL; } - // Clear the bitmap with the transparent color memset(bitmap->Bits(), fBackgroundOpacity, bitmap->BitsLength()); - // Render the picture to the bitmap - BitmapHWInterface interface(bitmap); - DrawingEngine* engine = interface.CreateDrawingEngine(); - if (engine == NULL) { - delete bitmap; - return NULL; - } - - OffscreenCanvas canvas(engine, fDrawState); - canvas.PushState(); - - if (engine->LockParallelAccess()) { - // FIXME ConstrainClippingRegion docs says passing NULL disables - // all clipping. This doesn't work and will crash in Painter. - BRegion clipping; - clipping.Include(fViewBounds); - engine->ConstrainClippingRegion(&clipping); - fPicture->Play(&canvas); - engine->UnlockParallelAccess(); - } - - canvas.PopState(); - delete engine; - return bitmap; } +void +AlphaMask::_Generate() +{ + ServerBitmap* const bitmap = _RenderSource(); + BReference bitmapRef(bitmap, true); + if (bitmap == NULL) { + _SetNoClipping(); + return; + } + + const int32 width = fBounds.IntegerWidth() + 1; + const int32 height = fBounds.IntegerHeight() + 1; + + delete[] fBits; + fBits = new(std::nothrow) uint8[width * height]; + + uint8* source = bitmap->Bits(); + uint8* destination = fBits; + uint32 numPixels = width * height; + + if (fPreviousMask != NULL) { + int32 previousStartX = fBounds.left - fPreviousMask->fBounds.left; + int32 previousStartY = fBounds.top - fPreviousMask->fBounds.top; + if (previousStartX < 0) + previousStartX = 0; + if (previousStartY < 0) + previousStartY = 0; + + for (int32 y = previousStartY; y < previousStartY + height; y++) { + uint8* previousRow = fPreviousMask->fBuffer.row_ptr(y); + for (int32 x = previousStartX; x < previousStartX + width; x++) { + uint8 sourceAlpha = fInverse ? 255 - source[3] : source[3]; + *destination = sourceAlpha * previousRow[x] / 255; + destination++; + source += 4; + } + } + } else { + while (numPixels--) { + *destination = fInverse ? 255 - source[3] : source[3]; + destination++; + source += 4; + } + } + + fBuffer.attach(fBits, width, height, width); + _AttachMaskToBuffer(); +} + + +void +AlphaMask::_SetNoClipping() +{ + fBuffer.attach(NULL, 0, 0, 0); + _AttachMaskToBuffer(); +} + + void AlphaMask::_AttachMaskToBuffer() { uint8 outsideOpacity = fInverse ? 255 - fBackgroundOpacity : fBackgroundOpacity; - fCachedMask.attach(fBuffer, fViewOffset.x + fOrigin.x, - fViewOffset.y + fOrigin.y, outsideOpacity); + AlphaMask* previousMask = fPreviousMask; + while (previousMask != NULL && outsideOpacity != 0) { + uint8 previousOutsideOpacity = previousMask->fInverse + ? 255 - previousMask->fBackgroundOpacity + : previousMask->fBackgroundOpacity; + outsideOpacity = outsideOpacity * previousOutsideOpacity / 255; + previousMask = previousMask->fPreviousMask; + } + + const IntPoint maskOffset = _Offset(); + const int32 offsetX = fBounds.left + maskOffset.x + fViewOrigin.x; + const int32 offsetY = fBounds.top + maskOffset.y + fViewOrigin.y; + + fMask.attach(fBuffer, offsetX, offsetY, outsideOpacity); +} + + +// #pragma mark - UniformAlphaMask + + +UniformAlphaMask::UniformAlphaMask(uint8 opacity) + : + AlphaMask(opacity) +{ + fBounds.Set(0, 0, 0, 0); + _SetNoClipping(); +} + + +ServerBitmap* +UniformAlphaMask::_RenderSource() +{ + return NULL; +} + + +IntPoint +UniformAlphaMask::_Offset() +{ + return IntPoint(0, 0); +} + + +// #pragma mark - VectorAlphaMask + + +template +VectorAlphaMask::VectorAlphaMask(AlphaMask* previousMask, + BPoint where, bool inverse) + : + AlphaMask(previousMask, inverse), + fWhere(where) +{ +} + + +template +ServerBitmap* +VectorAlphaMask::_RenderSource() +{ + fBounds = static_cast(this)->DetermineBoundingBox(); + if (fPreviousMask != NULL) + fBounds = fBounds & fPreviousMask->fBounds; + if (!fBounds.IsValid()) + return NULL; + + ServerBitmap* bitmap = _CreateTemporaryBitmap(fBounds); + if (bitmap == NULL) + return NULL; + + // Render the picture to the bitmap + BitmapHWInterface interface(bitmap); + DrawingEngine* engine = interface.CreateDrawingEngine(); + if (engine == NULL) { + bitmap->ReleaseReference(); + return NULL; + } + engine->SetRendererOffset(fBounds.left, fBounds.top); + + OffscreenCanvas canvas(engine, + static_cast(this)->GetDrawState()); + + DrawState* const drawState = canvas.CurrentState(); + drawState->SetDrawingMode(B_OP_ALPHA); + drawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + drawState->SetDrawingModeLocked(true); + canvas.PushState(); + + if (engine->LockParallelAccess()) { + BRegion clipping; + clipping.Set((clipping_rect)fBounds); + engine->ConstrainClippingRegion(&clipping); + static_cast(this)->DrawVectors(&canvas); + engine->UnlockParallelAccess(); + } + + delete engine; + + return bitmap; +} + + +template +IntPoint +VectorAlphaMask::_Offset() +{ + return fWhere; +} + + + +// #pragma mark - PictureAlphaMask + + +PictureAlphaMask::PictureAlphaMask(AlphaMask* previousMask, + ServerPicture* picture, const DrawState& drawState, BPoint where, + bool inverse) + : + VectorAlphaMask(previousMask, where, inverse), + fPicture(picture), + fDrawState(new DrawState(drawState)) +{ + _Generate(); +} + + +PictureAlphaMask::~PictureAlphaMask() +{ + delete fDrawState; +} + + +void +PictureAlphaMask::DrawVectors(Canvas* canvas) +{ + fPicture->Play(canvas); +} + + +BRect +PictureAlphaMask::DetermineBoundingBox() const +{ + BRect boundingBox; + PictureBoundingBoxPlayer::Play(fPicture, fDrawState, &boundingBox); + + if (!boundingBox.IsValid()) + return boundingBox; + + // Round up and add an additional 2 pixels on the bottom/right to + // compensate for the various types of rounding used in Painter. + boundingBox.left = floorf(boundingBox.left); + boundingBox.right = ceilf(boundingBox.right) + 2; + boundingBox.top = floorf(boundingBox.top); + boundingBox.bottom = ceilf(boundingBox.bottom) + 2; + + return boundingBox; +} + + +const DrawState& +PictureAlphaMask::GetDrawState() const +{ + return *fDrawState; +} + + +// #pragma mark - ShapeAlphaMask + + +ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, BPoint where, + bool inverse) + : + VectorAlphaMask(previousMask, where, inverse), + fDrawState() +{ + _Generate(); +} + + +void +ShapeAlphaMask::DrawVectors(Canvas* canvas) +{ + // TODO +} + + +BRect +ShapeAlphaMask::DetermineBoundingBox() const +{ + // TODO + return BRect(0, 0, 0, 0); +} + + +const DrawState& +ShapeAlphaMask::GetDrawState() const +{ + return fDrawState; } diff --git a/src/servers/app/drawing/AlphaMask.h b/src/servers/app/drawing/AlphaMask.h index 81503c0874..4b9d2ffcb3 100644 --- a/src/servers/app/drawing/AlphaMask.h +++ b/src/servers/app/drawing/AlphaMask.h @@ -13,59 +13,123 @@ #include "DrawState.h" #include "drawing/Painter/defines.h" +#include "IntRect.h" +class BShape; class ServerBitmap; class ServerPicture; +// #pragma mark - AlphaMask + + class AlphaMask : public BReferenceable { public: - AlphaMask(ServerPicture* mask, bool inverse, - BPoint origin, const DrawState& drawState); + AlphaMask(AlphaMask* previousMask, + bool inverse); AlphaMask(uint8 backgroundOpacity); - ~AlphaMask(); + virtual ~AlphaMask(); - void Update(BRect bounds, BPoint offset); - BPoint Update(BPoint offset); + IntPoint SetViewOrigin(IntPoint viewOrigin); - void SetPrevious(AlphaMask* mask); + scanline_unpacked_masked_type* Scanline() + { return &fScanline; } - scanline_unpacked_masked_type* Generate(); - agg::clipped_alpha_mask* Mask(); + agg::clipped_alpha_mask* Mask() + { return &fMask; } + +protected: + ServerBitmap* _CreateTemporaryBitmap(BRect bounds) const; + void _Generate(); + void _SetNoClipping(); private: - ServerBitmap* _RenderPicture() const; + virtual ServerBitmap* _RenderSource() = 0; + virtual IntPoint _Offset() = 0; + void _AttachMaskToBuffer(); +public: + BReference fPreviousMask; + IntRect fBounds; private: - AlphaMask* fPreviousMask; - - ServerPicture* fPicture; + IntPoint fViewOrigin; const bool fInverse; - BPoint fOrigin; - // position of this mask, relative to - // either its parent mask, or (if there - // is none) the canvas uint8 fBackgroundOpacity; - DrawState fDrawState; - // draw state used for drawing fPicture - - BRect fViewBounds; - // determines alpha mask size - BPoint fViewOffset; - // position of alpha mask in screen - // coordinates - - uint8* fCachedBitmap; - BRect fCachedBounds; - BPoint fCachedOffset; + uint8* fBits; agg::rendering_buffer fBuffer; - agg::clipped_alpha_mask fCachedMask; + agg::clipped_alpha_mask fMask; scanline_unpacked_masked_type fScanline; }; +class UniformAlphaMask : public AlphaMask { +public: + UniformAlphaMask(uint8 opacity); + +private: + virtual ServerBitmap* _RenderSource(); + virtual IntPoint _Offset(); +}; + + +// #pragma mark - VectorAlphaMask + + +template +class VectorAlphaMask : public AlphaMask { +public: + VectorAlphaMask(AlphaMask* previousMask, + BPoint where, bool inverse); + +private: + virtual ServerBitmap* _RenderSource(); + virtual IntPoint _Offset(); + +protected: + BPoint fWhere; +}; + + +// #pragma mark - PictureAlphaMask + + +class PictureAlphaMask : public VectorAlphaMask { +public: + PictureAlphaMask(AlphaMask* previousMask, + ServerPicture* picture, + const DrawState& drawState, BPoint where, + bool inverse); + virtual ~PictureAlphaMask(); + + void DrawVectors(Canvas* canvas); + BRect DetermineBoundingBox() const; + const DrawState& GetDrawState() const; + +private: + BReference fPicture; + DrawState* fDrawState; +}; + + +// #pragma mark - ShapeAlphaMask + + +class ShapeAlphaMask : public VectorAlphaMask { +public: + ShapeAlphaMask(AlphaMask* previousMask, + BPoint where, bool inverse); + + void DrawVectors(Canvas* canvas); + BRect DetermineBoundingBox() const; + const DrawState& GetDrawState() const; + +private: + DrawState fDrawState; +}; + + #endif // ALPHA_MASK_H diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index c8e83756c2..e382b8dfd4 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -280,6 +280,7 @@ Painter::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset) // and messed up the state. For other graphics state changes, the // Painter methods are used directly, so this function is much less // speed critical than it used to be. + SetTransform(state->CombinedTransform(), xOffset, yOffset); SetPenSize(state->PenSize()); @@ -289,7 +290,7 @@ Painter::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset) fSubpixelPrecise = state->SubPixelPrecise(); if (state->GetAlphaMask() != NULL) { - fMaskedUnpackedScanline = state->GetAlphaMask()->Generate(); + fMaskedUnpackedScanline = state->GetAlphaMask()->Scanline(); fClippedAlphaMask = state->GetAlphaMask()->Mask(); } else { fMaskedUnpackedScanline = NULL;