⛏️ index : haiku.git

/*
 * 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 <marcus@overhagen.de>
 *		Julian Harnath <julian.harnath@rwth-aachen.de>
 *		Stephan Aßmus <superstippi@gmx.de>
 */

#include "ServerPicture.h"

#include <new>
#include <stdio.h>
#include <stack>

#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 <LinkReceiver.h>
#include <OffsetFile.h>
#include <ObjectListPrivate.h>
#include <PicturePlayer.h>
#include <PictureProtocol.h>
#include <PortLink.h>
#include <ServerProtocol.h>
#include <ShapePrivate.h>
#include <StackOrHeapArray.h>

#include <Bitmap.h>
#include <Debug.h>
#include <List.h>
#include <Shape.h>


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<uint32>	fOpStack;
	stack<BPoint>	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<BShape*>(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 && filled) {
			fCanvas->GetDrawingEngine()->FillShape(frame, opCount, opList,
				ptCount, ptList, *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


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


static void
move_pen_by(void* _canvas, const BPoint& delta)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetPenLocation(
		canvas->CurrentState()->PenLocation() + delta);
}


static void
stroke_line(void* _canvas, const BPoint& _start, const BPoint& _end)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BPoint start = _start;
	BPoint end = _end;

	const SimpleTransform transform = canvas->PenToScreenTransform();
	transform.Apply(&start);
	transform.Apply(&end);
	canvas->GetDrawingEngine()->StrokeLine(start, end);

	canvas->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
}


static void
draw_rect(void* _canvas, const BRect& _rect, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BRect rect = _rect;

	canvas->PenToScreenTransform().Apply(&rect);
	if (fill)
		canvas->GetDrawingEngine()->FillRect(rect);
	else
		canvas->GetDrawingEngine()->StrokeRect(rect);
}


static void
draw_round_rect(void* _canvas, const BRect& _rect, const BPoint& radii,
	bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BRect rect = _rect;

	canvas->PenToScreenTransform().Apply(&rect);
	float scale = canvas->CurrentState()->CombinedScale();
	canvas->GetDrawingEngine()->DrawRoundRect(rect, radii.x * scale,
		radii.y * scale, fill);
}


static void
draw_bezier(void* _canvas, const BPoint viewPoints[4], bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	const size_t kNumPoints = 4;

	BPoint points[kNumPoints];
	canvas->PenToScreenTransform().Apply(points, viewPoints, kNumPoints);
	canvas->GetDrawingEngine()->DrawBezier(points, fill);
}


static void
draw_arc(void* _canvas, const BPoint& center, const BPoint& radii,
	float startTheta, float arcTheta, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	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()->DrawArc(rect, startTheta, arcTheta, fill);
}


static void
draw_ellipse(void* _canvas, const BRect& _rect, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BRect rect = _rect;
	canvas->PenToScreenTransform().Apply(&rect);
	canvas->GetDrawingEngine()->DrawEllipse(rect, fill);
}


static void
draw_polygon(void* _canvas, size_t numPoints, const BPoint viewPoints[],
	bool isClosed, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	if (numPoints == 0)
		return;

	BStackOrHeapArray<BPoint, 200> points(numPoints);
	if (!points.IsValid())
		return;

	canvas->PenToScreenTransform().Apply(points, viewPoints, numPoints);

	BRect polyFrame;
	get_polygon_frame(points, numPoints, &polyFrame);

	canvas->GetDrawingEngine()->DrawPolygon(points, numPoints, polyFrame,
		fill, isClosed && numPoints > 2);
}


static void
draw_shape(void* _canvas, const BShape& shape, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ShapePainter drawShape(canvas, NULL);

	drawShape.Iterate(&shape);
	drawShape.Draw(shape.Bounds(), fill);
}


static void
draw_rect_gradient(void* _canvas, const BRect& _rect, BGradient& gradient, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BRect rect = _rect;

	const SimpleTransform transform =
		canvas->PenToScreenTransform();
	transform.Apply(&rect);
	transform.Apply(&gradient);

	canvas->GetDrawingEngine()->FillRect(rect, gradient);
}


static void
draw_round_rect_gradient(void* _canvas, const BRect& _rect, const BPoint& radii, BGradient& gradient,
	bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BRect rect = _rect;

	const SimpleTransform transform =
		canvas->PenToScreenTransform();
	transform.Apply(&rect);
	transform.Apply(&gradient);
	float scale = canvas->CurrentState()->CombinedScale();
	canvas->GetDrawingEngine()->FillRoundRect(rect, radii.x * scale,
		radii.y * scale, gradient);
}


