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