From eec73693e030e7455f2312ab49613b1b85076c73 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sat, 5 Sep 2015 14:43:19 +0200 Subject: [PATCH] PicturePlayer: Rework to be more safe against corrupted data. This introduces a more sane API (currently private) that allows for safer and possibly more efficient implementations: * It uses a struct of named and typed function pointers instead of just a void pointer array. This adds type safety to the callbacks so the compiler can figure out if things match up before subtle bugs get introduced. * It provides bounds for all strings/buffers passed to the callbacks. * It uses const references instead of implicitly copying arguments. * It folds stroke_x/fill_x pairs into draw_x functions with a fill argument to reduce the amount of functions needed. * It uses unsigned values where negative values make no sense. The old API has been implemented on top of the new one using adapter functions. It makes copies of all data passed to the callbacks which effectively keeps the picture data from being modified. This matches with the R5 behaviour. This also reimplements the buffer parsing to be safe against corrupted data by validating that the types actually fit in the provided sizes and buffers (using a templated reader). Since this class is used from the app_server with user provided data, making it more safe is important even though it comes with a slight overhead (replicating R5 behaviour, i.e. crashing the app_server when corrupted data is fed, doesn't seem very appropriate here). --- headers/private/interface/PicturePlayer.h | 76 +- src/kits/interface/PicturePlayer.cpp | 1118 ++++++++++++++++----- 2 files changed, 948 insertions(+), 246 deletions(-) diff --git a/headers/private/interface/PicturePlayer.h b/headers/private/interface/PicturePlayer.h index 1a74b5cefe..bd714b74b5 100644 --- a/headers/private/interface/PicturePlayer.h +++ b/headers/private/interface/PicturePlayer.h @@ -14,25 +14,93 @@ #include +#include #include #include -#include + +class BAffineTransform; class BList; +class BPicture; +class BShape; namespace BPrivate { + +struct picture_player_callbacks { + void (*move_pen_by)(void* userData, const BPoint& where); + void (*stroke_line)(void* userData, const BPoint& start, const BPoint& end); + void (*draw_rect)(void* userData, const BRect& rect, bool fill); + void (*draw_round_rect)(void* userData, const BRect& rect, + const BPoint& radii, bool fill); + void (*draw_bezier)(void* userData, size_t numControlPoints, + const BPoint controlPoints[], bool fill); + void (*draw_arc)(void* userData, const BPoint& center, const BPoint& radii, + float startTheta, float arcTheta, bool fill); + void (*draw_ellipse)(void* userData, const BRect& rect, bool fill); + void (*draw_polygon)(void* userData, size_t numPoints, + const BPoint points[], bool isClosed, bool fill); + void (*draw_shape)(void* userData, const BShape& shape, bool fill); + void (*draw_string)(void* userData, const char* string, size_t length, + float spaceEscapement, float nonSpaceEscapement); + void (*draw_pixels)(void* userData, const BRect& source, + const BRect& destination, uint32 width, uint32 height, + size_t bytesPerRow, color_space pixelFormat, uint32 flags, + const void* data, size_t length); + void (*draw_picture)(void* userData, const BPoint& where, int32 token); + void (*set_clipping_rects)(void* userData, size_t numRects, + const BRect rects[]); + void (*clip_to_picture)(void* userData, const BPicture& picture, + const BPoint& where, bool clipToInverse); + void (*push_state)(void* userData); + void (*pop_state)(void* userData); + void (*enter_state_change)(void* userData); + void (*exit_state_change)(void* userData); + void (*enter_font_state)(void* userData); + void (*exit_font_state)(void* userData); + void (*set_origin)(void* userData, const BPoint& origin); + void (*set_pen_location)(void* userData, const BPoint& location); + void (*set_drawing_mode)(void* userData, drawing_mode mode); + void (*set_line_mode)(void* userData, cap_mode capMode, join_mode joinMode, + float miterLimit); + void (*set_pen_size)(void* userData, float size); + void (*set_fore_color)(void* userData, const rgb_color& color); + void (*set_back_color)(void* userData, const rgb_color& color); + void (*set_stipple_pattern)(void* userData, const pattern& patter); + void (*set_scale)(void* userData, float scale); + void (*set_font_family)(void* userData, const char* familyName, + size_t length); + void (*set_font_style)(void* userData, const char* styleName, + size_t length); + void (*set_font_spacing)(void* userData, uint8 spacing); + void (*set_font_size)(void* userData, float size); + void (*set_font_rotation)(void* userData, float rotation); + void (*set_font_encoding)(void* userData, uint8 encoding); + void (*set_font_flags)(void* userData, uint32 flags); + void (*set_font_shear)(void* userData, float shear); + void (*set_font_face)(void* userData, uint16 face); + void (*set_blending_mode)(void* userData, source_alpha alphaSourceMode, + alpha_function alphaFunctionMode); + void (*set_transform)(void* userData, const BAffineTransform& transform); +}; + + class PicturePlayer { public: PicturePlayer(); - PicturePlayer(const void *data, size_t size, BList *pictures); + PicturePlayer(const void* data, size_t size, BList* pictures); virtual ~PicturePlayer(); - status_t Play(void **callBackTable, int32 tableEntries, - void *userData); + status_t Play(void** callbacks, int32 tableEntries, + void* userData); + status_t Play(const picture_player_callbacks& callbacks, + size_t callbacksSize, void* userData); private: + status_t _Play(const picture_player_callbacks& callbacks, void* userData, + const void* data, size_t length, uint16 parentOp); + const void* fData; size_t fSize; BList* fPictures; diff --git a/src/kits/interface/PicturePlayer.cpp b/src/kits/interface/PicturePlayer.cpp index f337bc1896..4b32428a4a 100644 --- a/src/kits/interface/PicturePlayer.cpp +++ b/src/kits/interface/PicturePlayer.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -23,30 +24,10 @@ using BPrivate::PicturePlayer; -typedef void (*fnc)(void*); -typedef void (*fnc_BPoint)(void*, BPoint); -typedef void (*fnc_BPointBPoint)(void*, BPoint, BPoint); -typedef void (*fnc_BRect)(void*, BRect); -typedef void (*fnc_BRectBPoint)(void*, BRect, BPoint); -typedef void (*fnc_PBPoint)(void*, const BPoint*); -typedef void (*fnc_i)(void*, int32); -typedef void (*fnc_iPBPointb)(void*, int32, const BPoint*, bool); -typedef void (*fnc_iPBPoint)(void*, int32, const BPoint*); -typedef void (*fnc_Pc)(void*, const char*); -typedef void (*fnc_Pcff)(void*, const char*, float, float); -typedef void (*fnc_BPointBPointff)(void*, BPoint, BPoint, float, float); -typedef void (*fnc_s)(void*, int16); -typedef void (*fnc_ssf)(void*, int16, int16, float); -typedef void (*fnc_f)(void*, float); -typedef void (*fnc_Color)(void*, rgb_color); -typedef void (*fnc_Pattern)(void*, pattern); -typedef void (*fnc_ss)(void *, int16, int16); -typedef void (*fnc_PBRecti)(void*, const BRect*, uint32); -typedef void (*fnc_DrawPixels)(void *, BRect, BRect, int32, int32, int32, - int32, int32, const void *); -typedef void (*fnc_DrawPicture)(void *, BPoint, int32); -typedef void (*fnc_BShape)(void*, BShape*); -typedef void (*fnc_BAffineTransform)(void*, BAffineTransform); +struct adapter_context { + void* user_data; + void** function_table; +}; static void @@ -55,6 +36,421 @@ nop() } +static void +move_pen_by(void* _context, const BPoint& delta) +{ + adapter_context* context = reinterpret_cast(_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(_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(_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(_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(_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(_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(_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(_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(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(_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(_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(_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(_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(_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(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, const BPicture& picture, const BPoint& origin, + bool clipToInverse) +{ +} + + +static void +push_state(void* _context) +{ + adapter_context* context = reinterpret_cast(_context); + ((void (*)(void*))context->function_table[22])(context->user_data); +} + + +static void +pop_state(void* _context) +{ + adapter_context* context = reinterpret_cast(_context); + ((void (*)(void*))context->function_table[23])(context->user_data); +} + + +static void +enter_state_change(void* _context) +{ + adapter_context* context = reinterpret_cast(_context); + ((void (*)(void*))context->function_table[24])(context->user_data); +} + + +static void +exit_state_change(void* _context) +{ + adapter_context* context = reinterpret_cast(_context); + ((void (*)(void*))context->function_table[25])(context->user_data); +} + + +static void +enter_font_state(void* _context) +{ + adapter_context* context = reinterpret_cast(_context); + ((void (*)(void*))context->function_table[26])(context->user_data); +} + + +static void +exit_font_state(void* _context) +{ + adapter_context* context = reinterpret_cast(_context); + ((void (*)(void*))context->function_table[27])(context->user_data); +} + + +static void +set_origin(void* _context, const BPoint& origin) +{ + adapter_context* context = reinterpret_cast(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_context); + ((void (*)(void*, source_alpha, alpha_function)) + context->function_table[47])(context->user_data, alphaSrcMode, + alphaFncMode); +} + + #if DEBUG > 1 static const char * PictureOpToString(int op) @@ -132,241 +528,403 @@ PicturePlayer::~PicturePlayer() status_t -PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData) +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 + }; + // 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 + bool + Get(const T*& typed, size_t count = 1) + { + if (fRemaining < sizeof(T) * count) + return false; + + typed = reinterpret_cast(fBuffer); + fRemaining -= sizeof(T) * count; + fBuffer += sizeof(T) * count; + return true; + } + + template + bool + GetRemaining(const T*& buffer, size_t& size) + { + if (fRemaining == 0) + return false; + + buffer = reinterpret_cast(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 - FILE *file = fopen("/var/log/PicturePlayer.log", "a"); - fprintf(file, "Start rendering BPicture...\n"); + printf("Start rendering %sBPicture...\n", parentOp != 0 ? "sub " : ""); bigtime_t startTime = system_time(); int32 numOps = 0; #endif - // 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 **functionTable = callBackTable; - void *dummyTable[kOpsTableSize] = { - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop, - (void *)nop, (void *)nop, (void *)nop, (void *)nop - }; - if ((uint32)tableEntries < kOpsTableSize) { -#if DEBUG - fprintf(file, "PicturePlayer: A smaller than needed function table was supplied.\n"); -#endif - functionTable = dummyTable; - memcpy(functionTable, callBackTable, tableEntries * sizeof(void *)); - } + DataReader pictureReader(buffer, length); - const char *data = reinterpret_cast(fData); - size_t pos = 0; + while (pictureReader.Remaining() > 0) { + const picture_data_entry_header* header; + const uint8* opData; + if (!pictureReader.Get(header) + || !pictureReader.Get(opData, header->size)) { + return B_BAD_DATA; + } - int32 fontStateBlockSize = -1; - int32 stateBlockSize = -1; + DataReader reader(opData, header->size); - while ((pos + 6) <= fSize) { - int16 op = *reinterpret_cast(data); - int32 size = *reinterpret_cast(data + 2); - pos += 6; - data += 6; + // Disallow ops that don't fit the parent. + switch (parentOp) { + case 0: + // No parent op, no restrictions. + break; - if (pos + size > fSize) - debugger("PicturePlayer::Play: buffer overrun\n"); + 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(); - fprintf(file, "Op %s ", PictureOpToString(op)); + printf("Op %s ", PictureOpToString(header->op)); #endif - switch (op) { + switch (header->op) { case B_PIC_MOVE_PEN_BY: { - ((fnc_BPoint)functionTable[1])(userData, - *reinterpret_cast(data)); /* where */ + 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: { - ((fnc_BPointBPoint)functionTable[2])(userData, - *reinterpret_cast(data), /* start */ - *reinterpret_cast(data + sizeof(BPoint))); /* end */ + 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: - { - ((fnc_BRect)functionTable[3])(userData, - *reinterpret_cast(data)); /* rect */ - break; - } - case B_PIC_FILL_RECT: { - ((fnc_BRect)functionTable[4])(userData, - *reinterpret_cast(data)); /* 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: - { - ((fnc_BRectBPoint)functionTable[5])(userData, - *reinterpret_cast(data), /* rect */ - *reinterpret_cast(data + sizeof(BRect))); /* radii */ - break; - } - case B_PIC_FILL_ROUND_RECT: { - ((fnc_BRectBPoint)functionTable[6])(userData, - *reinterpret_cast(data), /* rect */ - *reinterpret_cast(data + sizeof(BRect))); /* radii */ + 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: - { - ((fnc_PBPoint)functionTable[7])(userData, - reinterpret_cast(data)); - break; - } - case B_PIC_FILL_BEZIER: { - ((fnc_PBPoint)functionTable[8])(userData, - reinterpret_cast(data)); + 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: - { - ((fnc_BPointBPointff)functionTable[9])(userData, - *reinterpret_cast(data), /* center */ - *reinterpret_cast(data + sizeof(BPoint)), /* radii */ - *reinterpret_cast(data + 2 * sizeof(BPoint)), /* startTheta */ - *reinterpret_cast(data + 2 * sizeof(BPoint) + sizeof(float))); /* arcTheta */ - break; - } - case B_PIC_FILL_ARC: { - ((fnc_BPointBPointff)functionTable[10])(userData, - *reinterpret_cast(data), /* center */ - *reinterpret_cast(data + sizeof(BPoint)), /* radii */ - *reinterpret_cast(data + 2 * sizeof(BPoint)), /* startTheta */ - *reinterpret_cast(data + 2 * sizeof(BPoint) + sizeof(float))); /* arcTheta */ + 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: - { - const BRect *rect = reinterpret_cast(data); - BPoint radii((rect->Width() + 1) / 2.0f, (rect->Height() + 1) / 2.0f); - BPoint center = rect->LeftTop() + radii; - ((fnc_BPointBPoint)functionTable[11])(userData, center, radii); - break; - } - case B_PIC_FILL_ELLIPSE: { - const BRect *rect = reinterpret_cast(data); - BPoint radii((rect->Width() + 1) / 2.0f, (rect->Height() + 1) / 2.0f); - BPoint center = rect->LeftTop() + radii; - ((fnc_BPointBPoint)functionTable[12])(userData, center, radii); + 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: - { - int32 numPoints = *reinterpret_cast(data); - ((fnc_iPBPointb)functionTable[13])(userData, - numPoints, - reinterpret_cast(data + sizeof(int32)), /* points */ - *reinterpret_cast(data + sizeof(int32) + numPoints * sizeof(BPoint))); /* is-closed */ - break; - } - case B_PIC_FILL_POLYGON: { - ((fnc_iPBPoint)functionTable[14])(userData, - *reinterpret_cast(data), /* numPoints */ - reinterpret_cast(data + sizeof(int32))); /* points */ + 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 bool stroke = (op == B_PIC_STROKE_SHAPE); - int32 opCount = *reinterpret_cast(data); - int32 ptCount = *reinterpret_cast(data + sizeof(int32)); - const uint32 *opList = reinterpret_cast(data + 2 * sizeof(int32)); - const BPoint *ptList = reinterpret_cast(data + 2 * sizeof(int32) + opCount * sizeof(uint32)); + 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, ptCount, opList, ptList); + shape.SetData(*opCount, *pointCount, opList, pointList); - const int32 tableIndex = stroke ? 15 : 16; - ((fnc_BShape)functionTable[tableIndex])(userData, &shape); + callbacks.draw_shape(userData, shape, + header->op == B_PIC_FILL_SHAPE); break; } case B_PIC_DRAW_STRING: { - ((fnc_Pcff)functionTable[17])(userData, - reinterpret_cast(data + 2 * sizeof(float)), /* string */ - *reinterpret_cast(data), /* escapement.space */ - *reinterpret_cast(data + sizeof(float))); /* escapement.nonspace */ + 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_PIXELS: { - ((fnc_DrawPixels)functionTable[18])(userData, - *reinterpret_cast(data), /* src */ - *reinterpret_cast(data + 1 * sizeof(BRect)), /* dst */ - *reinterpret_cast(data + 2 * sizeof(BRect)), /* width */ - *reinterpret_cast(data + 2 * sizeof(BRect) + 1 * sizeof(int32)), /* height */ - *reinterpret_cast(data + 2 * sizeof(BRect) + 2 * sizeof(int32)), /* bytesPerRow */ - *reinterpret_cast(data + 2 * sizeof(BRect) + 3 * sizeof(int32)), /* pixelFormat */ - *reinterpret_cast(data + 2 * sizeof(BRect) + 4 * sizeof(int32)), /* flags */ - reinterpret_cast(data + 2 * sizeof(BRect) + 5 * sizeof(int32))); /* data */ + 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: { - ((fnc_DrawPicture)functionTable[19])(userData, - *reinterpret_cast(data), - *reinterpret_cast(data + sizeof(BPoint))); + 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: { - // TODO: Not sure if it's compatible with R5's BPicture version - const uint32 numRects = *reinterpret_cast(data); - const BRect *rects = reinterpret_cast(data + sizeof(uint32)); - ((fnc_PBRecti)functionTable[20])(userData, rects, numRects); + 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: { - ((fnc_PBRecti)functionTable[20])(userData, NULL, 0); + if (callbacks.set_clipping_rects == NULL) + break; + + callbacks.set_clipping_rects(userData, 0, NULL); break; } @@ -378,170 +936,270 @@ PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData) case B_PIC_PUSH_STATE: { - ((fnc)functionTable[22])(userData); + if (callbacks.push_state == NULL) + break; + + callbacks.push_state(userData); break; } case B_PIC_POP_STATE: { - ((fnc)functionTable[23])(userData); + if (callbacks.pop_state == NULL) + break; + + callbacks.pop_state(userData); break; } case B_PIC_ENTER_STATE_CHANGE: - { - ((fnc)functionTable[24])(userData); - stateBlockSize = size; - break; - } - case B_PIC_ENTER_FONT_STATE: { - ((fnc)functionTable[26])(userData); - fontStateBlockSize = size; + 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: { - ((fnc_BPoint)functionTable[28])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_BPoint)functionTable[29])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_s)functionTable[30])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_ssf)functionTable[31])(userData, - *reinterpret_cast(data), /* cap-mode */ - *reinterpret_cast(data + 1 * sizeof(int16)), /* join-mode */ - *reinterpret_cast(data + 2 * sizeof(int16))); /* miter-limit */ + 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: { - ((fnc_f)functionTable[32])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_Color)functionTable[33])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_Color)functionTable[34])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_Pattern)functionTable[35])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_f)functionTable[36])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_Pc)functionTable[37])(userData, - reinterpret_cast(data)); /* string */ + 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: { - ((fnc_Pc)functionTable[38])(userData, - reinterpret_cast(data)); /* string */ + 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: { - ((fnc_i)functionTable[39])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_f)functionTable[40])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_f)functionTable[41])(userData, - *reinterpret_cast(data)); /* rotation */ + 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: { - ((fnc_i)functionTable[42])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_i)functionTable[43])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_f)functionTable[44])(userData, - *reinterpret_cast(data)); /* 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: { - ((fnc_i)functionTable[46])(userData, - *reinterpret_cast(data)); /* flags */ + 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: { - ((fnc_ss)functionTable[47])(userData, - *reinterpret_cast(data), /* alphaSrcMode */ - *reinterpret_cast(data + sizeof(int16))); /* alphaFncMode */ + 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: { - ((fnc_BAffineTransform)functionTable[48])(userData, - *reinterpret_cast(data)); + const BAffineTransform* transform; + if (callbacks.set_transform == NULL || !reader.Get(transform)) + break; + + callbacks.set_transform(userData, *transform); break; } @@ -549,42 +1207,18 @@ PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData) break; } - // Skip the already handled block unless it's one of these two, - // since they can contain other nested ops. - if (op != B_PIC_ENTER_STATE_CHANGE && op != B_PIC_ENTER_FONT_STATE) { - pos += size; - data += size; - if (stateBlockSize > 0) - stateBlockSize -= size + 6; - if (fontStateBlockSize > 0) - fontStateBlockSize -= size + 6; - } - - // call the exit_state_change hook if needed - if (stateBlockSize == 0) { - ((fnc)functionTable[25])(userData); - stateBlockSize = -1; - } - - // call the exit_font_state hook if needed - if (fontStateBlockSize == 0) { - ((fnc)functionTable[27])(userData); - fontStateBlockSize = -1; - } #if DEBUG numOps++; #if DEBUG > 1 - fprintf(file, "executed in %" B_PRId64 " usecs\n", system_time() + printf("executed in %" B_PRId64 " usecs\n", system_time() - startOpTime); #endif #endif - // TODO: what if too much was read, should we return B_ERROR? } #if DEBUG - fprintf(file, "Done! %" B_PRId32 " ops, rendering completed in %" - B_PRId64 " usecs.\n", numOps, system_time() - startTime); - fclose(file); + printf("Done! %" B_PRId32 " ops, rendering completed in %" B_PRId64 + " usecs.\n", numOps, system_time() - startTime); #endif return B_OK; }