static void
draw_bezier_gradient(void* _canvas, const BPoint viewPoints[4], BGradient& gradient, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	const size_t kNumPoints = 4;

	BPoint points[kNumPoints];
	const SimpleTransform transform =
		canvas->PenToScreenTransform();
	transform.Apply(points, viewPoints, kNumPoints);
	transform.Apply(&gradient);
	canvas->GetDrawingEngine()->FillBezier(points, gradient);
}


static void
draw_arc_gradient(void* _canvas, const BPoint& center, const BPoint& radii,
	float startTheta, float arcTheta, BGradient& gradient, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BRect rect(center.x - radii.x, center.y - radii.y,
		center.x + radii.x - 1, center.y + radii.y - 1);
	const SimpleTransform transform =
		canvas->PenToScreenTransform();
	transform.Apply(&rect);
	transform.Apply(&gradient);
	canvas->GetDrawingEngine()->FillArc(rect, startTheta, arcTheta, gradient);
}


static void
draw_ellipse_gradient(void* _canvas, const BRect& _rect, BGradient& gradient, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BRect rect = _rect;

	const SimpleTransform transform =
		canvas->PenToScreenTransform();
	transform.Apply(&rect);
	transform.Apply(&gradient);
	canvas->GetDrawingEngine()->FillEllipse(rect, gradient);
}


static void
draw_polygon_gradient(void* _canvas, size_t numPoints, const BPoint viewPoints[],
	bool isClosed, BGradient& gradient, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	if (numPoints == 0)
		return;

	BStackOrHeapArray<BPoint, 200> points(numPoints);
	if (!points.IsValid())
		return;

	const SimpleTransform transform =
		canvas->PenToScreenTransform();
	transform.Apply(points, viewPoints, numPoints);
	transform.Apply(&gradient);

	BRect polyFrame;
	get_polygon_frame(points, numPoints, &polyFrame);

	canvas->GetDrawingEngine()->FillPolygon(points, numPoints, polyFrame,
		gradient, isClosed && numPoints > 2);
}


static void
draw_shape_gradient(void* _canvas, const BShape& shape, BGradient& gradient, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ShapePainter drawShape(canvas, &gradient);

	drawShape.Iterate(&shape);
	drawShape.Draw(shape.Bounds(), fill);
}


static void
draw_string(void* _canvas, const char* string, size_t length, float deltaSpace,
	float deltaNonSpace)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	// 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 = canvas->CurrentState()->PenLocation();

	escapement_delta delta = { deltaSpace, deltaNonSpace };
	canvas->PenToScreenTransform().Apply(&location);
	location = canvas->GetDrawingEngine()->DrawString(string, length,
		location, &delta);

	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
	// by the view only
}


static void
draw_string_locations(void* _canvas, const char* string, size_t length,
	const BPoint* _locations, size_t locationsCount)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BStackOrHeapArray<BPoint, 200> locations(locationsCount);
	if (!locations.IsValid())
		return;

	const SimpleTransform transform = canvas->PenToScreenTransform();
	for (size_t i = 0; i < locationsCount; i++) {
		locations[i] = _locations[i];
		transform.Apply(&locations[i]);
	}

	BPoint location = canvas->GetDrawingEngine()->DrawString(string, length,
		locations);

	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
	// by the view only
}


static void
draw_pixels(void* _canvas, const BRect& src, const BRect& _dest, uint32 width,
	uint32 height, size_t bytesPerRow, color_space pixelFormat, uint32 options,
	const void* data, size_t length)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	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;
	canvas->PenToScreenTransform().Apply(&dest);
	canvas->GetDrawingEngine()->DrawBitmap(&bitmap, src, dest, options);
}


static void
draw_picture(void* _canvas, const BPoint& where, int32 token)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BReference<ServerPicture> picture(canvas->GetPicture(token), true);
	if (picture != NULL) {
		canvas->PushState();
		canvas->SetDrawingOrigin(where);

		canvas->PushState();
		picture->Play(canvas);
		canvas->PopState();

		canvas->PopState();
	}
}


static void
set_clipping_rects(void* _canvas, size_t numRects, const clipping_rect rects[])
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	if (numRects == 0)
		canvas->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]);
		canvas->SetUserClipping(&region);
	}
	canvas->UpdateCurrentDrawingRegion();
}


