hrev43928 adds 1 changeset to branch 'master' old head: d1abffcaa267478916c5922ec1712cf27509bcec new head: 8d33dc2971c49b5fe2eb9401e77406199d524d8f ---------------------------------------------------------------------------- 8d33dc2: An html5 <canvas> based drawing engine This implements an html5 rendering engine. Inspired by the Broadway GDK backend for Gnome. Work in progress. For now it just connects and dumps debug output. [ François Revol <revol@xxxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev43928 Commit: 8d33dc2971c49b5fe2eb9401e77406199d524d8f URL: http://cgit.haiku-os.org/haiku/commit/?id=8d33dc2 Author: François Revol <revol@xxxxxxx> Date: Sun Apr 1 09:04:41 2012 UTC ---------------------------------------------------------------------------- 28 files changed, 4477 insertions(+), 2 deletions(-) src/servers/app/Jamfile | 3 +- src/servers/app/ScreenManager.cpp | 11 +- src/servers/app/drawing/Jamfile | 1 + .../app/drawing/html5/CanvasEventStream.cpp | 218 +++ src/servers/app/drawing/html5/CanvasEventStream.h | 45 + src/servers/app/drawing/html5/CanvasMessage.cpp | 500 +++++++ src/servers/app/drawing/html5/CanvasMessage.h | 371 +++++ .../app/drawing/html5/HTML5DrawingEngine.cpp | 1117 ++++++++++++++++ src/servers/app/drawing/html5/HTML5DrawingEngine.h | 180 +++ src/servers/app/drawing/html5/HTML5HWInterface.cpp | 612 +++++++++ src/servers/app/drawing/html5/HTML5HWInterface.h | 131 ++ src/servers/app/drawing/html5/Jamfile | 32 + src/servers/app/drawing/html5/NetSender.cpp | 75 + src/servers/app/drawing/html5/NetSender.h | 34 + .../app/drawing/html5/StreamingRingBuffer.cpp | 176 +++ .../app/drawing/html5/StreamingRingBuffer.h | 47 + src/servers/app/drawing/html5/WebHandler.cpp | 85 ++ src/servers/app/drawing/html5/WebHandler.h | 58 + src/servers/app/drawing/html5/WebServer.cpp | 155 +++ src/servers/app/drawing/html5/WebServer.h | 47 + src/servers/app/drawing/html5/WebWorker.cpp | 152 +++ src/servers/app/drawing/html5/WebWorker.h | 39 + src/servers/app/drawing/html5/base64.cpp | 115 ++ src/servers/app/drawing/html5/base64.h | 15 + src/servers/app/drawing/html5/desktop.html | 29 + src/servers/app/drawing/html5/desktop.html.h | 30 + src/servers/app/drawing/html5/haiku.js | 100 ++ src/servers/app/drawing/html5/haiku.js.h | 101 ++ ---------------------------------------------------------------------------- diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index f31ebbb..0b917f3 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -84,7 +84,8 @@ Server app_server : # libraries : libtranslation.so libbe.so libbnetapi.so - libasdrawing.a libasremote.a libpainter.a libagg.a $(HAIKU_FREETYPE_LIB) + libasdrawing.a libasremote.a libashtml5.a + libpainter.a libagg.a $(HAIKU_FREETYPE_LIB) libstackandtile.a liblinprog.a libtextencoding.so libshared.a $(TARGET_LIBSTDC++) diff --git a/src/servers/app/ScreenManager.cpp b/src/servers/app/ScreenManager.cpp index 308939a..064bb79 100644 --- a/src/servers/app/ScreenManager.cpp +++ b/src/servers/app/ScreenManager.cpp @@ -15,6 +15,7 @@ #include "ServerConfig.h" #include "remote/RemoteHWInterface.h" +#include "html5/HTML5HWInterface.h" #include <Autolock.h> #include <Entry.h> @@ -109,7 +110,15 @@ ScreenManager::AcquireScreens(ScreenOwner* owner, int32* wishList, // there's a specific target screen we want to initialize // TODO: right now we only support remote screens, but we could // also target specific accelerants to support other graphics cards - RemoteHWInterface* interface = new(nothrow) RemoteHWInterface(target); + HWInterface* interface; + /* + if (strncmp(target, "vnc:", 4) == 0) + interface = new(nothrow) VNCHWInterface(target); + else*/ + if (strncmp(target, "html5:", 6) == 0) + interface = new(nothrow) HTML5HWInterface(target); + else + interface = new(nothrow) RemoteHWInterface(target); if (interface != NULL) { screen_item* item = _AddHWInterface(interface); if (item != NULL && list.AddItem(item->screen)) { diff --git a/src/servers/app/drawing/Jamfile b/src/servers/app/drawing/Jamfile index ee5c465..f84cc4f 100644 --- a/src/servers/app/drawing/Jamfile +++ b/src/servers/app/drawing/Jamfile @@ -30,3 +30,4 @@ StaticLibrary libasdrawing.a : SubInclude HAIKU_TOP src servers app drawing Painter ; SubInclude HAIKU_TOP src servers app drawing remote ; +SubInclude HAIKU_TOP src servers app drawing html5 ; diff --git a/src/servers/app/drawing/html5/CanvasEventStream.cpp b/src/servers/app/drawing/html5/CanvasEventStream.cpp new file mode 100644 index 0000000..ec9cc99 --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasEventStream.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz <mmlr@xxxxxxxx> + */ + +#include "CanvasEventStream.h" + +#include "CanvasMessage.h" +#include "StreamingRingBuffer.h" + +#include <Autolock.h> + +#include <new> + + +CanvasEventStream::CanvasEventStream() + : + fEventList(10, true), + fEventListLocker("canvas event list"), + fEventNotification(-1), + fWaitingOnEvent(false), + fLatestMouseMovedEvent(NULL), + fMousePosition(0, 0), + fMouseButtons(0), + fModifiers(0) +{ + fEventNotification = create_sem(0, "canvas event notification"); +} + + +CanvasEventStream::~CanvasEventStream() +{ + delete_sem(fEventNotification); +} + + +void +CanvasEventStream::UpdateScreenBounds(BRect bounds) +{ +} + + +bool +CanvasEventStream::GetNextEvent(BMessage** _event) +{ + BAutolock lock(fEventListLocker); + while (fEventList.CountItems() == 0) { + fWaitingOnEvent = true; + lock.Unlock(); + + status_t result; + do { + result = acquire_sem(fEventNotification); + } while (result == B_INTERRUPTED); + + lock.Lock(); + if (!lock.IsLocked()) + return false; + } + + *_event = fEventList.RemoveItemAt(0); + return true; +} + + +status_t +CanvasEventStream::InsertEvent(BMessage* event) +{ + BAutolock lock(fEventListLocker); + if (!lock.IsLocked()) + return B_ERROR; + + if (!fEventList.AddItem(event)) + return B_ERROR; + + if (event->what == B_MOUSE_MOVED) + fLatestMouseMovedEvent = event; + + return B_OK; +} + + +BMessage* +CanvasEventStream::PeekLatestMouseMoved() +{ + return fLatestMouseMovedEvent; +} + + +bool +CanvasEventStream::EventReceived(CanvasMessage& message) +{ + uint16 code = message.Code(); + uint32 what = 0; + switch (code) { + case RP_MOUSE_MOVED: + what = B_MOUSE_MOVED; + break; + case RP_MOUSE_DOWN: + what = B_MOUSE_DOWN; + break; + case RP_MOUSE_UP: + what = B_MOUSE_UP; + break; + case RP_MOUSE_WHEEL_CHANGED: + what = B_MOUSE_WHEEL_CHANGED; + break; + case RP_KEY_DOWN: + what = B_KEY_DOWN; + break; + case RP_KEY_UP: + what = B_KEY_UP; + break; + case RP_MODIFIERS_CHANGED: + what = B_MODIFIERS_CHANGED; + break; + } + + if (what == 0) + return false; + + BMessage* event = new BMessage(what); + if (event == NULL) + return false; + + event->AddInt64("when", system_time()); + + switch (code) { + case RP_MOUSE_MOVED: + case RP_MOUSE_DOWN: + case RP_MOUSE_UP: + { + message.Read(fMousePosition); + if (code != RP_MOUSE_MOVED) + message.Read(fMouseButtons); + + event->AddPoint("where", fMousePosition); + event->AddInt32("buttons", fMouseButtons); + event->AddInt32("modifiers", fModifiers); + + if (code == RP_MOUSE_DOWN) { + int32 clicks; + if (message.Read(clicks) == B_OK) + event->AddInt32("clicks", clicks); + } + + if (code == RP_MOUSE_MOVED) + fLatestMouseMovedEvent = event; + break; + } + + case RP_MOUSE_WHEEL_CHANGED: + { + float xDelta, yDelta; + message.Read(xDelta); + message.Read(yDelta); + event->AddFloat("be:wheel_delta_x", xDelta); + event->AddFloat("be:wheel_delta_y", yDelta); + break; + } + + case RP_KEY_DOWN: + case RP_KEY_UP: + { + int32 numBytes; + if (message.Read(numBytes) != B_OK) + break; + + char* bytes = (char*)malloc(numBytes + 1); + if (bytes == NULL) + break; + + if (message.ReadList(bytes, numBytes) != B_OK) { + free(bytes); + break; + } + + for (int32 i = 0; i < numBytes; i++) + event->AddInt8("byte", (int8)bytes[i]); + + bytes[numBytes] = 0; + event->AddData("bytes", B_STRING_TYPE, bytes, numBytes + 1, false); + event->AddInt32("modifiers", fModifiers); + + int32 rawChar; + if (message.Read(rawChar) == B_OK) + event->AddInt32("raw_char", rawChar); + + int32 key; + if (message.Read(key) == B_OK) + event->AddInt32("key", key); + + free(bytes); + break; + } + + case RP_MODIFIERS_CHANGED: + { + event->AddInt32("be:old_modifiers", fModifiers); + message.Read(fModifiers); + event->AddInt32("modifiers", fModifiers); + break; + } + } + + BAutolock lock(fEventListLocker); + fEventList.AddItem(event); + if (fWaitingOnEvent) { + fWaitingOnEvent = false; + lock.Unlock(); + release_sem(fEventNotification); + } + + return true; +} diff --git a/src/servers/app/drawing/html5/CanvasEventStream.h b/src/servers/app/drawing/html5/CanvasEventStream.h new file mode 100644 index 0000000..68d0050 --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasEventStream.h @@ -0,0 +1,45 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz <mmlr@xxxxxxxx> + */ +#ifndef CANVAS_EVENT_STREAM_H +#define CANVAS_EVENT_STREAM_H + +#include "EventStream.h" + +#include <Locker.h> +#include <ObjectList.h> + +class CanvasMessage; + +class CanvasEventStream : public EventStream { +public: + CanvasEventStream(); +virtual ~CanvasEventStream(); + +virtual bool IsValid() { return true; } +virtual void SendQuit() {} + +virtual void UpdateScreenBounds(BRect bounds); +virtual bool GetNextEvent(BMessage** _event); +virtual status_t InsertEvent(BMessage* event); +virtual BMessage* PeekLatestMouseMoved(); + + bool EventReceived(CanvasMessage& message); + +private: + BObjectList<BMessage> fEventList; + BLocker fEventListLocker; + sem_id fEventNotification; + bool fWaitingOnEvent; + BMessage* fLatestMouseMovedEvent; + + BPoint fMousePosition; + uint32 fMouseButtons; + uint32 fModifiers; +}; + +#endif // CANVAS_EVENT_STREAM_H diff --git a/src/servers/app/drawing/html5/CanvasMessage.cpp b/src/servers/app/drawing/html5/CanvasMessage.cpp new file mode 100644 index 0000000..367d0d4 --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasMessage.cpp @@ -0,0 +1,500 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz <mmlr@xxxxxxxx> + * François Revol <revol@xxxxxxx> + */ + +#include "CanvasMessage.h" + +#include "DrawState.h" +#include "ServerBitmap.h" +#include "ServerCursor.h" + +#include <Bitmap.h> +#include <Font.h> +#include <View.h> + +#include <Gradient.h> +#include <GradientLinear.h> +#include <GradientRadial.h> +#include <GradientRadialFocus.h> +#include <GradientDiamond.h> +#include <GradientConic.h> + +#include <new> + + +status_t +CanvasMessage::NextMessage(uint16& code) +{ + if (fDataLeft > 0) { + // discard remainder of message + int32 readSize = fSource->Read(NULL, fDataLeft); + if (readSize < 0) + return readSize; + } + + static const uint32 kHeaderSize = sizeof(uint16) + sizeof(uint32); + + fDataLeft = kHeaderSize; + Read(code); + uint32 dataLeft; + status_t result = Read(dataLeft); + if (result != B_OK) + return result; + + if (dataLeft < kHeaderSize) + return B_ERROR; + + fDataLeft = dataLeft - kHeaderSize; + fCode = code; + return B_OK; +} + + +void +CanvasMessage::Cancel() +{ + fAvailable += fWriteIndex; + fWriteIndex = 0; +} + + +void +CanvasMessage::AddBitmap(const ServerBitmap& bitmap, bool minimal) +{ + //TODO:send PNG / as data: or as http: url + Add(bitmap.Width()); + Add(bitmap.Height()); + Add(bitmap.BytesPerRow()); + + if (!minimal) { + Add(bitmap.ColorSpace()); + Add(bitmap.Flags()); + } + + uint32 bitsLength = bitmap.BitsLength(); + Add(bitsLength); + + if (!_MakeSpace(bitsLength)) + return; + + memcpy(fBuffer + fWriteIndex, bitmap.Bits(), bitsLength); + fWriteIndex += bitsLength; + fAvailable -= bitsLength; +} + + +void +CanvasMessage::AddFont(const ServerFont& font) +{ + //TODO:Use TTF/WOFF URL + Add(font.Direction()); + Add((uint8)font.Encoding()); + Add(font.Flags()); + Add((uint8)font.Spacing()); + Add(font.Shear()); + Add(font.Rotation()); + Add(font.FalseBoldWidth()); + Add(font.Size()); + Add(font.Face()); + Add(font.GetFamilyAndStyle()); +} + + +void +CanvasMessage::AddDrawState(const DrawState& drawState) +{ + Add(drawState.PenSize()); + Add(drawState.SubPixelPrecise()); + Add(drawState.GetDrawingMode()); + Add(drawState.AlphaSrcMode()); + Add(drawState.AlphaFncMode()); + AddPattern(drawState.GetPattern()); + Add(drawState.LineCapMode()); + Add(drawState.LineJoinMode()); + Add(drawState.MiterLimit()); + Add(drawState.HighColor()); + Add(drawState.LowColor()); +} + + +void +CanvasMessage::AddArrayLine(const ViewLineArrayInfo& line) +{ + Add(line.startPoint); + Add(line.endPoint); + Add(line.color); +} + + +void +CanvasMessage::AddCursor(const ServerCursor& cursor) +{ + //TODO:send as .cur data: + Add(cursor.GetHotSpot()); + AddBitmap(cursor); +} + + +void +CanvasMessage::AddPattern(const Pattern& pattern) +{ + Add(pattern.GetPattern()); +} + + +void +CanvasMessage::AddGradient(const BGradient& gradient) +{ + Add(gradient.GetType()); + + switch (gradient.GetType()) { + case BGradient::TYPE_NONE: + break; + + case BGradient::TYPE_LINEAR: + { + const BGradientLinear* linear + = dynamic_cast<const BGradientLinear *>(&gradient); + if (linear == NULL) + return; + + Add(linear->Start()); + Add(linear->End()); + break; + } + + case BGradient::TYPE_RADIAL: + { + const BGradientRadial* radial + = dynamic_cast<const BGradientRadial *>(&gradient); + if (radial == NULL) + return; + + Add(radial->Center()); + Add(radial->Radius()); + break; + } + + case BGradient::TYPE_RADIAL_FOCUS: + { + const BGradientRadialFocus* radialFocus + = dynamic_cast<const BGradientRadialFocus *>(&gradient); + if (radialFocus == NULL) + return; + + Add(radialFocus->Center()); + Add(radialFocus->Focal()); + Add(radialFocus->Radius()); + break; + } + + case BGradient::TYPE_DIAMOND: + { + const BGradientDiamond* diamond + = dynamic_cast<const BGradientDiamond *>(&gradient); + if (diamond == NULL) + return; + + Add(diamond->Center()); + break; + } + + case BGradient::TYPE_CONIC: + { + const BGradientConic* conic + = dynamic_cast<const BGradientConic *>(&gradient); + if (conic == NULL) + return; + + Add(conic->Center()); + Add(conic->Angle()); + break; + } + } + + int32 stopCount = gradient.CountColorStops(); + Add(stopCount); + + for (int32 i = 0; i < stopCount; i++) { + BGradient::ColorStop* stop = gradient.ColorStopAt(i); + if (stop == NULL) + return; + + Add(stop->color); + Add(stop->offset); + } +} + + +status_t +CanvasMessage::ReadString(char** _string, size_t& _length) +{ + uint32 length; + status_t result = Read(length); + if (result != B_OK) + return result; + + if (length > fDataLeft) + return B_ERROR; + + char *string = (char *)malloc(length + 1); + if (string == NULL) + return B_NO_MEMORY; + + int32 readSize = fSource->Read(string, length); + if (readSize < 0) { + free(string); + return readSize; + } + + if ((uint32)readSize != length) { + free(string); + return B_ERROR; + } + + fDataLeft -= readSize; + + string[length] = 0; + *_string = string; + _length = length; + return B_OK; +} + + +status_t +CanvasMessage::ReadBitmap(BBitmap** _bitmap, bool minimal, + color_space colorSpace, uint32 flags) +{ + uint32 bitsLength; + int32 width, height, bytesPerRow; + + Read(width); + Read(height); + Read(bytesPerRow); + + if (!minimal) { + Read(colorSpace); + Read(flags); + } + + Read(bitsLength); + + if (bitsLength > fDataLeft) + return B_ERROR; + +#ifndef CLIENT_COMPILE + flags = B_BITMAP_NO_SERVER_LINK; +#endif + + BBitmap *bitmap = new(std::nothrow) BBitmap( + BRect(0, 0, width - 1, height - 1), flags, colorSpace, bytesPerRow); + if (bitmap == NULL) + return B_NO_MEMORY; + + status_t result = bitmap->InitCheck(); + if (result != B_OK) { + delete bitmap; + return result; + } + + if (bitmap->BitsLength() < (int32)bitsLength) { + delete bitmap; + return B_ERROR; + } + + int32 readSize = fSource->Read(bitmap->Bits(), bitsLength); + if ((uint32)readSize != bitsLength) { + delete bitmap; + return readSize < 0 ? readSize : B_ERROR; + } + + fDataLeft -= readSize; + *_bitmap = bitmap; + return B_OK; +} + + +status_t +CanvasMessage::ReadFontState(BFont& font) +{ + uint8 encoding, spacing; + uint16 face; + uint32 flags, familyAndStyle; + font_direction direction; + float falseBoldWidth, rotation, shear, size; + + Read(direction); + Read(encoding); + Read(flags); + Read(spacing); + Read(shear); + Read(rotation); + Read(falseBoldWidth); + Read(size); + Read(face); + status_t result = Read(familyAndStyle); + if (result != B_OK) + return result; + + font.SetFamilyAndStyle(familyAndStyle); + font.SetEncoding(encoding); + font.SetFlags(flags); + font.SetSpacing(spacing); + font.SetShear(shear); + font.SetRotation(rotation); + font.SetFalseBoldWidth(falseBoldWidth); + font.SetSize(size); + font.SetFace(face); + return B_OK; +} + + +status_t +CanvasMessage::ReadViewState(BView& view, ::pattern& pattern) +{ + bool subPixelPrecise; + float penSize, miterLimit; + drawing_mode drawingMode; + source_alpha sourceAlpha; + alpha_function alphaFunction; + cap_mode capMode; + join_mode joinMode; + rgb_color highColor, lowColor; + + Read(penSize); + Read(subPixelPrecise); + Read(drawingMode); + Read(sourceAlpha); + Read(alphaFunction); + Read(pattern); + Read(capMode); + Read(joinMode); + Read(miterLimit); + Read(highColor); + status_t result = Read(lowColor); + if (result != B_OK) + return result; + + uint32 flags = view.Flags() & ~B_SUBPIXEL_PRECISE; + view.SetFlags(flags | (subPixelPrecise ? B_SUBPIXEL_PRECISE : 0)); + view.SetPenSize(penSize); + view.SetDrawingMode(drawingMode); + view.SetBlendingMode(sourceAlpha, alphaFunction); + view.SetLineMode(capMode, joinMode, miterLimit); + view.SetHighColor(highColor); + view.SetLowColor(lowColor); + return B_OK; +} + + +status_t +CanvasMessage::ReadGradient(BGradient** _gradient) +{ + BGradient::Type type; + Read(type); + + BGradient *gradient = NULL; + switch (type) { + case BGradient::TYPE_NONE: + break; + + case BGradient::TYPE_LINEAR: + { + BPoint start, end; + + Read(start); + Read(end); + + gradient = new(std::nothrow) BGradientLinear(start, end); + break; + } + + case BGradient::TYPE_RADIAL: + { + BPoint center; + float radius; + + Read(center); + Read(radius); + + gradient = new(std::nothrow) BGradientRadial(center, radius); + break; + } + + case BGradient::TYPE_RADIAL_FOCUS: + { + BPoint center, focal; + float radius; + + Read(center); + Read(focal); + Read(radius); + + gradient = new(std::nothrow) BGradientRadialFocus(center, radius, + focal); + break; + } + + case BGradient::TYPE_DIAMOND: + { + BPoint center; + + Read(center); + + gradient = new(std::nothrow) BGradientDiamond(center); + break; + } + + case BGradient::TYPE_CONIC: + { + BPoint center; + float angle; + + Read(center); + Read(angle); + + gradient = new(std::nothrow) BGradientConic(center, angle); + break; + } + } + + if (gradient == NULL) + return B_NO_MEMORY; + + int32 stopCount; + status_t result = Read(stopCount); + if (result != B_OK) { + delete gradient; + return result; + } + + for (int32 i = 0; i < stopCount; i++) { + rgb_color color; + float offset; + + Read(color); + result = Read(offset); + if (result != B_OK) + return result; + + gradient->AddColor(color, offset); + } + + *_gradient = gradient; + return B_OK; +} + + +status_t +CanvasMessage::ReadArrayLine(BPoint& startPoint, BPoint& endPoint, + rgb_color& color) +{ + Read(startPoint); + Read(endPoint); + return Read(color); +} diff --git a/src/servers/app/drawing/html5/CanvasMessage.h b/src/servers/app/drawing/html5/CanvasMessage.h new file mode 100644 index 0000000..ca3648c --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasMessage.h @@ -0,0 +1,371 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz <mmlr@xxxxxxxx> + * François Revol <revol@xxxxxxx> + */ +#ifndef CANVAS_MESSAGE_H +#define CANCAS_MESSAGE_H + +#include "PatternHandler.h" +#include <ViewPrivate.h> + +#include "StreamingRingBuffer.h" +#include "base64.h" + +#include <GraphicsDefs.h> +#include <Region.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +class BBitmap; +class BFont; +class BGradient; +class BView; +class DrawState; +class Pattern; +class RemotePainter; +class ServerBitmap; +class ServerCursor; +class ServerFont; +class ViewLineArrayInfo; + +enum { + RP_INIT_CONNECTION = 1, + RP_UPDATE_DISPLAY_MODE, + RP_CLOSE_CONNECTION, + + RP_CREATE_STATE = 20, + RP_DELETE_STATE, + RP_ENABLE_SYNC_DRAWING, + RP_DISABLE_SYNC_DRAWING, + RP_INVALIDATE_RECT, + RP_INVALIDATE_REGION, + + RP_SET_OFFSETS = 40, + RP_SET_HIGH_COLOR, + RP_SET_LOW_COLOR, + RP_SET_PEN_SIZE, + RP_SET_STROKE_MODE, + RP_SET_BLENDING_MODE, + RP_SET_PATTERN, + RP_SET_DRAWING_MODE, + RP_SET_FONT, + + RP_CONSTRAIN_CLIPPING_REGION = 60, + RP_COPY_RECT_NO_CLIPPING, + RP_INVERT_RECT, + RP_DRAW_BITMAP, + RP_DRAW_BITMAP_RECTS, + + RP_STROKE_ARC = 80, + RP_STROKE_BEZIER, + RP_STROKE_ELLIPSE, + RP_STROKE_POLYGON, + RP_STROKE_RECT, + RP_STROKE_ROUND_RECT, + RP_STROKE_SHAPE, + RP_STROKE_TRIANGLE, + RP_STROKE_LINE, + RP_STROKE_LINE_ARRAY, + + RP_FILL_ARC = 100, + RP_FILL_BEZIER, + RP_FILL_ELLIPSE, + RP_FILL_POLYGON, + RP_FILL_RECT, + RP_FILL_ROUND_RECT, + RP_FILL_SHAPE, + RP_FILL_TRIANGLE, + RP_FILL_REGION, + + RP_FILL_ARC_GRADIENT = 120, + RP_FILL_BEZIER_GRADIENT, + RP_FILL_ELLIPSE_GRADIENT, + RP_FILL_POLYGON_GRADIENT, + RP_FILL_RECT_GRADIENT, + RP_FILL_ROUND_RECT_GRADIENT, + RP_FILL_SHAPE_GRADIENT, + RP_FILL_TRIANGLE_GRADIENT, + RP_FILL_REGION_GRADIENT, + + RP_STROKE_POINT_COLOR = 140, + RP_STROKE_LINE_1PX_COLOR, + RP_STROKE_RECT_1PX_COLOR, + + RP_FILL_RECT_COLOR = 160, + RP_FILL_REGION_COLOR_NO_CLIPPING, + + RP_DRAW_STRING = 180, + RP_DRAW_STRING_WITH_OFFSETS, + RP_DRAW_STRING_RESULT, + RP_STRING_WIDTH, + RP_STRING_WIDTH_RESULT, + RP_READ_BITMAP, + RP_READ_BITMAP_RESULT, + + RP_SET_CURSOR = 200, + RP_SET_CURSOR_VISIBLE, + RP_MOVE_CURSOR_TO, + + RP_MOUSE_MOVED = 220, + RP_MOUSE_DOWN, + RP_MOUSE_UP, + RP_MOUSE_WHEEL_CHANGED, + + RP_KEY_DOWN = 240, + RP_KEY_UP, + RP_UNMAPPED_KEY_DOWN, + RP_UNMAPPED_KEY_UP, + RP_MODIFIERS_CHANGED +}; + + +class CanvasMessage { +public: + CanvasMessage(StreamingRingBuffer* source, + StreamingRingBuffer *target); + ~CanvasMessage(); + + void Start(uint16 code); + status_t Flush(); + void Cancel(); + + status_t NextMessage(uint16& code); + uint16 Code() { return fCode; } + uint32 DataLeft() { return fDataLeft; } + + template<typename T> + void Add(const T& value); + + void AddString(const char* string, size_t length); + void AddRegion(const BRegion& region); + void AddGradient(const BGradient& gradient); + + void AddBitmap(const ServerBitmap& bitmap, + bool minimal = false); + void AddFont(const ServerFont& font); + void AddPattern(const Pattern& pattern); + void AddDrawState(const DrawState& drawState); + void AddArrayLine(const ViewLineArrayInfo& line); + void AddCursor(const ServerCursor& cursor); + + template<typename T> + void AddList(const T* array, int32 count); + + template<typename T> + status_t Read(T& value); + + status_t ReadRegion(BRegion& region); + status_t ReadFontState(BFont& font); + // sets font state + status_t ReadViewState(BView& view, ::pattern& pattern); + // sets viewstate and returns pattern + + status_t ReadString(char** _string, size_t& length); + status_t ReadBitmap(BBitmap** _bitmap, + bool minimal = false, + color_space colorSpace = B_RGB32, + uint32 flags = 0); + status_t ReadGradient(BGradient** _gradient); + status_t ReadArrayLine(BPoint& startPoint, + BPoint& endPoint, rgb_color& color); + + template<typename T> + status_t ReadList(T* array, int32 count); + +private: + bool _MakeSpace(size_t size); + + StreamingRingBuffer* fSource; + StreamingRingBuffer* fTarget; + + uint8* fBuffer; + size_t fAvailable; + size_t fWriteIndex; + uint32 fDataLeft; + uint16 fCode; +}; + + +inline +CanvasMessage::CanvasMessage(StreamingRingBuffer* source, + StreamingRingBuffer* target) + : + fSource(source), + fTarget(target), + fBuffer(NULL), + fAvailable(0), + fWriteIndex(0), + fDataLeft(0) +{ +} + + +inline +CanvasMessage::~CanvasMessage() +{ + if (fWriteIndex > 0) + Flush(); + free(fBuffer); +} + + +inline void +CanvasMessage::Start(uint16 code) +{ + if (fWriteIndex > 0) + Flush(); + + Add(code); + +// uint32 sizeDummy; +// Add(sizeDummy); +} + + +inline status_t +CanvasMessage::Flush() +{ + if (fWriteIndex == 0) + return B_NO_INIT; + + // end by the multipart boundary + static const char boundary[] = "--x\r\n\r\n"; + AddString(boundary, sizeof(boundary) - 1); + + uint32 length = fWriteIndex; + fAvailable += fWriteIndex; + fWriteIndex = 0; + +// memcpy(fBuffer + sizeof(uint16) * 2, &length, sizeof(uint32)); + return fTarget->Write(fBuffer, length); +} + + +template<typename T> +inline void +CanvasMessage::Add(const T& value) +{ + ssize_t done; + + if (!_MakeSpace(sizeof(T) * 2)) // 4/3 actually + return; + + //memcpy(fBuffer + fWriteIndex, &value, sizeof(T)); + done = encode_base64((char *)fBuffer + fWriteIndex, (const char *)(&value), + sizeof(T)); + fWriteIndex += done; + fAvailable -= done; +} + + +inline void +CanvasMessage::AddString(const char* string, size_t length) +{ + Add(length); + if (length > fAvailable && !_MakeSpace(length)) + return; + + memcpy(fBuffer + fWriteIndex, string, length); + fWriteIndex += length; + fAvailable -= length; +} + + +inline void +CanvasMessage::AddRegion(const BRegion& region) +{ + int32 rectCount = region.CountRects(); + Add(rectCount); + + for (int32 i = 0; i < rectCount; i++) + Add(region.RectAt(i)); +} + + +template<typename T> +inline void +CanvasMessage::AddList(const T* array, int32 count) +{ + for (int32 i = 0; i < count; i++) + Add(array[i]); +} + + +template<typename T> +inline status_t +CanvasMessage::Read(T& value) +{ + //TODO + if (fDataLeft < sizeof(T)) + return B_ERROR; + + int32 readSize = fSource->Read(&value, sizeof(T)); + if (readSize < 0) + return readSize; + + if (readSize != sizeof(T)) + return B_ERROR; + + fDataLeft -= sizeof(T); + return B_OK; +} + + +inline status_t +CanvasMessage::ReadRegion(BRegion& region) +{ + region.MakeEmpty(); + + int32 rectCount; + Read(rectCount); + + for (int32 i = 0; i < rectCount; i++) { + BRect rect; + status_t result = Read(rect); + if (result != B_OK) + return result; + + region.Include(rect); + } + + return B_OK; +} + + +template<typename T> +inline status_t +CanvasMessage::ReadList(T* array, int32 count) +{ + for (int32 i = 0; i < count; i++) { + status_t result = Read(array[i]); + if (result != B_OK) + return result; + } + + return B_OK; +} + + +inline bool +CanvasMessage::_MakeSpace(size_t size) +{ + if (fAvailable >= size) + return true; + + size_t extraSize = size + 20; + uint8 *newBuffer = (uint8*)realloc(fBuffer, fWriteIndex + extraSize); + if (newBuffer == NULL) + return false; + + fAvailable = extraSize; + fBuffer = newBuffer; + return true; +} + +#endif // CANVAS_MESSAGE_H diff --git a/src/servers/app/drawing/html5/HTML5DrawingEngine.cpp b/src/servers/app/drawing/html5/HTML5DrawingEngine.cpp new file mode 100644 index 0000000..f1434a3 --- /dev/null +++ b/src/servers/app/drawing/html5/HTML5DrawingEngine.cpp @@ -0,0 +1,1117 @@ +/* + * Copyright 2009-2010, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz <mmlr@xxxxxxxx> + * François Revol <revol@xxxxxxx> + */ + +#include "HTML5DrawingEngine.h" +#include "CanvasMessage.h" + +#include "BitmapDrawingEngine.h" +#include "DrawState.h" + +#include <Bitmap.h> +#include <utf8_functions.h> + +#include <new> + + +HTML5DrawingEngine::HTML5DrawingEngine(HTML5HWInterface* interface) + : + DrawingEngine(interface), + fHWInterface(interface), + fToken((uint32)this), // TODO: need to redo that for 64 bit + fExtendWidth(0), + fCallbackAdded(false), + fResultNotify(-1), + fStringWidthResult(0.0f), + fReadBitmapResult(NULL), + fBitmapDrawingEngine(NULL) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_CREATE_STATE); + message.Add(fToken); +} + + +HTML5DrawingEngine::~HTML5DrawingEngine() +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DELETE_STATE); + message.Add(fToken); + message.Flush(); + + delete fBitmapDrawingEngine; + + if (fCallbackAdded) + fHWInterface->RemoveCallback(fToken); + if (fResultNotify >= 0) + delete_sem(fResultNotify); +} + + +// #pragma mark - + + +void +HTML5DrawingEngine::FrameBufferChanged() +{ + // Not allowed +} + + +// #pragma mark - + + +void +HTML5DrawingEngine::SetCopyToFrontEnabled(bool enabled) +{ + DrawingEngine::SetCopyToFrontEnabled(enabled); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(enabled ? RP_ENABLE_SYNC_DRAWING : RP_DISABLE_SYNC_DRAWING); + message.Add(fToken); +} + + +// #pragma mark - + + +//! the HTML5DrawingEngine needs to be locked! +void +HTML5DrawingEngine::ConstrainClippingRegion(const BRegion* region) +{ + if (fClippingRegion == *region) + return; + + fClippingRegion = *region; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_CONSTRAIN_CLIPPING_REGION); + message.Add(fToken); + message.AddRegion(*region); +} + + +void +HTML5DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset, + int32 yOffset) +{ + SetPenSize(state->PenSize()); + SetDrawingMode(state->GetDrawingMode()); + SetBlendingMode(state->AlphaSrcMode(), state->AlphaFncMode()); + SetPattern(state->GetPattern().GetPattern()); + SetStrokeMode(state->LineCapMode(), state->LineJoinMode(), + state->MiterLimit()); + SetHighColor(state->HighColor()); + SetLowColor(state->LowColor()); + SetFont(state->Font()); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_OFFSETS); + message.Add(fToken); + message.Add(xOffset); + message.Add(yOffset); +} + + +void +HTML5DrawingEngine::SetHighColor(const rgb_color& color) +{ + if (fState.HighColor() == color) + return; + + fState.SetHighColor(color); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_HIGH_COLOR); + message.Add(fToken); + message.Add(color); +} + + +void +HTML5DrawingEngine::SetLowColor(const rgb_color& color) +{ + if (fState.LowColor() == color) + return; + + fState.SetLowColor(color); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_LOW_COLOR); + message.Add(fToken); + message.Add(color); +} + + +void +HTML5DrawingEngine::SetPenSize(float size) +{ + if (fState.PenSize() == size) + return; + + fState.SetPenSize(size); + fExtendWidth = -(size / 2); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_PEN_SIZE); + message.Add(fToken); + message.Add(size); +} + + +void +HTML5DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode, + float miterLimit) +{ + if (fState.LineCapMode() == lineCap && fState.LineJoinMode() == joinMode + && fState.MiterLimit() == miterLimit) + return; + + fState.SetLineCapMode(lineCap); + fState.SetLineJoinMode(joinMode); + fState.SetMiterLimit(miterLimit); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_STROKE_MODE); + message.Add(fToken); + message.Add(lineCap); + message.Add(joinMode); + message.Add(miterLimit); +} + + +void +HTML5DrawingEngine::SetBlendingMode(source_alpha sourceAlpha, + alpha_function alphaFunc) +{ + if (fState.AlphaSrcMode() == sourceAlpha + && fState.AlphaFncMode() == alphaFunc) + return; + + fState.SetBlendingMode(sourceAlpha, alphaFunc); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_BLENDING_MODE); + message.Add(fToken); + message.Add(sourceAlpha); + message.Add(alphaFunc); +} + + +void +HTML5DrawingEngine::SetPattern(const struct pattern& pattern) +{ + if (fState.GetPattern() == pattern) + return; + + fState.SetPattern(pattern); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_PATTERN); + message.Add(fToken); + message.Add(pattern); +} + + +void +HTML5DrawingEngine::SetDrawingMode(drawing_mode mode) +{ + if (fState.GetDrawingMode() == mode) + return; + + fState.SetDrawingMode(mode); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_DRAWING_MODE); + message.Add(fToken); + message.Add(mode); +} + + +void +HTML5DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode) +{ + oldMode = fState.GetDrawingMode(); + SetDrawingMode(mode); +} + + +void +HTML5DrawingEngine::SetFont(const ServerFont& font) +{ + if (fState.Font() == font) + return; + + fState.SetFont(font); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_FONT); + message.Add(fToken); + message.AddFont(font); +} + + +void +HTML5DrawingEngine::SetFont(const DrawState* state) +{ + SetFont(state->Font()); +} + + +// #pragma mark - + + +BRect +HTML5DrawingEngine::CopyRect(BRect rect, int32 xOffset, int32 yOffset) const +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_COPY_RECT_NO_CLIPPING); + message.Add(xOffset); + message.Add(yOffset); + message.Add(rect); + return rect.OffsetBySelf(xOffset, yOffset); +} + + +void +HTML5DrawingEngine::InvertRect(BRect rect) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_INVERT_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect, + const BRect& _viewRect, uint32 options) +{ + BRect bitmapRect = _bitmapRect; + BRect viewRect = _viewRect; + double xScale = (bitmapRect.Width() + 1) / (viewRect.Width() + 1); + double yScale = (bitmapRect.Height() + 1) / (viewRect.Height() + 1); + + // constrain rect to passed bitmap bounds + // and transfer the changes to the viewRect with the right scale + BRect actualBitmapRect = bitmap->Bounds(); + if (bitmapRect.left < actualBitmapRect.left) { + float diff = actualBitmapRect.left - bitmapRect.left; + viewRect.left += diff / xScale; + bitmapRect.left = actualBitmapRect.left; + } + if (bitmapRect.top < actualBitmapRect.top) { + float diff = actualBitmapRect.top - bitmapRect.top; + viewRect.top += diff / yScale; + bitmapRect.top = actualBitmapRect.top; + } + if (bitmapRect.right > actualBitmapRect.right) { + float diff = bitmapRect.right - actualBitmapRect.right; + viewRect.right -= diff / xScale; + bitmapRect.right = actualBitmapRect.right; + } + if (bitmapRect.bottom > actualBitmapRect.bottom) { + float diff = bitmapRect.bottom - actualBitmapRect.bottom; + viewRect.bottom -= diff / yScale; + bitmapRect.bottom = actualBitmapRect.bottom; + } + + BRegion clippedRegion(viewRect); + clippedRegion.IntersectWith(&fClippingRegion); + + int32 rectCount = clippedRegion.CountRects(); + if (rectCount == 0) + return; + + if (rectCount > 1 || (rectCount == 1 && clippedRegion.RectAt(0) != viewRect) + || viewRect.Width() < bitmapRect.Width() + || viewRect.Height() < bitmapRect.Height()) { + UtilityBitmap** bitmaps; + if (_ExtractBitmapRegions(*bitmap, options, bitmapRect, viewRect, + xScale, yScale, clippedRegion, bitmaps) != B_OK) { + return; + } + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DRAW_BITMAP_RECTS); + message.Add(fToken); + message.Add(options); + message.Add(bitmap->ColorSpace()); + message.Add(bitmap->Flags()); + message.Add(rectCount); + + for (int32 i = 0; i < rectCount; i++) { + message.Add(clippedRegion.RectAt(i)); + message.AddBitmap(*bitmaps[i], true); + delete bitmaps[i]; + } + + free(bitmaps); + return; + } + + // TODO: we may want to cache/checksum bitmaps + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DRAW_BITMAP); + message.Add(fToken); + message.Add(bitmapRect); + message.Add(viewRect); + message.Add(options); + message.AddBitmap(*bitmap); +} + + +void +HTML5DrawingEngine::DrawArc(BRect rect, const float& angle, const float& span, + bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ARC : RP_STROKE_ARC); + message.Add(fToken); + message.Add(rect); + message.Add(angle); + message.Add(span); +} + +void +HTML5DrawingEngine::FillArc(BRect rect, const float& angle, const float& span, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ARC_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.Add(angle); + message.Add(span); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawBezier(BPoint* points, bool filled) +{ + BRect bounds = _BuildBounds(points, 4); + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_BEZIER : RP_STROKE_BEZIER); + message.Add(fToken); + message.AddList(points, 4); +} + + +void +HTML5DrawingEngine::FillBezier(BPoint* points, const BGradient& gradient) +{ + BRect bounds = _BuildBounds(points, 4); + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_BEZIER_GRADIENT); + message.Add(fToken); + message.AddList(points, 4); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawEllipse(BRect rect, bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ELLIPSE : RP_STROKE_ELLIPSE); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::FillEllipse(BRect rect, const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ELLIPSE_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, bool filled, bool closed) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_POLYGON : RP_STROKE_POLYGON); + message.Add(fToken); + message.Add(bounds); + message.Add(closed); + message.Add(numPoints); + for (int32 i = 0; i < numPoints; i++) + message.Add(pointList[i]); +} + + +void +HTML5DrawingEngine::FillPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, const BGradient& gradient, bool closed) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_POLYGON_GRADIENT); + message.Add(fToken); + message.Add(bounds); + message.Add(closed); + message.Add(numPoints); + for (int32 i = 0; i < numPoints; i++) + message.Add(pointList[i]); + message.AddGradient(gradient); +} + + +// #pragma mark - rgb_color versions + + +void +HTML5DrawingEngine::StrokePoint(const BPoint& point, const rgb_color& color) +{ + BRect bounds(point, point); + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_POINT_COLOR); + message.Add(fToken); + message.Add(point); + message.Add(color); +} + + +void +HTML5DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end, + const rgb_color& color) +{ + BPoint points[2] = { start, end }; + BRect bounds = _BuildBounds(points, 2); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE_1PX_COLOR); + message.Add(fToken); + message.AddList(points, 2); + message.Add(color); +} + + +void +HTML5DrawingEngine::StrokeRect(BRect rect, const rgb_color &color) +{ + BRect bounds = rect; + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_RECT_1PX_COLOR); + message.Add(fToken); + message.Add(rect); + message.Add(color); +} + + +void +HTML5DrawingEngine::FillRect(BRect rect, const rgb_color& color) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT_COLOR); + message.Add(fToken); + message.Add(rect); + message.Add(color); +} + + +void +HTML5DrawingEngine::FillRegion(BRegion& region, const rgb_color& color) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION_COLOR_NO_CLIPPING); + message.AddRegion(region); + message.Add(color); +} + + +// #pragma mark - DrawState versions + + +void +HTML5DrawingEngine::StrokeRect(BRect rect) +{ + BRect bounds = rect; + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::FillRect(BRect rect) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::FillRect(BRect rect, const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::FillRegion(BRegion& region) +{ + BRegion clippedRegion = region; + clippedRegion.IntersectWith(&fClippingRegion); + if (clippedRegion.CountRects() == 0) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION); + message.Add(fToken); + message.AddRegion(clippedRegion.CountRects() < region.CountRects() + ? clippedRegion : region); +} + + +void +HTML5DrawingEngine::FillRegion(BRegion& region, const BGradient& gradient) +{ + BRegion clippedRegion = region; + clippedRegion.IntersectWith(&fClippingRegion); + if (clippedRegion.CountRects() == 0) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION_GRADIENT); + message.Add(fToken); + message.AddRegion(clippedRegion.CountRects() < region.CountRects() + ? clippedRegion : region); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawRoundRect(BRect rect, float xRadius, float yRadius, + bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ROUND_RECT : RP_STROKE_ROUND_RECT); + message.Add(fToken); + message.Add(rect); + message.Add(xRadius); + message.Add(yRadius); +} + + +void +HTML5DrawingEngine::FillRoundRect(BRect rect, float xRadius, float yRadius, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ROUND_RECT_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.Add(xRadius); + message.Add(yRadius); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, + const uint32* opList, int32 pointCount, const BPoint* pointList, + bool filled, const BPoint& viewToScreenOffset, float viewScale) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_SHAPE : RP_STROKE_SHAPE); + message.Add(fToken); + message.Add(bounds); + message.Add(opCount); + message.AddList(opList, opCount); + message.Add(pointCount); + message.AddList(pointList, pointCount); + message.Add(viewToScreenOffset); + message.Add(viewScale); +} + + +void +HTML5DrawingEngine::FillShape(const BRect& bounds, int32 opCount, + const uint32* opList, int32 pointCount, const BPoint* pointList, + const BGradient& gradient, const BPoint& viewToScreenOffset, + float viewScale) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_SHAPE_GRADIENT); + message.Add(fToken); + message.Add(bounds); + message.Add(opCount); + message.AddList(opList, opCount); + message.Add(pointCount); + message.AddList(pointList, pointCount); + message.AddGradient(gradient); + message.Add(viewToScreenOffset); + message.Add(viewScale); +} + + +void +HTML5DrawingEngine::DrawTriangle(BPoint* points, const BRect& bounds, + bool filled) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_TRIANGLE : RP_STROKE_TRIANGLE); + message.Add(fToken); + message.AddList(points, 3); + message.Add(bounds); +} + + +void +HTML5DrawingEngine::FillTriangle(BPoint* points, const BRect& bounds, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_TRIANGLE_GRADIENT); + message.Add(fToken); + message.Add(points[0]); + message.Add(points[1]); + message.Add(points[2]); + message.Add(bounds); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end) +{ + BPoint points[2] = { start, end }; + BRect bounds = _BuildBounds(points, 2); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE); + message.Add(fToken); + message.AddList(points, 2); +} + + +void +HTML5DrawingEngine::StrokeLineArray(int32 numLines, + const ViewLineArrayInfo *lineData) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE_ARRAY); + message.Add(fToken); + message.Add(numLines); + for (int32 i = 0; i < numLines; i++) + message.AddArrayLine(lineData[i]); +} + + +// #pragma mark - string functions + + +BPoint +HTML5DrawingEngine::DrawString(const char* string, int32 length, + const BPoint& point, escapement_delta* delta) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + + message.Start(RP_DRAW_STRING); + message.Add(fToken); + message.Add(point); + message.AddString(string, length); + message.Add(delta != NULL); + if (delta != NULL) + message.AddList(delta, length); + + status_t result = _AddCallback(); + if (message.Flush() != B_OK) + return point; + + if (result != B_OK) + return point; + + do { + result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, + 1 * 1000 * 1000); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return point; + + return fDrawStringResult; +} + + +BPoint +HTML5DrawingEngine::DrawString(const char* string, int32 length, + const BPoint* offsets) +{ + // Guaranteed to have at least one point. + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + + message.Start(RP_DRAW_STRING_WITH_OFFSETS); + message.Add(fToken); + message.AddString(string, length); + message.AddList(offsets, UTF8CountChars(string, length)); + + status_t result = _AddCallback(); + if (message.Flush() != B_OK) + return offsets[0]; + + if (result != B_OK) + return offsets[0]; + + do { + result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, + 1 * 1000 * 1000); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return offsets[0]; + + return fDrawStringResult; +} + + +float +HTML5DrawingEngine::StringWidth(const char* string, int32 length, + escapement_delta* delta) +{ + // TODO: decide if really needed and use callback if so + return fState.Font().StringWidth(string, length, delta); +} + + +// #pragma mark - + + +status_t +HTML5DrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor, + BRect bounds) +{ + if (_AddCallback() != B_OK) + return B_UNSUPPORTED; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + + message.Start(RP_READ_BITMAP); + message.Add(fToken); + message.Add(bounds); + message.Add(drawCursor); + if (message.Flush() != B_OK) + return B_UNSUPPORTED; + + status_t result; + do { + result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, + 100 * 1000 * 1000); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return result; + + BBitmap* read = fReadBitmapResult; + if (read == NULL) + return B_UNSUPPORTED; + + result = bitmap->ImportBits(read->Bits(), read->BitsLength(), + read->BytesPerRow(), read->ColorSpace()); + delete read; + return result; +} + + +// #pragma mark - + + +status_t +HTML5DrawingEngine::_AddCallback() +{ + if (fCallbackAdded) + return B_OK; + + if (fResultNotify < 0) + fResultNotify = create_sem(0, "drawing engine result"); + if (fResultNotify < 0) + return fResultNotify; + + status_t result = fHWInterface->AddCallback(fToken, &_DrawingEngineResult, + this); + + fCallbackAdded = result == B_OK; + return result; +} + + +bool +HTML5DrawingEngine::_DrawingEngineResult(void* cookie, CanvasMessage& message) +{ + HTML5DrawingEngine* engine = (HTML5DrawingEngine*)cookie; + + switch (message.Code()) { + case RP_DRAW_STRING_RESULT: + if (message.Read(engine->fDrawStringResult) != B_OK) + return false; + break; + + case RP_STRING_WIDTH_RESULT: + if (message.Read(engine->fStringWidthResult) != B_OK) + return false; + break; + + case RP_READ_BITMAP_RESULT: + if (message.ReadBitmap(&engine->fReadBitmapResult) != B_OK) + return false; + break; + + default: + return false; + } + + release_sem(engine->fResultNotify); + return true; +} + + +BRect +HTML5DrawingEngine::_BuildBounds(BPoint* points, int32 pointCount) +{ + BRect bounds(1000000, 1000000, 0, 0); + for (int32 i = 0; i < pointCount; i++) { + bounds.left = min_c(bounds.left, points[i].x); + bounds.top = min_c(bounds.top, points[i].y); + bounds.right = max_c(bounds.right, points[i].x); + bounds.bottom = max_c(bounds.bottom, points[i].y); + } + + return bounds; +} + + +status_t +HTML5DrawingEngine::_ExtractBitmapRegions(ServerBitmap& bitmap, uint32 options, + const BRect& bitmapRect, const BRect& viewRect, double xScale, + double yScale, BRegion& region, UtilityBitmap**& bitmaps) +{ + int32 rectCount = region.CountRects(); + bitmaps = (UtilityBitmap**)malloc(rectCount * sizeof(UtilityBitmap*)); + if (bitmaps == NULL) + return B_NO_MEMORY; + + for (int32 i = 0; i < rectCount; i++) { + BRect sourceRect = region.RectAt(i).OffsetByCopy(-viewRect.LeftTop()); + int32 targetWidth = (int32)(sourceRect.Width() + 1.5); + int32 targetHeight = (int32)(sourceRect.Height() + 1.5); + + if (xScale != 1.0) { + sourceRect.left = (int32)(sourceRect.left * xScale + 0.5); + sourceRect.right = (int32)(sourceRect.right * xScale + 0.5); + if (xScale < 1.0) + targetWidth = (int32)(sourceRect.Width() + 1.5); + } + + if (yScale != 1.0) { + sourceRect.top = (int32)(sourceRect.top * yScale + 0.5); + sourceRect.bottom = (int32)(sourceRect.bottom * yScale + 0.5); + if (yScale < 1.0) + targetHeight = (int32)(sourceRect.Height() + 1.5); + } + + sourceRect.OffsetBy(bitmapRect.LeftTop()); + // sourceRect is now the part of the bitmap we want copied + + status_t result = B_OK; + if ((xScale > 1.0 || yScale > 1.0) + && (targetWidth * targetHeight < (int32)(sourceRect.Width() + 1.5) + * (int32)(sourceRect.Height() + 1.5))) { + // the target bitmap is smaller than the source, scale it locally + // and send over the smaller version to avoid sending any extra data + if (fBitmapDrawingEngine == NULL) { + fBitmapDrawingEngine + = new(std::nothrow) BitmapDrawingEngine(B_RGBA32); + if (fBitmapDrawingEngine == NULL) + result = B_NO_MEMORY; + } + + if (result == B_OK) { + result = fBitmapDrawingEngine->SetSize(targetWidth, + targetHeight); + } + + if (result == B_OK) { + fBitmapDrawingEngine->SetDrawingMode(B_OP_COPY); + + switch (bitmap.ColorSpace()) { + case B_RGBA32: + case B_RGBA32_BIG: + case B_RGBA15: + case B_RGBA15_BIG: + break; + + default: + { + // we need to clear the background if there may be + // transparency through transparent magic (we use + // B_OP_COPY when we draw alpha enabled bitmaps, so we + // don't need to clear there) + // TODO: this is not actually correct, as we're going to + // loose the transparency with the conversion to the + // original non-alpha colorspace happening in + // ExportToBitmap + rgb_color background = { 0, 0, 0, 0 }; + fBitmapDrawingEngine->FillRect( + BRect(0, 0, targetWidth - 1, targetHeight -1), + background); + fBitmapDrawingEngine->SetDrawingMode(B_OP_OVER); + break; + } + } + + fBitmapDrawingEngine->DrawBitmap(&bitmap, sourceRect, + BRect(0, 0, targetWidth - 1, targetHeight - 1), options); + bitmaps[i] = fBitmapDrawingEngine->ExportToBitmap(targetWidth, + targetHeight, bitmap.ColorSpace()); + if (bitmaps[i] == NULL) + result = B_NO_MEMORY; + } + } else { + // source is smaller or equal target, extract the relevant rects + // directly without any scaling and conversion + targetWidth = (int32)(sourceRect.Width() + 1.5); + targetHeight = (int32)(sourceRect.Height() + 1.5); + + bitmaps[i] = new(std::nothrow) UtilityBitmap( + BRect(0, 0, targetWidth - 1, targetHeight - 1), + bitmap.ColorSpace(), 0); + if (bitmaps[i] == NULL) + result = B_NO_MEMORY; + + result = bitmaps[i]->ImportBits(bitmap.Bits(), bitmap.BitsLength(), + bitmap.BytesPerRow(), bitmap.ColorSpace(), sourceRect.LeftTop(), + BPoint(0, 0), targetWidth, targetHeight); + if (result != B_OK) + delete bitmaps[i]; + } + + if (result != B_OK) { + for (int32 j = 0; j < i; j++) + delete bitmaps[j]; + free(bitmaps); + return result; + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/html5/HTML5DrawingEngine.h b/src/servers/app/drawing/html5/HTML5DrawingEngine.h new file mode 100644 index 0000000..5a2c547 --- /dev/null +++ b/src/servers/app/drawing/html5/HTML5DrawingEngine.h @@ -0,0 +1,180 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz <mmlr@xxxxxxxx> + * François Revol <revol@xxxxxxx> + */ +#ifndef HTML5_DRAWING_ENGINE_H +#define HTML5_DRAWING_ENGINE_H + +#include "DrawingEngine.h" +#include "DrawState.h" +#include "HTML5HWInterface.h" +#include "ServerFont.h" + +class BPoint; +class BRect; +class BRegion; + +class BitmapDrawingEngine; +class ServerBitmap; + +class HTML5DrawingEngine : public DrawingEngine { +public: + HTML5DrawingEngine( + HTML5HWInterface* interface); + virtual ~HTML5DrawingEngine(); + + // HWInterfaceListener interface + virtual void FrameBufferChanged(); + + virtual void SetCopyToFrontEnabled(bool enabled); + + // for screen shots + virtual status_t ReadBitmap(ServerBitmap* bitmap, + bool drawCursor, BRect bounds); + + // clipping for all drawing functions, passing a NULL region + // will remove any clipping (drawing allowed everywhere) + virtual void ConstrainClippingRegion(const BRegion* region); + + virtual void SetDrawState(const DrawState* state, + int32 xOffset = 0, int32 yOffset = 0); + + virtual void SetHighColor(const rgb_color& color); + virtual void SetLowColor(const rgb_color& color); + virtual void SetPenSize(float size); + virtual void SetStrokeMode(cap_mode lineCap, + join_mode joinMode, float miterLimit); + virtual void SetPattern(const struct pattern& pattern); + virtual void SetDrawingMode(drawing_mode mode); + virtual void SetDrawingMode(drawing_mode mode, + drawing_mode& oldMode); + virtual void SetBlendingMode(source_alpha srcAlpha, + alpha_function alphaFunc); + virtual void SetFont(const ServerFont& font); + virtual void SetFont(const DrawState* state); + + // drawing functions + virtual void InvertRect(BRect rect); + + virtual void DrawBitmap(ServerBitmap* bitmap, + const BRect& bitmapRect, + const BRect& viewRect, uint32 options = 0); + + // drawing primitives + virtual void DrawArc(BRect rect, const float& angle, + const float& span, bool filled); + virtual void FillArc(BRect rect, const float& angle, + const float& span, + const BGradient& gradient); + + virtual void DrawBezier(BPoint* points, bool filled); + virtual void FillBezier(BPoint* points, + const BGradient& gradient); + + virtual void DrawEllipse(BRect rect, bool filled); + virtual void FillEllipse(BRect rect, + const BGradient& gradient); + + virtual void DrawPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, bool filled, bool closed); + virtual void FillPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, const BGradient& gradient, + bool closed); + + // these rgb_color versions are used internally by the server + virtual void StrokePoint(const BPoint& point, + const rgb_color& color); + virtual void StrokeRect(BRect rect, const rgb_color &color); + virtual void FillRect(BRect rect, const rgb_color &color); + virtual void FillRegion(BRegion& region, + const rgb_color& color); + + virtual void StrokeRect(BRect rect); + virtual void FillRect(BRect rect); + virtual void FillRect(BRect rect, const BGradient& gradient); + + virtual void FillRegion(BRegion& region); + virtual void FillRegion(BRegion& region, + const BGradient& gradient); + + virtual void DrawRoundRect(BRect rect, float xRadius, + float yRadius, bool filled); + virtual void FillRoundRect(BRect rect, float xRadius, + float yRadius, const BGradient& gradient); + + virtual void DrawShape(const BRect& bounds, + int32 opCount, const uint32* opList, + int32 pointCount, const BPoint* pointList, + bool filled, + const BPoint& viewToScreenOffset, + float viewScale); + virtual void FillShape(const BRect& bounds, + int32 opCount, const uint32* opList, + int32 pointCount, const BPoint* pointList, + const BGradient& gradient, + const BPoint& viewToScreenOffset, + float viewScale); + + virtual void DrawTriangle(BPoint* points, + const BRect& bounds, bool filled); + virtual void FillTriangle(BPoint* points, + const BRect& bounds, + const BGradient& gradient); + + // these versions are used by the Decorator + virtual void StrokeLine(const BPoint& start, + const BPoint& end, const rgb_color& color); + virtual void StrokeLine(const BPoint& start, + const BPoint& end); + virtual void StrokeLineArray(int32 numlines, + const ViewLineArrayInfo* data); + + // returns the pen position behind the (virtually) drawn string + virtual BPoint DrawString(const char* string, int32 length, + const BPoint& point, + escapement_delta* delta = NULL); +virtual BPoint DrawString(const char* string, int32 length, + const BPoint* offsets); + + virtual float StringWidth(const char* string, int32 length, + escapement_delta* delta = NULL); + + // software rendering backend invoked by CopyRegion() for the sorted + // individual rects + virtual BRect CopyRect(BRect rect, int32 xOffset, + int32 yOffset) const; + +private: + status_t _AddCallback(); + + static bool _DrawingEngineResult(void* cookie, + CanvasMessage& message); + + BRect _BuildBounds(BPoint* points, int32 pointCount); + status_t _ExtractBitmapRegions(ServerBitmap& bitmap, + uint32 options, const BRect& bitmapRect, + const BRect& viewRect, double xScale, + double yScale, BRegion& region, [ *** diff truncated: 2166 lines dropped *** ]