1 /**
2 Common part of the renderers.
3 
4 Copyright: Guillaume Piolat 2021.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module printed.canvas.internals;
8 
9 import std.range, std.traits;
10 
11 
12 /// Validate line dash pattern. A dash pattern is valid if all values are
13 /// finite and non-negative.
14 bool isValidLineDashPattern(float[] segments)
15 {
16     import std.algorithm : all;
17     import std.range.primitives;
18 
19     return segments.all!(x => 0 <= x && x <= float.infinity);
20 }
21 
22 
23 /// Normalize line dash pattern, i.e. the array returned will always have an
24 /// even number of entries.
25 ///
26 /// Returns: a copy of segments if the number of entries is even; otherwise
27 ///          the concatenation of segments with itself.
28 float[] normalizeLineDashPattern(float[] segments)
29 {
30     if (segments.length % 2 == 0)
31         return segments.dup;
32     else
33         return segments ~ segments;
34 }
35 
36 
37 public
38 {
39     /// Writes a big endian integer/float to output.
40     void writeBE(T, R)(ref R output, T n) if (isOutputRange!(R, ubyte))
41     {
42         writeFunction!(T, R, false)(output, n);
43     }
44 }
45 
46 private
47 {
48     // read/write 64-bits float
49     union float_uint
50     {
51         float f;
52         uint i;
53     }
54 
55     // read/write 64-bits float
56     union double_ulong
57     {
58         double f;
59         ulong i;
60     }
61 
62     uint float2uint(float x) pure nothrow
63     {
64         float_uint fi;
65         fi.f = x;
66         return fi.i;
67     }
68 
69     float uint2float(int x) pure nothrow
70     {
71         float_uint fi;
72         fi.i = x;
73         return fi.f;
74     }
75 
76     ulong double2ulong(double x) pure nothrow
77     {
78         double_ulong fi;
79         fi.f = x;
80         return fi.i;
81     }
82 
83     double ulong2double(ulong x) pure nothrow
84     {
85         double_ulong fi;
86         fi.i = x;
87         return fi.f;
88     }
89 
90     private template IntegerLargerThan(int numBytes) if (numBytes >= 1 && numBytes <= 8)
91     {
92         static if (numBytes == 1)
93             alias IntegerLargerThan = ubyte;
94         else static if (numBytes == 2)
95             alias IntegerLargerThan = ushort;
96         else static if (numBytes <= 4)
97             alias IntegerLargerThan = uint;
98         else
99             alias IntegerLargerThan = ulong;
100     }
101 
102     // Generic integer writing
103     void writeInteger(R, int NumBytes, bool LittleEndian)(ref R output, IntegerLargerThan!NumBytes n) if (isOutputRange!(R, ubyte))
104     {
105         alias T = IntegerLargerThan!NumBytes;
106 
107         auto u = cast(Unsigned!T)n;
108 
109         static if (LittleEndian)
110         {
111             for (int i = 0; i < NumBytes; ++i)
112             {
113                 ubyte b = (u >> (i * 8)) & 255;
114                 output.put(b);
115             }
116         }
117         else
118         {
119             for (int i = 0; i < NumBytes; ++i)
120             {
121                 ubyte b = (u >> ( (NumBytes - 1 - i) * 8) ) & 255;
122                 output.put(b);
123             }
124         }
125     }
126 
127     void writeFunction(T, R, bool endian)(ref R output, T n) if (isOutputRange!(R, ubyte))
128     {
129         static if (isIntegral!T)
130             writeInteger!(R, T.sizeof, endian)(output, n);
131         else static if (is(T : float))
132             writeInteger!(R, 4, endian)(output, float2uint(n));
133         else static if (is(T : double))
134             writeInteger!(R, 8, endian)(output, double2ulong(n));
135         else
136             static assert(false, "Unsupported type " ~ T.stringof);
137     }
138 }