static void
clip_to_picture(void* _canvas, int32 pictureToken, const BPoint& where,
	bool clipToInverse)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BReference<ServerPicture> picture(canvas->GetPicture(pictureToken), true);
	if (picture == NULL)
		return;
	BReference<AlphaMask> mask(new(std::nothrow) PictureAlphaMask(canvas->GetAlphaMask(),
		picture, *canvas->CurrentState(), where, clipToInverse), true);
	canvas->SetAlphaMask(mask);
	canvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0),
		canvas->Bounds());
	canvas->ResyncDrawState();
}


static void
push_state(void* _canvas)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->PushState();
}


static void
pop_state(void* _canvas)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->PopState();

	BPoint p(0, 0);
	canvas->PenToScreenTransform().Apply(&p);
	canvas->GetDrawingEngine()->SetDrawState(canvas->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
static void
enter_state_change(void* _canvas)
{
}


static void
exit_state_change(void* _canvas)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->ResyncDrawState();
}


static void
enter_font_state(void* _canvas)
{
}


static void
exit_font_state(void* _canvas)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->GetDrawingEngine()->SetFont(canvas->CurrentState()->Font());
}


static void
set_origin(void* _canvas, const BPoint& pt)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetOrigin(pt);
}


static void
set_pen_location(void* _canvas, const BPoint& pt)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	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
}


static void
set_drawing_mode(void* _canvas, drawing_mode mode)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	if (canvas->CurrentState()->SetDrawingMode(mode))
		canvas->GetDrawingEngine()->SetDrawingMode(mode);
}


static void
set_line_mode(void* _canvas, cap_mode capMode, join_mode joinMode,
	float miterLimit)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	DrawState* state = canvas->CurrentState();
	state->SetLineCapMode(capMode);
	state->SetLineJoinMode(joinMode);
	state->SetMiterLimit(miterLimit);
	canvas->GetDrawingEngine()->SetStrokeMode(capMode, joinMode, miterLimit);
}


static void
set_pen_size(void* _canvas, float size)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	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(void* _canvas, const rgb_color& color)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetHighColor(color);
	canvas->GetDrawingEngine()->SetHighColor(color);
}


static void
set_back_color(void* _canvas, const rgb_color& color)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetLowColor(color);
	canvas->GetDrawingEngine()->SetLowColor(color);
}


static void
set_stipple_pattern(void* _canvas, const pattern& pattern)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetPattern(Pattern(pattern));
	canvas->GetDrawingEngine()->SetPattern(pattern);
}


static void
set_scale(void* _canvas, float scale)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetScale(scale);
	canvas->ResyncDrawState();

	// Update the drawing engine draw state, since some stuff
	// (for example the pen size) needs to be recalculated.
}


static void
set_font_family(void* _canvas, const char* _family, size_t length)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BString family(_family, length);

	gFontManager->Lock();
	FontStyle* fontStyle = gFontManager->GetStyleByIndex(family, 0);
	ServerFont font;
	font.SetStyle(fontStyle);
	gFontManager->Unlock();
	canvas->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE);
}


static void
set_font_style(void* _canvas, const char* _style, size_t length)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BString style(_style, length);

	ServerFont font(canvas->CurrentState()->Font());

	gFontManager->Lock();
	FontStyle* fontStyle = gFontManager->GetStyle(font.Family(), style);

	font.SetStyle(fontStyle);
	gFontManager->Unlock();
	canvas->CurrentState()->SetFont(font, B_FONT_FAMILY_AND_STYLE);
}


static void
set_font_spacing(void* _canvas, uint8 spacing)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetSpacing(spacing);
	canvas->CurrentState()->SetFont(font, B_FONT_SPACING);
}


static void
set_font_size(void* _canvas, float size)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetSize(size);
	canvas->CurrentState()->SetFont(font, B_FONT_SIZE);
}


static void
set_font_rotation(void* _canvas, float rotation)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetRotation(rotation);
	canvas->CurrentState()->SetFont(font, B_FONT_ROTATION);
}


static void
set_font_encoding(void* _canvas, uint8 encoding)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetEncoding(encoding);
	canvas->CurrentState()->SetFont(font, B_FONT_ENCODING);
}


static void
set_font_flags(void* _canvas, uint32 flags)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetFlags(flags);
	canvas->CurrentState()->SetFont(font, B_FONT_FLAGS);
}


static void
set_font_shear(void* _canvas, float shear)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetShear(shear);
	canvas->CurrentState()->SetFont(font, B_FONT_SHEAR);
}


static void
set_font_face(void* _canvas, uint16 face)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ServerFont font;
	font.SetFace(face);
	canvas->CurrentState()->SetFont(font, B_FONT_FACE);
}


