forked from mathog/libUEMF
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupmf.c
More file actions
8661 lines (7665 loc) · 358 KB
/
upmf.c
File metadata and controls
8661 lines (7665 loc) · 358 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
@file upmf.c
@brief Functions for manipulating EMF+ files and structures.
EMF+ is much more object based than is EMF or WMF, so the U_PMR_*_set and most U_PMF_*_set functions
return a pointer to a PseudoObject. PseudoObjects are structs that contain a data field to hold the
object in EMF+ file byte order, size information, and some type information. This is sufficient to allow
complex records to be built up from the various sorts of nested objects which they normally contain.
If something goes wrong a NULL pointer is returned and recsize is set to 0.
EMF+ does not use a separate set of endian functions, _get and _set routines convert from/to
the EMF+ file byte order on the fly.
WARNING: Microsoft's EMF+ documentation is little-endian for everything EXCEPT
bitfields, which are big-endian. See EMF+ manual section 1.3.2
That documentation also uses 0 as the MOST significant bit, N-1 as the least.
This code is little-endian throughout, and 0 is the LEAST significant bit
*/
/*
File: upmf.c
Version: 0.0.13
Date: 21-MAR-2019
Author: David Mathog, Biology Division, Caltech
email: mathog@caltech.edu
Copyright: 2019 David Mathog and California Institute of Technology (Caltech)
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iconv.h>
#include <wchar.h>
#include <errno.h>
#include <string.h>
#include <limits.h> // for INT_MAX, INT_MIN
#include <math.h> // for sin, cos, tan2, use U_ROUND() instead of roundf()
#include <stddef.h> /* for offsetof() macro */
#if 0
#include <windef.h> //Not actually used, looking for collisions
#include <winnt.h> //Not actually used, looking for collisions
#include <wingdi.h> //Not actually used, looking for collisions
#endif
#include "upmf.h" // includes uemf.h
#include "uemf_endian.h" // for U_swap* functions
//! \cond
/// things Doxygen should not process
/* remove this after debugging is completed */
void dumphex(uint8_t *buf,unsigned int num){
for(; num; num--,buf++){
printf("%2.2X",*buf);
}
}
/* Prototypes for functions used here and defined in uemf_endian.c, but which are not supposed
to be used in end user code. */
void U_swap2(void *ul, unsigned int count);
void U_swap4(void *ul, unsigned int count);
//! \endcond
/**
\brief Utility function for writing one or more EMF+ records in a PseudoObject to the EMF output file
\return 1 on success, 0 on error.
\param po U_PSEUDO_OBJ to write, it is deleted after it is written
\param sum U_PSEUDO_OBJ to use for scratch space
\param et EMFTRACK used to write records to EMF file
*/
int U_PMR_write(U_PSEUDO_OBJ *po, U_PSEUDO_OBJ *sum, EMFTRACK *et){
char *rec;
int status = 0;
sum->Used = 0; /* clean it out, retaining allocated memory */
sum = U_PO_append(sum, "EMF+", 4); /* indicates that this comment holds an EMF+ record */
if(!sum)goto end;
sum = U_PO_append(sum, po->Data, po->Used); /* the EMF+ record itself */
if(!sum)goto end;
U_PO_free(&po); /* delete the PseudoObject */
rec = U_EMRCOMMENT_set(sum->Used, sum->Data); /* stuff it into the EMF comment */
if(!emf_append((PU_ENHMETARECORD)rec, et, 1))goto end; /* write it to the EMF file, delete the record, check status */
status = 1;
end:
return(status);
}
/**
\brief Utility function to draw a line.
\return 1 on success, 0 on error.
\param PenID Index of U_PMF_PEN object to use in the EMF+ object table (0-63, inclusive)
\param PathID Index of U_PMF_PATH object to use in the EMF+ object table (0-63, inclusive)
\param Start U_PMF_POINTF coordinates of start of line.
\param End U_PMF_POINTF coordinates of end of line.
\param Dashed Set if the line is dashed, clear if it is not.
\param sum PseudoObject used for scratch space
\param et EMFTRACK used to write records to EMF file
*/
int U_PMR_drawline(uint32_t PenID, uint32_t PathID, U_PMF_POINTF Start, U_PMF_POINTF End, int Dashed, U_PSEUDO_OBJ *sum, EMFTRACK *et){
U_DPSEUDO_OBJ *dpath;
U_PSEUDO_OBJ *poPath;
U_PSEUDO_OBJ *po;
int status=0;
int PTP_value = ( Dashed ? U_PTP_DashMode : U_PTP_None);
dpath = U_PATH_create(0, NULL, 0, 0); /* create an empty path*/
if(dpath){
if(U_PATH_moveto(dpath, Start, PTP_value) && U_PATH_lineto(dpath, End, PTP_value)){
poPath = U_PMF_PATH_set2(U_PMF_GRAPHICSVERSIONOBJ_set(2), dpath);
if(poPath){
po = U_PMR_OBJECT_PO_set(PathID, poPath);
U_PO_free(&poPath);
if(po){
U_PMR_write(po, sum, et);
po = U_PMR_DRAWPATH_set(PathID, PenID);
if(po){
U_PMR_write(po, sum, et);
status = 1;
}
}
}
}
U_DPO_free(&dpath);
}
return(status);
}
/**
\brief Utility function for drawing strings onto the baseline in one call.
\return 1 on success, 0 on error.
\param string Text to draw in UTF-8 format
\param Vpos StringAlignment Enumeration. Always drawn on baseline, but using one of these three modes.
\param FontID Index of U_PMF_FONT object to use in the EMF+ object table (0-63, inclusive)
\param BrushID U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
\param FormatID index of U_PMF_STRINGFORMAT object to use in the EMF+ Object Table.
\param Sfs StringFormat structure. Ignored values: StringAlignment, LineAlign, Flags
\param FontName Name of font to draw with
\param Height Height of font in pixels (positive)
\param fip U_FontInfoParams (ascent, descent, and so forth)
\param FontFlags FontStyle Flags
\param x X position in pixels of left side of EM box of first character
\param y Y position in pixels of baseline of first character
\param sum PseudoObject used for scratch space
\param et EMFTRACK used to write records to EMF file
EMF+ manual 2.3.4.14, Microsoft name: EmfPlusDrawString Record, Index 0x1C
For most fonts Ascent and Descent are used to adjust the bounding box to properly position the
baseline. Some fonts, like Verdana, are strange and they position the baseline on the bottom of
the bounding box if that box has the same height as the font. For those fonts specify 0.0 for
both Ascent and Descent.
*/
int U_PMR_drawstring( const char *string, int Vpos, uint32_t FontID, const U_PSEUDO_OBJ *BrushID, uint32_t FormatID,
U_PMF_STRINGFORMAT Sfs, const char *FontName, U_FLOAT Height, U_FontInfoParams *fip, uint32_t FontFlags,
U_FLOAT x, U_FLOAT y, U_PSEUDO_OBJ *sum, EMFTRACK *et){
U_PSEUDO_OBJ *po;
U_PSEUDO_OBJ *poSF;
U_PSEUDO_OBJ *poFont;
U_PSEUDO_OBJ *poRect;
U_FLOAT rx,ry,rw,rh,rd;
uint16_t *UFontName;
uint16_t *Text16;
int slen;
int status = 0;
double aval, dval;
Sfs.Flags = U_SF_NoFitBlackBox + U_SF_NoClip;
if(Vpos < U_SA_Near || Vpos > U_SA_Far)return(0);
Sfs.StringAlignment = U_SA_Near; // Horizontal
Sfs.LineAlign = Vpos; // Vertical
UFontName = U_Utf8ToUtf16le(FontName, 0, NULL);
slen = strlen(FontName);
poFont = U_PMF_FONT_set(U_PMF_GRAPHICSVERSIONOBJ_set(2), Height, U_UT_World, FontFlags, slen, UFontName);
if(poFont){
po = U_PMR_OBJECT_PO_set(FontID, poFont); /* font to use */
if(po){
U_PMR_write(po, sum, et);
poSF = U_PMF_STRINGFORMAT_set(&Sfs, NULL);
if(poSF){
po = U_PMR_OBJECT_PO_set(FormatID, poSF);
U_PO_free(&poSF);
if(po){
U_PMR_write(po, sum, et);
rw = 4*Height*slen; /* This could probably be any value */
rh = Height;
rx = x;
if(fip->LineGap > -fip->Descent){ aval = fip->yMax; } // sylfaen, palatino
else { aval = fip->Ascent; } // others
if(fip->LineGap && (fip->LineGap < -fip->Descent)){ dval = ((double) (fip->Descent - fip->LineGap)) / ((double) fip->EmSize); } //shruti and some others
else { dval = ((double) fip->Descent ) / ((double) fip->EmSize); }
switch(Vpos){
case U_SA_Near:
rd = Height * aval / ((double) fip->EmSize);
break;
case U_SA_Center:
rd = 0.5 * ( Height * aval / ((double) fip->EmSize) + Height * ( 1.0 + dval));
break;
case U_SA_Far:
rd = Height * ( 1.0 + dval);
break;
}
ry = y - rd; /* draw from upper left corner, which is shifted to put baseline on y */
poRect = U_PMF_RECTF4_set(rx, ry, rw, rh);
#if 0
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x-100, ry}, (U_PMF_POINTF){x+100, ry}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x-100, ry+rh}, (U_PMF_POINTF){x+100, ry+rh}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x-100, ry}, (U_PMF_POINTF){x-100, ry + Height * (double) fip->Ascent / ((double) fip->EmSize)}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 90, ry}, (U_PMF_POINTF){x- 90, ry - Height * (double) fip->Descent / ((double) fip->EmSize)}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 80, ry}, (U_PMF_POINTF){x- 80, ry + Height * (double) fip->yMax / ((double) fip->EmSize)}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 70, ry}, (U_PMF_POINTF){x- 70, ry - Height * (double) fip->yMin / ((double) fip->EmSize)}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 60, ry}, (U_PMF_POINTF){x- 60, ry + Height * (double) fip->LineGap / ((double) fip->EmSize)}, 0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 50, ry}, (U_PMF_POINTF){x- 50, ry + Height * ( 1.0 - (((double) (fip->LineGap - fip->Descent)) / ((double) fip->EmSize)) )}, 0, sum, et);
#endif
Text16 = U_Utf8ToUtf16le(string, 0, NULL);
slen = strlen(string);
po = U_PMR_DRAWSTRING_set(FontID, BrushID, FormatID, slen, poRect, Text16);
if(po){
U_PMR_write(po, sum, et);
status = 1; /* Success!!! */
}
U_PO_free(&poRect);
free(Text16);
}
}
}
U_PO_free(&poFont);
}
free(UFontName);
return(status);
}
/**
\brief Allocate and construct an array of U_POINT16 objects from a set of U_PMF_POINTF objects, endianness in and out is LE
\returns pointer to an array of U_POINT16 structures.
\param points pointer to the source U_POINT structures
\param count number of members in points
If a coordinate is out of range it saturates at boundary.
*/
U_PMF_POINT *POINTF_To_POINT16_LE(U_PMF_POINTF *points, int count){
U_PMF_POINT *newpts;
U_PMF_POINTF ptfl;
int i;
newpts = (U_PMF_POINT *) malloc(count * sizeof(U_PMF_POINT));
for(i=0; i<count; i++){
memcpy(&ptfl, &(points[i]), 8);
if(U_BYTE_SWAP){ U_swap4(&ptfl,2); } /* on BE platforms swap going in and coming out */
newpts[i].X = U_MNMX(ptfl.X, INT16_MIN, INT16_MAX);
newpts[i].Y = U_MNMX(ptfl.Y, INT16_MIN, INT16_MAX);
if(U_BYTE_SWAP){ U_swap2(&(newpts[i]),2); }
}
return(newpts);
}
/**
\brief Look up the name of the EMR+ record by type. Returns U_EMR_INVALID if out of range.
\return name of the PMR record, "U_EMR_INVALID" if out of range.
\param idx PMR record type WITHOUT the U_PMR_RECFLAG bit.
*/
char *U_pmr_names(unsigned int idx){
if(idx<U_PMR_MIN || idx > U_PMR_MAX){ idx = 0; }
static char *U_PMR_NAMES[U_PMR_MAX+1]={
"U_PMR_INVALID",
"U_PMR_Header",
"U_PMR_EndOfFile",
"U_PMR_Comment",
"U_PMR_GetDC",
"U_PMR_MultiFormatStart",
"U_PMR_MultiFormatSection",
"U_PMR_MultiFormatEnd",
"U_PMR_Object",
"U_PMR_Clear",
"U_PMR_FillRects",
"U_PMR_DrawRects",
"U_PMR_FillPolygon",
"U_PMR_DrawLines",
"U_PMR_FillEllipse",
"U_PMR_DrawEllipse",
"U_PMR_FillPie",
"U_PMR_DrawPie",
"U_PMR_DrawArc",
"U_PMR_FillRegion",
"U_PMR_FillPath",
"U_PMR_DrawPath",
"U_PMR_FillClosedCurve",
"U_PMR_DrawClosedCurve",
"U_PMR_DrawCurve",
"U_PMR_DrawBeziers",
"U_PMR_DrawImage",
"U_PMR_DrawImagePoints",
"U_PMR_DrawString",
"U_PMR_SetRenderingOrigin",
"U_PMR_SetAntiAliasMode",
"U_PMR_SetTextRenderingHint",
"U_PMR_SetTextContrast",
"U_PMR_SetInterpolationMode",
"U_PMR_SetPixelOffsetMode",
"U_PMR_SetCompositingMode",
"U_PMR_SetCompositingQuality",
"U_PMR_Save",
"U_PMR_Restore",
"U_PMR_BeginContainer",
"U_PMR_BeginContainerNoParams",
"U_PMR_EndContainer",
"U_PMR_SetWorldTransform",
"U_PMR_ResetWorldTransform",
"U_PMR_MultiplyWorldTransform",
"U_PMR_TranslateWorldTransform",
"U_PMR_ScaleWorldTransform",
"U_PMR_RotateWorldTransform",
"U_PMR_SetPageTransform",
"U_PMR_ResetClip",
"U_PMR_SetClipRect",
"U_PMR_SetClipPath",
"U_PMR_SetClipRegion",
"U_PMR_OffsetClip",
"U_PMR_DrawDriverstring",
"U_PMR_StrokeFillPath",
"U_PMR_SerializableObject",
"U_PMR_SetTSGraphics",
"U_PMR_SetTSClip"
};
return(U_PMR_NAMES[idx]);
}
/**
\brief Convert from PseudoObject OID to ObjectType enumeration.
\returns OT value on success, 0 if no match
\param OID PseudoObject OID (based on EMF+ manual chapter number. )
Only OTs that may be stored in the EMF+ object table are supported.
*/
int U_OID_To_OT(uint32_t OID){
int otype;
if( OID==U_PMF_BRUSH_OID ){ otype = U_OT_Brush; }
else if(OID==U_PMF_PEN_OID ){ otype = U_OT_Pen; }
else if(OID==U_PMF_PATH_OID ){ otype = U_OT_Path; }
else if(OID==U_PMF_REGION_OID ){ otype = U_OT_Region; }
else if(OID==U_PMF_IMAGE_OID ){ otype = U_OT_Image; }
else if(OID==U_PMF_FONT_OID ){ otype = U_OT_Font; }
else if(OID==U_PMF_STRINGFORMAT_OID ){ otype = U_OT_StringFormat; }
else if(OID==U_PMF_IMAGEATTRIBUTES_OID){ otype = U_OT_ImageAttributes; }
else if(OID==U_PMF_CUSTOMLINECAP_OID ){ otype = U_OT_CustomLineCap; }
else { otype = U_OT_Invalid; }
return(otype);
}
/**
\brief Convert from PseudoObject OID to BrushType enumeration.
\returns BT value on success, -1 if no match
\param OID PseudoObject OID (based on EMF+ manual chapter number. )
Only OIDs that map to BT's are supported.
*/
int U_OID_To_BT(uint32_t OID){
int otype;
if( OID==U_PMF_HATCHBRUSHDATA_OID ){ otype = U_BT_HatchFill; }
else if(OID==U_PMF_LINEARGRADIENTBRUSHDATA_OID ){ otype = U_BT_LinearGradient; }
else if(OID==U_PMF_PATHGRADIENTBRUSHDATA_OID ){ otype = U_BT_PathGradient; }
else if(OID==U_PMF_SOLIDBRUSHDATA_OID ){ otype = U_BT_SolidColor; }
else if(OID==U_PMF_TEXTUREBRUSHDATA_OID ){ otype = U_BT_TextureFill; }
else { otype = -1; }
return(otype);
}
/**
\brief Convert from PseudoObject OID to CustomLineCapDataType Enumeration.
\returns BT value on success, -1 if no match
\param OID PseudoObject OID (based on EMF+ manual chapter number. )
Only OIDs that map to CLCDT's are supported.
*/
int U_OID_To_CLCDT(uint32_t OID){
int otype;
if( OID==U_PMF_CUSTOMLINECAPDATA_OID ){ otype = U_CLCDT_Default; }
else if(OID==U_PMF_CUSTOMLINECAPARROWDATA_OID ){ otype = U_CLCDT_AdjustableArrow; }
else { otype = -1; }
return(otype);
}
/**
\brief Convert from PseudoObject OID to ImageDataType Enumeration.
\returns BT value on success, -1 if no match
\param OID PseudoObject OID (based on EMF+ manual chapter number. )
Only OIDs that map to IDT's are supported.
*/
int U_OID_To_IDT(uint32_t OID){
int otype;
if( OID==U_PMF_BITMAP_OID ){ otype = U_IDT_Bitmap; }
else if(OID==U_PMF_METAFILE_OID ){ otype = U_IDT_Metafile; }
else { otype = -1; }
return(otype);
}
/**
\brief Convert from PseudoObject OID to RegionNodeDataType Enumeration.
\returns BT value on success, -1 if no match
\param OID PseudoObject OID (based on EMF+ manual chapter number. )
Only OIDs that map to RNDT's are supported.
*/
int U_OID_To_RNDT(uint32_t OID){
int otype;
if( OID==U_PMF_REGIONNODECHILDNODES_OID ){ otype = U_RNDT_Kids; } /* there are 5 types, which must be specified separately */
else if(OID==U_PMF_RECTF_OID ){ otype = U_RNDT_Rect; }
else if(OID==U_PMF_REGIONNODEPATH_OID ){ otype = U_RNDT_Path; }
else { otype = -1; }
return(otype);
}
/**
\brief Append data to an U_OBJ_ACCUM structure.
\param oa pointer to the U_OBJ_ACCUM structure
\param data data to add
\param size bytes in data
\param Type object type
\param Id Object ID
\returns 0 on success, !0 on error. -1 on Type change, -2 on Id change
Safe to test for Id, Type changes by calling with size=0.
*/
int U_OA_append(U_OBJ_ACCUM *oa, const char *data, int size, int Type, int Id){
int tail;
if(!oa)return(2);
if(oa->used){
if(oa->Type != Type)return(-1);
if(oa->Id != Id)return(-2);
}
tail = oa->used;
if(oa->used + size >= oa->space){
oa->space += size;
char *newaccum = (char *) realloc(oa->accum, oa->space);
if(!newaccum){
oa->space -= size; /* put it back the way it was */
return(1);
}
oa->accum = newaccum;
}
memcpy(oa->accum + tail,data,size);
oa->used += size;
oa->Type = Type;
oa->Id = Id;
return(0);
}
/**
\brief Clear an U_OBJ_ACCUM structure. Accumulated storage is retained.
\param oa pointer to the U_OBJ_ACCUM structure
\returns 0 on success, !0 on error.
*/
int U_OA_clear(U_OBJ_ACCUM *oa){
if(!oa)return(2);
oa->used=0;
/* Type and Id may be ignored as they are reset on the first append */
return(0);
}
/**
\brief Release an U_OBJ_ACCUM structure. Accumulated storage is free'd.
\param oa pointer to the U_OBJ_ACCUM structure
\returns 0 on success, !0 on error.
*/
int U_OA_release(U_OBJ_ACCUM *oa){
if(!oa)return(2);
oa->used=0;
oa->space = 0;
if(oa->accum)free(oa->accum);
oa->accum=NULL;
return(0);
}
/**
\brief Create and set an U_PSEUDO_OBJ
\returns pointer to the U_PSEUDO_OBJ, NULL on error
\param Data Data to copy into the PseudoObject's data. If NULL, space is allocated, but is cleared instead of filled.
\param Size Number of bytes to allocate for Data (may be >Use if padding is present)
\param Use Number of data bytes in Data (whether or not Data is actually copied)
\param Type Type numbers are from manual section: 1.2.3.47 -> 0x01020347
If Data is NULL and Size is 0 an empty PseudoObject is created. One byte of storage
is allocated for Data, Size is set to 1, and Used to 0.
If Data is NULL and Size is !0 a zero filled PseudoObject is created.
If Data is !Null and Size is !0 a data filled PseudoObject is created.
*/
U_PSEUDO_OBJ *U_PO_create(char *Data, size_t Size, size_t Use, uint32_t Type){
if(Use>Size)return(NULL);
size_t tSize = (Size ? Size : 1);
U_PSEUDO_OBJ *po = (U_PSEUDO_OBJ *)malloc(sizeof(U_PSEUDO_OBJ));
if(po){
po->Data = malloc(tSize);
if(po->Data){
po->Size = tSize;
po->Used = Use;
po->Type = Type;
if(Data){ memcpy(po->Data, Data, Use); } /* if Use < Size uninitialized bytes will be present! */
else { memset(po->Data, 0, tSize); }
}
else {
free(po);
po=NULL;
}
}
return(po);
}
/**
\brief Append data to a U_PSEUDO_OBJ object and return it
\returns pointer to the U_PSEUDO_OBJ object, NULL on error
\param po PseudoObject to append to. Cannot be NULL.
\param Data Data to copy into the PseudoObject's data. If NULL, space is allocated (if necessary) and cleared instead of filled.
\param Size Number of data bytes in Data
*/
U_PSEUDO_OBJ *U_PO_append(U_PSEUDO_OBJ *po, const char *Data, size_t Size){
/* po cannot be NULL,as in U_PO_po_append(), because there would be no way to determine the TYPE of the resulting PO */
if(po){
if(!po->Data || po->Used + Size > po->Size){
po->Size = po->Used + Size;
char *newData = realloc(po->Data, po->Size);
if(!newData){
po->Size -= Size; /* put it back the way it was*/
po=NULL; /* skip the rest of the actions, does not affect po in caller */
}
else {
po->Data = newData;
}
}
if(po){ /* po->Data ready to append new data */
if(Data){ memcpy(po->Data + po->Used, Data, Size); }
else { memset(po->Data + po->Used, 0, Size); }
po->Used += Size;
}
}
return(po);
}
/**
\brief Append data to a U_PSEUDO_OBJ object and return it
\returns pointer to the U_PSEUDO_OBJ object, NULL on error
\param po PseudoObject to append to. May be NULL.
\param Src PseudoObject to append.
\param StripE Set: leading Elements in Src->Data is not copied, Clear: it is copied.
*/
U_PSEUDO_OBJ *U_PO_po_append(U_PSEUDO_OBJ *po, U_PSEUDO_OBJ *Src, int StripE){
if(!Src){ return(NULL); }
if((StripE && (Src->Used == 4)) || !Src->Used){ return(po); } /* appending nothing is not an error */
char *Data = Src->Data;
size_t Size = Src->Used; /* append only what is used */
U_PSEUDO_OBJ *ipo = po;
if(StripE){ Size -= 4; }
if(!ipo){
ipo = U_PO_create(NULL, 0, 0, Src->Type); /* create an empty pseudoobject */
}
if(ipo){
if(!ipo->Data || ipo->Used + Size > ipo->Size){
ipo->Size = ipo->Used + Size;
char *newData = realloc(ipo->Data, ipo->Size);
if(!newData){
if(ipo != po)U_PO_free(&ipo);
}
else {
ipo->Data = newData;
}
}
if(ipo){
if(Data){
if(StripE){ memcpy(ipo->Data + ipo->Used, Data + 4, Size); } /* Size is already 4 less, skip the leading Elements value */
else { memcpy(ipo->Data + ipo->Used, Data, Size); } /* copy everything */
}
else { memset(ipo->Data + ipo->Used, 0, Size); } /* set everything */
ipo->Used += Size;
}
}
return(ipo);
}
/**
\brief Free an U_PSEUDO_OBJ structure. All associated memory is released.
\param po Address of a pointer to the U_PSEUDO_OBJ structure, Pointer is set to NULL.
\returns 1 on success, 0 on error.
*/
int U_PO_free(U_PSEUDO_OBJ **po){
if(!po)return(0);
if(!*po)return(1);
if((*po)->Data)free((*po)->Data);
free(*po);
*po=NULL;
return(1);
}
/** \brief create a PseudoObject with data in the correct byte order for an EMF+ file.
\returns The PseudoObject on success, NULL on error.
\param Type the type of the PseudoObject that is created. Allowed values are in U_PID_Values.
\param List an array of U_SERIAL_DESC structures containing the data to store.
The U_PMF_SERIAL_set() function should not ever be called directly by end user code.
Each U_SERIAL_DESC element in List consists of Data fields and a description of that data. List is terminated
by the first U_SERIAL_DESC element having a TE value of U_XX.
Data fields: an array of a basic type of Units bytes repeated Reps times with the target byte order
described in TE.
Ptrs: Address of the first byte of the data fields.
Units: Number of bytes of in each data field unit
Reps: Number of repeats of the unit in data fields.
If a Ptr is NULL, and Units*Reps is not zero, then Units*Reps 0x00 bytes are stored.
If a Ptr is NULL, and Units*Reps is zero, this U_SERIAL_DESC is ignored.
if a Ptr is NOT NULL, and Units * Reps is not zero, then the data is stored in the indicated byte order.
If a Ptr is NOT NULL, and Units or Reps is zero an error is signaled.
TE: (Target Endian) the byte order in which to store each unit of a data field as defined in U_Endian.
Byte swapping is only enabled when Units is 2 or 4. In addition to the byte order values U_XE, U_LE,
and U_BE, and the array terminator U_XX, the value may also be U_RP. U_RP means there is only a
single unit in the data fields, but it is to be copied to the target Reps times. That is, the data
was passed in with a form of run length encoding.
Creates an empty PseudoObject if all pointers are NULL and all sizes are zero.
*/
U_PSEUDO_OBJ *U_PMF_SERIAL_set(uint32_t Type, const U_SERIAL_DESC *List){
U_PSEUDO_OBJ *po=NULL;
size_t Total=0;
size_t FSize;
char *cptr;
char *hptr;
const U_SERIAL_DESC *lptr;
if(!List)return(NULL);
for(lptr=List; lptr->TE != U_XX; lptr++){
FSize = lptr->Units * lptr->Reps;
if(!FSize && lptr->Ptr)return(po);
Total += FSize;
}
po = U_PO_create(NULL, Total, Total, Type);
if(po){
cptr = po->Data;
for(lptr=List; lptr->TE != U_XX; lptr++){
FSize = lptr->Units * lptr->Reps;
if(FSize){ /* Ptr is not NULL, that would have been detected already */
hptr = cptr;
if(lptr->TE & U_RP){ U_PMF_REPCPY_DSTSHIFT(&cptr, lptr->Ptr, lptr->Units, lptr->Reps); }
else { U_PMF_MEMCPY_DSTSHIFT(&cptr, lptr->Ptr, FSize); }
if(((lptr->TE & U_LE) && U_IS_BE) || ((lptr->TE & U_BE) && U_IS_LE)){
if(lptr->Units==2){ U_swap2(hptr,lptr->Reps); }
else if(lptr->Units==4){ U_swap4(hptr,lptr->Reps); }
}
}
}
}
return(po);
}
/**
\brief Create U_DPSEUDO_OBJ's for the Points and Types of a path
\param Elements Number of elements in Points. May be zero, which creates an empty path.
\param Points Array of U_PMF_POINTF values.
\param First Apply to first point, unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration. Must have U_PPT_Start set.
\param Others Apply to all other points, unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration. Must have U_PPT_Line or U_PPT_Bezier set.
\returns pointer to the DU_PSEUDO_OBJ object, NULL on error
*/
U_DPSEUDO_OBJ *U_PATH_create(int Elements, const U_PMF_POINTF *Points, uint8_t First, uint8_t Others){
if(Elements){
if(!Points){ return(NULL); }
if( (First & U_PPT_MASK) != U_PPT_Start ){ return(NULL); }
if(!(Others & U_PPT_Bezier)){ return(NULL); } /* will pass if either line or bezier is set */
}
U_DPSEUDO_OBJ *Path = (U_DPSEUDO_OBJ *)calloc(sizeof(U_DPSEUDO_OBJ),1); /* make poTypes and poPoints NULL */
const U_SERIAL_DESC List[] = { {NULL,0,0,U_XX} };
if(Path){
Path->Elements = Elements;
Path->poPoints = U_PMF_SERIAL_set(U_PMF_RAW_OID, List); /* Empty PO to hold points as raw data */
if(!Elements){
Path->poTypes = U_PMF_SERIAL_set(U_PMF_RAW_OID, List); /* Empty PO to hold types as raw data */
}
else {
Path->poPoints = U_PO_append(Path->poPoints, (char *)Points, Elements*sizeof(U_PMF_POINTF));
if(Path->poPoints){
U_PSEUDO_OBJ *tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First | U_PPT_Start, Others); /* PO holding types, has leading Elements value */
Path->poTypes = U_PO_po_append(NULL, tpo, U_PMF_DROP_ELEMENTS); /* remove the leading Elements value*/
U_PO_free(&tpo);
if(!Path->poTypes){ U_PO_free(&Path->poPoints); }
}
if(!Path->poPoints){ U_DPO_free(&Path); }
}
}
return(Path);
}
/**
\brief Free U_DPSEUDO_OBJ's
\returns 1 on success, 0 on error.
*/
int U_DPO_free(U_DPSEUDO_OBJ **dpo){
if(!dpo){ return(0); }
if(!*dpo){ return(1); }
U_DPSEUDO_OBJ *kpo = *dpo;
if(kpo->poPoints){ U_PO_free(&kpo->poPoints); }
if(kpo->poTypes){ U_PO_free(&kpo->poTypes); }
free(*dpo);
*dpo=NULL;
return(1);
}
/**
\brief Clear U_DPSEUDO_OBJ's. Memory is retained, Elements and Used values are set to 0.
\returns 1 on success, 0 on error.
It is much more efficient to clear a DPO and reuse it than to free that DPO and create another.
*/
int U_DPO_clear(U_DPSEUDO_OBJ *dpo){
if(!dpo){ return(0); }
if(dpo->poPoints){ dpo->poPoints->Used = 0; }
if(dpo->poTypes){ dpo->poTypes->Used = 0; }
dpo->Elements = 0;
return(1);
}
/**
\brief Append a "moveto" point to a path
\param Path Address of a DoublePseudoObject holding the path to append to.
\param Point Point to move to.
\param Flags Flags may be (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, U_PTP_CloseSubpath)
\returns 1 on success, 0 on error.
*/
int U_PATH_moveto(U_DPSEUDO_OBJ *Path, U_PMF_POINTF Point, uint8_t Flags){
if(!Path){ return(0); }
U_PSEUDO_OBJ *tpo;
U_PSEUDO_OBJ *tpo2;
uint8_t Type = (Flags & U_PTP_NotClose) | U_PPT_Start;
tpo = U_PMF_POINTF_set(1, &Point);
if(!tpo){ return(0); }
tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poPoints = tpo2;
tpo = U_PMF_PATHPOINTTYPE_set(1, &Type);
if(!tpo){ return(0); }
tpo2= U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poTypes = tpo2;
Path->Elements++;
return(1);
}
/**
\brief Append a "lineto" point to a path
\param Path Address of a DoublePseudoObject holding the path to append to.
\param Point U_PMF_POINTF point to draw to.
\param Flags Flags may be (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, U_PTP_CloseSubpath)
\returns 1 on success, 0 on error.
*/
int U_PATH_lineto(U_DPSEUDO_OBJ *Path, U_PMF_POINTF Point, uint8_t Flags){
if(!Path || !Path->Elements){ return(0); } /* must be at least one point to extend from */
if(Path->poTypes->Data[Path->Elements - 1] & U_PTP_CloseSubpath){ return(0); } /* cannot extend a closed subpath */
U_PSEUDO_OBJ *tpo;
U_PSEUDO_OBJ *tpo2;
uint8_t Type = (Flags & U_PTP_NotClose) | U_PPT_Line;
tpo = U_PMF_POINTF_set(1, &Point);
if(!tpo){ return(0); }
tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poPoints = tpo2;
tpo = U_PMF_PATHPOINTTYPE_set(1, &Type);
if(!tpo){ return(0); }
tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poTypes = tpo2;
Path->Elements++;
return(1);
}
/**
\brief Set the closepath bit in the last point
\param Path Address of a DoublePseudoObject holding the path to act upon.
\returns 1 on success, 0 on error.
*/
int U_PATH_closepath(U_DPSEUDO_OBJ *Path){
if(!Path || !Path->poTypes){ return(0); }
uint32_t Elements = Path->Elements;
uint8_t *Type = (uint8_t *)(Path->poTypes->Data) + Elements - 1;
if(*Type & U_PPT_Start){ return(0); } /* single point closed path makes no sense */
*Type = *Type | U_PTP_CloseSubpath;
return(1);
}
/**
\brief Append a "polylineto" set of point to a path
\param Path Address of a DoublePseudoObject holding the path to append to.
\param Elements number of Points and Flags
\param Points Line points.
\param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
\param StartSeg If set, use U_PPT_Start PathPointType enumeration for first point, otherwise use U_PPT_Line.
\returns 1 on success, 0 on error.
*/
int U_PATH_polylineto(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags, uint8_t StartSeg){
if(!Path || !Points){ return(0); }
if(!Elements){ return(1); } /* harmless - do nothing */
U_PSEUDO_OBJ *tpo;
U_PSEUDO_OBJ *tpo2;
uint8_t First, Others;
tpo = U_PMF_POINTF_set(Elements, Points);
tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poPoints = tpo2;
if(StartSeg){ First = (Flags & U_PTP_NotClose) | U_PPT_Start; }
else { First = (Flags & U_PTP_NotClose) | U_PPT_Line; }
Others = (Flags & U_PTP_NotClose) | U_PPT_Line;
tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First, Others);
if(!tpo){ return(0); }
tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poTypes = tpo2;
Path->Elements += Elements;
return(1);
}
/**
\brief Append a "polybezierto" set of point to a path
\param Path Address of a DoublePseudoObject holding the path to append to.
\param Elements number of Points
\param Points Bezier points. Optional starting point, then N sets of 3, example: [P1] (Q12A Q12B P2) (Q23A Q23B P3).
\param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
\param StartSeg If set, use U_PPT_Start PathPointType enumeration for first point, otherwise use U_PPT_Bezier.
\returns 1 on success, 0 on error.
If Start is set Elements must be 1 + multiple of 3. Ie, P1 Q12A Q12B P2 Q23A Q23B P3
If Start is clear Elements must be a multiple of 3. Ie, (P1, already in path) Q12A Q12B P2 Q23A Q23B P3
*/
int U_PATH_polybezierto(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags, uint8_t StartSeg){
if(!Path || !Points){ return(0); }
if(!Elements){
if(StartSeg){ return(0); } /* cannot have both a NEW segment and ZERO points */
else{ return(1); } /* harmless - do nothing */
}
if(StartSeg && ((Elements - 1) % 3)){ return(0); } /* new segment must be 1 + N*3 points */
if(!StartSeg && (Elements % 3)){ return(0); } /* extend segment must be N*3 points */
U_PSEUDO_OBJ *tpo;
U_PSEUDO_OBJ *tpo2;
uint8_t First, Others;
tpo = U_PMF_POINTF_set(Elements, Points);
tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poPoints = tpo2;
if(StartSeg){ First = (Flags & U_PTP_NotClose) | U_PPT_Start; }
else { First = (Flags & U_PTP_NotClose) | U_PPT_Bezier; }
Others = (Flags & U_PTP_NotClose) | U_PPT_Bezier;
tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First, Others);
if(!tpo){ return(0); }
tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
U_PO_free(&tpo);
if(!tpo2)return(0);
Path->poTypes = tpo2;
Path->Elements += Elements;
return(1);
}
/**
\brief Append a "polygon" set of points to a path.
\param Path Address of a DoublePseudoObject holding the path to append to.
\param Elements number of Points and Flags
\param Points Line points.
\param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
\returns 1 on success, 0 on error.
*/
int U_PATH_polygon(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags){
int status = U_PATH_polylineto(Path, Elements, Points, Flags, U_SEG_NEW);
if(status){
status = U_PATH_closepath(Path);
}
return(status);
}
//! \cond
// These are not exposed in the API
/* Parameterized Ellipse coordinates */
U_PMF_POINTF U_eparam(U_FLOAT a, U_FLOAT b, U_PMF_POINTF *center, double Ang, double Theta){
U_PMF_POINTF point;
point.X = center->X + a*cos(Theta)*cos(Ang) - b*sin(Theta)*sin(Ang);
point.Y = center->Y + a*sin(Theta)*cos(Ang) + b*cos(Theta)*sin(Ang);
return(point);
}
/* Parameterized Ellipse derivative */
U_PMF_POINTF U_eparam2(U_FLOAT a, U_FLOAT b, double Ang, double Theta){
U_PMF_POINTF point;
point.X = -a*cos(Theta)*sin(Ang) - b*sin(Theta)*cos(Ang);
point.Y = -a*sin(Theta)*sin(Ang) + b*cos(Theta)*cos(Ang);
return(point);
}
double U_aparam(double Ang1, double Ang2){
double Alpha;
double t2;
t2 = tan((Ang2 - Ang1)/2.0);
t2 *= t2;
Alpha = sin(Ang2 - Ang1) * (sqrt(4 + 3*t2) -1.0)/3.0;
return(Alpha);
}
/* Parameterized Bezier point Q1 or Q2 derivative */
U_PMF_POINTF U_qparam(double Alpha, double a, double b, U_PMF_POINTF *Point, double Ang, double Theta, int mode){
U_PMF_POINTF Q, Prime;
Prime = U_eparam2(a,b,Ang,Theta);
if(mode==1){ /* Q1, anything else is Q2*/
Q.X = Point->X + Alpha * Prime.X;
Q.Y = Point->Y + Alpha * Prime.Y;
}
else {
Q.X = Point->X - Alpha * Prime.X;
Q.Y = Point->Y - Alpha * Prime.Y;
}
return(Q);
}
//! \endcond
/**
\brief Append an "arcto" set of points to a path (Bezier points are calculated, and these are appended
\param Path Address of a pointer to the U_PSEUDO_OBJ that holds points
\param Start Start angle, >=0.0, degrees clockwise from 3:00
\param Sweep Sweep angle, -360<= angle <=360, degrees clockwise from Start
\param Rot Rotation angle to apply to coordinate system (Start and Rect), positive is degrees clockwise
\param Rect U_PMF_RECTF that defines the bounding rectangle.
\param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
\param StartSeg If set, the arc starts a new segment, if clear, continue the existing segment. Starting a new segment does not automatically apply U_PATH_closepath to the existing path.
\returns 1 on success, 0 on error.
Based on Luc Maisonobe's work, http://www.spaceroots.org/documents/ellipse/
*/
int U_PATH_arcto(U_DPSEUDO_OBJ *Path, U_FLOAT Start, U_FLOAT Sweep, U_FLOAT Rot, U_PMF_RECTF *Rect, uint8_t Flags, int StartSeg){
U_PMF_POINTF Bz[3];
U_PMF_POINTF Center;
U_PMF_POINTF P1,P2;
double a, b;
int done = 0;
int fpoint = 0;
double L1, L2; /* These are degrees CounterClockwise from 3:00 */
double Ang1, Ang2; /* These are parametric angles, derived from L1, L2*/
double Alpha; /* Dimensionless number used for Q1, Q2 */
double Theta; /* Rot in radians */
double Slop; /* do not let rounding errors cause spurious end points */
if(!Path){ return(0); }
if((Sweep > 360.0) || (Sweep < -360.0)){ return(0); }
/* Start should be between 0 and 360 degrees, but it does not really matter because sin, and cos will accept anything */
/* the sign on Sweep and Start is correct bacause LM's derivation has y positive up, but GDI+ has y positive down. */
a = Rect->Width/2.0;
b = Rect->Height/2.0;
if(!a || !b){ return(0); }
Center.X = Rect->X + a;
Center.Y = Rect->Y + b;
/* convert to radians */
Start = (2.0 * U_PI * Start)/360.0;
Sweep = (2.0 * U_PI * Sweep)/360.0;
Theta = (2.0 * U_PI * Rot )/360.0;
Slop = Sweep/100000.0;