/* * Copyright 2001-2019, 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 * Stephan Aßmus */ #include "ServerPicture.h" #include #include #include #include "AlphaMask.h" #include "DrawingEngine.h" #include "DrawState.h" #include "GlobalFontManager.h" #include "Layer.h" #include "ServerApp.h" #include "ServerBitmap.h" #include "ServerFont.h" #include "ServerTokenSpace.h" #include "View.h" #include "Window.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using std::stack; class ShapePainter : public BShapeIterator { public: ShapePainter(Canvas* canvas, BGradient* gradient); virtual ~ShapePainter(); status_t Iterate(const BShape* shape); virtual status_t IterateMoveTo(BPoint* point); virtual status_t IterateLineTo(int32 lineCount, BPoint* linePts); virtual status_t IterateBezierTo(int32 bezierCount, BPoint* bezierPts); virtual status_t IterateClose(); virtual status_t IterateArcTo(float& rx, float& ry, float& angle, bool largeArc, bool counterClockWise, BPoint& point); void Draw(BRect frame, bool filled); private: Canvas* fCanvas; BGradient* fGradient; stack fOpStack; stack fPtStack; }; ShapePainter::ShapePainter(Canvas* canvas, BGradient* gradient) : fCanvas(canvas), fGradient(gradient) { } ShapePainter::~ShapePainter() { } status_t ShapePainter::Iterate(const BShape* shape) { // this class doesn't modify the shape data return BShapeIterator::Iterate(const_cast(shape)); } status_t ShapePainter::IterateMoveTo(BPoint* point) { try { fOpStack.push(OP_MOVETO); fPtStack.push(*point); } catch (std::bad_alloc&) { return B_NO_MEMORY; } return B_OK; } status_t ShapePainter::IterateLineTo(int32 lineCount, BPoint* linePts) { try { fOpStack.push(OP_LINETO | lineCount); for (int32 i = 0; i < lineCount; i++) fPtStack.push(linePts[i]); } catch (std::bad_alloc&) { return B_NO_MEMORY; } return B_OK; } status_t ShapePainter::IterateBezierTo(int32 bezierCount, BPoint* bezierPts) { bezierCount *= 3; try { fOpStack.push(OP_BEZIERTO | bezierCount); for (int32 i = 0; i < bezierCount; i++) fPtStack.push(bezierPts[i]); } catch (std::bad_alloc&) { return B_NO_MEMORY; } return B_OK; } status_t ShapePainter::IterateArcTo(float& rx, float& ry, float& angle, bool largeArc, bool counterClockWise, BPoint& point) { uint32 op; if (largeArc) { if (counterClockWise) op = OP_LARGE_ARC_TO_CCW; else op = OP_LARGE_ARC_TO_CW; } else { if (counterClockWise) op = OP_SMALL_ARC_TO_CCW; else op = OP_SMALL_ARC_TO_CW; } try { fOpStack.push(op | 3); fPtStack.push(BPoint(rx, ry)); fPtStack.push(BPoint(angle, 0)); fPtStack.push(point); } catch (std::bad_alloc&) { return B_NO_MEMORY; } return B_OK; } status_t ShapePainter::IterateClose() { try { fOpStack.push(OP_CLOSE); } catch (std::bad_alloc&) { return B_NO_MEMORY; } return B_OK; } void ShapePainter::Draw(BRect frame, bool filled) { // We're going to draw the currently iterated shape. // TODO: This can be more efficient by skipping the conversion. int32 opCount = fOpStack.size(); int32 ptCount = fPtStack.size(); if (opCount > 0 && ptCount > 0) { int32 i; uint32* opList = new(std::nothrow) uint32[opCount]; if (opList == NULL) return; BPoint* ptList = new(std::nothrow) BPoint[ptCount]; if (ptList == NULL) { delete[] opList; return; } for (i = opCount - 1; i >= 0; i--) { opList[i] = fOpStack.top(); fOpStack.pop(); } for (i = ptCount - 1; i >= 0; i--) { ptList[i] = fPtStack.top(); fPtStack.pop(); } // this might seem a bit weird, but under R5, the shapes // are always offset by the current pen location BPoint screenOffset = fCanvas->CurrentState()->PenLocation(); frame.OffsetBy(screenOffset); const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&screenOffset); transform.Apply(&frame); /* stroked gradients are not yet supported */ if (fGradient != NULL) { fCanvas->GetDrawingEngine()->DrawShape(frame, opCount, opList, ptCount, ptList, filled, *fGradient, screenOffset, fCanvas->Scale()); } else { fCanvas->GetDrawingEngine()->DrawShape(frame, opCount, opList, ptCount, ptList, filled, screenOffset, fCanvas->Scale()); } delete[] opList; delete[] ptList; } } // #pragma mark - drawing functions class CanvasCallbacks: public BPrivate::PicturePlayerCallbacks { public: CanvasCallbacks(Canvas* const canvas); virtual void MovePenBy(const BPoint& where); virtual void StrokeLine(const BPoint& start, const BPoint& end); virtual void DrawRect(const BRect& rect, bool fill); virtual void DrawRoundRect(const BRect& rect, const BPoint& radii, bool fill); virtual void DrawBezier(const BPoint controlPoints[4], bool fill); virtual void DrawArc(const BPoint& center, const BPoint& radii, float startTheta, float arcTheta, bool fill); virtual void DrawEllipse(const BRect& rect, bool fill); virtual void DrawPolygon(size_t numPoints, const BPoint points[], bool isClosed, bool fill); virtual void DrawShape(const BShape& shape, bool fill); virtual void DrawString(const char* string, size_t length, float spaceEscapement, float nonSpaceEscapement); virtual void DrawPixels(const BRect& source, const BRect& destination, uint32 width, uint32 height, size_t bytesPerRow, color_space pixelFormat, uint32 flags, const void* data, size_t length); virtual void DrawPicture(const BPoint& where, int32 token); virtual void SetClippingRects(size_t numRects, const clipping_rect rects[]); virtual void ClipToPicture(int32 token, const BPoint& where, bool clipToInverse); virtual void PushState(); virtual void PopState(); virtual void EnterStateChange(); virtual void ExitStateChange(); virtual void EnterFontState(); virtual void ExitFontState(); virtual void SetOrigin(const BPoint& origin); virtual void SetPenLocation(const BPoint& location); virtual void SetDrawingMode(drawing_mode mode); virtual void SetLineMode(cap_mode capMode, join_mode joinMode, float miterLimit); virtual void SetPenSize(float size); virtual void SetForeColor(const rgb_color& color); virtual void SetBackColor(const rgb_color& color); virtual void SetStipplePattern(const pattern& patter); virtual void SetScale(float scale); virtual void SetFontFamily(const char* familyName, size_t length); virtual void SetFontStyle(const char* styleName, size_t length); virtual void SetFontSpacing(uint8 spacing); virtual void SetFontSize(float size); virtual void SetFontRotation(float rotation); virtual void SetFontEncoding(uint8 encoding); virtual void SetFontFlags(uint32 flags); virtual void SetFontShear(float shear); virtual void SetFontFace(uint16 face); virtual void SetBlendingMode(source_alpha alphaSourceMode, alpha_function alphaFunctionMode); virtual void SetTransform(const BAffineTransform& transform); virtual void TranslateBy(double x, double y); virtual void ScaleBy(double x, double y); virtual void RotateBy(double angleRadians); virtual void BlendLayer(Layer* layer); virtual void ClipToRect(const BRect& rect, bool inverse); virtual void ClipToShape(int32 opCount, const uint32 opList[], int32 ptCount, const BPoint ptList[], bool inverse); virtual void DrawStringLocations(const char* string, size_t length, const BPoint locations[], size_t locationCount); virtual void DrawRectGradient(const BRect& rect, BGradient& gradient, bool fill); virtual void DrawRoundRectGradient(const BRect& rect, const BPoint& radii, BGradient& gradient, bool fill); virtual void DrawBezierGradient(const BPoint controlPoints[4], BGradient& gradient, bool fill); virtual void DrawArcGradient(const BPoint& center, const BPoint& radii, float startTheta, float arcTheta, BGradient& gradient, bool fill); virtual void DrawEllipseGradient(const BRect& rect, BGradient& gradient, bool fill); virtual void DrawPolygonGradient(size_t numPoints, const BPoint points[], bool isClosed, BGradient& gradient, bool fill); virtual void DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill); virtual void SetFillRule(int32 fillRule); virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, BGradient& gradient); private: Canvas* const fCanvas; }; CanvasCallbacks::CanvasCallbacks(Canvas* const canvas) : fCanvas(canvas) { } static void get_polygon_frame(const BPoint* points, uint32 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); } void CanvasCallbacks::MovePenBy(const BPoint& delta) { fCanvas->CurrentState()->SetPenLocation( fCanvas->CurrentState()->PenLocation() + delta); } void CanvasCallbacks::StrokeLine(const BPoint& _start, const BPoint& _end) { BPoint start = _start; BPoint end = _end; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&start); transform.Apply(&end); fCanvas->GetDrawingEngine()->StrokeLine(start, end); fCanvas->CurrentState()->SetPenLocation(_end); // 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 } void CanvasCallbacks::DrawRect(const BRect& _rect, bool fill) { BRect rect = _rect; fCanvas->PenToScreenTransform().Apply(&rect); if (fill) fCanvas->GetDrawingEngine()->FillRect(rect); else fCanvas->GetDrawingEngine()->StrokeRect(rect); } void CanvasCallbacks::DrawRoundRect(const BRect& _rect, const BPoint& radii, bool fill) { BRect rect = _rect; fCanvas->PenToScreenTransform().Apply(&rect); float scale = fCanvas->CurrentState()->CombinedScale(); fCanvas->GetDrawingEngine()->DrawRoundRect(rect, radii.x * scale, radii.y * scale, fill); } void CanvasCallbacks::DrawBezier(const BPoint viewPoints[4], bool fill) { const size_t kNumPoints = 4; BPoint points[kNumPoints]; fCanvas->PenToScreenTransform().Apply(points, viewPoints, kNumPoints); fCanvas->GetDrawingEngine()->DrawBezier(points, fill); } void CanvasCallbacks::DrawArc(const BPoint& center, const BPoint& radii, float startTheta, float arcTheta, bool fill) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); fCanvas->PenToScreenTransform().Apply(&rect); fCanvas->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, fill); } void CanvasCallbacks::DrawEllipse(const BRect& _rect, bool fill) { BRect rect = _rect; fCanvas->PenToScreenTransform().Apply(&rect); fCanvas->GetDrawingEngine()->DrawEllipse(rect, fill); } void CanvasCallbacks::DrawPolygon(size_t numPoints, const BPoint viewPoints[], bool isClosed, bool fill) { if (numPoints == 0) return; BStackOrHeapArray points(numPoints); if (!points.IsValid()) return; fCanvas->PenToScreenTransform().Apply(points, viewPoints, numPoints); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); fCanvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, fill, isClosed && numPoints > 2); } void CanvasCallbacks::DrawShape(const BShape& shape, bool fill) { ShapePainter drawShape(fCanvas, NULL); drawShape.Iterate(&shape); drawShape.Draw(shape.Bounds(), fill); } void CanvasCallbacks::DrawRectGradient(const BRect& _rect, BGradient& gradient, bool fill) { BRect rect = _rect; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(&gradient); fCanvas->GetDrawingEngine()->FillRect(rect, gradient); } void CanvasCallbacks::DrawRoundRectGradient(const BRect& _rect, const BPoint& radii, BGradient& gradient, bool fill) { BRect rect = _rect; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(&gradient); float scale = fCanvas->CurrentState()->CombinedScale(); fCanvas->GetDrawingEngine()->DrawRoundRect(rect, radii.x * scale, radii.y * scale, fill, gradient); } void CanvasCallbacks::DrawBezierGradient(const BPoint viewPoints[4], BGradient& gradient, bool fill) { const size_t kNumPoints = 4; BPoint points[kNumPoints]; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(points, viewPoints, kNumPoints); transform.Apply(&gradient); fCanvas->GetDrawingEngine()->DrawBezier(points, fill, gradient); } void CanvasCallbacks::DrawArcGradient(const BPoint& center, const BPoint& radii, float startTheta, float arcTheta, BGradient& gradient, bool fill) { BRect rect(center.x - radii.x, center.y - radii.y, center.x + radii.x - 1, center.y + radii.y - 1); const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(&gradient); fCanvas->GetDrawingEngine()->DrawArc(rect, startTheta, arcTheta, fill, gradient); } void CanvasCallbacks::DrawEllipseGradient(const BRect& _rect, BGradient& gradient, bool fill) { BRect rect = _rect; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(&gradient); fCanvas->GetDrawingEngine()->DrawEllipse(rect, fill, gradient); } void CanvasCallbacks::DrawPolygonGradient(size_t numPoints, const BPoint viewPoints[], bool isClosed, BGradient& gradient, bool fill) { if (numPoints == 0) return; BStackOrHeapArray points(numPoints); if (!points.IsValid()) return; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(points, viewPoints, numPoints); transform.Apply(&gradient); BRect polyFrame; get_polygon_frame(points, numPoints, &polyFrame); fCanvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame, isClosed && numPoints > 2, fill, gradient); } void CanvasCallbacks::DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill) { ShapePainter drawShape(fCanvas, &gradient); drawShape.Iterate(&shape); drawShape.Draw(shape.Bounds(), fill); } void CanvasCallbacks::StrokeLineGradient(const BPoint& _start, const BPoint& _end, BGradient& gradient) { BPoint start = _start; BPoint end = _end; const SimpleTransform transform = fCanvas->PenToScreenTransform(); transform.Apply(&start); transform.Apply(&end); fCanvas->GetDrawingEngine()->StrokeLine(start, end, gradient); fCanvas->CurrentState()->SetPenLocation(_end); // 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 } void CanvasCallbacks::DrawString(const char* string, size_t length, 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 = fCanvas->CurrentState()->PenLocation(); escapement_delta delta = { deltaSpace, deltaNonSpace }; fCanvas->PenToScreenTransform().Apply(&location); location = fCanvas->GetDrawingEngine()->DrawString(string, length, location, &delta); fCanvas->PenToScreenTransform().Apply(&location); fCanvas->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 } void CanvasCallbacks::DrawStringLocations(const char* string, size_t length, const BPoint* _locations, size_t locationsCount) { BStackOrHeapArray locations(locationsCount); if (!locations.IsValid()) return; const SimpleTransform transform = fCanvas->PenToScreenTransform(); for (size_t i = 0; i < locationsCount; i++) { locations[i] = _locations[i]; transform.Apply(&locations[i]); } BPoint location = fCanvas->GetDrawingEngine()->DrawString(string, length, locations); fCanvas->PenToScreenTransform().Apply(&location); fCanvas->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 } void CanvasCallbacks::DrawPixels(const BRect& src, const BRect& _dest, uint32 width, uint32 height, size_t bytesPerRow, color_space pixelFormat, uint32 options, const void* data, size_t length) { UtilityBitmap bitmap(BRect(0, 0, width - 1, height - 1), (color_space)pixelFormat, 0, bytesPerRow); if (!bitmap.IsValid()) return; memcpy(bitmap.Bits(), data, std::min(height * bytesPerRow, length)); BRect dest = _dest; fCanvas->PenToScreenTransform().Apply(&dest); fCanvas->GetDrawingEngine()->DrawBitmap(&bitmap, src, dest, options); } void CanvasCallbacks::DrawPicture(const BPoint& where, int32 token) { BReference picture(fCanvas->GetPicture(token), true); if (picture != NULL) { fCanvas->PushState(); fCanvas->SetDrawingOrigin(where); fCanvas->PushState(); picture->Play(fCanvas); fCanvas->PopState(); fCanvas->PopState(); } } void CanvasCallbacks::SetClippingRects(size_t numRects, const clipping_rect rects[]) { if (numRects == 0) fCanvas->SetUserClipping(NULL); else { // 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]); fCanvas->SetUserClipping(®ion); } fCanvas->UpdateCurrentDrawingRegion(); } void CanvasCallbacks::ClipToPicture(int32 pictureToken, const BPoint& where, bool clipToInverse) { BReference picture(fCanvas->GetPicture(pictureToken), true); if (picture == NULL) return; BReference mask(new(std::nothrow) PictureAlphaMask(fCanvas->GetAlphaMask(), picture, *fCanvas->CurrentState(), where, clipToInverse), true); fCanvas->SetAlphaMask(mask); fCanvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0), fCanvas->Bounds()); fCanvas->ResyncDrawState(); } void CanvasCallbacks::PushState() { fCanvas->PushState(); } void CanvasCallbacks::PopState() { fCanvas->PopState(); BPoint p(0, 0); fCanvas->PenToScreenTransform().Apply(&p); fCanvas->GetDrawingEngine()->SetDrawState(fCanvas->CurrentState(), (int32)p.x, (int32)p.y); } // TODO: Be smart and actually take advantage of these methods: // only apply state changes when they are called void CanvasCallbacks::EnterStateChange() { } void CanvasCallbacks::ExitStateChange() { fCanvas->ResyncDrawState(); } void CanvasCallbacks::EnterFontState() { } void CanvasCallbacks::ExitFontState() { fCanvas->GetDrawingEngine()->SetFont(fCanvas->CurrentState()->Font()); } void CanvasCallbacks::SetOrigin(const BPoint& pt) { fCanvas->CurrentState()->SetOrigin(pt); } void CanvasCallbacks::SetPenLocation(const BPoint& pt) { fCanvas->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 } void CanvasCallbacks::SetDrawingMode(drawing_mode mode) { if (fCanvas->CurrentState()->SetDrawingMode(mode)) fCanvas->GetDrawingEngine()->SetDrawingMode(mode); } void CanvasCallbacks::SetLineMode(cap_mode capMode, join_mode joinMode, float miterLimit) { DrawState* state = fCanvas->CurrentState(); state->SetLineCapMode(capMode); state->SetLineJoinMode(joinMode); state->SetMiterLimit(miterLimit); fCanvas->GetDrawingEngine()->SetStrokeMode(capMode, joinMode, miterLimit); } void CanvasCallbacks::SetPenSize(float size) { fCanvas->CurrentState()->SetPenSize(size); fCanvas->GetDrawingEngine()->SetPenSize( fCanvas->CurrentState()->PenSize()); // DrawState::PenSize() returns the scaled pen size, so we // need to use that value to set the drawing engine pen size. } void CanvasCallbacks::SetForeColor(const rgb_color& color) { fCanvas->CurrentState()->SetHighColor(color); fCanvas->GetDrawingEngine()->SetHighColor(color); } void CanvasCallbacks::SetBackColor(const rgb_color& color) { fCanvas->CurrentState()->SetLowColor(color); fCanvas->GetDrawingEngine()->SetLowColor(color); } void CanvasCallbacks::SetStipplePattern(const pattern& pattern) { fCanvas->CurrentState()->SetPattern(Pattern(pattern)); fCanvas->GetDrawingEngine()->SetPattern(pattern); } void CanvasCallbacks::SetScale(float scale) { fCanvas->CurrentState()->SetScale(scale); fCanvas->ResyncDrawState(); // Update the drawing engine draw state, since some stuff // (for example the pen size) needs to be recalculated. } void CanvasCallbacks::SetFontFamily(const char* _family, size_t length) { BString family(_family, length); gFontManager->Lock(); FontStyle* fontStyle = gFontManager->GetStyleByIndex(family, 0); ServerFont font; font.SetStyle(fontStyle); gFontManager->Unlock(); fCanvas->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); } void CanvasCallbacks::SetFontStyle(const char* _style, size_t length) { BString style(_style, length); ServerFont font(fCanvas->CurrentState()->Font()); gFontManager->Lock(); FontStyle* fontStyle = gFontManager->GetStyle(font.Family(), style); font.SetStyle(fontStyle); gFontManager->Unlock(); fCanvas->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE); } void CanvasCallbacks::SetFontSpacing(uint8 spacing) { ServerFont font; font.SetSpacing(spacing); fCanvas->CurrentState()->SetFont(font, B_FONT_SPACING); } void CanvasCallbacks::SetFontSize(float size) { ServerFont font; font.SetSize(size); fCanvas->CurrentState()->SetFont(font, B_FONT_SIZE); } void CanvasCallbacks::SetFontRotation(float rotation) { ServerFont font; font.SetRotation(rotation); fCanvas->CurrentState()->SetFont(font, B_FONT_ROTATION); } void CanvasCallbacks::SetFontEncoding(uint8 encoding) { ServerFont font; font.SetEncoding(encoding); fCanvas->CurrentState()->SetFont(font, B_FONT_ENCODING); } void CanvasCallbacks::SetFontFlags(uint32 flags) { ServerFont font; font.SetFlags(flags); fCanvas->CurrentState()->SetFont(font, B_FONT_FLAGS); } void CanvasCallbacks::SetFontShear(float shear) { ServerFont font; font.SetShear(shear); fCanvas->CurrentState()->SetFont(font, B_FONT_SHEAR); } void CanvasCallbacks::SetFontFace(uint16 face) { ServerFont font; font.SetFace(face); fCanvas->CurrentState()->SetFont(font, B_FONT_FACE); } void CanvasCallbacks::SetBlendingMode(source_alpha alphaSrcMode, alpha_function alphaFncMode) { fCanvas->CurrentState()->SetBlendingMode(alphaSrcMode, alphaFncMode); } void CanvasCallbacks::SetFillRule(int32 fillRule) { fCanvas->CurrentState()->SetFillRule(fillRule); fCanvas->GetDrawingEngine()->SetFillRule(fillRule); } void CanvasCallbacks::SetTransform(const BAffineTransform& transform) { BPoint leftTop(0, 0); fCanvas->PenToScreenTransform().Apply(&leftTop); fCanvas->CurrentState()->SetTransform(transform); fCanvas->GetDrawingEngine()->SetTransform( fCanvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y); } void CanvasCallbacks::TranslateBy(double x, double y) { BPoint leftTop(0, 0); fCanvas->PenToScreenTransform().Apply(&leftTop); BAffineTransform transform = fCanvas->CurrentState()->Transform(); transform.PreTranslateBy(x, y); fCanvas->CurrentState()->SetTransform(transform); fCanvas->GetDrawingEngine()->SetTransform( fCanvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y); } void CanvasCallbacks::ScaleBy(double x, double y) { BPoint leftTop(0, 0); fCanvas->PenToScreenTransform().Apply(&leftTop); BAffineTransform transform = fCanvas->CurrentState()->Transform(); transform.PreScaleBy(x, y); fCanvas->CurrentState()->SetTransform(transform); fCanvas->GetDrawingEngine()->SetTransform( fCanvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y); } void CanvasCallbacks::RotateBy(double angleRadians) { BPoint leftTop(0, 0); fCanvas->PenToScreenTransform().Apply(&leftTop); BAffineTransform transform = fCanvas->CurrentState()->Transform(); transform.PreRotateBy(angleRadians); fCanvas->CurrentState()->SetTransform(transform); fCanvas->GetDrawingEngine()->SetTransform( fCanvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y); } void CanvasCallbacks::BlendLayer(Layer* layer) { fCanvas->BlendLayer(layer); } void CanvasCallbacks::ClipToRect(const BRect& rect, bool inverse) { bool needDrawStateUpdate = fCanvas->ClipToRect(rect, inverse); if (needDrawStateUpdate) { fCanvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0), fCanvas->Bounds()); fCanvas->ResyncDrawState(); } fCanvas->UpdateCurrentDrawingRegion(); } void CanvasCallbacks::ClipToShape(int32 opCount, const uint32 opList[], int32 ptCount, const BPoint ptList[], bool inverse) { shape_data shapeData; // TODO: avoid copies shapeData.opList = (uint32*)malloc(opCount * sizeof(uint32)); memcpy(shapeData.opList, opList, opCount * sizeof(uint32)); shapeData.ptList = (BPoint*)malloc(ptCount * sizeof(BPoint)); memcpy((void*)shapeData.ptList, ptList, ptCount * sizeof(BPoint)); shapeData.opCount = opCount; shapeData.opSize = opCount * sizeof(uint32); shapeData.ptCount = ptCount; shapeData.ptSize = ptCount * sizeof(BPoint); fCanvas->ClipToShape(&shapeData, inverse); fCanvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0), fCanvas->Bounds()); fCanvas->ResyncDrawState(); free(shapeData.opList); free(shapeData.ptList); } // #pragma mark - ServerPicture ServerPicture::ServerPicture() : fFile(NULL), fOwner(NULL) { fToken = gTokenSpace.NewToken(kPictureToken, this); fData.SetTo(new(std::nothrow) BMallocIO()); PictureDataWriter::SetTo(fData.Get()); } ServerPicture::ServerPicture(const ServerPicture& picture) : fFile(NULL), fData(NULL), fOwner(NULL) { fToken = gTokenSpace.NewToken(kPictureToken, this); BMallocIO* mallocIO = new(std::nothrow) BMallocIO(); if (mallocIO == NULL) return; fData.SetTo(mallocIO); const off_t size = picture.DataLength(); if (mallocIO->SetSize(size) < B_OK) return; picture.fData->ReadAt(0, const_cast(mallocIO->Buffer()), size); PictureDataWriter::SetTo(fData.Get()); } ServerPicture::ServerPicture(const char* fileName, int32 offset) : fFile(NULL), fData(NULL), fOwner(NULL) { fToken = gTokenSpace.NewToken(kPictureToken, this); fFile.SetTo(new(std::nothrow) BFile(fileName, B_READ_WRITE)); if (!fFile.IsSet()) return; BPrivate::Storage::OffsetFile* offsetFile = new(std::nothrow) BPrivate::Storage::OffsetFile(fFile.Get(), offset); if (offsetFile == NULL || offsetFile->InitCheck() != B_OK) { delete offsetFile; return; } fData.SetTo(offsetFile); PictureDataWriter::SetTo(fData.Get()); } ServerPicture::~ServerPicture() { ASSERT(fOwner == NULL); gTokenSpace.RemoveToken(fToken); if (fPictures.IsSet()) { for (int32 i = fPictures->CountItems(); i-- > 0;) { ServerPicture* picture = fPictures->ItemAt(i); picture->SetOwner(NULL); picture->ReleaseReference(); } } if (fPushed != NULL) fPushed->SetOwner(NULL); } bool ServerPicture::SetOwner(ServerApp* owner) { if (owner == fOwner) return true; // Acquire an extra reference, since calling RemovePicture() // May remove the last reference and then we will self-destruct right then. // Setting fOwner to NULL would access free'd memory. If owner is another // ServerApp, it's expected to already have a reference of course. BReference _(this); if (fOwner != NULL) fOwner->RemovePicture(this); fOwner = NULL; if (owner == NULL) return true; if (!owner->AddPicture(this)) return false; fOwner = owner; return true; } void ServerPicture::EnterStateChange() { BeginOp(B_PIC_ENTER_STATE_CHANGE); } void ServerPicture::ExitStateChange() { EndOp(); } void ServerPicture::SyncState(Canvas* canvas) { // TODO: Finish this EnterStateChange(); WriteSetOrigin(canvas->CurrentState()->Origin()); WriteSetPenLocation(canvas->CurrentState()->PenLocation()); WriteSetPenSize(canvas->CurrentState()->UnscaledPenSize()); WriteSetScale(canvas->CurrentState()->Scale()); WriteSetLineMode(canvas->CurrentState()->LineCapMode(), canvas->CurrentState()->LineJoinMode(), canvas->CurrentState()->MiterLimit()); //WriteSetPattern(*canvas->CurrentState()->GetPattern().GetInt8()); WriteSetDrawingMode(canvas->CurrentState()->GetDrawingMode()); WriteSetHighColor(canvas->CurrentState()->HighColor()); WriteSetLowColor(canvas->CurrentState()->LowColor()); ExitStateChange(); } void ServerPicture::WriteFontState(const ServerFont& font, uint16 mask) { BeginOp(B_PIC_ENTER_FONT_STATE); if (mask & B_FONT_FAMILY_AND_STYLE) { WriteSetFontFamily(font.Family()); WriteSetFontStyle(font.Style()); } if (mask & B_FONT_SIZE) { WriteSetFontSize(font.Size()); } if (mask & B_FONT_SHEAR) { WriteSetFontShear(font.Shear()); } if (mask & B_FONT_ROTATION) { WriteSetFontRotation(font.Rotation()); } if (mask & B_FONT_FALSE_BOLD_WIDTH) { // TODO: Implement // WriteSetFalseBoldWidth(font.FalseBoldWidth()); } if (mask & B_FONT_SPACING) { WriteSetFontSpacing(font.Spacing()); } if (mask & B_FONT_ENCODING) { WriteSetFontEncoding(font.Encoding()); } if (mask & B_FONT_FACE) { WriteSetFontFace(font.Face()); } if (mask & B_FONT_FLAGS) { WriteSetFontFlags(font.Flags()); } EndOp(); } void ServerPicture::Play(Canvas* target) { // TODO: for now: then change PicturePlayer // to accept a BPositionIO object BMallocIO* mallocIO = dynamic_cast(fData.Get()); if (mallocIO == NULL) return; CanvasCallbacks callbacks(target); BPrivate::PicturePlayer player(mallocIO->Buffer(), mallocIO->BufferLength(), PictureList::Private(fPictures.Get()).AsBList()); player.Play(callbacks); } /*! Acquires a reference to the pushed picture. */ void ServerPicture::PushPicture(ServerPicture* picture) { if (fPushed != NULL) debugger("already pushed a picture"); fPushed.SetTo(picture, false); } /*! Returns a reference with the popped picture. */ ServerPicture* ServerPicture::PopPicture() { return fPushed.Detach(); } void ServerPicture::AppendPicture(ServerPicture* picture) { // A pushed picture is the same as an appended one PushPicture(picture); } bool ServerPicture::NestPicture(ServerPicture* picture) { if (!fPictures.IsSet()) fPictures.SetTo(new(std::nothrow) PictureList); if (!fPictures.IsSet() || !fPictures->AddItem(picture)) return false; picture->AcquireReference(); return true; } off_t ServerPicture::DataLength() const { if (!fData.IsSet()) return 0; off_t size; fData->GetSize(&size); return size; } status_t ServerPicture::ImportData(BPrivate::LinkReceiver& link) { int32 size = 0; link.Read(&size); off_t oldPosition = fData->Position(); fData->Seek(0, SEEK_SET); status_t status = B_NO_MEMORY; char* buffer = new(std::nothrow) char[size]; if (buffer) { status = B_OK; ssize_t read = link.Read(buffer, size); if (read < B_OK || fData->Write(buffer, size) < B_OK) status = B_ERROR; delete [] buffer; } fData->Seek(oldPosition, SEEK_SET); return status; } status_t ServerPicture::ExportData(BPrivate::PortLink& link) { link.StartMessage(B_OK); off_t oldPosition = fData->Position(); fData->Seek(0, SEEK_SET); int32 subPicturesCount = 0; if (fPictures.IsSet()) subPicturesCount = fPictures->CountItems(); link.Attach(subPicturesCount); if (subPicturesCount > 0) { for (int32 i = 0; i < subPicturesCount; i++) { ServerPicture* subPicture = fPictures->ItemAt(i); link.Attach(subPicture->Token()); } } off_t size = 0; fData->GetSize(&size); link.Attach((int32)size); status_t status = B_NO_MEMORY; char* buffer = new(std::nothrow) char[size]; if (buffer) { status = B_OK; ssize_t read = fData->Read(buffer, size); if (read < B_OK || link.Attach(buffer, read) < B_OK) status = B_ERROR; delete [] buffer; } if (status != B_OK) { link.CancelMessage(); link.StartMessage(B_ERROR); } fData->Seek(oldPosition, SEEK_SET); return status; }