static void
set_blending_mode(void* _canvas, source_alpha alphaSrcMode,
	alpha_function alphaFncMode)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetBlendingMode(alphaSrcMode, alphaFncMode);
}


static void
set_fill_rule(void* _canvas, int32 fillRule)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetFillRule(fillRule);
	canvas->GetDrawingEngine()->SetFillRule(fillRule);
}


static void
set_transform(void* _canvas, const BAffineTransform& transform)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BPoint leftTop(0, 0);
	canvas->PenToScreenTransform().Apply(&leftTop);

	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(
		canvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y);
}


static void
translate_by(void* _canvas, double x, double y)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BPoint leftTop(0, 0);
	canvas->PenToScreenTransform().Apply(&leftTop);

	BAffineTransform transform = canvas->CurrentState()->Transform();
	transform.PreTranslateBy(x, y);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(
		canvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y);
}


static void
scale_by(void* _canvas, double x, double y)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BPoint leftTop(0, 0);
	canvas->PenToScreenTransform().Apply(&leftTop);

	BAffineTransform transform = canvas->CurrentState()->Transform();
	transform.PreScaleBy(x, y);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(
		canvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y);
}


static void
rotate_by(void* _canvas, double angleRadians)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);

	BPoint leftTop(0, 0);
	canvas->PenToScreenTransform().Apply(&leftTop);

	BAffineTransform transform = canvas->CurrentState()->Transform();
	transform.PreRotateBy(angleRadians);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(
		canvas->CurrentState()->CombinedTransform(), leftTop.x, leftTop.y);
}


static void
blend_layer(void* _canvas, Layer* layer)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->BlendLayer(layer);
}


static void
clip_to_rect(void* _canvas, const BRect& rect, bool inverse)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	bool needDrawStateUpdate = canvas->ClipToRect(rect, inverse);
	if (needDrawStateUpdate) {
		canvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0),
			canvas->Bounds());
		canvas->ResyncDrawState();
	}
	canvas->UpdateCurrentDrawingRegion();
}


static void
clip_to_shape(void* _canvas, int32 opCount, const uint32 opList[],
	int32 ptCount, const BPoint ptList[], bool inverse)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	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);

	canvas->ClipToShape(&shapeData, inverse);
	canvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0),
		canvas->Bounds());
	canvas->ResyncDrawState();

	free(shapeData.opList);
	free(shapeData.ptList);
}


static const BPrivate::picture_player_callbacks kPicturePlayerCallbacks = {
	move_pen_by,
	stroke_line,
	draw_rect,
	draw_round_rect,
	draw_bezier,
	draw_arc,
	draw_ellipse,
	draw_polygon,
	draw_shape,
	draw_string,
	draw_pixels,
	draw_picture,
	set_clipping_rects,
	clip_to_picture,
	push_state,
	pop_state,
	enter_state_change,
	exit_state_change,
	enter_font_state,
	exit_font_state,
	set_origin,
	set_pen_location,
	set_drawing_mode,
	set_line_mode,
	set_pen_size,
	set_fore_color,
	set_back_color,
	set_stipple_pattern,
	set_scale,
	set_font_family,
	set_font_style,
	set_font_spacing,
	set_font_size,
	set_font_rotation,
	set_font_encoding,
	set_font_flags,
	set_font_shear,
	set_font_face,
	set_blending_mode,
	set_transform,
	translate_by,
	scale_by,
	rotate_by,
	blend_layer,
	clip_to_rect,
	clip_to_shape,
	draw_string_locations,
	draw_rect_gradient,
	draw_round_rect_gradient,
	draw_bezier_gradient,
	draw_arc_gradient,
	draw_ellipse_gradient,
	draw_polygon_gradient,
	draw_shape_gradient,
	set_fill_rule
};


// #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<void*>(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<ServerPicture> _(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<BMallocIO*>(fData.Get());
	if (mallocIO == NULL)
		return;

	BPrivate::PicturePlayer player(mallocIO->Buffer(),
		mallocIO->BufferLength(), PictureList::Private(fPictures.Get()).AsBList());
	player.Play(kPicturePlayerCallbacks, sizeof(kPicturePlayerCallbacks),
		target);
}


/*!	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<int32>(&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<int32>(subPicturesCount);
	if (subPicturesCount > 0) {
		for (int32 i = 0; i < subPicturesCount; i++) {
			ServerPicture* subPicture = fPictures->ItemAt(i);
			link.Attach<int32>(subPicture->Token());
		}
	}

	off_t size = 0;
	fData->GetSize(&size);
	link.Attach<int32>((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;
}