/*
 * 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 "FontManager.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 <Bitmap.h>
#include <Debug.h>
#include <List.h>
#include <Shape.h>
 
 
using std::stack;
 
 
class ShapePainter : public BShapeIterator {
public:
	ShapePainter(Canvas* canvas);
	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;
	stack<uint32>	fOpStack;
	stack<BPoint>	fPtStack;
};
 
 
ShapePainter::ShapePainter(Canvas* canvas)
	:
	fCanvas(canvas)
{
}
 
 
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();
		}
 
		BPoint offset(fCanvas->CurrentState()->PenLocation());
		fCanvas->PenToScreenTransform().Apply(&offset);
		fCanvas->GetDrawingEngine()->DrawShape(frame, opCount, opList,
			ptCount, ptList, filled, offset, 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, size_t numPoints, const BPoint viewPoints[],
	bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
 
	const size_t kSupportedPoints = 4;
	if (numPoints != kSupportedPoints)
		return;
 
	BPoint points[kSupportedPoints];
	canvas->PenToScreenTransform().Apply(points, viewPoints, kSupportedPoints);
	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;
 
	const size_t kMaxStackCount = 200;
	char stackData[kMaxStackCount * sizeof(BPoint)];
	BPoint* points = (BPoint*)stackData;
	if (numPoints > kMaxStackCount) {
		points = (BPoint*)malloc(numPoints * sizeof(BPoint));
		if (points == NULL)
			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);
 
	if (numPoints > kMaxStackCount)
		free(points);
}
 
 
static void
draw_shape(void* _canvas, const BShape& shape, bool fill)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	ShapePainter drawShape(canvas);
 
	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);
 
	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);
 
	ServerPicture* picture = canvas->GetPicture(token);
	if (picture != NULL) {
		canvas->PushState();
		canvas->SetDrawingOrigin(where);
 
		canvas->PushState();
		picture->Play(canvas);
		canvas->PopState();
 
		canvas->PopState();
		picture->ReleaseReference();
	}
}
 
 
static void
set_clipping_rects(void* _canvas, size_t numRects, const BRect 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);
 
	ServerPicture* picture = canvas->GetPicture(pictureToken);
	if (picture == NULL)
		return;
	AlphaMask* mask = new(std::nothrow) PictureAlphaMask(canvas->GetAlphaMask(),
		picture, *canvas->CurrentState(), where, clipToInverse);
	canvas->SetAlphaMask(mask);
	canvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0),
		canvas->Bounds());
	canvas->ResyncDrawState();
	if (mask != NULL)
		mask->ReleaseReference();
 
	picture->ReleaseReference();
}
 
 
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);
 
	FontStyle* fontStyle = gFontManager->GetStyleByIndex(family, 0);
	ServerFont font;
	font.SetStyle(fontStyle);
	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());
 
	FontStyle* fontStyle = gFontManager->GetStyle(font.Family(), style);
 
	font.SetStyle(fontStyle);
	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_transform(void* _canvas, const BAffineTransform& transform)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(transform);
}
 
 
static void
translate_by(void* _canvas, double x, double y)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BAffineTransform transform = canvas->CurrentState()->Transform();
	transform.PreTranslateBy(x, y);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(transform);
}
 
 
static void
scale_by(void* _canvas, double x, double y)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BAffineTransform transform = canvas->CurrentState()->Transform();
	transform.PreScaleBy(x, y);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(transform);
}
 
 
static void
rotate_by(void* _canvas, double angleRadians)
{
	Canvas* const canvas = reinterpret_cast<Canvas*>(_canvas);
	BAffineTransform transform = canvas->CurrentState()->Transform();
	transform.PreRotateBy(angleRadians);
	canvas->CurrentState()->SetTransform(transform);
	canvas->GetDrawingEngine()->SetTransform(transform);
}
 
 
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
};
 
 
// #pragma mark - ServerPicture
 
 
ServerPicture::ServerPicture()
	:
	fFile(NULL),
	fPictures(NULL),
	fPushed(NULL),
	fOwner(NULL)
{
	fToken = gTokenSpace.NewToken(kPictureToken, this);
	fData = new(std::nothrow) BMallocIO();
 
	PictureDataWriter::SetTo(fData);
}
 
 
ServerPicture::ServerPicture(const ServerPicture& picture)
	:
	fFile(NULL),
	fData(NULL),
	fPictures(NULL),
	fPushed(NULL),
	fOwner(NULL)
{
	fToken = gTokenSpace.NewToken(kPictureToken, this);
 
	BMallocIO* mallocIO = new(std::nothrow) BMallocIO();
	if (mallocIO == NULL)
		return;
 
	fData = 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);
}
 
 
ServerPicture::ServerPicture(const char* fileName, int32 offset)
	:
	fFile(NULL),
	fData(NULL),
	fPictures(NULL),
	fPushed(NULL),
	fOwner(NULL)
{
	fToken = gTokenSpace.NewToken(kPictureToken, this);
 
	fFile = new(std::nothrow) BFile(fileName, B_READ_WRITE);
	if (fFile == NULL)
		return;
 
	BPrivate::Storage::OffsetFile* offsetFile
		= new(std::nothrow) BPrivate::Storage::OffsetFile(fFile, offset);
	if (offsetFile == NULL || offsetFile->InitCheck() != B_OK) {
		delete offsetFile;
		return;
	}
 
	fData = offsetFile;
 
	PictureDataWriter::SetTo(fData);
}
 
 
ServerPicture::~ServerPicture()
{
	ASSERT(fOwner == NULL);
 
	delete fData;
	delete fFile;
	gTokenSpace.RemoveToken(fToken);
 
	if (fPictures != NULL) {
		for (int32 i = fPictures->CountItems(); i-- > 0;) {
			ServerPicture* picture = fPictures->ItemAt(i);
			picture->SetOwner(NULL);
			picture->ReleaseReference();
		}
 
		delete fPictures;
	}
 
	if (fPushed != NULL) {
		fPushed->SetOwner(NULL);
		fPushed->ReleaseReference();
	}
}
 
 
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);
	if (mallocIO == NULL)
		return;
 
	BPrivate::PicturePlayer player(mallocIO->Buffer(),
		mallocIO->BufferLength(), PictureList::Private(fPictures).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 = picture;
	fPushed->AcquireReference();
}
 
 
/*!	Returns a reference with the popped picture.
*/
ServerPicture*
ServerPicture::PopPicture()
{
	ServerPicture* old = fPushed;
	fPushed = NULL;
	return old;
}
 
 
void
ServerPicture::AppendPicture(ServerPicture* picture)
{
	// A pushed picture is the same as an appended one
	PushPicture(picture);
}
 
 
bool
ServerPicture::NestPicture(ServerPicture* picture)
{
	if (fPictures == NULL)
		fPictures = new(std::nothrow) PictureList;
 
	if (fPictures == NULL || !fPictures->AddItem(picture))
		return false;
 
	picture->AcquireReference();
	return true;
}
 
 
off_t
ServerPicture::DataLength() const
{
	if (fData == NULL)
		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 != NULL)
		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;
}

V630 The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors.

V630 The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors.