/*
 * Copyright 2001-2018, Haiku Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Marc Flerackers (mflerackers@androme.be)
 *		Stefano Ceccherini (stefano.ceccherini@gmail.com)
 *		Marcus Overhagen (marcus@overhagen.de)
 *		Stephan Aßmus <superstippi@gmx.de>
 */
 
/**	PicturePlayer is used to play picture data. */
 
#include <PicturePlayer.h>
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <AffineTransform.h>
#include <PictureProtocol.h>
#include <Shape.h>
 
 
using BPrivate::PicturePlayer;
 
 
struct adapter_context {
	void* user_data;
	void** function_table;
};
 
 
static void
nop()
{
}
 
 
static void
move_pen_by(void* _context, const BPoint& delta)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BPoint))context->function_table[1])(context->user_data,
		delta);
}
 
 
static void
stroke_line(void* _context, const BPoint& start, const BPoint& end)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BPoint, BPoint))context->function_table[2])(
		context->user_data, start, end);
}
 
 
static void
draw_rect(void* _context, const BRect& rect, bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BRect))context->function_table[fill ? 4 : 3])(
		context->user_data, rect);
}
 
 
static void
draw_round_rect(void* _context, const BRect& rect, const BPoint& radii,
	bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BRect, BPoint))context->function_table[fill ? 6 : 5])(
		context->user_data, rect, radii);
}
 
 
static void
draw_bezier(void* _context, size_t numPoints, const BPoint _points[], bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	if (numPoints != 4)
		return;
 
	BPoint points[4] = { _points[0], _points[1], _points[2], _points[3] };
	((void (*)(void*, BPoint*))context->function_table[fill ? 8 : 7])(
		context->user_data, points);
}
 
 
static void
draw_arc(void* _context, const BPoint& center, const BPoint& radii,
	float startTheta, float arcTheta, bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BPoint, BPoint, float, float))
		context->function_table[fill ? 10 : 9])(context->user_data, center,
			radii, startTheta, arcTheta);
}
 
 
static void
draw_ellipse(void* _context, const BRect& rect, bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	BPoint radii((rect.Width() + 1) / 2.0f, (rect.Height() + 1) / 2.0f);
	BPoint center = rect.LeftTop() + radii;
	((void (*)(void*, BPoint, BPoint))
		context->function_table[fill ? 12 : 11])(context->user_data, center,
			radii);
}
 
 
static void
draw_polygon(void* _context, size_t numPoints, const BPoint _points[],
	bool isClosed, bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
 
	// This is rather ugly but works for such a trivial class.
	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;
	}
 
	memcpy((void*)points, _points, numPoints * sizeof(BPoint));
 
	((void (*)(void*, int32, BPoint*, bool))
		context->function_table[fill ? 14 : 13])(context->user_data, numPoints,
			points, isClosed);
 
	if (numPoints > kMaxStackCount)
		free(points);
}
 
 
static void
draw_shape(void* _context, const BShape& shape, bool fill)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BShape))context->function_table[fill ? 16 : 15])(
		context->user_data, shape);
}
 
 
static void
draw_string(void* _context, const char* _string, size_t length,
	float deltaSpace, float deltaNonSpace)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	char* string = strndup(_string, length);
 
	((void (*)(void*, char*, float, float))
		context->function_table[17])(context->user_data, string, deltaSpace,
			deltaNonSpace);
 
	free(string);
}
 
 
static void
draw_pixels(void* _context, const BRect& src, const BRect& dest, uint32 width,
	uint32 height, size_t bytesPerRow, color_space pixelFormat, uint32 options,
	const void* _data, size_t length)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	void* data = malloc(length);
	if (data == NULL)
		return;
 
	memcpy(data, _data, length);
 
	((void (*)(void*, BRect, BRect, int32, int32, int32, int32, int32, void*))
		context->function_table[18])(context->user_data, src, dest, width,
			height, bytesPerRow, pixelFormat, options, data);
 
	free(data);
}
 
 
static void
draw_picture(void* _context, const BPoint& where, int32 token)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BPoint, int32))context->function_table[19])(
		context->user_data, where, token);
}
 
 
static void
set_clipping_rects(void* _context, size_t numRects, const BRect _rects[])
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
 
	// This is rather ugly but works for such a trivial class.
	const size_t kMaxStackCount = 100;
	char stackData[kMaxStackCount * sizeof(BRect)];
	BRect* rects = (BRect*)stackData;
	if (numRects > kMaxStackCount) {
		rects = (BRect*)malloc(numRects * sizeof(BRect));
		if (rects == NULL)
			return;
	}
 
	memcpy((void*)rects, _rects, numRects * sizeof(BRect));
 
	((void (*)(void*, BRect*, uint32))context->function_table[20])(
		context->user_data, rects, numRects);
 
	if (numRects > kMaxStackCount)
		free(rects);
}
 
 
static void
clip_to_picture(void* _context, int32 token, const BPoint& origin,
	bool clipToInverse)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, int32, BPoint, bool))context->function_table[21])(
			context->user_data, token, origin, clipToInverse);
}
 
 
static void
push_state(void* _context)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*))context->function_table[22])(context->user_data);
}
 
 
static void
pop_state(void* _context)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*))context->function_table[23])(context->user_data);
}
 
 
static void
enter_state_change(void* _context)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*))context->function_table[24])(context->user_data);
}
 
 
static void
exit_state_change(void* _context)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*))context->function_table[25])(context->user_data);
}
 
 
static void
enter_font_state(void* _context)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*))context->function_table[26])(context->user_data);
}
 
 
static void
exit_font_state(void* _context)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*))context->function_table[27])(context->user_data);
}
 
 
static void
set_origin(void* _context, const BPoint& origin)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BPoint))context->function_table[28])(context->user_data,
		origin);
}
 
 
static void
set_pen_location(void* _context, const BPoint& penLocation)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, BPoint))context->function_table[29])(context->user_data,
		penLocation);
}
 
 
static void
set_drawing_mode(void* _context, drawing_mode mode)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, drawing_mode))context->function_table[30])(
		context->user_data, mode);
}
 
 
static void
set_line_mode(void* _context, cap_mode capMode, join_mode joinMode,
	float miterLimit)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, cap_mode, join_mode, float))context->function_table[31])(
		context->user_data, capMode, joinMode, miterLimit);
}
 
 
static void
set_pen_size(void* _context, float size)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, float))context->function_table[32])(context->user_data,
		size);
}
 
 
static void
set_fore_color(void* _context, const rgb_color& color)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, rgb_color))context->function_table[33])(
		context->user_data, color);
}
 
 
static void
set_back_color(void* _context, const rgb_color& color)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, rgb_color))context->function_table[34])(
		context->user_data, color);
}
 
 
static void
set_stipple_pattern(void* _context, const pattern& stipplePattern)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, pattern))context->function_table[35])(context->user_data,
		stipplePattern);
}
 
 
static void
set_scale(void* _context, float scale)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, float))context->function_table[36])(context->user_data,
		scale);
}
 
 
static void
set_font_family(void* _context, const char* _family, size_t length)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	char* family = strndup(_family, length);
 
	((void (*)(void*, char*))context->function_table[37])(context->user_data,
		family);
 
	free(family);
}
 
 
static void
set_font_style(void* _context, const char* _style, size_t length)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	char* style = strndup(_style, length);
 
	((void (*)(void*, char*))context->function_table[38])(context->user_data,
		style);
 
	free(style);
}
 
 
static void
set_font_spacing(void* _context, uint8 spacing)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, int32))context->function_table[39])(context->user_data,
		spacing);
}
 
 
static void
set_font_size(void* _context, float size)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, float))context->function_table[40])(context->user_data,
		size);
}
 
 
static void
set_font_rotation(void* _context, float rotation)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, float))context->function_table[41])(context->user_data,
		rotation);
}
 
 
static void
set_font_encoding(void* _context, uint8 encoding)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, int32))context->function_table[42])(context->user_data,
		encoding);
}
 
 
static void
set_font_flags(void* _context, uint32 flags)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, int32))context->function_table[43])(context->user_data,
		flags);
}
 
 
static void
set_font_shear(void* _context, float shear)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, float))context->function_table[44])(context->user_data,
		shear);
}
 
 
static void
set_font_face(void* _context, uint16 face)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, int32))context->function_table[46])(context->user_data,
		face);
}
 
 
static void
set_blending_mode(void* _context, source_alpha alphaSrcMode,
	alpha_function alphaFncMode)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, source_alpha, alpha_function))
		context->function_table[47])(context->user_data, alphaSrcMode,
			alphaFncMode);
}
 
 
static void
set_transform(void* _context, const BAffineTransform& transform)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, const BAffineTransform&))
		context->function_table[48])(context->user_data, transform);
}
 
 
static void
translate_by(void* _context, double x, double y)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, double, double))
		context->function_table[49])(context->user_data, x, y);
}
 
 
static void
scale_by(void* _context, double x, double y)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, double, double))
		context->function_table[50])(context->user_data, x, y);
}
 
 
static void
rotate_by(void* _context, double angleRadians)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, double))
		context->function_table[51])(context->user_data, angleRadians);
}
 
 
static void
blend_layer(void* _context, Layer* layer)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, Layer*))
		context->function_table[52])(context->user_data, layer);
}
 
 
static void
clip_to_rect(void* _context, const BRect& rect, bool inverse)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, const BRect&, bool))
		context->function_table[53])(context->user_data, rect, inverse);
}
 
 
static void
clip_to_shape(void* _context, int32 opCount, const uint32 opList[],
	int32 ptCount, const BPoint ptList[], bool inverse)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	((void (*)(void*, int32, const uint32*, int32, const BPoint*, bool))
		context->function_table[54])(context->user_data, opCount, opList,
			ptCount, ptList, inverse);
}
 
 
static void
draw_string_locations(void* _context, const char* _string, size_t length,
	const BPoint* locations, size_t locationCount)
{
	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
	char* string = strndup(_string, length);
 
	((void (*)(void*, char*, const BPoint*, size_t))
		context->function_table[55])(context->user_data, string, locations,
			locationCount);
 
	free(string);
}
 
 
 
