1/*
2 * Copyright 2009-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Lotz <mmlr@mlotz.ch>
7 *		Fran��ois Revol <revol@free.fr>
8 */
9
10#include "HTML5DrawingEngine.h"
11#include "CanvasMessage.h"
12
13#include "BitmapDrawingEngine.h"
14#include "DrawState.h"
15
16#include <Bitmap.h>
17#include <utf8_functions.h>
18
19#include <new>
20
21
22HTML5DrawingEngine::HTML5DrawingEngine(HTML5HWInterface* interface)
23	:
24	DrawingEngine(interface),
25	fHWInterface(interface),
26	fToken((uint32)this), // TODO: need to redo that for 64 bit
27	fExtendWidth(0),
28	fCallbackAdded(false),
29	fResultNotify(-1),
30	fStringWidthResult(0.0f),
31	fReadBitmapResult(NULL),
32	fBitmapDrawingEngine(NULL)
33{
34	CanvasMessage message(NULL, fHWInterface->SendBuffer());
35	message.Start(RP_CREATE_STATE);
36	message.Add(fToken);
37}
38
39
40HTML5DrawingEngine::~HTML5DrawingEngine()
41{
42	CanvasMessage message(NULL, fHWInterface->SendBuffer());
43	message.Start(RP_DELETE_STATE);
44	message.Add(fToken);
45	message.Flush();
46
47	delete fBitmapDrawingEngine;
48
49	if (fCallbackAdded)
50		fHWInterface->RemoveCallback(fToken);
51	if (fResultNotify >= 0)
52		delete_sem(fResultNotify);
53}
54
55
56// #pragma mark -
57
58
59void
60HTML5DrawingEngine::FrameBufferChanged()
61{
62	// Not allowed
63}
64
65
66// #pragma mark -
67
68
69void
70HTML5DrawingEngine::SetCopyToFrontEnabled(bool enabled)
71{
72	DrawingEngine::SetCopyToFrontEnabled(enabled);
73
74	CanvasMessage message(NULL, fHWInterface->SendBuffer());
75	message.Start(enabled ? RP_ENABLE_SYNC_DRAWING : RP_DISABLE_SYNC_DRAWING);
76	message.Add(fToken);
77}
78
79
80// #pragma mark -
81
82
83//! the HTML5DrawingEngine needs to be locked!
84void
85HTML5DrawingEngine::ConstrainClippingRegion(const BRegion* region)
86{
87	if (fClippingRegion == *region)
88		return;
89
90	fClippingRegion = *region;
91
92	CanvasMessage message(NULL, fHWInterface->SendBuffer());
93	message.Start(RP_CONSTRAIN_CLIPPING_REGION);
94	message.Add(fToken);
95	message.AddRegion(*region);
96}
97
98
99void
100HTML5DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset,
101	int32 yOffset)
102{
103	SetPenSize(state->PenSize());
104	SetDrawingMode(state->GetDrawingMode());
105	SetBlendingMode(state->AlphaSrcMode(), state->AlphaFncMode());
106	SetPattern(state->GetPattern().GetPattern());
107	SetStrokeMode(state->LineCapMode(), state->LineJoinMode(),
108		state->MiterLimit());
109	SetHighColor(state->HighColor());
110	SetLowColor(state->LowColor());
111	SetFont(state->Font());
112
113	CanvasMessage message(NULL, fHWInterface->SendBuffer());
114	message.Start(RP_SET_OFFSETS);
115	message.Add(fToken);
116	message.Add(xOffset);
117	message.Add(yOffset);
118}
119
120
121void
122HTML5DrawingEngine::SetHighColor(const rgb_color& color)
123{
124	if (fState.HighColor() == color)
125		return;
126
127	fState.SetHighColor(color);
128
129	CanvasMessage message(NULL, fHWInterface->SendBuffer());
130	message.Start(RP_SET_HIGH_COLOR);
131	message.Add(fToken);
132	message.Add(color);
133}
134
135
136void
137HTML5DrawingEngine::SetLowColor(const rgb_color& color)
138{
139	if (fState.LowColor() == color)
140		return;
141
142	fState.SetLowColor(color);
143
144	CanvasMessage message(NULL, fHWInterface->SendBuffer());
145	message.Start(RP_SET_LOW_COLOR);
146	message.Add(fToken);
147	message.Add(color);
148}
149
150
151void
152HTML5DrawingEngine::SetPenSize(float size)
153{
154	if (fState.PenSize() == size)
155		return;
156
157	fState.SetPenSize(size);
158	fExtendWidth = -(size / 2);
159
160	CanvasMessage message(NULL, fHWInterface->SendBuffer());
161	message.Start(RP_SET_PEN_SIZE);
162	message.Add(fToken);
163	message.Add(size);
164}
165
166
167void
168HTML5DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode,
169	float miterLimit)
170{
171	if (fState.LineCapMode() == lineCap && fState.LineJoinMode() == joinMode
172		&& fState.MiterLimit() == miterLimit)
173		return;
174
175	fState.SetLineCapMode(lineCap);
176	fState.SetLineJoinMode(joinMode);
177	fState.SetMiterLimit(miterLimit);
178
179	CanvasMessage message(NULL, fHWInterface->SendBuffer());
180	message.Start(RP_SET_STROKE_MODE);
181	message.Add(fToken);
182	message.Add(lineCap);
183	message.Add(joinMode);
184	message.Add(miterLimit);
185}
186
187
188void
189HTML5DrawingEngine::SetBlendingMode(source_alpha sourceAlpha,
190	alpha_function alphaFunc)
191{
192	if (fState.AlphaSrcMode() == sourceAlpha
193		&& fState.AlphaFncMode() == alphaFunc)
194		return;
195
196	fState.SetBlendingMode(sourceAlpha, alphaFunc);
197
198	CanvasMessage message(NULL, fHWInterface->SendBuffer());
199	message.Start(RP_SET_BLENDING_MODE);
200	message.Add(fToken);
201	message.Add(sourceAlpha);
202	message.Add(alphaFunc);
203}
204
205
206void
207HTML5DrawingEngine::SetPattern(const struct pattern& pattern)
208{
209	if (fState.GetPattern() == pattern)
210		return;
211
212	fState.SetPattern(pattern);
213
214	CanvasMessage message(NULL, fHWInterface->SendBuffer());
215	message.Start(RP_SET_PATTERN);
216	message.Add(fToken);
217	message.Add(pattern);
218}
219
220
221void
222HTML5DrawingEngine::SetDrawingMode(drawing_mode mode)
223{
224	if (fState.GetDrawingMode() == mode)
225		return;
226
227	fState.SetDrawingMode(mode);
228
229	CanvasMessage message(NULL, fHWInterface->SendBuffer());
230	message.Start(RP_SET_DRAWING_MODE);
231	message.Add(fToken);
232	message.Add(mode);
233}
234
235
236void
237HTML5DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode)
238{
239	oldMode = fState.GetDrawingMode();
240	SetDrawingMode(mode);
241}
242
243
244void
245HTML5DrawingEngine::SetFont(const ServerFont& font)
246{
247	if (fState.Font() == font)
248		return;
249
250	fState.SetFont(font);
251
252	CanvasMessage message(NULL, fHWInterface->SendBuffer());
253	message.Start(RP_SET_FONT);
254	message.Add(fToken);
255	message.AddFont(font);
256}
257
258
259void
260HTML5DrawingEngine::SetFont(const DrawState* state)
261{
262	SetFont(state->Font());
263}
264
265
266// #pragma mark -
267
268
269BRect
270HTML5DrawingEngine::CopyRect(BRect rect, int32 xOffset, int32 yOffset) const
271{
272	CanvasMessage message(NULL, fHWInterface->SendBuffer());
273	message.Start(RP_COPY_RECT_NO_CLIPPING);
274	message.Add(xOffset);
275	message.Add(yOffset);
276	message.Add(rect);
277	return rect.OffsetBySelf(xOffset, yOffset);
278}
279
280
281void
282HTML5DrawingEngine::InvertRect(BRect rect)
283{
284	if (!fClippingRegion.Intersects(rect))
285		return;
286
287	CanvasMessage message(NULL, fHWInterface->SendBuffer());
288	message.Start(RP_INVERT_RECT);
289	message.Add(fToken);
290	message.Add(rect);
291}
292
293
294void
295HTML5DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect,
296	const BRect& _viewRect, uint32 options)
297{
298	BRect bitmapRect = _bitmapRect;
299	BRect viewRect = _viewRect;
300	double xScale = (bitmapRect.Width() + 1) / (viewRect.Width() + 1);
301	double yScale = (bitmapRect.Height() + 1) / (viewRect.Height() + 1);
302
303	// constrain rect to passed bitmap bounds
304	// and transfer the changes to the viewRect with the right scale
305	BRect actualBitmapRect = bitmap->Bounds();
306	if (bitmapRect.left < actualBitmapRect.left) {
307		float diff = actualBitmapRect.left - bitmapRect.left;
308		viewRect.left += diff / xScale;
309		bitmapRect.left = actualBitmapRect.left;
310	}
311	if (bitmapRect.top < actualBitmapRect.top) {
312		float diff = actualBitmapRect.top - bitmapRect.top;
313		viewRect.top += diff / yScale;
314		bitmapRect.top = actualBitmapRect.top;
315	}
316	if (bitmapRect.right > actualBitmapRect.right) {
317		float diff = bitmapRect.right - actualBitmapRect.right;
318		viewRect.right -= diff / xScale;
319		bitmapRect.right = actualBitmapRect.right;
320	}
321	if (bitmapRect.bottom > actualBitmapRect.bottom) {
322		float diff = bitmapRect.bottom - actualBitmapRect.bottom;
323		viewRect.bottom -= diff / yScale;
324		bitmapRect.bottom = actualBitmapRect.bottom;
325	}
326
327	BRegion clippedRegion(viewRect);
328	clippedRegion.IntersectWith(&fClippingRegion);
329
330	int32 rectCount = clippedRegion.CountRects();
331	if (rectCount == 0)
332		return;
333
334	if (rectCount > 1 || (rectCount == 1 && clippedRegion.RectAt(0) != viewRect)
335		|| viewRect.Width() < bitmapRect.Width()
336		|| viewRect.Height() < bitmapRect.Height()) {
337		UtilityBitmap** bitmaps;
338		if (_ExtractBitmapRegions(*bitmap, options, bitmapRect, viewRect,
339				xScale, yScale, clippedRegion, bitmaps) != B_OK) {
340			return;
341		}
342
343		CanvasMessage message(NULL, fHWInterface->SendBuffer());
344		message.Start(RP_DRAW_BITMAP_RECTS);
345		message.Add(fToken);
346		message.Add(options);
347		message.Add(bitmap->ColorSpace());
348		message.Add(bitmap->Flags());
349		message.Add(rectCount);
350
351		for (int32 i = 0; i < rectCount; i++) {
352			message.Add(clippedRegion.RectAt(i));
353			message.AddBitmap(*bitmaps[i], true);
354			delete bitmaps[i];
355		}
356
357		free(bitmaps);
358		return;
359	}
360
361	// TODO: we may want to cache/checksum bitmaps
362	CanvasMessage message(NULL, fHWInterface->SendBuffer());
363	message.Start(RP_DRAW_BITMAP);
364	message.Add(fToken);
365	message.Add(bitmapRect);
366	message.Add(viewRect);
367	message.Add(options);
368	message.AddBitmap(*bitmap);
369}
370
371
372void
373HTML5DrawingEngine::DrawArc(BRect rect, const float& angle, const float& span,
374	bool filled)
375{
376	BRect bounds = rect;
377	if (!filled)
378		bounds.InsetBy(fExtendWidth, fExtendWidth);
379
380	if (!fClippingRegion.Intersects(bounds))
381		return;
382
383	CanvasMessage message(NULL, fHWInterface->SendBuffer());
384	message.Start(filled ? RP_FILL_ARC : RP_STROKE_ARC);
385	message.Add(fToken);
386	message.Add(rect);
387	message.Add(angle);
388	message.Add(span);
389}
390
391void
392HTML5DrawingEngine::FillArc(BRect rect, const float& angle, const float& span,
393	const BGradient& gradient)
394{
395	if (!fClippingRegion.Intersects(rect))
396		return;
397
398	CanvasMessage message(NULL, fHWInterface->SendBuffer());
399	message.Start(RP_FILL_ARC_GRADIENT);
400	message.Add(fToken);
401	message.Add(rect);
402	message.Add(angle);
403	message.Add(span);
404	message.AddGradient(gradient);
405}
406
407
408void
409HTML5DrawingEngine::DrawBezier(BPoint* points, bool filled)
410{
411	BRect bounds = _BuildBounds(points, 4);
412	if (!filled)
413		bounds.InsetBy(fExtendWidth, fExtendWidth);
414
415	if (!fClippingRegion.Intersects(bounds))
416		return;
417
418	CanvasMessage message(NULL, fHWInterface->SendBuffer());
419	message.Start(filled ? RP_FILL_BEZIER : RP_STROKE_BEZIER);
420	message.Add(fToken);
421	message.AddList(points, 4);
422}
423
424
425void
426HTML5DrawingEngine::FillBezier(BPoint* points, const BGradient& gradient)
427{
428	BRect bounds = _BuildBounds(points, 4);
429	if (!fClippingRegion.Intersects(bounds))
430		return;
431
432	CanvasMessage message(NULL, fHWInterface->SendBuffer());
433	message.Start(RP_FILL_BEZIER_GRADIENT);
434	message.Add(fToken);
435	message.AddList(points, 4);
436	message.AddGradient(gradient);
437}
438
439
440void
441HTML5DrawingEngine::DrawEllipse(BRect rect, bool filled)
442{
443	BRect bounds = rect;
444	if (!filled)
445		bounds.InsetBy(fExtendWidth, fExtendWidth);
446
447	if (!fClippingRegion.Intersects(bounds))
448		return;
449
450	CanvasMessage message(NULL, fHWInterface->SendBuffer());
451	message.Start(filled ? RP_FILL_ELLIPSE : RP_STROKE_ELLIPSE);
452	message.Add(fToken);
453	message.Add(rect);
454}
455
456
457void
458HTML5DrawingEngine::FillEllipse(BRect rect, const BGradient& gradient)
459{
460	if (!fClippingRegion.Intersects(rect))
461		return;
462
463	CanvasMessage message(NULL, fHWInterface->SendBuffer());
464	message.Start(RP_FILL_ELLIPSE_GRADIENT);
465	message.Add(fToken);
466	message.Add(rect);
467	message.AddGradient(gradient);
468}
469
470
471void
472HTML5DrawingEngine::DrawPolygon(BPoint* pointList, int32 numPoints,
473	BRect bounds, bool filled, bool closed)
474{
475	BRect clipBounds = bounds;
476	if (!filled)
477		clipBounds.InsetBy(fExtendWidth, fExtendWidth);
478
479	if (!fClippingRegion.Intersects(clipBounds))
480		return;
481
482	CanvasMessage message(NULL, fHWInterface->SendBuffer());
483	message.Start(filled ? RP_FILL_POLYGON : RP_STROKE_POLYGON);
484	message.Add(fToken);
485	message.Add(bounds);
486	message.Add(closed);
487	message.Add(numPoints);
488	for (int32 i = 0; i < numPoints; i++)
489		message.Add(pointList[i]);
490}
491
492
493void
494HTML5DrawingEngine::FillPolygon(BPoint* pointList, int32 numPoints,
495	BRect bounds, const BGradient& gradient, bool closed)
496{
497	if (!fClippingRegion.Intersects(bounds))
498		return;
499
500	CanvasMessage message(NULL, fHWInterface->SendBuffer());
501	message.Start(RP_FILL_POLYGON_GRADIENT);
502	message.Add(fToken);
503	message.Add(bounds);
504	message.Add(closed);
505	message.Add(numPoints);
506	for (int32 i = 0; i < numPoints; i++)
507		message.Add(pointList[i]);
508	message.AddGradient(gradient);
509}
510
511
512// #pragma mark - rgb_color versions
513
514
515void
516HTML5DrawingEngine::StrokePoint(const BPoint& point, const rgb_color& color)
517{
518	BRect bounds(point, point);
519	bounds.InsetBy(fExtendWidth, fExtendWidth);
520
521	if (!fClippingRegion.Intersects(bounds))
522		return;
523
524	CanvasMessage message(NULL, fHWInterface->SendBuffer());
525	message.Start(RP_STROKE_POINT_COLOR);
526	message.Add(fToken);
527	message.Add(point);
528	message.Add(color);
529}
530
531
532void
533HTML5DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end,
534	const rgb_color& color)
535{
536	BPoint points[2] = { start, end };
537	BRect bounds = _BuildBounds(points, 2);
538
539	if (!fClippingRegion.Intersects(bounds))
540		return;
541
542	CanvasMessage message(NULL, fHWInterface->SendBuffer());
543	message.Start(RP_STROKE_LINE_1PX_COLOR);
544	message.Add(fToken);
545	message.AddList(points, 2);
546	message.Add(color);
547}
548
549
550void
551HTML5DrawingEngine::StrokeRect(BRect rect, const rgb_color &color)
552{
553	BRect bounds = rect;
554	bounds.InsetBy(fExtendWidth, fExtendWidth);
555
556	if (!fClippingRegion.Intersects(bounds))
557		return;
558
559	CanvasMessage message(NULL, fHWInterface->SendBuffer());
560	message.Start(RP_STROKE_RECT_1PX_COLOR);
561	message.Add(fToken);
562	message.Add(rect);
563	message.Add(color);
564}
565
566
567void
568HTML5DrawingEngine::FillRect(BRect rect, const rgb_color& color)
569{
570	if (!fClippingRegion.Intersects(rect))
571		return;
572
573	CanvasMessage message(NULL, fHWInterface->SendBuffer());
574	message.Start(RP_FILL_RECT_COLOR);
575	message.Add(fToken);
576	message.Add(rect);
577	message.Add(color);
578}
579
580
581void
582HTML5DrawingEngine::FillRegion(BRegion& region, const rgb_color& color)
583{
584	CanvasMessage message(NULL, fHWInterface->SendBuffer());
585	message.Start(RP_FILL_REGION_COLOR_NO_CLIPPING);
586	message.AddRegion(region);
587	message.Add(color);
588}
589
590
591// #pragma mark - DrawState versions
592
593
594void
595HTML5DrawingEngine::StrokeRect(BRect rect)
596{
597	BRect bounds = rect;
598	bounds.InsetBy(fExtendWidth, fExtendWidth);
599
600	if (!fClippingRegion.Intersects(bounds))
601		return;
602
603	CanvasMessage message(NULL, fHWInterface->SendBuffer());
604	message.Start(RP_STROKE_RECT);
605	message.Add(fToken);
606	message.Add(rect);
607}
608
609
610void
611HTML5DrawingEngine::FillRect(BRect rect)
612{
613	if (!fClippingRegion.Intersects(rect))
614		return;
615
616	CanvasMessage message(NULL, fHWInterface->SendBuffer());
617	message.Start(RP_FILL_RECT);
618	message.Add(fToken);
619	message.Add(rect);
620}
621
622
623void
624HTML5DrawingEngine::FillRect(BRect rect, const BGradient& gradient)
625{
626	if (!fClippingRegion.Intersects(rect))
627		return;
628
629	CanvasMessage message(NULL, fHWInterface->SendBuffer());
630	message.Start(RP_FILL_RECT_GRADIENT);
631	message.Add(fToken);
632	message.Add(rect);
633	message.AddGradient(gradient);
634}
635
636
637void
638HTML5DrawingEngine::FillRegion(BRegion& region)
639{
640	BRegion clippedRegion = region;
641	clippedRegion.IntersectWith(&fClippingRegion);
642	if (clippedRegion.CountRects() == 0)
643		return;
644
645	CanvasMessage message(NULL, fHWInterface->SendBuffer());
646	message.Start(RP_FILL_REGION);
647	message.Add(fToken);
648	message.AddRegion(clippedRegion.CountRects() < region.CountRects()
649		? clippedRegion : region);
650}
651
652
653void
654HTML5DrawingEngine::FillRegion(BRegion& region, const BGradient& gradient)
655{
656	BRegion clippedRegion = region;
657	clippedRegion.IntersectWith(&fClippingRegion);
658	if (clippedRegion.CountRects() == 0)
659		return;
660
661	CanvasMessage message(NULL, fHWInterface->SendBuffer());
662	message.Start(RP_FILL_REGION_GRADIENT);
663	message.Add(fToken);
664	message.AddRegion(clippedRegion.CountRects() < region.CountRects()
665		? clippedRegion : region);
666	message.AddGradient(gradient);
667}
668
669
670void
671HTML5DrawingEngine::DrawRoundRect(BRect rect, float xRadius, float yRadius,
672	bool filled)
673{
674	BRect bounds = rect;
675	if (!filled)
676		bounds.InsetBy(fExtendWidth, fExtendWidth);
677
678	if (!fClippingRegion.Intersects(bounds))
679		return;
680
681	CanvasMessage message(NULL, fHWInterface->SendBuffer());
682	message.Start(filled ? RP_FILL_ROUND_RECT : RP_STROKE_ROUND_RECT);
683	message.Add(fToken);
684	message.Add(rect);
685	message.Add(xRadius);
686	message.Add(yRadius);
687}
688
689
690void
691HTML5DrawingEngine::FillRoundRect(BRect rect, float xRadius, float yRadius,
692	const BGradient& gradient)
693{
694	if (!fClippingRegion.Intersects(rect))
695		return;
696
697	CanvasMessage message(NULL, fHWInterface->SendBuffer());
698	message.Start(RP_FILL_ROUND_RECT_GRADIENT);
699	message.Add(fToken);
700	message.Add(rect);
701	message.Add(xRadius);
702	message.Add(yRadius);
703	message.AddGradient(gradient);
704}
705
706
707void
708HTML5DrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
709	const uint32* opList, int32 pointCount, const BPoint* pointList,
710	bool filled, const BPoint& viewToScreenOffset, float viewScale)
711{
712	BRect clipBounds = bounds;
713	if (!filled)
714		clipBounds.InsetBy(fExtendWidth, fExtendWidth);
715
716	if (!fClippingRegion.Intersects(clipBounds))
717		return;
718
719	CanvasMessage message(NULL, fHWInterface->SendBuffer());
720	message.Start(filled ? RP_FILL_SHAPE : RP_STROKE_SHAPE);
721	message.Add(fToken);
722	message.Add(bounds);
723	message.Add(opCount);
724	message.AddList(opList, opCount);
725	message.Add(pointCount);
726	message.AddList(pointList, pointCount);
727	message.Add(viewToScreenOffset);
728	message.Add(viewScale);
729}
730
731
732void
733HTML5DrawingEngine::FillShape(const BRect& bounds, int32 opCount,
734	const uint32* opList, int32 pointCount, const BPoint* pointList,
735	const BGradient& gradient, const BPoint& viewToScreenOffset,
736	float viewScale)
737{
738	if (!fClippingRegion.Intersects(bounds))
739		return;
740
741	CanvasMessage message(NULL, fHWInterface->SendBuffer());
742	message.Start(RP_FILL_SHAPE_GRADIENT);
743	message.Add(fToken);
744	message.Add(bounds);
745	message.Add(opCount);
746	message.AddList(opList, opCount);
747	message.Add(pointCount);
748	message.AddList(pointList, pointCount);
749	message.AddGradient(gradient);
750	message.Add(viewToScreenOffset);
751	message.Add(viewScale);
752}
753
754
755void
756HTML5DrawingEngine::DrawTriangle(BPoint* points, const BRect& bounds,
757	bool filled)
758{
759	BRect clipBounds = bounds;
760	if (!filled)
761		clipBounds.InsetBy(fExtendWidth, fExtendWidth);
762
763	if (!fClippingRegion.Intersects(clipBounds))
764		return;
765
766	CanvasMessage message(NULL, fHWInterface->SendBuffer());
767	message.Start(filled ? RP_FILL_TRIANGLE : RP_STROKE_TRIANGLE);
768	message.Add(fToken);
769	message.AddList(points, 3);
770	message.Add(bounds);
771}
772
773
774void
775HTML5DrawingEngine::FillTriangle(BPoint* points, const BRect& bounds,
776	const BGradient& gradient)
777{
778	if (!fClippingRegion.Intersects(bounds))
779		return;
780
781	CanvasMessage message(NULL, fHWInterface->SendBuffer());
782	message.Start(RP_FILL_TRIANGLE_GRADIENT);
783	message.Add(fToken);
784	message.Add(points[0]);
785	message.Add(points[1]);
786	message.Add(points[2]);
787	message.Add(bounds);
788	message.AddGradient(gradient);
789}
790
791
792void
793HTML5DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end)
794{
795	BPoint points[2] = { start, end };
796	BRect bounds = _BuildBounds(points, 2);
797
798	if (!fClippingRegion.Intersects(bounds))
799		return;
800
801	CanvasMessage message(NULL, fHWInterface->SendBuffer());
802	message.Start(RP_STROKE_LINE);
803	message.Add(fToken);
804	message.AddList(points, 2);
805}
806
807
808void
809HTML5DrawingEngine::StrokeLineArray(int32 numLines,
810	const ViewLineArrayInfo *lineData)
811{
812	CanvasMessage message(NULL, fHWInterface->SendBuffer());
813	message.Start(RP_STROKE_LINE_ARRAY);
814	message.Add(fToken);
815	message.Add(numLines);
816	for (int32 i = 0; i < numLines; i++)
817		message.AddArrayLine(lineData[i]);
818}
819
820
821// #pragma mark - string functions
822
823
824BPoint
825HTML5DrawingEngine::DrawString(const char* string, int32 length,
826	const BPoint& point, escapement_delta* delta)
827{
828	CanvasMessage message(NULL, fHWInterface->SendBuffer());
829
830	message.Start(RP_DRAW_STRING);
831	message.Add(fToken);
832	message.Add(point);
833	message.AddString(string, length);
834	message.Add(delta != NULL);
835	if (delta != NULL)
836		message.AddList(delta, length);
837
838	status_t result = _AddCallback();
839	if (message.Flush() != B_OK)
840		return point;
841
842	if (result != B_OK)
843		return point;
844
845	do {
846		result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
847			1 * 1000 * 1000);
848	} while (result == B_INTERRUPTED);
849
850	if (result != B_OK)
851		return point;
852
853	return fDrawStringResult;
854}
855
856
857BPoint
858HTML5DrawingEngine::DrawString(const char* string, int32 length,
859	const BPoint* offsets)
860{
861	// Guaranteed to have at least one point.
862	CanvasMessage message(NULL, fHWInterface->SendBuffer());
863
864	message.Start(RP_DRAW_STRING_WITH_OFFSETS);
865	message.Add(fToken);
866	message.AddString(string, length);
867	message.AddList(offsets, UTF8CountChars(string, length));
868
869	status_t result = _AddCallback();
870	if (message.Flush() != B_OK)
871		return offsets[0];
872
873	if (result != B_OK)
874		return offsets[0];
875
876	do {
877		result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
878			1 * 1000 * 1000);
879	} while (result == B_INTERRUPTED);
880
881	if (result != B_OK)
882		return offsets[0];
883
884	return fDrawStringResult;
885}
886
887
888float
889HTML5DrawingEngine::StringWidth(const char* string, int32 length,
890	escapement_delta* delta)
891{
892	// TODO: decide if really needed and use callback if so
893	return fState.Font().StringWidth(string, length, delta);
894}
895
896
897// #pragma mark -
898
899
900status_t
901HTML5DrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor,
902	BRect bounds)
903{
904	if (_AddCallback() != B_OK)
905		return B_UNSUPPORTED;
906
907	CanvasMessage message(NULL, fHWInterface->SendBuffer());
908
909	message.Start(RP_READ_BITMAP);
910	message.Add(fToken);
911	message.Add(bounds);
912	message.Add(drawCursor);
913	if (message.Flush() != B_OK)
914		return B_UNSUPPORTED;
915
916	status_t result;
917	do {
918		result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
919			100 * 1000 * 1000);
920	} while (result == B_INTERRUPTED);
921
922	if (result != B_OK)
923		return result;
924
925	BBitmap* read = fReadBitmapResult;
926	if (read == NULL)
927		return B_UNSUPPORTED;
928
929	result = bitmap->ImportBits(read->Bits(), read->BitsLength(),
930		read->BytesPerRow(), read->ColorSpace());
931	delete read;
932	return result;
933}
934
935
936// #pragma mark -
937
938
939status_t
940HTML5DrawingEngine::_AddCallback()
941{
942	if (fCallbackAdded)
943		return B_OK;
944
945	if (fResultNotify < 0)
946		fResultNotify = create_sem(0, "drawing engine result");
947	if (fResultNotify < 0)
948		return fResultNotify;
949
950	status_t result = fHWInterface->AddCallback(fToken, &_DrawingEngineResult,
951		this);
952
953	fCallbackAdded = result == B_OK;
954	return result;
955}
956
957
958bool
959HTML5DrawingEngine::_DrawingEngineResult(void* cookie, CanvasMessage& message)
960{
961	HTML5DrawingEngine* engine = (HTML5DrawingEngine*)cookie;
962
963	switch (message.Code()) {
964		case RP_DRAW_STRING_RESULT:
965			if (message.Read(engine->fDrawStringResult) != B_OK)
966				return false;
967			break;
968
969		case RP_STRING_WIDTH_RESULT:
970			if (message.Read(engine->fStringWidthResult) != B_OK)
971				return false;
972			break;
973
974		case RP_READ_BITMAP_RESULT:
975			if (message.ReadBitmap(&engine->fReadBitmapResult) != B_OK)
976				return false;
977			break;
978
979		default:
980			return false;
981	}
982
983	release_sem(engine->fResultNotify);
984	return true;
985}
986
987
988BRect
989HTML5DrawingEngine::_BuildBounds(BPoint* points, int32 pointCount)
990{
991	BRect bounds(1000000, 1000000, 0, 0);
992	for (int32 i = 0; i < pointCount; i++) {
993		bounds.left = min_c(bounds.left, points[i].x);
994		bounds.top = min_c(bounds.top, points[i].y);
995		bounds.right = max_c(bounds.right, points[i].x);
996		bounds.bottom = max_c(bounds.bottom, points[i].y);
997	}
998
999	return bounds;
1000}
1001
1002
1003status_t
1004HTML5DrawingEngine::_ExtractBitmapRegions(ServerBitmap& bitmap, uint32 options,
1005	const BRect& bitmapRect, const BRect& viewRect, double xScale,
1006	double yScale, BRegion& region, UtilityBitmap**& bitmaps)
1007{
1008	int32 rectCount = region.CountRects();
1009	bitmaps = (UtilityBitmap**)malloc(rectCount * sizeof(UtilityBitmap*));
1010	if (bitmaps == NULL)
1011		return B_NO_MEMORY;
1012
1013	for (int32 i = 0; i < rectCount; i++) {
1014		BRect sourceRect = region.RectAt(i).OffsetByCopy(-viewRect.LeftTop());
1015		int32 targetWidth = (int32)(sourceRect.Width() + 1.5);
1016		int32 targetHeight = (int32)(sourceRect.Height() + 1.5);
1017
1018		if (xScale != 1.0) {
1019			sourceRect.left = (int32)(sourceRect.left * xScale + 0.5);
1020			sourceRect.right = (int32)(sourceRect.right * xScale + 0.5);
1021			if (xScale < 1.0)
1022				targetWidth = (int32)(sourceRect.Width() + 1.5);
1023		}
1024
1025		if (yScale != 1.0) {
1026			sourceRect.top = (int32)(sourceRect.top * yScale + 0.5);
1027			sourceRect.bottom = (int32)(sourceRect.bottom * yScale + 0.5);
1028			if (yScale < 1.0)
1029				targetHeight = (int32)(sourceRect.Height() + 1.5);
1030		}
1031
1032		sourceRect.OffsetBy(bitmapRect.LeftTop());
1033			// sourceRect is now the part of the bitmap we want copied
1034
1035		status_t result = B_OK;
1036		if ((xScale > 1.0 || yScale > 1.0)
1037			&& (targetWidth * targetHeight < (int32)(sourceRect.Width() + 1.5)
1038				* (int32)(sourceRect.Height() + 1.5))) {
1039			// the target bitmap is smaller than the source, scale it locally
1040			// and send over the smaller version to avoid sending any extra data
1041			if (fBitmapDrawingEngine == NULL) {
1042				fBitmapDrawingEngine
1043					= new(std::nothrow) BitmapDrawingEngine(B_RGBA32);
1044				if (fBitmapDrawingEngine == NULL)
1045					result = B_NO_MEMORY;
1046			}
1047
1048			if (result == B_OK) {
1049				result = fBitmapDrawingEngine->SetSize(targetWidth,
1050					targetHeight);
1051			}
1052
1053			if (result == B_OK) {
1054				fBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
1055
1056				switch (bitmap.ColorSpace()) {
1057					case B_RGBA32:
1058					case B_RGBA32_BIG:
1059					case B_RGBA15:
1060					case B_RGBA15_BIG:
1061						break;
1062
1063					default:
1064					{
1065						// we need to clear the background if there may be
1066						// transparency through transparent magic (we use
1067						// B_OP_COPY when we draw alpha enabled bitmaps, so we
1068						// don't need to clear there)
1069						// TODO: this is not actually correct, as we're going to
1070						// loose the transparency with the conversion to the
1071						// original non-alpha colorspace happening in
1072						// ExportToBitmap
1073						rgb_color background = { 0, 0, 0, 0 };
1074						fBitmapDrawingEngine->FillRect(
1075							BRect(0, 0, targetWidth - 1, targetHeight -1),
1076							background);
1077						fBitmapDrawingEngine->SetDrawingMode(B_OP_OVER);
1078						break;
1079					}
1080				}
1081
1082				fBitmapDrawingEngine->DrawBitmap(&bitmap, sourceRect,
1083					BRect(0, 0, targetWidth - 1, targetHeight - 1), options);
1084				bitmaps[i] = fBitmapDrawingEngine->ExportToBitmap(targetWidth,
1085					targetHeight, bitmap.ColorSpace());
1086				if (bitmaps[i] == NULL)
1087					result = B_NO_MEMORY;
1088			}
1089		} else {
1090			// source is smaller or equal target, extract the relevant rects
1091			// directly without any scaling and conversion
1092			targetWidth = (int32)(sourceRect.Width() + 1.5);
1093			targetHeight = (int32)(sourceRect.Height() + 1.5);
1094
1095			bitmaps[i] = new(std::nothrow) UtilityBitmap(
1096				BRect(0, 0, targetWidth - 1, targetHeight - 1),
1097				bitmap.ColorSpace(), 0);
1098			if (bitmaps[i] == NULL)
1099				result = B_NO_MEMORY;
1100
1101			result = bitmaps[i]->ImportBits(bitmap.Bits(), bitmap.BitsLength(),
1102				bitmap.BytesPerRow(), bitmap.ColorSpace(), sourceRect.LeftTop(),
1103				BPoint(0, 0), targetWidth, targetHeight);
1104			if (result != B_OK)
1105				delete bitmaps[i];
1106		}
1107
1108		if (result != B_OK) {
1109			for (int32 j = 0; j < i; j++)
1110				delete bitmaps[j];
1111			free(bitmaps);
1112			return result;
1113		}
1114	}
1115
1116	return B_OK;
1117}
1118