1/*
2 * Copyright 2006-2018 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stefano Ceccherini, stefano.ceccherini@gmail.com
7 *		Julian Harnath, <julian.harnath@rwth-achen.de>
8 *		Stephan A��mus <superstippi@gmx.de>
9 */
10
11#include <PictureDataWriter.h>
12
13#include <stdio.h>
14#include <string.h>
15
16#include <DataIO.h>
17#include <Gradient.h>
18#include <Point.h>
19#include <Rect.h>
20#include <Region.h>
21
22#include <PictureProtocol.h>
23
24#define THROW_ERROR(error) throw (status_t)(error)
25
26
27// TODO: Review writing of strings. AFAIK in the picture data format
28// They are not supposed to be NULL terminated
29// (at least, it's not mandatory) so we should write their size too.
30PictureDataWriter::PictureDataWriter()
31	:
32	fData(NULL)
33{
34}
35
36
37PictureDataWriter::PictureDataWriter(BPositionIO* data)
38	:
39	fData(data)
40{
41}
42
43
44PictureDataWriter::~PictureDataWriter()
45{
46}
47
48
49status_t
50PictureDataWriter::SetTo(BPositionIO* data)
51{
52	if (data == NULL)
53		return B_BAD_VALUE;
54
55	fData = data;
56
57	return B_OK;
58}
59
60
61status_t
62PictureDataWriter::WriteSetOrigin(const BPoint& point)
63{
64	try {
65		BeginOp(B_PIC_SET_ORIGIN);
66		Write<BPoint>(point);
67		EndOp();
68	} catch (status_t& status) {
69		return status;
70	}
71
72	return B_OK;
73}
74
75
76status_t
77PictureDataWriter::WriteInvertRect(const BRect& rect)
78{
79	try {
80		WritePushState();
81		WriteSetDrawingMode(B_OP_INVERT);
82
83		BeginOp(B_PIC_FILL_RECT);
84		Write<BRect>(rect);
85		EndOp();
86
87		WritePopState();
88	} catch (status_t& status) {
89		return status;
90	}
91
92	return B_OK;
93}
94
95
96status_t
97PictureDataWriter::WriteSetDrawingMode(const drawing_mode& mode)
98{
99	try {
100		BeginOp(B_PIC_SET_DRAWING_MODE);
101		Write<int16>((int16)mode);
102		EndOp();
103	} catch (status_t& status) {
104		return status;
105	}
106
107	return B_OK;
108}
109
110
111status_t
112PictureDataWriter::WriteSetPenLocation(const BPoint& point)
113{
114	try {
115		BeginOp(B_PIC_SET_PEN_LOCATION);
116		Write<BPoint>(point);
117		EndOp();
118	} catch (status_t& status) {
119		return status;
120	}
121
122	return B_OK;
123}
124
125
126status_t
127PictureDataWriter::WriteSetPenSize(const float& penSize)
128{
129	try {
130		BeginOp(B_PIC_SET_PEN_SIZE);
131		Write<float>(penSize);
132		EndOp();
133	} catch (status_t& status) {
134		return status;
135	}
136
137	return B_OK;
138}
139
140
141status_t
142PictureDataWriter::WriteSetLineMode(const cap_mode& cap, const join_mode& join,
143	const float& miterLimit)
144{
145	try {
146		BeginOp(B_PIC_SET_LINE_MODE);
147		Write<int16>((int16)cap);
148		Write<int16>((int16)join);
149		Write<float>(miterLimit);
150		EndOp();
151	} catch (status_t& status) {
152		return status;
153	}
154
155	return B_OK;
156}
157
158
159status_t
160PictureDataWriter::WriteSetFillRule(int32 fillRule)
161{
162	try {
163		BeginOp(B_PIC_SET_FILL_RULE);
164		Write<int32>(fillRule);
165		EndOp();
166	} catch (status_t& status) {
167		return status;
168	}
169
170	return B_OK;
171}
172
173
174status_t
175PictureDataWriter::WriteSetScale(const float& scale)
176{
177	try {
178		BeginOp(B_PIC_SET_SCALE);
179		Write<float>(scale);
180		EndOp();
181	} catch (status_t& status) {
182		return status;
183	}
184
185	return B_OK;
186}
187
188
189status_t
190PictureDataWriter::WriteSetTransform(BAffineTransform transform)
191{
192	try {
193		BeginOp(B_PIC_SET_TRANSFORM);
194		Write<BAffineTransform>(transform);
195		EndOp();
196	} catch (status_t& status) {
197		return status;
198	}
199
200	return B_OK;
201}
202
203
204status_t
205PictureDataWriter::WriteTranslateBy(double x, double y)
206{
207	try {
208		BeginOp(B_PIC_AFFINE_TRANSLATE);
209		Write<double>(x);
210		Write<double>(y);
211		EndOp();
212	} catch (status_t& status) {
213		return status;
214	}
215
216	return B_OK;
217}
218
219
220status_t
221PictureDataWriter::WriteScaleBy(double x, double y)
222{
223	try {
224		BeginOp(B_PIC_AFFINE_SCALE);
225		Write<double>(x);
226		Write<double>(y);
227		EndOp();
228	} catch (status_t& status) {
229		return status;
230	}
231
232	return B_OK;
233}
234
235
236status_t
237PictureDataWriter::WriteRotateBy(double angleRadians)
238{
239	try {
240		BeginOp(B_PIC_AFFINE_ROTATE);
241		Write<double>(angleRadians);
242		EndOp();
243	} catch (status_t& status) {
244		return status;
245	}
246
247	return B_OK;
248}
249
250
251status_t
252PictureDataWriter::WriteSetPattern(const ::pattern& pattern)
253{
254	try {
255		BeginOp(B_PIC_SET_STIPLE_PATTERN);
256		Write< ::pattern>(pattern);
257		EndOp();
258	} catch (status_t& status) {
259		return status;
260	}
261
262	return B_OK;
263}
264
265
266status_t
267PictureDataWriter::WriteClipToPicture(int32 pictureToken,
268						const BPoint& origin, bool inverse)
269{
270	// TODO: I don't know if it's compatible with R5's BPicture version
271	try {
272		BeginOp(B_PIC_CLIP_TO_PICTURE);
273		Write<int32>(pictureToken);
274		Write<BPoint>(origin);
275		Write<bool>(inverse);
276		EndOp();
277	} catch (status_t& status) {
278		return status;
279	}
280
281	return B_OK;
282}
283
284
285status_t
286PictureDataWriter::WriteSetClipping(const BRegion& region)
287{
288	// TODO: I don't know if it's compatible with R5's BPicture version
289	try {
290		const int32 numRects = region.CountRects();
291		if (numRects > 0 && region.Frame().IsValid()) {
292			BeginOp(B_PIC_SET_CLIPPING_RECTS);
293			Write<uint32>(numRects);
294			for (int32 i = 0; i < numRects; i++)
295				Write<BRect>(region.RectAt(i));
296
297			EndOp();
298		} else
299			WriteClearClipping();
300	} catch (status_t& status) {
301		return status;
302	}
303
304	return B_OK;
305}
306
307
308status_t
309PictureDataWriter::WriteClearClipping()
310{
311	try {
312		BeginOp(B_PIC_CLEAR_CLIPPING_RECTS);
313		EndOp();
314	} catch (status_t& status) {
315		return status;
316	}
317
318	return B_OK;
319}
320
321
322status_t
323PictureDataWriter::WriteSetHighColor(const rgb_color& color)
324{
325	try {
326		BeginOp(B_PIC_SET_FORE_COLOR);
327		Write<rgb_color>(color);
328		EndOp();
329	} catch (status_t& status) {
330		return status;
331	}
332
333	return B_OK;
334}
335
336
337status_t
338PictureDataWriter::WriteSetLowColor(const rgb_color& color)
339{
340	try {
341		BeginOp(B_PIC_SET_BACK_COLOR);
342		Write<rgb_color>(color);
343		EndOp();
344	} catch (status_t& status) {
345		return status;
346	}
347
348	return B_OK;
349}
350
351
352status_t
353PictureDataWriter::WriteDrawRect(const BRect& rect, const bool& fill)
354{
355	try {
356		BeginOp(fill ? B_PIC_FILL_RECT : B_PIC_STROKE_RECT);
357		Write<BRect>(rect);
358		EndOp();
359	} catch (status_t& status) {
360		return status;
361	}
362
363	return B_OK;
364}
365
366
367status_t
368PictureDataWriter::WriteDrawRoundRect(const BRect& rect, const BPoint& radius,
369	const bool& fill)
370{
371	try {
372		BeginOp(fill ? B_PIC_FILL_ROUND_RECT : B_PIC_STROKE_ROUND_RECT);
373		Write<BRect>(rect);
374		Write<BPoint>(radius);
375		EndOp();
376	} catch (status_t& status) {
377		return status;
378	}
379
380	return B_OK;
381}
382
383
384status_t
385PictureDataWriter::WriteDrawEllipse(const BRect& rect, const bool& fill)
386{
387	try {
388		BeginOp(fill ? B_PIC_FILL_ELLIPSE : B_PIC_STROKE_ELLIPSE);
389		Write<BRect>(rect);
390		EndOp();
391	} catch (status_t& status) {
392		return status;
393	}
394
395	return B_OK;
396}
397
398
399status_t
400PictureDataWriter::WriteDrawArc(const BPoint& center, const BPoint& radius,
401	const float& startTheta, const float& arcTheta, const bool& fill)
402{
403	try {
404		BeginOp(fill ? B_PIC_FILL_ARC : B_PIC_STROKE_ARC);
405		Write<BPoint>(center);
406		Write<BPoint>(radius);
407		Write<float>(startTheta);
408		Write<float>(arcTheta);
409		EndOp();
410	} catch (status_t& status) {
411		return status;
412	}
413
414	return B_OK;
415}
416
417
418status_t
419PictureDataWriter::WriteDrawPolygon(const int32& numPoints, BPoint* points,
420	const bool& isClosed, const bool& fill)
421{
422	try {
423		BeginOp(fill ? B_PIC_FILL_POLYGON : B_PIC_STROKE_POLYGON);
424		Write<int32>(numPoints);
425		for (int32 i = 0; i < numPoints; i++)
426			Write<BPoint>(points[i]);
427
428		if (!fill)
429			Write<uint8>((uint8)isClosed);
430
431		EndOp();
432	} catch (status_t& status) {
433		return status;
434	}
435
436	return B_OK;
437}
438
439
440status_t
441PictureDataWriter::WriteDrawBezier(const BPoint points[4], const bool& fill)
442{
443	try {
444		BeginOp(fill ? B_PIC_FILL_BEZIER : B_PIC_STROKE_BEZIER);
445		for (int32 i = 0; i < 4; i++)
446			Write<BPoint>(points[i]);
447
448		EndOp();
449	} catch (status_t& status) {
450		return status;
451	}
452
453	return B_OK;
454}
455
456
457status_t
458PictureDataWriter::WriteStrokeLine(const BPoint& start, const BPoint& end)
459{
460	try {
461		BeginOp(B_PIC_STROKE_LINE);
462		Write<BPoint>(start);
463		Write<BPoint>(end);
464		EndOp();
465	} catch (status_t& status) {
466		return status;
467	}
468
469	return B_OK;
470}
471
472
473status_t
474PictureDataWriter::WriteDrawString(const BPoint& where, const char* string,
475	const int32& length, const escapement_delta& escapement)
476{
477	try {
478		BeginOp(B_PIC_SET_PEN_LOCATION);
479		Write<BPoint>(where);
480		EndOp();
481
482		BeginOp(B_PIC_DRAW_STRING);
483		Write<float>(escapement.space);
484		Write<float>(escapement.nonspace);
485		//WriteData(string, length + 1);
486			// TODO: is string 0 terminated? why is length given?
487		WriteData(string, length);
488		Write<uint8>(0);
489		EndOp();
490	} catch (status_t& status) {
491		return status;
492	}
493
494	return B_OK;
495}
496
497
498status_t
499PictureDataWriter::WriteDrawString(const char* string,
500	int32 length, const BPoint* locations, int32 locationCount)
501{
502	try {
503		BeginOp(B_PIC_DRAW_STRING_LOCATIONS);
504		Write<int32>(locationCount);
505		for (int32 i = 0; i < locationCount; i++) {
506			Write<BPoint>(locations[i]);
507		}
508		WriteData(string, length);
509		Write<uint8>(0);
510		EndOp();
511	} catch (status_t& status) {
512		return status;
513	}
514
515	return B_OK;
516}
517
518
519status_t
520PictureDataWriter::WriteDrawShape(const int32& opCount, const void* opList,
521	const int32& ptCount, const void* ptList, const bool& fill)
522{
523	try {
524		BeginOp(fill ? B_PIC_FILL_SHAPE : B_PIC_STROKE_SHAPE);
525		Write<int32>(opCount);
526		Write<int32>(ptCount);
527		WriteData(opList, opCount * sizeof(uint32));
528		WriteData(ptList, ptCount * sizeof(BPoint));
529		EndOp();
530	} catch (status_t& status) {
531		return status;
532	}
533
534	return B_OK;
535}
536
537
538status_t
539PictureDataWriter::WriteDrawRectGradient(const BRect& rect, const BGradient& gradient, const bool& fill)
540{
541	try {
542		BeginOp(fill ? B_PIC_FILL_RECT_GRADIENT : B_PIC_STROKE_RECT_GRADIENT);
543		Write<BRect>(rect);
544		gradient.Flatten(fData);
545		EndOp();
546	} catch (status_t& status) {
547		return status;
548	}
549
550	return B_OK;
551}
552
553
554status_t
555PictureDataWriter::WriteDrawRoundRectGradient(const BRect& rect, const BPoint& radius, const BGradient& gradient,
556	const bool& fill)
557{
558	try {
559		BeginOp(fill ? B_PIC_FILL_ROUND_RECT_GRADIENT : B_PIC_STROKE_ROUND_RECT_GRADIENT);
560		Write<BRect>(rect);
561		Write<BPoint>(radius);
562		gradient.Flatten(fData);
563		EndOp();
564	} catch (status_t& status) {
565		return status;
566	}
567
568	return B_OK;
569}
570
571
572status_t
573PictureDataWriter::WriteDrawBezierGradient(const BPoint points[4], const BGradient& gradient, const bool& fill)
574{
575	try {
576		BeginOp(fill ? B_PIC_FILL_BEZIER_GRADIENT : B_PIC_STROKE_BEZIER_GRADIENT);
577		for (int32 i = 0; i < 4; i++)
578			Write<BPoint>(points[i]);
579
580		gradient.Flatten(fData);
581		EndOp();
582	} catch (status_t& status) {
583		return status;
584	}
585
586	return B_OK;
587}
588
589
590status_t
591PictureDataWriter::WriteDrawArcGradient(const BPoint& center, const BPoint& radius,
592	const float& startTheta, const float& arcTheta, const BGradient& gradient, const bool& fill)
593{
594	try {
595		BeginOp(fill ? B_PIC_FILL_ARC_GRADIENT : B_PIC_STROKE_ARC_GRADIENT);
596		Write<BPoint>(center);
597		Write<BPoint>(radius);
598		Write<float>(startTheta);
599		Write<float>(arcTheta);
600		gradient.Flatten(fData);
601		EndOp();
602	} catch (status_t& status) {
603		return status;
604	}
605
606	return B_OK;
607}
608
609
610status_t
611PictureDataWriter::WriteDrawEllipseGradient(const BRect& rect, const BGradient& gradient, const bool& fill)
612{
613	try {
614		BeginOp(fill ? B_PIC_FILL_ELLIPSE_GRADIENT : B_PIC_STROKE_ELLIPSE_GRADIENT);
615		Write<BRect>(rect);
616		gradient.Flatten(fData);
617		EndOp();
618	} catch (status_t& status) {
619		return status;
620	}
621
622	return B_OK;
623}
624
625
626status_t
627PictureDataWriter::WriteDrawPolygonGradient(const int32& numPoints, BPoint* points,
628	const bool& isClosed, const BGradient& gradient, const bool& fill)
629{
630	try {
631		BeginOp(fill ? B_PIC_FILL_POLYGON_GRADIENT : B_PIC_STROKE_POLYGON_GRADIENT);
632		Write<int32>(numPoints);
633		for (int32 i = 0; i < numPoints; i++)
634			Write<BPoint>(points[i]);
635
636		if (!fill)
637			Write<uint8>((uint8)isClosed);
638
639		gradient.Flatten(fData);
640		EndOp();
641	} catch (status_t& status) {
642		return status;
643	}
644
645	return B_OK;
646}
647
648
649status_t
650PictureDataWriter::WriteDrawShapeGradient(const int32& opCount, const void* opList,
651	const int32& ptCount, const void* ptList, const BGradient& gradient, const bool& fill)
652{
653	try {
654		BeginOp(fill ? B_PIC_FILL_SHAPE_GRADIENT : B_PIC_STROKE_SHAPE_GRADIENT);
655		Write<int32>(opCount);
656		Write<int32>(ptCount);
657		WriteData(opList, opCount * sizeof(uint32));
658		WriteData(ptList, ptCount * sizeof(BPoint));
659		gradient.Flatten(fData);
660		EndOp();
661	} catch (status_t& status) {
662		return status;
663	}
664
665	return B_OK;
666}
667
668
669status_t
670PictureDataWriter::WriteDrawBitmap(const BRect& srcRect, const BRect& dstRect,
671	const int32& width, const int32& height, const int32& bytesPerRow,
672	const int32& colorSpace, const int32& flags, const void* data,
673	const int32& length)
674{
675	if (length != height * bytesPerRow)
676		debugger("PictureDataWriter::WriteDrawBitmap: invalid length");
677	try {
678		BeginOp(B_PIC_DRAW_PIXELS);
679		Write<BRect>(srcRect);
680		Write<BRect>(dstRect);
681		Write<int32>(width);
682		Write<int32>(height);
683		Write<int32>(bytesPerRow);
684		Write<int32>(colorSpace);
685		Write<int32>(flags);
686		WriteData(data, length);
687		EndOp();
688	} catch (status_t& status) {
689		return status;
690	}
691
692	return B_OK;
693}
694
695
696status_t
697PictureDataWriter::WriteDrawPicture(const BPoint& where, const int32& token)
698{
699	// TODO: I'm not sure about this function. I think we need
700	// to attach the picture data too.
701	// The token won't be sufficient in many cases (for example, when
702	// we archive/flatten the picture.
703	try {
704		BeginOp(B_PIC_DRAW_PICTURE);
705		Write<BPoint>(where);
706		Write<int32>(token);
707		EndOp();
708	} catch (status_t& status) {
709		return status;
710	}
711
712	return B_OK;
713}
714
715
716status_t
717PictureDataWriter::WriteSetFontFamily(const font_family family)
718{
719	try {
720		BeginOp(B_PIC_SET_FONT_FAMILY);
721		WriteData(family, strlen(family));
722		Write<uint8>(0);
723		EndOp();
724	} catch (status_t& status) {
725		return status;
726	}
727
728	return B_OK;
729}
730
731
732status_t
733PictureDataWriter::WriteSetFontStyle(const font_style style)
734{
735	try {
736		BeginOp(B_PIC_SET_FONT_STYLE);
737		WriteData(style, strlen(style));
738		Write<uint8>(0);
739		EndOp();
740	} catch (status_t& status) {
741		return status;
742	}
743
744	return B_OK;
745}
746
747
748status_t
749PictureDataWriter::WriteSetFontSpacing(const int32& spacing)
750{
751	try {
752		BeginOp(B_PIC_SET_FONT_SPACING);
753		Write<int32>(spacing);
754		EndOp();
755	} catch (status_t& status) {
756		return status;
757	}
758
759	return B_OK;
760}
761
762
763status_t
764PictureDataWriter::WriteSetFontSize(const float& size)
765{
766	try {
767		BeginOp(B_PIC_SET_FONT_SIZE);
768		Write<float>(size);
769		EndOp();
770	} catch (status_t& status) {
771		return status;
772	}
773
774	return B_OK;
775}
776
777
778status_t
779PictureDataWriter::WriteSetFontRotation(const float& rotation)
780{
781	try {
782		BeginOp(B_PIC_SET_FONT_ROTATE);
783		Write<float>(rotation);
784		EndOp();
785	} catch (status_t& status) {
786		return status;
787	}
788
789	return B_OK;
790}
791
792
793status_t
794PictureDataWriter::WriteSetFontEncoding(const int32& encoding)
795{
796	try {
797		BeginOp(B_PIC_SET_FONT_ENCODING);
798		Write<int32>(encoding);
799		EndOp();
800	} catch (status_t& status) {
801		return status;
802	}
803
804	return B_OK;
805}
806
807
808status_t
809PictureDataWriter::WriteSetFontFlags(const int32& flags)
810{
811	try {
812		BeginOp(B_PIC_SET_FONT_FLAGS);
813		Write<int32>(flags);
814		EndOp();
815	} catch (status_t& status) {
816		return status;
817	}
818
819	return B_OK;
820}
821
822
823status_t
824PictureDataWriter::WriteSetFontShear(const float& shear)
825{
826	try {
827		BeginOp(B_PIC_SET_FONT_SHEAR);
828		Write<float>(shear);
829		EndOp();
830	} catch (status_t& status) {
831		return status;
832	}
833
834	return B_OK;
835}
836
837
838status_t
839PictureDataWriter::WriteSetFontFace(const int32& face)
840{
841	try {
842		BeginOp(B_PIC_SET_FONT_FACE);
843		Write<int32>(face);
844		EndOp();
845	} catch (status_t& status) {
846		return status;
847	}
848
849	return B_OK;
850}
851
852
853status_t
854PictureDataWriter::WritePushState()
855{
856	try {
857		BeginOp(B_PIC_PUSH_STATE);
858		EndOp();
859	} catch (status_t& status) {
860		return status;
861	}
862
863	return B_OK;
864}
865
866
867status_t
868PictureDataWriter::WritePopState()
869{
870	try {
871		BeginOp(B_PIC_POP_STATE);
872		EndOp();
873	} catch (status_t& status) {
874		return status;
875	}
876
877	return B_OK;
878}
879
880
881status_t
882PictureDataWriter::WriteBlendLayer(Layer* layer)
883{
884	try {
885		BeginOp(B_PIC_BLEND_LAYER);
886		Write<Layer*>(layer);
887		EndOp();
888	} catch (status_t& status) {
889		return status;
890	}
891
892	return B_OK;
893}
894
895
896status_t
897PictureDataWriter::WriteClipToRect(const BRect& rect, bool inverse)
898{
899	try {
900		BeginOp(B_PIC_CLIP_TO_RECT);
901		Write<bool>(inverse);
902		Write<BRect>(rect);
903		EndOp();
904	} catch (status_t& status) {
905		return status;
906	}
907
908	return B_OK;
909}
910
911
912status_t
913PictureDataWriter::WriteClipToShape(int32 opCount, const void* opList,
914	int32 ptCount, const void* ptList, bool inverse)
915{
916	try {
917		BeginOp(B_PIC_CLIP_TO_SHAPE);
918		Write<bool>(inverse);
919		Write<int32>(opCount);
920		Write<int32>(ptCount);
921		WriteData(opList, opCount * sizeof(uint32));
922		WriteData(ptList, ptCount * sizeof(BPoint));
923		EndOp();
924	} catch (status_t& status) {
925		return status;
926	}
927
928	return B_OK;
929}
930
931
932// private
933void
934PictureDataWriter::BeginOp(const int16& op)
935{
936	if (fData == NULL)
937		THROW_ERROR(B_NO_INIT);
938
939	fStack.push(fData->Position());
940	fData->Write(&op, sizeof(op));
941
942	// Init the size of the opcode block to 0
943	int32 size = 0;
944	fData->Write(&size, sizeof(size));
945}
946
947
948void
949PictureDataWriter::EndOp()
950{
951	if (fData == NULL)
952		THROW_ERROR(B_NO_INIT);
953
954	off_t curPos = fData->Position();
955	off_t stackPos = fStack.top();
956	fStack.pop();
957
958	// The size of the op is calculated like this:
959	// current position on the stream minus the position on the stack,
960	// minus the space occupied by the op code itself (int16)
961	// and the space occupied by the size field (int32)
962	int32 size = curPos - stackPos - sizeof(int32) - sizeof(int16);
963
964	// Size was set to 0 in BeginOp()
965	// Now we overwrite it with the correct value
966	fData->Seek(stackPos + sizeof(int16), SEEK_SET);
967	fData->Write(&size, sizeof(size));
968	fData->Seek(curPos, SEEK_SET);
969}
970
971
972void
973PictureDataWriter::WriteData(const void* data, size_t size)
974{
975	ssize_t result = fData->Write(data, size);
976	if (result < 0)
977		THROW_ERROR(result);
978
979	if ((size_t)result != size)
980		THROW_ERROR(B_IO_ERROR);
981}
982