#if DEBUG > 1
static const char *
PictureOpToString(int op)
{
	#define RETURN_STRING(x) case x: return #x
 
	switch(op) {
		RETURN_STRING(B_PIC_MOVE_PEN_BY);
		RETURN_STRING(B_PIC_STROKE_LINE);
		RETURN_STRING(B_PIC_STROKE_RECT);
		RETURN_STRING(B_PIC_FILL_RECT);
		RETURN_STRING(B_PIC_STROKE_ROUND_RECT);
		RETURN_STRING(B_PIC_FILL_ROUND_RECT);
		RETURN_STRING(B_PIC_STROKE_BEZIER);
		RETURN_STRING(B_PIC_FILL_BEZIER);
		RETURN_STRING(B_PIC_STROKE_POLYGON);
		RETURN_STRING(B_PIC_FILL_POLYGON);
		RETURN_STRING(B_PIC_STROKE_SHAPE);
		RETURN_STRING(B_PIC_FILL_SHAPE);
		RETURN_STRING(B_PIC_DRAW_STRING);
		RETURN_STRING(B_PIC_DRAW_STRING_LOCATIONS);
		RETURN_STRING(B_PIC_DRAW_PIXELS);
		RETURN_STRING(B_PIC_DRAW_PICTURE);
		RETURN_STRING(B_PIC_STROKE_ARC);
		RETURN_STRING(B_PIC_FILL_ARC);
		RETURN_STRING(B_PIC_STROKE_ELLIPSE);
		RETURN_STRING(B_PIC_FILL_ELLIPSE);
 
		RETURN_STRING(B_PIC_ENTER_STATE_CHANGE);
		RETURN_STRING(B_PIC_SET_CLIPPING_RECTS);
		RETURN_STRING(B_PIC_CLIP_TO_PICTURE);
		RETURN_STRING(B_PIC_PUSH_STATE);
		RETURN_STRING(B_PIC_POP_STATE);
		RETURN_STRING(B_PIC_CLEAR_CLIPPING_RECTS);
 
		RETURN_STRING(B_PIC_SET_ORIGIN);
		RETURN_STRING(B_PIC_SET_PEN_LOCATION);
		RETURN_STRING(B_PIC_SET_DRAWING_MODE);
		RETURN_STRING(B_PIC_SET_LINE_MODE);
		RETURN_STRING(B_PIC_SET_PEN_SIZE);
		RETURN_STRING(B_PIC_SET_SCALE);
		RETURN_STRING(B_PIC_SET_TRANSFORM);
		RETURN_STRING(B_PIC_SET_FORE_COLOR);
		RETURN_STRING(B_PIC_SET_BACK_COLOR);
		RETURN_STRING(B_PIC_SET_STIPLE_PATTERN);
		RETURN_STRING(B_PIC_ENTER_FONT_STATE);
		RETURN_STRING(B_PIC_SET_BLENDING_MODE);
		RETURN_STRING(B_PIC_SET_FONT_FAMILY);
		RETURN_STRING(B_PIC_SET_FONT_STYLE);
		RETURN_STRING(B_PIC_SET_FONT_SPACING);
		RETURN_STRING(B_PIC_SET_FONT_ENCODING);
		RETURN_STRING(B_PIC_SET_FONT_FLAGS);
		RETURN_STRING(B_PIC_SET_FONT_SIZE);
		RETURN_STRING(B_PIC_SET_FONT_ROTATE);
		RETURN_STRING(B_PIC_SET_FONT_SHEAR);
		RETURN_STRING(B_PIC_SET_FONT_BPP);
		RETURN_STRING(B_PIC_SET_FONT_FACE);
 
		RETURN_STRING(B_PIC_AFFINE_TRANSLATE);
		RETURN_STRING(B_PIC_AFFINE_SCALE);
		RETURN_STRING(B_PIC_AFFINE_ROTATE);
 
		RETURN_STRING(B_PIC_BLEND_LAYER);
 
		default: return "Unknown op";
	}
	#undef RETURN_STRING
}
#endif
 
 
PicturePlayer::PicturePlayer(const void *data, size_t size, BList *pictures)
	:	fData(data),
		fSize(size),
		fPictures(pictures)
{
}
 
 
PicturePlayer::~PicturePlayer()
{
}
 
 
status_t
PicturePlayer::Play(void** callBackTable, int32 tableEntries, void* userData)
{
	const BPrivate::picture_player_callbacks kAdapterCallbacks = {
		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
	};
 
	// We don't check if the functions in the table are NULL, but we
	// check the tableEntries to see if the table is big enough.
	// If an application supplies the wrong size or an invalid pointer,
	// it's its own fault.
 
	// If the caller supplied a function table smaller than needed,
	// we use our dummy table, and copy the supported ops from the supplied one.
	void *dummyTable[kOpsTableSize];
 
	adapter_context adapterContext;
	adapterContext.user_data = userData;
	adapterContext.function_table = callBackTable;
 
	if ((size_t)tableEntries < kOpsTableSize) {
		memcpy(dummyTable, callBackTable, tableEntries * sizeof(void*));
		for (size_t i = (size_t)tableEntries; i < kOpsTableSize; i++)
			dummyTable[i] = (void*)nop;
 
		adapterContext.function_table = dummyTable;
	}
 
	return _Play(kAdapterCallbacks, &adapterContext, fData, fSize, 0);
}
 
 
status_t
PicturePlayer::Play(const picture_player_callbacks& callbacks,
	size_t callbacksSize, void* userData)
{
	return _Play(callbacks, userData, fData, fSize, 0);
}
 
 
class DataReader {
public:
		DataReader(const void* buffer, size_t length)
			:
			fBuffer((const uint8*)buffer),
			fRemaining(length)
		{
		}
 
		size_t
		Remaining() const
		{
			return fRemaining;
		}
 
		template<typename T>
		bool
		Get(const T*& typed, size_t count = 1)
		{
			if (fRemaining < sizeof(T) * count)
				return false;
 
			typed = reinterpret_cast<const T *>(fBuffer);
			fRemaining -= sizeof(T) * count;
			fBuffer += sizeof(T) * count;
			return true;
		}
 
		template<typename T>
		bool
		GetRemaining(const T*& buffer, size_t& size)
		{
			if (fRemaining == 0)
				return false;
 
			buffer = reinterpret_cast<const T*>(fBuffer);
			size = fRemaining;
			fRemaining = 0;
			return true;
		}
 
private:
		const uint8*	fBuffer;
		size_t			fRemaining;
};
 
 
struct picture_data_entry_header {
	uint16 op;
	uint32 size;
} _PACKED;
 
 
status_t
PicturePlayer::_Play(const picture_player_callbacks& callbacks, void* userData,
	const void* buffer, size_t length, uint16 parentOp)
{
#if DEBUG
	printf("Start rendering %sBPicture...\n", parentOp != 0 ? "sub " : "");
	bigtime_t startTime = system_time();
	int32 numOps = 0;
#endif
 
	DataReader pictureReader(buffer, length);
 
	while (pictureReader.Remaining() > 0) {
		const picture_data_entry_header* header;
		const uint8* opData = NULL;
		if (!pictureReader.Get(header)
			|| !pictureReader.Get(opData, header->size)) {
			return B_BAD_DATA;
		}
 
		DataReader reader(opData, header->size);
 
		// Disallow ops that don't fit the parent.
		switch (parentOp) {
			case 0:
				// No parent op, no restrictions.
				break;
 
			case B_PIC_ENTER_STATE_CHANGE:
				if (header->op <= B_PIC_ENTER_STATE_CHANGE
					|| header->op > B_PIC_SET_TRANSFORM) {
					return B_BAD_DATA;
				}
				break;
 
			case B_PIC_ENTER_FONT_STATE:
				if (header->op < B_PIC_SET_FONT_FAMILY
					|| header->op > B_PIC_SET_FONT_FACE) {
					return B_BAD_DATA;
					}
				break;
 
			default:
				return B_BAD_DATA;
		}
 
#if DEBUG > 1
		bigtime_t startOpTime = system_time();
		printf("Op %s ", PictureOpToString(header->op));
#endif
		switch (header->op) {
			case B_PIC_MOVE_PEN_BY:
			{
				const BPoint* where;
				if (callbacks.move_pen_by == NULL || !reader.Get(where))
					break;
 
				callbacks.move_pen_by(userData, *where);
				break;
			}
 
			case B_PIC_STROKE_LINE:
			{
				const BPoint* start;
				const BPoint* end;
				if (callbacks.stroke_line == NULL || !reader.Get(start)
					|| !reader.Get(end)) {
					break;
				}
 
				callbacks.stroke_line(userData, *start, *end);
				break;
			}
 
			case B_PIC_STROKE_RECT:
			case B_PIC_FILL_RECT:
			{
				const BRect* rect;
				if (callbacks.draw_rect == NULL || !reader.Get(rect))
					break;
 
				callbacks.draw_rect(userData, *rect,
					header->op == B_PIC_FILL_RECT);
				break;
			}
 
			case B_PIC_STROKE_ROUND_RECT:
			case B_PIC_FILL_ROUND_RECT:
			{
				const BRect* rect;
				const BPoint* radii;
				if (callbacks.draw_round_rect == NULL || !reader.Get(rect)
					|| !reader.Get(radii)) {
					break;
				}
 
				callbacks.draw_round_rect(userData, *rect, *radii,
					header->op == B_PIC_FILL_ROUND_RECT);
				break;
			}
 
			case B_PIC_STROKE_BEZIER:
			case B_PIC_FILL_BEZIER:
			{
				const size_t kNumControlPoints = 4;
				const BPoint* controlPoints;
				if (callbacks.draw_bezier == NULL
					|| !reader.Get(controlPoints, kNumControlPoints)) {
					break;
				}
 
				callbacks.draw_bezier(userData, kNumControlPoints,
					controlPoints, header->op == B_PIC_FILL_BEZIER);
				break;
			}
 
			case B_PIC_STROKE_ARC:
			case B_PIC_FILL_ARC:
			{
				const BPoint* center;
				const BPoint* radii;
				const float* startTheta;
				const float* arcTheta;
				if (callbacks.draw_arc == NULL || !reader.Get(center)
					|| !reader.Get(radii) || !reader.Get(startTheta)
					|| !reader.Get(arcTheta)) {
					break;
				}
 
				callbacks.draw_arc(userData, *center, *radii, *startTheta,
					*arcTheta, header->op == B_PIC_FILL_ARC);
				break;
			}
 
			case B_PIC_STROKE_ELLIPSE:
			case B_PIC_FILL_ELLIPSE:
			{
				const BRect* rect;
				if (callbacks.draw_ellipse == NULL || !reader.Get(rect))
					break;
 
				callbacks.draw_ellipse(userData, *rect,
					header->op == B_PIC_FILL_ELLIPSE);
				break;
			}
 
			case B_PIC_STROKE_POLYGON:
			case B_PIC_FILL_POLYGON:
			{
				const uint32* numPoints;
				const BPoint* points;
				if (callbacks.draw_polygon == NULL || !reader.Get(numPoints)
					|| !reader.Get(points, *numPoints)) {
					break;
				}
 
				bool isClosed = true;
				const bool* closedPointer;
				if (header->op != B_PIC_FILL_POLYGON) {
					if (!reader.Get(closedPointer))
						break;
 
					isClosed = *closedPointer;
				}
 
				callbacks.draw_polygon(userData, *numPoints, points, isClosed,
					header->op == B_PIC_FILL_POLYGON);
				break;
			}
 
			case B_PIC_STROKE_SHAPE:
			case B_PIC_FILL_SHAPE:
			{
				const uint32* opCount;
				const uint32* pointCount;
				const uint32* opList;
				const BPoint* pointList;
				if (callbacks.draw_shape == NULL || !reader.Get(opCount)
					|| !reader.Get(pointCount) || !reader.Get(opList, *opCount)
					|| !reader.Get(pointList, *pointCount)) {
					break;
				}
 
				// TODO: remove BShape data copying
				BShape shape;
				shape.SetData(*opCount, *pointCount, opList, pointList);
 
				callbacks.draw_shape(userData, shape,
					header->op == B_PIC_FILL_SHAPE);
				break;
			}
 
			case B_PIC_DRAW_STRING:
			{
				const float* escapementSpace;
				const float* escapementNonSpace;
				const char* string;
				size_t length;
				if (callbacks.draw_string == NULL
					|| !reader.Get(escapementSpace)
					|| !reader.Get(escapementNonSpace)
					|| !reader.GetRemaining(string, length)) {
					break;
				}
 
				callbacks.draw_string(userData, string, length,
					*escapementSpace, *escapementNonSpace);
				break;
			}
 
			case B_PIC_DRAW_STRING_LOCATIONS:
			{
				const uint32* pointCount;
				const BPoint* pointList;
				const char* string;
				size_t length;
				if (callbacks.draw_string_locations == NULL
					|| !reader.Get(pointCount)
					|| !reader.Get(pointList, *pointCount)
					|| !reader.GetRemaining(string, length)) {
					break;
				}
 
				callbacks.draw_string_locations(userData, string, length,
					pointList, *pointCount);
				break;
			}
 
			case B_PIC_DRAW_PIXELS:
			{
				const BRect* sourceRect;
				const BRect* destinationRect;
				const uint32* width;
				const uint32* height;
				const uint32* bytesPerRow;
				const uint32* colorSpace;
				const uint32* flags;
				const void* data;
				size_t length;
				if (callbacks.draw_pixels == NULL || !reader.Get(sourceRect)
					|| !reader.Get(destinationRect) || !reader.Get(width)
					|| !reader.Get(height) || !reader.Get(bytesPerRow)
					|| !reader.Get(colorSpace) || !reader.Get(flags)
					|| !reader.GetRemaining(data, length)) {
					break;
				}
 
				callbacks.draw_pixels(userData, *sourceRect, *destinationRect,
					*width, *height, *bytesPerRow, (color_space)*colorSpace,
					*flags, data, length);
				break;
			}
 
			case B_PIC_DRAW_PICTURE:
			{
				const BPoint* where;
				const int32* token;
				if (callbacks.draw_picture == NULL || !reader.Get(where)
					|| !reader.Get(token)) {
					break;
				}
 
				callbacks.draw_picture(userData, *where, *token);
				break;
			}
 
			case B_PIC_SET_CLIPPING_RECTS:
			{
				const uint32* numRects;
				const BRect* rects;
				if (callbacks.set_clipping_rects == NULL
					|| !reader.Get(numRects) || !reader.Get(rects, *numRects)) {
					break;
				}
 
				callbacks.set_clipping_rects(userData, *numRects, rects);
				break;
			}
 
			case B_PIC_CLEAR_CLIPPING_RECTS:
			{
				if (callbacks.set_clipping_rects == NULL)
					break;
 
				callbacks.set_clipping_rects(userData, 0, NULL);
				break;
			}
 
			case B_PIC_CLIP_TO_PICTURE:
			{
				const int32* token;
				const BPoint* where;
				const bool* inverse;
				if (callbacks.clip_to_picture == NULL || !reader.Get(token)
					|| !reader.Get(where) || !reader.Get(inverse))
					break;
 
				callbacks.clip_to_picture(userData, *token, *where, *inverse);
				break;
			}
 
			case B_PIC_PUSH_STATE:
			{
				if (callbacks.push_state == NULL)
					break;
 
				callbacks.push_state(userData);
				break;
			}
 
			case B_PIC_POP_STATE:
			{
				if (callbacks.pop_state == NULL)
					break;
 
				callbacks.pop_state(userData);
				break;
			}
 
			case B_PIC_ENTER_STATE_CHANGE:
			case B_PIC_ENTER_FONT_STATE:
			{
				const void* data;
				size_t length;
				if (!reader.GetRemaining(data, length))
					break;
 
				if (header->op == B_PIC_ENTER_STATE_CHANGE) {
					if (callbacks.enter_state_change != NULL)
						callbacks.enter_state_change(userData);
				} else if (callbacks.enter_font_state != NULL)
					callbacks.enter_font_state(userData);
 
				status_t result = _Play(callbacks, userData, data, length,
					header->op);
				if (result != B_OK)
					return result;
 
				if (header->op == B_PIC_ENTER_STATE_CHANGE) {
					if (callbacks.exit_state_change != NULL)
						callbacks.exit_state_change(userData);
				} else if (callbacks.exit_font_state != NULL)
					callbacks.exit_font_state(userData);
 
				break;
			}
 
			case B_PIC_SET_ORIGIN:
			{
				const BPoint* origin;
				if (callbacks.set_origin == NULL || !reader.Get(origin))
					break;
 
				callbacks.set_origin(userData, *origin);
				break;
			}
 
			case B_PIC_SET_PEN_LOCATION:
			{
				const BPoint* location;
				if (callbacks.set_pen_location == NULL || !reader.Get(location))
					break;
 
				callbacks.set_pen_location(userData, *location);
				break;
			}
 
			case B_PIC_SET_DRAWING_MODE:
			{
				const uint16* mode;
				if (callbacks.set_drawing_mode == NULL || !reader.Get(mode))
					break;
 
				callbacks.set_drawing_mode(userData, (drawing_mode)*mode);
				break;
			}
 
			case B_PIC_SET_LINE_MODE:
			{
				const uint16* capMode;
				const uint16* joinMode;
				const float* miterLimit;
				if (callbacks.set_line_mode == NULL || !reader.Get(capMode)
					|| !reader.Get(joinMode) || !reader.Get(miterLimit)) {
					break;
				}
 
				callbacks.set_line_mode(userData, (cap_mode)*capMode,
					(join_mode)*joinMode, *miterLimit);
				break;
			}
 
			case B_PIC_SET_PEN_SIZE:
			{
				const float* penSize;
				if (callbacks.set_pen_size == NULL || !reader.Get(penSize))
					break;
 
				callbacks.set_pen_size(userData, *penSize);
				break;
			}
 
			case B_PIC_SET_FORE_COLOR:
			{
				const rgb_color* color;
				if (callbacks.set_fore_color == NULL || !reader.Get(color))
					break;
 
				callbacks.set_fore_color(userData, *color);
				break;
			}
 
			case B_PIC_SET_BACK_COLOR:
			{
				const rgb_color* color;
				if (callbacks.set_back_color == NULL || !reader.Get(color))
					break;
 
				callbacks.set_back_color(userData, *color);
				break;
			}
 
			case B_PIC_SET_STIPLE_PATTERN:
			{
				const pattern* stipplePattern;
				if (callbacks.set_stipple_pattern == NULL
					|| !reader.Get(stipplePattern)) {
					break;
				}
 
				callbacks.set_stipple_pattern(userData, *stipplePattern);
				break;
			}
 
			case B_PIC_SET_SCALE:
			{
				const float* scale;
				if (callbacks.set_scale == NULL || !reader.Get(scale))
					break;
 
				callbacks.set_scale(userData, *scale);
				break;
			}
 
			case B_PIC_SET_FONT_FAMILY:
			{
				const char* family;
				size_t length;
				if (callbacks.set_font_family == NULL
					|| !reader.GetRemaining(family, length)) {
					break;
				}
 
				callbacks.set_font_family(userData, family, length);
				break;
			}
 
			case B_PIC_SET_FONT_STYLE:
			{
				const char* style;
				size_t length;
				if (callbacks.set_font_style == NULL
					|| !reader.GetRemaining(style, length)) {
					break;
				}
 
				callbacks.set_font_style(userData, style, length);
				break;
			}
 
			case B_PIC_SET_FONT_SPACING:
			{
				const uint32* spacing;
				if (callbacks.set_font_spacing == NULL || !reader.Get(spacing))
					break;
 
				callbacks.set_font_spacing(userData, *spacing);
				break;
			}
 
			case B_PIC_SET_FONT_SIZE:
			{
				const float* size;
				if (callbacks.set_font_size == NULL || !reader.Get(size))
					break;
 
				callbacks.set_font_size(userData, *size);
				break;
			}
 
			case B_PIC_SET_FONT_ROTATE:
			{
				const float* rotation;
				if (callbacks.set_font_rotation == NULL
					|| !reader.Get(rotation)) {
					break;
				}
 
				callbacks.set_font_rotation(userData, *rotation);
				break;
			}
 
			case B_PIC_SET_FONT_ENCODING:
			{
				const uint32* encoding;
				if (callbacks.set_font_encoding == NULL
					|| !reader.Get(encoding)) {
					break;
				}
 
				callbacks.set_font_encoding(userData, *encoding);
				break;
			}
 
			case B_PIC_SET_FONT_FLAGS:
			{
				const uint32* flags;
				if (callbacks.set_font_flags == NULL || !reader.Get(flags))
					break;
 
				callbacks.set_font_flags(userData, *flags);
				break;
			}
 
			case B_PIC_SET_FONT_SHEAR:
			{
				const float* shear;
				if (callbacks.set_font_shear == NULL || !reader.Get(shear))
					break;
 
				callbacks.set_font_shear(userData, *shear);
				break;
			}
 
			case B_PIC_SET_FONT_FACE:
			{
				const uint32* face;
				if (callbacks.set_font_face == NULL || !reader.Get(face))
					break;
 
				callbacks.set_font_face(userData, *face);
				break;
			}
 
			case B_PIC_SET_BLENDING_MODE:
			{
				const uint16* alphaSourceMode;
				const uint16* alphaFunctionMode;
				if (callbacks.set_blending_mode == NULL
					|| !reader.Get(alphaSourceMode)
					|| !reader.Get(alphaFunctionMode)) {
					break;
				}
 
				callbacks.set_blending_mode(userData,
					(source_alpha)*alphaSourceMode,
					(alpha_function)*alphaFunctionMode);
				break;
			}
 
			case B_PIC_SET_TRANSFORM:
			{
				const BAffineTransform* transform;
				if (callbacks.set_transform == NULL || !reader.Get(transform))
					break;
 
				callbacks.set_transform(userData, *transform);
				break;
			}
 
			case B_PIC_AFFINE_TRANSLATE:
			{
				const double* x;
				const double* y;
				if (callbacks.translate_by == NULL || !reader.Get(x)
					|| !reader.Get(y)) {
					break;
				}
 
				callbacks.translate_by(userData, *x, *y);
				break;
			}
 
			case B_PIC_AFFINE_SCALE:
			{
				const double* x;
				const double* y;
				if (callbacks.scale_by == NULL || !reader.Get(x)
					|| !reader.Get(y)) {
					break;
				}
 
				callbacks.scale_by(userData, *x, *y);
				break;
			}
 
			case B_PIC_AFFINE_ROTATE:
			{
				const double* angleRadians;
				if (callbacks.rotate_by == NULL || !reader.Get(angleRadians))
					break;
 
				callbacks.rotate_by(userData, *angleRadians);
				break;
			}
 
			case B_PIC_BLEND_LAYER:
			{
				Layer* const* layer;
				if (callbacks.blend_layer == NULL || !reader.Get<Layer*>(layer))
					break;
 
				callbacks.blend_layer(userData, *layer);
				break;
			}
 
			case B_PIC_CLIP_TO_RECT:
			{
				const bool* inverse;
				const BRect* rect;
 
				if (callbacks.clip_to_rect == NULL || !reader.Get(inverse)
					|| !reader.Get(rect)) {
					break;
				}
 
				callbacks.clip_to_rect(userData, *rect, *inverse);
				break;
			}
 
			case B_PIC_CLIP_TO_SHAPE:
			{
				const bool* inverse;
				const uint32* opCount;
				const uint32* pointCount;
				const uint32* opList;
				const BPoint* pointList;
				if (callbacks.clip_to_shape == NULL || !reader.Get(inverse)
					|| !reader.Get(opCount) || !reader.Get(pointCount)
					|| !reader.Get(opList, *opCount)
					|| !reader.Get(pointList, *pointCount)) {
					break;
				}
 
				callbacks.clip_to_shape(userData, *opCount, opList,
					*pointCount, pointList, *inverse);
				break;
			}
 
			default:
				break;
		}
 
#if DEBUG
		numOps++;
#if DEBUG > 1
		printf("executed in %" B_PRId64 " usecs\n", system_time()
			- startOpTime);
#endif
#endif
	}
 
#if DEBUG
	printf("Done! %" B_PRId32 " ops, rendering completed in %" B_PRId64
		" usecs.\n", numOps, system_time() - startTime);
#endif
	return B_OK;
}

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.