egra: added some useful primitives to agg drawer
[iv.d.git] / egra / gfx / aggmini.d
blobabb48422c07c0aa122189a24b1dd1e6e84c8792e
1 /* *****************************************************************************
2 Anti-Grain Geometry - Version 2.1 Lite
3 Copyright (C) 2002-2003 Maxim Shemanarev (McSeem)
4 D port, additional work: Ketmar Dark
6 Permission to copy, use, modify, sell and distribute this software
7 is granted provided this copyright notice appears in all copies.
8 This software is provided "as is" without express or implied
9 warranty, and with no claim as to its suitability for any purpose.
11 The author gratefully acknowleges the support of David Turner,
12 Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
13 libray - in producing this work. See http://www.freetype.org for details.
15 Initially the rendering algorithm was designed by David Turner and the
16 other authors of the FreeType library - see the above notice. I nearly
17 created a similar renderer, but still I was far from David's work.
18 I completely redesigned the original code and adapted it for Anti-Grain
19 ideas. Two functions - renderLine and renderScanLine are the core of
20 the algorithm - they calculate the exact coverage of each pixel cell
21 of the polygon. I left these functions almost as is, because there's
22 no way to improve the perfection - hats off to David and his group!
24 All other code is very different from the original.
25 ***************************************************************************** */
26 module iv.egra.gfx.aggmini;
27 private:
28 nothrow @safe @nogc:
30 private enum DisableCopyingMixin = "@disable this (this); void opAssign() (in auto ref typeof(this)) { static assert(0, `assigning disabled`); }";
32 import iv.egra.gfx.base;
33 import iv.egra.gfx.lowlevel;
35 /* Bezier curve rasterizer.
37 * De Casteljau Bezier rasterizer is faster, but currently rasterizing curves with cusps sligtly wrong.
38 * It doesn't really matter in practice.
40 * AFD tesselator is somewhat slower, but does cusps better.
42 * McSeem rasterizer should have the best quality, bit it is the slowest method. Basically, you will
43 * never notice any visial difference (and this code is not really debugged), so you probably should
44 * not use it. It is there for further experiments.
46 public enum AGGTesselation {
47 DeCasteljau, /// default: standard well-known tesselation algorithm
48 AFD, /// adaptive forward differencing
49 DeCasteljauMcSeem, /// standard well-known tesselation algorithm, with improvements from Maxim Shemanarev; slowest one, but should give best results
52 // Default tesselator for Bezier curves.
53 //public __gshared AGGTesselation AGGDefaultTesselator = AGGTesselation.DeCasteljau;
56 // ////////////////////////////////////////////////////////////////////////// //
57 // a pixel cell
58 align(1) struct Cell {
59 align(1):
60 public:
61 static uint packCoord (in int x, in int y) pure nothrow @safe @nogc { pragma(inline, true); return (y<<16)|(x&0xffff); }
63 public:
64 uint packedCoord;
65 int cover;
66 int area;
68 public nothrow @trusted @nogc:
69 this (in int cx, in int cy, in int c, in int a) pure { pragma(inline, true); packedCoord = packCoord(cx, cy); cover = c; area = a; }
71 @property int x () const pure { pragma(inline, true); return cast(short)(packedCoord&0xffff); }
72 @property int y () const pure { pragma(inline, true); return cast(short)(packedCoord>>16); }
74 void setCover (in int c, in int a) { pragma(inline, true); cover = c; area = a; }
75 void addCover (in int c, in int a) { pragma(inline, true); cover += c; area += a; }
77 void setCoord (in int cx, in int cy) { pragma(inline, true); packedCoord = packCoord(cx, cy); }
79 void set (in int cx, in int cy, in int c, in int a) { pragma(inline, true); packedCoord = packCoord(cx, cy); cover = c; area = a; }
83 // ////////////////////////////////////////////////////////////////////////// //
84 /* An internal class that implements the main rasterization algorithm.
85 Used in the rasterizer. Should not be used direcly.
87 struct Outline {
88 private nothrow @trusted @nogc:
89 enum : uint {
90 CellBlockShift = 12U,
91 CellBlockSize = cast(uint)(1<<CellBlockShift),
92 CellBlockMask = cast(uint)(CellBlockSize-1),
93 CellBlockPool = 256U,
94 CellBlockLimit = 1024U,
97 enum QSortThreshold = 9;
99 enum : uint {
100 NotClosed = 1U,
101 SortRequired = 2U,
104 private:
105 uint mNumBlocks;
106 uint mMaxBlocks;
107 uint mCurBlock;
108 uint mNumCells;
109 Cell** mCells;
110 Cell* mCurCellPtr;
111 Cell** mSortedCells;
112 uint mSortedSize;
113 Cell mCurCell = Cell(0x7fff, 0x7fff, 0, 0);
114 int mCurX;
115 int mCurY;
116 int mCloseX;
117 int mCloseY;
118 int mMinX = 0x7fffffff;
119 int mMinY = 0x7fffffff;
120 int mMaxX = -0x7fffffff;
121 int mMaxY = -0x7fffffff;
122 uint mFlags = SortRequired;
124 private:
125 void allocateBlock () {
126 import core.stdc.stdlib : realloc;
127 if (mCurBlock >= mNumBlocks) {
128 import core.stdc.string : memset;
129 if (mNumBlocks >= mMaxBlocks) {
130 Cell** newCells = cast(Cell**)realloc(mCells, (mMaxBlocks+CellBlockPool)*(Cell*).sizeof);
131 if (newCells is null) assert(0, "out of memory");
132 mCells = newCells;
133 mMaxBlocks += CellBlockPool;
135 auto cc = cast(Cell*)realloc(null, Cell.sizeof*CellBlockSize);
136 if (cc is null) assert(0, "out of memory");
137 memset(cc, 0, Cell.sizeof*CellBlockSize);
138 //foreach (ref c; cc[0..CellBlockSize]) c = Cell.init;
139 mCells[mNumBlocks++] = cc;
141 mCurCellPtr = mCells[mCurBlock++];
144 public:
145 mixin(DisableCopyingMixin);
147 ~this () {
148 import core.stdc.stdlib : free;
149 free(mSortedCells);
150 if (mNumBlocks) {
151 Cell** ptr = mCells+mNumBlocks-1;
152 while (mNumBlocks--) {
153 free(*ptr);
154 --ptr;
156 free(mCells);
160 void reset () {
161 mNumCells = 0;
162 mCurBlock = 0;
163 mCurCell.set(0x7fff, 0x7fff, 0, 0);
164 mFlags |= SortRequired;
165 mFlags &= ~NotClosed;
166 mMinX = 0x7fffffff;
167 mMinY = 0x7fffffff;
168 mMaxX = -0x7fffffff;
169 mMaxY = -0x7fffffff;
172 void moveTo (in int x, in int y) {
173 if ((mFlags&SortRequired) == 0) reset();
174 if (mFlags&NotClosed) lineTo(mCloseX, mCloseY);
175 setCurCell(x>>PolyBaseShift, y>>PolyBaseShift);
176 mCloseX = mCurX = x;
177 mCloseY = mCurY = y;
180 void lineTo (in int x, in int y) {
181 if ((mFlags&SortRequired) && ((mCurX^x)|(mCurY^y))) {
182 int c = mCurX>>PolyBaseShift;
183 if (c < mMinX) mMinX = c;
184 ++c;
185 if (c > mMaxX) mMaxX = c;
187 c = x>>PolyBaseShift;
188 if (c < mMinX) mMinX = c;
189 ++c;
190 if (c > mMaxX) mMaxX = c;
192 renderLine(mCurX, mCurY, x, y);
193 mCurX = x;
194 mCurY = y;
195 mFlags |= NotClosed;
199 @property float currX () const pure { pragma(inline, true); return cast(float)mCurX/cast(float)PolyBaseSize; }
200 @property float currY () const pure { pragma(inline, true); return cast(float)mCurY/cast(float)PolyBaseSize; }
202 @property int minX () const pure { pragma(inline, true); return mMinX; }
203 @property int minY () const pure { pragma(inline, true); return mMinY; }
204 @property int maxX () const pure { pragma(inline, true); return mMaxX; }
205 @property int maxY () const pure { pragma(inline, true); return mMaxY; }
207 @property uint numCells () const pure { pragma(inline, true); return mNumCells; }
209 const(Cell)** cells () {
210 if (mFlags&NotClosed) {
211 lineTo(mCloseX, mCloseY);
212 mFlags &= ~NotClosed;
214 // perform sort only the first time
215 if (mFlags&SortRequired) {
216 addCurCell();
217 if (mNumCells == 0) return null;
218 sortCells();
219 mFlags &= ~SortRequired;
221 return cast(const(Cell)**)mSortedCells;
224 private:
225 void addCurCell () {
226 if (mCurCell.area|mCurCell.cover) {
227 if ((mNumCells&CellBlockMask) == 0) {
228 if (mNumBlocks >= CellBlockLimit) return;
229 allocateBlock();
231 *mCurCellPtr++ = mCurCell;
232 ++mNumCells;
236 void setCurCell (in int x, in int y) {
237 if (mCurCell.packedCoord != Cell.packCoord(x, y)) {
238 addCurCell();
239 mCurCell.set(x, y, 0, 0);
243 void renderScanLine (in int ey, in int x1, int y1, in int x2, in int y2) {
244 immutable int ex2 = x2>>PolyBaseShift;
246 // trivial case; happens often
247 if (y1 == y2) {
248 setCurCell(ex2, ey);
249 return;
252 int ex1 = x1>>PolyBaseShift;
253 immutable int fx1 = x1&PolyBaseMask;
254 immutable int fx2 = x2&PolyBaseMask;
256 // everything is located in a single cell: that is easy!
257 if (ex1 == ex2) {
258 immutable int delta = y2-y1;
259 mCurCell.addCover(delta, (fx1+fx2)*delta);
260 return;
263 // ok, we'll have to render a run of adjacent cells on the same scanline...
264 int p = (PolyBaseSize-fx1)*(y2-y1);
265 int first = PolyBaseSize;
266 int incr = 1;
268 int dx = x2-x1;
270 if (dx < 0) {
271 p = fx1*(y2-y1);
272 first = 0;
273 incr = -1;
274 dx = -dx;
277 int delta = p/dx;
278 int mod = p%dx;
279 if (mod < 0) { --delta; mod += dx; }
281 mCurCell.addCover(delta, (fx1+first)*delta);
283 ex1 += incr;
284 setCurCell(ex1, ey);
285 y1 += delta;
287 if (ex1 != ex2) {
288 p = PolyBaseSize*(y2-y1+delta);
289 int lift = p/dx;
290 int rem = p%dx;
291 if (rem < 0) { --lift; rem += dx; }
292 mod -= dx;
293 while (ex1 != ex2) {
294 delta = lift;
295 mod += rem;
296 if (mod >= 0) { mod -= dx; ++delta; }
297 mCurCell.addCover(delta, PolyBaseSize*delta);
298 y1 += delta;
299 ex1 += incr;
300 setCurCell(ex1, ey);
304 delta = y2-y1;
305 mCurCell.addCover(delta, (fx2+PolyBaseSize-first)*delta);
308 void renderLine (in int x1, in int y1, in int x2, in int y2) {
309 int ey1 = y1>>PolyBaseShift;
310 immutable int ey2 = y2>>PolyBaseShift;
312 if (ey1 < mMinY) mMinY = ey1;
313 if (ey1+1 > mMaxY) mMaxY = ey1+1;
314 if (ey2 < mMinY) mMinY = ey2;
315 if (ey2+1 > mMaxY) mMaxY = ey2+1;
317 immutable int fy1 = y1&PolyBaseMask;
318 immutable int fy2 = y2&PolyBaseMask;
320 // everything is on a single scanline
321 if (ey1 == ey2) {
322 renderScanLine(ey1, x1, fy1, x2, fy2);
323 return;
326 int dx = x2-x1;
327 int dy = y2-y1;
329 //int xFrom, xTo;
330 //int p, rem, mod, lift, delta, first, incr;
332 // vertical line: we have to calculate start and end cells,
333 // and then the common values of the area and coverage for
334 // all cells of the line. we know exactly there's only one
335 // cell, so, we don't have to call renderScanLine().
336 int incr = 1;
337 if (dx == 0) {
338 int ex = x1>>PolyBaseShift;
339 int twoFx = (x1-(ex<<PolyBaseShift))<<1;
340 int area;
342 int first = PolyBaseSize;
343 if (dy < 0) { first = 0; incr = -1; }
345 immutable int xFrom = x1;
347 //renderScanLine(ey1, xFrom, fy1, xFrom, first);
348 int delta = first-fy1;
349 mCurCell.addCover(delta, twoFx*delta);
351 ey1 += incr;
352 setCurCell(ex, ey1);
354 delta = first+first-PolyBaseSize;
355 area = twoFx*delta;
356 while (ey1 != ey2) {
357 //renderScanLine(ey1, xFrom, PolyBaseSize - first, xFrom, first);
358 mCurCell.setCover(delta, area);
359 ey1 += incr;
360 setCurCell(ex, ey1);
362 //renderScanLine(ey1, xFrom, PolyBaseSize - first, xFrom, fy2);
363 delta = fy2-PolyBaseSize+first;
364 mCurCell.addCover(delta, twoFx*delta);
365 return;
368 // ok, we have to render several scanlines
369 int p = (PolyBaseSize-fy1)*dx;
370 int first = PolyBaseSize;
372 if (dy < 0) {
373 p = fy1*dx;
374 first = 0;
375 incr = -1;
376 dy = -dy;
379 int delta = p/dy;
380 int mod = p%dy;
382 if (mod < 0) { --delta; mod += dy; }
384 int xFrom = x1+delta;
385 renderScanLine(ey1, x1, fy1, xFrom, first);
387 ey1 += incr;
388 setCurCell(xFrom>>PolyBaseShift, ey1);
390 if (ey1 != ey2) {
391 p = PolyBaseSize*dx;
392 int lift = p/dy;
393 int rem = p%dy;
395 if (rem < 0) { --lift; rem += dy; }
396 mod -= dy;
398 while (ey1 != ey2) {
399 delta = lift;
400 mod += rem;
401 if (mod >= 0) { mod -= dy; ++delta; }
403 immutable int xTo = xFrom+delta;
404 renderScanLine(ey1, xFrom, PolyBaseSize-first, xTo, first);
405 xFrom = xTo;
407 ey1 += incr;
408 setCurCell(xFrom>>PolyBaseShift, ey1);
412 renderScanLine(ey1, xFrom, PolyBaseSize-first, x2, fy2);
415 private:
416 static void qsortCells (Cell** start, uint num) {
417 static void swapCells (Cell** a, Cell** b) nothrow @trusted @nogc {
418 pragma(inline, true);
419 auto temp = *a;
420 *a = *b;
421 *b = temp;
424 static bool lessThan (Cell** a, Cell** b) nothrow @trusted @nogc pure { pragma(inline, true); return ((**a).packedCoord < (**b).packedCoord); }
426 Cell**[80] stack = void;
427 Cell*** top;
428 Cell** limit;
429 Cell** base;
431 limit = start+num;
432 base = start;
433 top = stack.ptr;
435 for (;;) {
436 immutable int len = cast(int)(limit-base);
438 if (len > QSortThreshold) {
439 // we use base + len/2 as the pivot
440 auto pivot = base+len/2;
441 swapCells(base, pivot);
443 auto i = base+1;
444 auto j = limit-1;
446 // now ensure that *i <= *base <= *j
447 if (lessThan(j, i)) swapCells(i, j);
448 if (lessThan(base, i)) swapCells(base, i);
449 if (lessThan(j, base)) swapCells(base, j);
451 for (;;) {
452 do { ++i; } while (lessThan(i, base));
453 do { --j; } while (lessThan(base, j));
454 if (i > j) break;
455 swapCells(i, j);
458 swapCells(base, j);
460 // now, push the largest sub-array
461 if (j-base > limit-i) {
462 top[0] = base;
463 top[1] = j;
464 base = i;
465 } else {
466 top[0] = i;
467 top[1] = limit;
468 limit = j;
470 top += 2;
471 } else {
472 // the sub-array is small, perform insertion sort
473 auto j = base;
474 auto i = j+1;
475 for (; i < limit; j = i, ++i) {
476 for (; lessThan(j+1, j); --j) {
477 swapCells(j+1, j);
478 if (j == base) break;
481 if (top > stack.ptr) {
482 top -= 2;
483 base = top[0];
484 limit = top[1];
485 } else {
486 break;
492 void sortCells () {
493 if (mNumCells == 0) return;
495 if (mNumCells > mSortedSize) {
496 import core.stdc.stdlib: realloc;
497 mSortedSize = mNumCells;
498 mSortedCells = cast(Cell**)realloc(mSortedCells, (mNumCells+1)*(Cell*).sizeof);
501 Cell** sortedPtr = mSortedCells;
502 Cell** blockPtr = mCells;
503 Cell* cellPtr;
505 uint nb = mNumCells>>CellBlockShift;
507 while (nb--) {
508 cellPtr = *blockPtr++;
509 uint i = CellBlockSize;
510 while (i--) *sortedPtr++ = cellPtr++;
513 cellPtr = *blockPtr++;
514 uint i = mNumCells&CellBlockMask;
515 while (i--) *sortedPtr++ = cellPtr++;
516 mSortedCells[mNumCells] = null;
517 qsortCells(mSortedCells, mNumCells);
522 // ////////////////////////////////////////////////////////////////////////// //
523 /*This class is used to transfer data from class outline (or a similar one)
524 to the rendering buffer. It's organized very simple. The class stores
525 information of horizontal spans to render it into a pixel-map buffer.
526 Each span has initial X, length, and an array of bytes that determine the
527 alpha-values for each pixel. So, the restriction of using this class is 256
528 levels of Anti-Aliasing, which is quite enough for any practical purpose.
529 Before using this class you should know the minimal and maximal pixel
530 coordinates of your scanline. The protocol of using is:
531 1. reset(minX, maxX)
532 2. addCell() / addSpan() - accumulate scanline. You pass Y-coordinate
533 into these functions in order to make scanline know the last Y. Before
534 calling addCell() / addSpan() you should check with method isReady(y)
535 if the last Y has changed. It also checks if the scanline is not empty.
536 When forming one scanline the next X coordinate must be always greater
537 than the last stored one, i.e. it works only with ordered coordinates.
538 3. If the current scanline isReady() you should render it and then call
539 resetSpans() before adding new cells/spans.
541 4. Rendering:
543 Scanline provides an iterator class that allows you to extract
544 the spans and the cover values for each pixel. Be aware that clipping
545 has not been done yet, so you should perform it yourself.
547 -------------------------------------------------------------------------
548 int baseX = sl.baseX; // base X. Should be added to the span's X
549 // "sl" is a const reference to the
550 // scanline passed in.
552 int y = sl.y; // Y-coordinate of the scanline
554 ************************************
555 ...Perform vertical clipping here...
556 ************************************
558 ubyte* row = mRBuf[y]; // the the address of the beginning of the current row
560 auto span = scanline.iterator;
562 uint spanCount = scanline.spanCount(); // number of spans; it's guaranteed that
563 // spanCount is always greater than 0
565 do {
566 int x = span.next+baseX; // the beginning X of the span
568 const(ubyte)* covers = span.covers; // the array of the cover values
570 int pixelCount = span.pixelCount; // number of pixels of the span;
571 // always greater than 0, still we
572 // shoud use "int" instead of "uint"
573 // because it's more convenient for clipping
575 **************************************
576 ...Perform horizontal clipping here...
577 ...you have x, covers, and pixCount..
578 **************************************
580 ubyte* dst = row+x; // calculate the start address of the row;
581 // in this case we assume a simple
582 // grayscale image 1-byte per pixel
583 do {
584 *dst++ = *covers++; // hypotetical rendering
585 } while (--pixelCount);
586 } while (--spanCount); // spanCount cannot be 0, so this loop is quite safe
587 ------------------------------------------------------------------------
589 The question is: why should we accumulate the whole scanline when we
590 could render just separate spans when they're ready?
591 That's because using the scaline is in general faster. When is consists
592 of more than one span the conditions for the processor cash system
593 are better, because switching between two different areas of memory
594 (that can be large ones) occures less frequently.
596 struct ScanLine {
597 private:
598 int mMinX;
599 uint mMaxLen;
600 int mDX;
601 int mDY;
602 int mLastX = 0x7fff;
603 int mLastY = 0x7fff;
604 ubyte* mCovers;
605 ubyte** mStartPtrs;
606 ushort* mCounts;
607 uint mNumSpans;
608 ubyte** mCurStartPtr;
609 ushort* mCurCount;
611 public:
612 enum AAShift = 8;
614 static struct Iterator {
615 private:
616 const(ubyte)* mCovers;
617 const(ushort)* mCurCount;
618 const(ubyte*)* mCurStartPtr;
620 public nothrow @trusted @nogc:
621 mixin(DisableCopyingMixin);
623 this (in ref ScanLine sl) pure {
624 pragma(inline, true);
625 mCovers = sl.mCovers;
626 mCurCount = sl.mCounts;
627 mCurStartPtr = sl.mStartPtrs;
630 int next () {
631 pragma(inline, true);
632 ++mCurCount;
633 ++mCurStartPtr;
634 return cast(int)(*mCurStartPtr-mCovers);
637 @property int pixelCount () const pure { pragma(inline, true); return cast(int)(*mCurCount); }
638 @property const(ubyte)* covers () const pure { pragma(inline, true); return *mCurStartPtr; }
641 public nothrow @trusted @nogc:
642 mixin(DisableCopyingMixin);
644 ~this () {
645 import core.stdc.stdlib : free;
646 if (mCounts !is null) free(mCounts);
647 if (mStartPtrs !is null) free(mStartPtrs);
648 if (mCovers !is null) free(mCovers);
651 auto iterator () const pure { pragma(inline, true); return Iterator(this); }
653 void reset (int minX, int maxX, int dx=0, int dy=0) {
654 uint maxLen = maxX-minX+2;
655 if (maxLen > mMaxLen) {
656 import core.stdc.stdlib : realloc;
657 mCovers = cast(ubyte*)realloc(mCovers, maxLen*mCovers[0].sizeof);
658 if (mCovers is null) assert(0, "out of memory");
659 mStartPtrs = cast(ubyte**)realloc(mStartPtrs, mStartPtrs[0].sizeof*maxLen);
660 if (mStartPtrs is null) assert(0, "out of memory");
661 mCounts = cast(ushort*)realloc(mCounts, mCounts[0].sizeof*maxLen);
662 if (mCounts is null) assert(0, "out of memory");
663 mMaxLen = maxLen;
665 mDX = dx;
666 mDY = dy;
667 mLastX = 0x7fff;
668 mLastY = 0x7fff;
669 mMinX = minX;
670 mCurCount = mCounts;
671 mCurStartPtr = mStartPtrs;
672 mNumSpans = 0;
675 void resetSpans () {
676 pragma(inline, true);
677 mLastX = 0x7fff;
678 mLastY = 0x7fff;
679 mCurCount = mCounts;
680 mCurStartPtr = mStartPtrs;
681 mNumSpans = 0;
684 void addSpan (int x, int y, uint num, uint cover) {
685 import core.stdc.string : memset;
686 x -= mMinX;
687 memset(mCovers+x, cover, num);
688 if (x == mLastX+1) {
689 (*mCurCount) += cast(ushort)num;
690 } else {
691 *++mCurCount = cast(ushort)num;
692 *++mCurStartPtr = mCovers+x;
693 ++mNumSpans;
695 mLastX = x+num-1;
696 mLastY = y;
699 void addCell (int x, int y, uint cover) {
700 x -= mMinX;
701 mCovers[x] = cast(ubyte)cover;
702 if (x == mLastX+1) {
703 ++(*mCurCount);
704 } else {
705 *++mCurCount = 1;
706 *++mCurStartPtr = mCovers+x;
707 ++mNumSpans;
709 mLastX = x;
710 mLastY = y;
713 @property bool isReady (int y) const pure { pragma(inline, true); return (mNumSpans && (y^mLastY)); }
714 @property int baseX () const pure { pragma(inline, true); return mMinX+mDX; }
715 @property int y () const pure { pragma(inline, true); return mLastY+mDY; }
716 @property uint spanCount () const pure { pragma(inline, true); return mNumSpans; }
720 // ////////////////////////////////////////////////////////////////////////// //
721 /* This class template is used basically for rendering scanlines.
722 Usage:
723 auto ras = Rasterizer();
725 // Making polygon
726 // ras.moveTo(...);
727 // ras.lineTo(...);
728 // ...
730 // Rendering
731 ras.render(ren, Color(255, 127, 0));
733 public struct Renderer {
734 public nothrow @trusted @nogc:
735 mixin(DisableCopyingMixin);
737 static void render (in ref ScanLine sl, in GxColor clr, in ref GxRect clipRect) {
738 if (clipRect.size.h < 1 || clipRect.size.w < 1 || sl.y < clipRect.pos.y || sl.y >= clipRect.pos.y+clipRect.size.h) return;
739 immutable GxColorU c = GxColorU(clr);
740 uint spanCount = sl.spanCount;
741 immutable int baseX = sl.baseX;
742 uint* row = vglTexBuf+cast(uint)sl.y*cast(uint)VBufWidth;
743 auto span = sl.iterator;
744 do {
745 int x = span.next+baseX;
746 int pixelCount = span.pixelCount;
747 int leftSkip = void;
748 if (clipRect.clipHStripe(ref x, sl.y, ref pixelCount, &leftSkip)) {
749 const(ubyte)* covers = span.covers+leftSkip;
750 memBlendColorCoverage(row+x, covers, clr, pixelCount);
752 } while (--spanCount);
757 // ////////////////////////////////////////////////////////////////////////// //
758 /* These constants determine the subpixel accuracy, to be more precise,
759 the number of bits of the fractional part of the coordinates.
760 The possible coordinate capacity in bits can be calculated by formula:
761 sizeof(int) * 8 - PolyBaseShift * 2, i.e, for 32-bit integers and
762 8-bits fractional part the capacity is 16 bits or [-32768...32767].
764 enum : uint {
765 PolyBaseShift = 8U,
766 PolyBaseSize = cast(uint)(1<<PolyBaseShift),
767 PolyBaseMask = cast(uint)(PolyBaseSize-1),
771 int polyCoord (in float c) pure nothrow @safe @nogc { pragma(inline, true); return (cast(int)(c*(PolyBaseSize*2))+1)/2; }
773 int dbl2fix (in float c) pure nothrow @safe @nogc { pragma(inline, true); return cast(int)(c*PolyBaseSize); }
774 float fix2dbl (in int c) pure nothrow @safe @nogc { pragma(inline, true); return cast(float)c/cast(float)PolyBaseSize; }
777 // ////////////////////////////////////////////////////////////////////////// //
778 /* Polygon rasterizer that is used to render filled polygons with
779 high-quality Anti-Aliasing. Internally, by default, the class uses
780 integer coordinates in format 24.8, i.e. 24 bits for integer part
781 and 8 bits for fractional - see PolyBaseShift. This class can be
782 used in the following way:
784 1. fillRule = FillRule.EvenOdd; // optional
785 2. gamma() - optional.
786 3. reset()
787 4. moveTo(x, y) / lineTo(x, y) - make the polygon. One can create
788 more than one contour, but each contour must consist of at least 3
789 vertices, i.e. moveTo(x1, y1); lineTo(x2, y2); lineTo(x3, y3);
790 is the absolute minimum of vertices that define a triangle.
791 The algorithm does not check either the number of vertices nor
792 coincidence of their coordinates, but in the worst case it just
793 won't draw anything.
794 The orger of the vertices (clockwise or counterclockwise)
795 is important when using the non-zero filling rule (FillNonZero).
796 In this case the vertex order of all the contours must be the same
797 if you want your intersecting polygons to be without "holes".
798 You actually can use different vertices order. If the contours do not
799 intersect each other the order is not important anyway. If they do,
800 contours with the same vertex order will be rendered without "holes"
801 while the intersecting contours with different orders will have "holes".
803 fillRule() and gamma() can be called anytime before "sweeping".
805 public struct Rasterizer {
806 public nothrow @trusted @nogc:
807 enum : uint {
808 AAShift = ScanLine.AAShift,
809 AANum = 1<<AAShift,
810 AAMask = AANum-1,
811 AA2Num = AANum*2,
812 AA2Mask = AA2Num-1,
815 private:
816 Outline mOutline;
817 ScanLine mScanline;
818 FillRule mFillingRule = FillRule.NonZero;
819 ubyte[256] mGamma = DefaultGamma[];
821 public:
822 mixin(DisableCopyingMixin);
824 void reset () { mOutline.reset(); }
826 @property FillRule fillRule () const pure { pragma(inline, true); return mFillingRule; }
827 @property void fillRule (FillRule v) { pragma(inline, true); mFillingRule = v; }
829 void gamma (in float g) {
830 foreach (immutable uint i; 0..256) {
831 //import std.math : pow;
832 import core.stdc.math : powf;
833 mGamma.ptr[i] = cast(ubyte)(powf(cast(float)i/255.0f, g)*255.0f);
837 void gamma (const(ubyte)[] g) {
838 if (g.length != 256) assert(0, "invalid gamma array");
839 mGamma[] = g[0..256];
842 @property float currX () const pure { pragma(inline, true); return mOutline.currX; }
843 @property float currY () const pure { pragma(inline, true); return mOutline.currY; }
845 void moveToFixed (int x, int y) {
846 if ((x>>PolyBaseShift) <= short.min+8 || (x>>PolyBaseShift) >= short.max-8 ||
847 (y>>PolyBaseShift) <= short.min+8 || (y>>PolyBaseShift) >= short.max-8) assert(0, "coordinates out of bounds");
848 mOutline.moveTo(x, y);
851 void lineToFixed (int x, int y) {
852 if ((x>>PolyBaseShift) <= short.min+8 || (x>>PolyBaseShift) >= short.max-8 ||
853 (y>>PolyBaseShift) <= short.min+8 || (y>>PolyBaseShift) >= short.max-8) assert(0, "coordinates out of bounds");
854 mOutline.lineTo(x, y);
857 void moveTo (in float x, in float y) { mOutline.moveTo(polyCoord(x), polyCoord(y)); }
858 void lineTo (in float x, in float y) { mOutline.lineTo(polyCoord(x), polyCoord(y)); }
860 @property int minX () const pure { pragma(inline, true); return mOutline.minX; }
861 @property int minY () const pure { pragma(inline, true); return mOutline.minY; }
862 @property int maxX () const pure { pragma(inline, true); return mOutline.maxX; }
863 @property int maxY () const pure { pragma(inline, true); return mOutline.maxY; }
865 private uint calculateAlpha (int area) const pure {
866 int cover = area>>(PolyBaseShift*2+1-AAShift);
867 if (cover < 0) cover = -cover;
868 if (mFillingRule == FillRule.EvenOdd) {
869 cover &= AA2Mask;
870 if (cover > AANum) cover = AA2Num-cover;
872 if (cover > AAMask) cover = AAMask;
873 return cover;
876 void render (in ref GxRect clipRect, in GxColor c, int dx=0, int dy=0) {
877 const(Cell)** cells = mOutline.cells();
878 if (mOutline.numCells() == 0) return;
880 mScanline.reset(mOutline.minX, mOutline.maxX, dx, dy);
882 int cover = 0;
883 const(Cell)* curCell = *cells++;
885 for (;;) {
886 const(Cell)* startCell = curCell;
888 int coord = curCell.packedCoord;
889 int x = curCell.x;
890 int y = curCell.y;
892 int area = startCell.area;
893 cover += startCell.cover;
895 // accumulate all start cells
896 while ((curCell = *cells++) !is null) {
897 if (curCell.packedCoord != coord) break;
898 area += curCell.area;
899 cover += curCell.cover;
902 if (area) {
903 int alpha = calculateAlpha((cover<<(PolyBaseShift+1))-area);
904 if (alpha) {
905 if (mScanline.isReady(y)) {
906 Renderer.render(mScanline, c, clipRect);
907 mScanline.resetSpans();
909 mScanline.addCell(x, y, mGamma[alpha]);
911 ++x;
914 if (!curCell) break;
916 if (curCell.x > x) {
917 int alpha = calculateAlpha(cover<<(PolyBaseShift+1));
918 if (alpha) {
919 if (mScanline.isReady(y)) {
920 Renderer.render(mScanline, c, clipRect);
921 mScanline.resetSpans();
923 mScanline.addSpan(x, y, curCell.x-x, mGamma[alpha]);
928 if (mScanline.spanCount) Renderer.render(mScanline, c, clipRect);
931 bool hitTest (int tx, int ty) {
932 const(Cell)** cells = mOutline.cells();
933 if (mOutline.numCells == 0) return false;
935 int cover = 0;
936 const(Cell)* curCell = *cells++;
938 for (;;) {
939 const(Cell)* startCell = curCell;
941 int coord = curCell.packedCoord;
942 int x = curCell.x;
943 int y = curCell.y;
945 if (y > ty) return false;
947 int area = startCell.area;
948 cover += startCell.cover;
950 while ((curCell = *cells++) !is null) {
951 if (curCell.packedCoord != coord) break;
952 area += curCell.area;
953 cover += curCell.cover;
956 if (area) {
957 int alpha = calculateAlpha((cover<<(PolyBaseShift+1))-area);
958 if (alpha) {
959 if (tx == x && ty == y) return true;
961 ++x;
964 if (curCell is null) break;
966 if (curCell.x > x) {
967 int alpha = calculateAlpha(cover<<(PolyBaseShift+1));
968 if (alpha) {
969 if (ty == y && tx >= x && tx <= curCell.x) return true;
973 return false;
976 private:
977 static immutable ubyte[256] DefaultGamma = [
978 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8,
979 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18,
980 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28,
981 29, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 37, 38,
982 39, 39, 40, 41, 41, 42, 43, 43, 44, 45, 45, 46, 47, 47, 48, 49,
983 49, 50, 51, 51, 52, 53, 53, 54, 55, 55, 56, 57, 57, 58, 59, 60,
984 60, 61, 62, 62, 63, 64, 65, 65, 66, 67, 68, 68, 69, 70, 71, 71,
985 72, 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 82, 83, 83, 84,
986 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
987 100,101,101,102,103,104,105,106,107,108,109,110,111,112,114,115,
988 116,117,118,119,120,121,122,123,124,126,127,128,129,130,131,132,
989 134,135,136,137,139,140,141,142,144,145,146,147,149,150,151,153,
990 154,155,157,158,159,161,162,164,165,166,168,169,171,172,174,175,
991 177,178,180,181,183,184,186,188,189,191,192,194,195,197,199,200,
992 202,204,205,207,209,210,212,214,215,217,219,220,222,224,225,227,
993 229,230,232,234,236,237,239,241,242,244,246,248,249,251,253,255
998 // ////////////////////////////////////////////////////////////////////////// //
999 public alias AGGPoint = AGGPointImpl!float;
1001 public struct AGGPointImpl(T) if (__traits(isIntegral, T) || __traits(isFloating, T)) {
1002 public nothrow @trusted @nogc:
1003 alias ValueT = T;
1004 alias Me = AGGPointImpl!T;
1006 public:
1007 static if (__traits(isFloating, T)) {
1008 T x, y;
1009 } else {
1010 T x=T.min, y=T.min;
1015 // ////////////////////////////////////////////////////////////////////////// //
1016 public enum FillRule {
1017 NonZero,
1018 EvenOdd,
1021 public enum LineCap {
1022 Butt,
1023 Square,
1024 Round,
1027 public enum LineJoin {
1028 Miter,
1029 MiterRevert,
1030 Round,
1031 Bevel,
1032 MiterRound,
1035 public enum InnerJoin {
1036 Bevel,
1037 Miter,
1038 Jag,
1039 Round,
1043 // ////////////////////////////////////////////////////////////////////////// //
1044 // some "fastgfx" backend
1045 public struct AGGDrawer {
1046 protected:
1047 static struct Vertex {
1048 float x, y; // fixed point
1049 bool asmove;
1052 protected:
1053 Rasterizer rast;
1054 float tessTol = 0.25f;
1055 float angleTol = 0; // 0.0f -- angle tolerance for McSeem Bezier rasterizer
1056 float cuspLimit = 0; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
1057 Vertex* vtx;
1058 uint vtxcount, vtxsize;
1059 // default tesselator for Bezier curves.
1060 AGGTesselation tesselator = AGGTesselation.DeCasteljau;
1062 nothrow @trusted @nogc:
1063 protected:
1064 enum distTol = cast(float)(1.0f/256.0f);
1065 enum KAPPA90 = 0.5522847493f; // length proportional to radius of a cubic bezier handle for 90deg arcs
1067 static bool ptEquals (in float x0, in float y0, in float x1, in float y1) pure {
1068 enum EPS = cast(float)(1.0f/256.0f);
1069 static bool eq (in float a, in float b) pure nothrow @safe @nogc { immutable float df = a-b; return (df < 0.0f ? (-df < EPS) : (df < EPS)); }
1070 return (eq(x0, x1) && eq(y0, y1));
1073 static float distPtSeg (in float x, in float y, in float px, in float py, in float qx, in float qy) pure nothrow @safe @nogc {
1074 immutable float pqx = qx-px;
1075 immutable float pqy = qy-py;
1076 float dx = x-px;
1077 float dy = y-py;
1078 immutable float d = pqx*pqx+pqy*pqy;
1079 float t = pqx*dx+pqy*dy;
1080 if (d > 0) t /= d;
1081 if (t < 0) t = 0; else if (t > 1) t = 1;
1082 dx = px+t*pqx-x;
1083 dy = py+t*pqy-y;
1084 return dx*dx+dy*dy;
1087 static float cross() (in float dx0, in float dy0, in float dx1, in float dy1) pure { pragma(inline, true); return (dx1*dy0-dx0*dy1); }
1088 static T min(T) (in T a, in T b) pure { pragma(inline, true); return (a < b ? a : b); }
1089 static T max(T) (in T a, in T b) pure { pragma(inline, true); return (a > b ? a : b); }
1090 static T sign(T) (in T a) pure { pragma(inline, true); return (a >= cast(T)0 ? cast(T)1 : cast(T)(-1)); }
1092 static float normalize (float* x, float* y) nothrow @safe @nogc {
1093 import core.stdc.math : sqrtf;
1094 float d = sqrtf((*x)*(*x)+(*y)*(*y));
1095 if (d > 1e-6f) {
1096 immutable float id = 1.0f/d;
1097 *x *= id;
1098 *y *= id;
1100 return d;
1103 void addVertexIntr (in float fx, in float fy, bool asMove=false) {
1104 //static bool eq (in float a, in float b) pure nothrow @safe @nogc { import std.math : abs; return (abs(a-b) < EPS); }
1105 if (vtxcount > 0 && ptEquals(fx, fy, vtx[vtxcount-1].x, vtx[vtxcount-1].y)) {
1106 if (asMove != vtx[vtxcount-1].asmove) return;
1108 if (vtxcount+1 > vtxsize) {
1109 import core.stdc.stdlib : realloc;
1110 uint newsz = (vtxsize|0xfff)+1;
1111 vtx = cast(Vertex*)realloc(vtx, newsz*Vertex.sizeof);
1112 if (vtx is null) assert(0, "out of memory");
1113 vtxsize = newsz;
1115 if (asMove && vtxcount > 0 && vtx[vtxcount-1].asmove) --vtxcount;
1116 vtx[vtxcount++] = Vertex(fx, fy, asMove);
1119 void addVertex (in float fx, in float fy, bool asMove=false) {
1120 pragma(inline, true);
1121 if (vtxcount == 0 && !asMove) addVertexIntr(0, 0, true);
1122 addVertexIntr(fx, fy, asMove);
1125 protected:
1126 Stroker mStroker;
1128 public:
1129 @property void lineCap (in LineCap lc) { pragma(inline, true); mStroker.lineCap(lc); }
1130 @property void lineJoin (in LineJoin lj) { pragma(inline, true); mStroker.lineJoin(lj); }
1131 @property void innerJoin (in InnerJoin ij) { pragma(inline, true); mStroker.innerJoin(ij); }
1133 @property LineCap lineCap () const pure { pragma(inline, true); return mStroker.lineCap(); }
1134 @property LineJoin lineJoin () const pure { pragma(inline, true); return mStroker.lineJoin(); }
1135 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mStroker.innerJoin(); }
1137 @property void width (in float w) { pragma(inline, true); mStroker.width(w); }
1138 @property void miterLimit (in float ml) { pragma(inline, true); mStroker.miterLimit(ml); }
1139 @property void miterLimitTheta (in float t) { pragma(inline, true); mStroker.miterLimitTheta(t); }
1140 @property void innerMiterLimit (in float ml) { pragma(inline, true); mStroker.innerMiterLimit(ml); }
1141 @property void approximationScale (in float as) { pragma(inline, true); mStroker.approximationScale(as); }
1143 @property float width () const pure { pragma(inline, true); return mStroker.width(); }
1144 @property float miterLimit () const pure { pragma(inline, true); return mStroker.miterLimit(); }
1145 @property float innerMiterLimit () const pure { pragma(inline, true); return mStroker.innerMiterLimit(); }
1146 @property float approximationScale () const pure { pragma(inline, true); return mStroker.approximationScale(); }
1148 @property void shorten (in float s) { pragma(inline, true); mStroker.shorten = s; }
1149 @property float shorten () const pure { pragma(inline, true); return mStroker.shorten; }
1151 @property FillRule fillRule () const pure { pragma(inline, true); return rast.fillRule; }
1152 @property void fillRule (FillRule v) { pragma(inline, true); rast.fillRule = v; }
1154 @property float currX () const pure { pragma(inline, true); return (vtxcount > 0 ? vtx[vtxcount-1].x : 0.0f); }
1155 @property float currY () const pure { pragma(inline, true); return (vtxcount > 0 ? vtx[vtxcount-1].y : 0.0f); }
1157 // ////////////////////////////////////////////////////////////////////////// //
1158 ref AGGDrawer beginFrame () {
1159 vtxcount = 0;
1160 //addVertex(0, 0, true);
1161 mStroker.width = 1.5f;
1162 rast.reset();
1163 return this;
1166 ref AGGDrawer cancelFrame () {
1167 vtxcount = 0;
1168 //addVertex(0, 0, true);
1169 rast.reset();
1170 return this;
1173 ref AGGDrawer endFrame (in GxColor c) {
1174 GxRect clipRect = gxClipRect;
1175 if (clipRect.intersect(0, 0, VBufWidth, VBufHeight)) {
1176 rast.render(clipRect, c);
1178 return beginFrame();
1181 ref AGGDrawer beginPath () {
1182 pragma(inline, true);
1183 vtxcount = 0;
1184 return this;
1187 ref AGGDrawer closePath () {
1188 if (vtxcount > 1 && !vtx[vtxcount-1].asmove) {
1189 uint vp = vtxcount;
1190 while (vp > 0 && !vtx[vp-1].asmove) --vp;
1191 addVertex(vtx[vp-1].x, vtx[vp-1].y); // close it
1193 return this;
1196 // starts new sub-path with specified point as first point
1197 ref AGGDrawer moveTo (in float x, in float y) { pragma(inline, true); addVertex(x, y, true); return this; }
1199 // adds line segment from the last point in the path to the specified point
1200 ref AGGDrawer lineTo (in float x, in float y) { pragma(inline, true); addVertex(x, y); return this; }
1202 // adds cubic bezier segment from last point in the path via two control points to the specified point
1203 ref AGGDrawer bezierTo (in float x2, in float y2, in float x3, in float y3, in float x4, in float y4) {
1204 pragma(inline, true);
1205 //tesselateBezierMcSeem(currX, currY, x2, y2, x3, y3, x4, y4, 0);
1206 tesselateBezier(currX, currY, x2, y2, x3, y3, x4, y4);
1207 return this;
1210 // adds quadratic bezier segment from last point in the path via a control point to the specified point
1211 void quadTo (in float cx, in float cy, in float x, in float y) {
1212 immutable float x0 = currX;
1213 immutable float y0 = currY;
1214 bezierTo(
1215 x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
1216 x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
1217 x, y,
1221 // adds an arc segment at the corner defined by the last path point, and two specified points
1222 ref AGGDrawer arcTo (in float x1, in float y1, in float x2, in float y2, in float radius) {
1223 import core.stdc.math : acosf, tanf, atan2f;
1225 immutable float x0 = currX;
1226 immutable float y0 = currY;
1228 // handle degenerate cases
1229 if (ptEquals(x0, y0, x1, y1) ||
1230 ptEquals(x1, y1, x2, y2) ||
1231 distPtSeg(x1, y1, x0, y0, x2, y2) < distTol*distTol ||
1232 radius < distTol)
1234 lineTo(x1, y1);
1235 return this;
1238 // calculate tangential circle to lines (x0, y0)-(x1, y1) and (x1, y1)-(x2, y2)
1239 float dx0 = x0-x1;
1240 float dy0 = y0-y1;
1241 float dx1 = x2-x1;
1242 float dy1 = y2-y1;
1243 normalize(&dx0, &dy0);
1244 normalize(&dx1, &dy1);
1245 immutable float a = acosf(dx0*dx1+dy0*dy1);
1246 immutable float d = radius/tanf(a/2.0f);
1248 if (d > 10000.0f) {
1249 lineTo(x1, y1);
1250 return this;
1253 float cx = void, cy = void, a0 = void, a1 = void;
1254 Winding dir = void;
1255 if (cross(dx0, dy0, dx1, dy1) > 0.0f) {
1256 cx = x1+dx0*d+dy0*radius;
1257 cy = y1+dy0*d+-dx0*radius;
1258 a0 = atan2f(dx0, -dy0);
1259 a1 = atan2f(-dx1, dy1);
1260 dir = Winding.CW;
1261 //printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
1262 } else {
1263 cx = x1+dx0*d+-dy0*radius;
1264 cy = y1+dy0*d+dx0*radius;
1265 a0 = atan2f(-dx0, dy0);
1266 a1 = atan2f(dx1, -dy1);
1267 dir = Winding.CCW;
1268 //printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
1271 return arc(dir, cx, cy, radius, a0, a1); // first is line
1275 // ////////////////////////////////////////////////////////////////////////// //
1276 // fill pathes
1277 void fill () {
1278 //pragma(inline, true);
1279 foreach (const ref Vertex vt; vtx[0..vtxcount]) {
1280 if (vt.asmove) rast.moveTo(vt.x, vt.y); else rast.lineTo(vt.x, vt.y);
1284 // stroke pathes
1285 void stroke () {
1286 //if (mStroker.width <= 1.0) { contour(); return; }
1287 mStroker.removeAll();
1289 bool waitingLine = true;
1290 foreach (const ref Vertex vt; vtx[0..vtxcount]) {
1291 mStroker.addVertex(vt.x, vt.y, (vt.asmove ? PathCommand.MoveTo : PathCommand.LineTo));
1293 mStroker.addVertex(0, 0, PathCommand.Stop);
1295 float x, y;
1296 mStroker.rewind();
1297 for (;;) {
1298 auto cmd = mStroker.vertex(&x, &y);
1299 if (isStop(cmd)) break;
1300 if (isMoveTo(cmd)) { rast.moveTo(x, y); continue; }
1301 if (isLineTo(cmd)) { rast.lineTo(x, y); continue; }
1302 if (isEndPoly(cmd)) {
1304 if (is_close(cmd) && !first) {
1305 //writeln("s=(", sx, ",", sy, "); c=(", x, ",", y, ")");
1306 pts.add(PathPoint(sx, sy, false));
1313 // contour pathes
1314 void contour () {
1315 Contourer ctr;
1316 ctr.removeAll();
1318 ctr.lineCap = mStroker.lineCap;
1319 ctr.lineJoin = mStroker.lineJoin;
1320 ctr.innerJoin = mStroker.innerJoin;
1322 ctr.width = mStroker.width;
1323 ctr.miterLimit = mStroker.miterLimit;
1324 ctr.innerMiterLimit = mStroker.innerMiterLimit;
1325 ctr.approximationScale = mStroker.approximationScale;
1327 bool waitingLine = true;
1328 foreach (const ref Vertex vt; vtx[0..vtxcount]) {
1329 ctr.addVertex(vt.x, vt.y, (vt.asmove ? PathCommand.MoveTo : PathCommand.LineTo));
1331 ctr.addVertex(0, 0, PathCommand.Stop);
1333 float x, y;
1334 ctr.rewind();
1335 for (;;) {
1336 auto cmd = ctr.vertex(&x, &y);
1337 if (isStop(cmd)) break;
1338 if (isMoveTo(cmd)) { rast.moveTo(x, y); continue; }
1339 if (isLineTo(cmd)) { rast.lineTo(x, y); continue; }
1344 // ////////////////////////////////////////////////////////////////////////// //
1345 enum Winding { CW, CCW }
1347 /* Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
1348 * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
1349 * Angles are specified in radians.
1351 * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
1353 ref AGGDrawer arc(string mode="original") (Winding dir, in float cx, in float cy, in float r,
1354 in float a0, in float a1)
1356 static assert(mode == "original" || mode == "move" || mode == "line");
1357 import core.stdc.math : fabsf, cosf, sinf;
1359 //int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
1360 static if (mode == "original") {
1361 bool asMove = (vtxcount == 0);
1362 } else static if (mode == "move") {
1363 enum asMove = true;
1364 } else static if (mode == "line") {
1365 enum asMove = false;
1366 } else {
1367 static assert(0, "wtf?!");
1370 // clamp angles
1371 float da = a1-a0;
1372 if (dir == Winding.CW) {
1373 if (fabsf(da) >= FLT_PI*2.0f) {
1374 da = FLT_PI*2.0f;
1375 } else {
1376 while (da < 0.0f) da += FLT_PI*2.0f;
1378 } else {
1379 if (fabsf(da) >= FLT_PI*2.0f) {
1380 da = -FLT_PI*2;
1381 } else {
1382 while (da > 0.0f) da -= FLT_PI*2.0f;
1386 // Split arc into max 90 degree segments.
1387 immutable int ndivs = max(1, min(cast(int)(fabsf(da)/(FLT_PI*0.5f)+0.5f), 5));
1388 immutable float hda = (da/cast(float)ndivs)/2.0f;
1389 float kappa = fabsf(4.0f/3.0f*(1.0f-cosf(hda))/sinf(hda));
1390 if (dir == Winding.CCW) kappa = -kappa;
1392 int nvals = 0;
1393 float px = 0, py = 0, ptanx = 0, ptany = 0;
1394 foreach (int i; 0..ndivs+1) {
1395 immutable float a = a0+da*(i/cast(float)ndivs);
1396 immutable float dx = cosf(a);
1397 immutable float dy = sinf(a);
1398 immutable float x = cx+dx*r;
1399 immutable float y = cy+dy*r;
1400 immutable float tanx = -dy*r*kappa;
1401 immutable float tany = dx*r*kappa;
1403 if (i == 0) {
1404 addVertex(x, y, asMove);
1405 } else {
1406 bezierTo(px+ptanx, py+ptany, x-tanx, y-tany, x, y);
1408 px = x;
1409 py = y;
1410 ptanx = tanx;
1411 ptany = tany;
1414 return this;
1417 // creates new rectangle shaped sub-path
1418 ref AGGDrawer rect (in float x, in float y, in float w, in float h) {
1419 if (w && h) {
1420 moveTo(x, y);
1421 lineTo(x, y+h);
1422 lineTo(x+w, y+h);
1423 lineTo(x+w, y);
1424 lineTo(x, y); // close it
1426 return this;
1429 // creates new rounded rectangle shaped sub-path
1430 ref AGGDrawer roundedRect (in float x, in float y, in float w, in float h, in float radius) {
1431 return roundedRectVarying(x, y, w, h, radius, radius, radius, radius);
1434 // creates new rounded rectangle shaped sub-path
1435 // specify ellipse width and height to round corners according to it
1436 ref AGGDrawer roundedRectEllipse (in float x, in float y, in float w, in float h, in float rw, in float rh) {
1437 if (rw < 0.1f || rh < 0.1f) return rect(x, y, w, h);
1438 moveTo(x+rw, y);
1439 lineTo(x+w-rw, y);
1440 bezierTo(x+w-rw*(1.0f-KAPPA90), y, x+w, y+rh*(1.0f-KAPPA90), x+w, y+rh);
1441 lineTo(x+w, y+h-rh);
1442 bezierTo(x+w, y+h-rh*(1.0f-KAPPA90), x+w-rw*(1.0f-KAPPA90), y+h, x+w-rw, y+h);
1443 lineTo(x+rw, y+h);
1444 bezierTo(x+rw*(1.0f-KAPPA90), y+h, x, y+h-rh*(1.0f-KAPPA90), x, y+h-rh);
1445 lineTo(x, y+rh);
1446 bezierTo(x, y+rh*(1.0f-KAPPA90), x+rw*(1.0f-KAPPA90), y, x+rw, y);
1447 return closePath();
1450 // creates new rounded rectangle shaped sub-path
1451 // this one allows you to specify different rounding radii for each corner
1452 ref AGGDrawer roundedRectVarying (in float x, in float y, in float w, in float h,
1453 in float radTopLeft, in float radTopRight,
1454 in float radBottomRight, in float radBottomLeft)
1456 import core.stdc.math : fabsf;
1457 if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) return rect(x, y, w, h);
1458 immutable float halfw = fabsf(w)*0.5f;
1459 immutable float halfh = fabsf(h)*0.5f;
1460 immutable float rxBL = min(radBottomLeft, halfw)*sign(w), ryBL = min(radBottomLeft, halfh)*sign(h);
1461 immutable float rxBR = min(radBottomRight, halfw)*sign(w), ryBR = min(radBottomRight, halfh)*sign(h);
1462 immutable float rxTR = min(radTopRight, halfw)*sign(w), ryTR = min(radTopRight, halfh)*sign(h);
1463 immutable float rxTL = min(radTopLeft, halfw)*sign(w), ryTL = min(radTopLeft, halfh)*sign(h);
1464 moveTo(x, y+ryTL);
1465 lineTo(x, y+h-ryBL);
1466 bezierTo(x, y+h-ryBL*(1.0f-KAPPA90), x+rxBL*(1.0f-KAPPA90), y+h, x+rxBL, y+h);
1467 lineTo(x+w-rxBR, y+h);
1468 bezierTo(x+w-rxBR*(1.0f-KAPPA90), y+h, x+w, y+h-ryBR*(1.0f-KAPPA90), x+w, y+h-ryBR);
1469 lineTo(x+w, y+ryTR);
1470 bezierTo(x+w, y+ryTR*(1.0f-KAPPA90), x+w-rxTR*(1.0f-KAPPA90), y, x+w-rxTR, y);
1471 lineTo(x+rxTL, y);
1472 bezierTo(x+rxTL*(1.0f-KAPPA90), y, x, y+ryTL*(1.0f-KAPPA90), x, y+ryTL);
1473 return closePath();
1476 // creates new ellipse shaped sub-path
1477 ref AGGDrawer ellipse (in float cx, in float cy, in float rx, in float ry) {
1478 moveTo(cx-rx, cy);
1479 bezierTo(cx-rx, cy+ry*KAPPA90, cx-rx*KAPPA90, cy+ry, cx, cy+ry);
1480 bezierTo(cx+rx*KAPPA90, cy+ry, cx+rx, cy+ry*KAPPA90, cx+rx, cy);
1481 bezierTo(cx+rx, cy-ry*KAPPA90, cx+rx*KAPPA90, cy-ry, cx, cy-ry);
1482 bezierTo(cx-rx*KAPPA90, cy-ry, cx-rx, cy-ry*KAPPA90, cx-rx, cy);
1483 return closePath();
1486 // creates new circle shaped sub-path
1487 ref AGGDrawer circle (in float cx, in float cy, in float r) {
1488 return ellipse(cx, cy, r, r);
1491 private:
1492 // ////////////////////////////////////////////////////////////////////////// //
1493 void tesselateBezierDCj (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int level/*, in int type*/) {
1494 import core.stdc.math : fabsf;
1495 if (level > 10) return;
1497 // check for collinear points, and use AFD tesselator on such curves (it is WAY faster for this case)
1499 if (level == 0 && ctx.tesselatortype == NVGTesselation.Combined) {
1500 static bool collinear (in float v0x, in float v0y, in float v1x, in float v1y, in float v2x, in float v2y) nothrow @trusted @nogc {
1501 immutable float cz = (v1x-v0x)*(v2y-v0y)-(v2x-v0x)*(v1y-v0y);
1502 return (fabsf(cz*cz) <= 0.01f); // arbitrary number, seems to work ok with NanoSVG output
1504 if (collinear(x1, y1, x2, y2, x3, y3) && collinear(x2, y2, x3, y3, x3, y4)) {
1505 //{ import core.stdc.stdio; printf("AFD fallback!\n"); }
1506 ctx.tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4, type);
1507 return;
1512 immutable float x12 = (x1+x2)*0.5f;
1513 immutable float y12 = (y1+y2)*0.5f;
1514 immutable float x23 = (x2+x3)*0.5f;
1515 immutable float y23 = (y2+y3)*0.5f;
1516 immutable float x34 = (x3+x4)*0.5f;
1517 immutable float y34 = (y3+y4)*0.5f;
1518 immutable float x123 = (x12+x23)*0.5f;
1519 immutable float y123 = (y12+y23)*0.5f;
1521 immutable float dx = x4-x1;
1522 immutable float dy = y4-y1;
1523 immutable float d2 = fabsf(((x2-x4)*dy-(y2-y4)*dx));
1524 immutable float d3 = fabsf(((x3-x4)*dy-(y3-y4)*dx));
1526 if ((d2+d3)*(d2+d3) < tessTol*(dx*dx+dy*dy)) {
1527 addVertex(x4, y4/*, type*/);
1528 return;
1531 immutable float x234 = (x23+x34)*0.5f;
1532 immutable float y234 = (y23+y34)*0.5f;
1533 immutable float x1234 = (x123+x234)*0.5f;
1534 immutable float y1234 = (y123+y234)*0.5f;
1536 // "taxicab" / "manhattan" check for flat curves
1537 if (fabsf(x1+x3-x2-x2)+fabsf(y1+y3-y2-y2)+fabsf(x2+x4-x3-x3)+fabsf(y2+y4-y3-y3) < tessTol/4) {
1538 addVertex(x1234, y1234/*, type*/);
1539 return;
1542 tesselateBezierDCj(x1, y1, x12, y12, x123, y123, x1234, y1234, level+1/*, 0*/);
1543 tesselateBezierDCj(x1234, y1234, x234, y234, x34, y34, x4, y4, level+1/*, type*/);
1546 // ////////////////////////////////////////////////////////////////////////// //
1547 // based on the ideas and code of Maxim Shemanarev. Rest in Peace, bro!
1548 // see http://www.antigrain.com/research/adaptive_bezier/index.html
1549 void tesselateBezierMcSeem (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int level/*, in int type*/) {
1550 //import std.math : atan2, PI;
1551 import core.stdc.math : fabsf, atan2f;
1553 enum CollinearEPS = 0.00000001f; // 0.00001f;
1554 enum AngleTolEPS = 0.01f;
1556 static float distSquared (in float x1, in float y1, in float x2, in float y2) pure nothrow @safe @nogc {
1557 pragma(inline, true);
1558 immutable float dx = x2-x1;
1559 immutable float dy = y2-y1;
1560 return dx*dx+dy*dy;
1564 if (level == 0) {
1565 addVertex(x1, y1/*, 0*/);
1566 tesselateBezierMcSeem(x1, y1, x2, y2, x3, y3, x4, y4, 1/*, type*/);
1567 addVertex(x4, y4/*, type*/);
1568 return;
1572 if (level >= 32) return; // recurse limit; practically, it should be never reached, but...
1574 // calculate all the mid-points of the line segments
1575 immutable float x12 = (x1+x2)*0.5f;
1576 immutable float y12 = (y1+y2)*0.5f;
1577 immutable float x23 = (x2+x3)*0.5f;
1578 immutable float y23 = (y2+y3)*0.5f;
1579 immutable float x34 = (x3+x4)*0.5f;
1580 immutable float y34 = (y3+y4)*0.5f;
1581 immutable float x123 = (x12+x23)*0.5f;
1582 immutable float y123 = (y12+y23)*0.5f;
1583 immutable float x234 = (x23+x34)*0.5f;
1584 immutable float y234 = (y23+y34)*0.5f;
1585 immutable float x1234 = (x123+x234)*0.5f;
1586 immutable float y1234 = (y123+y234)*0.5f;
1588 // try to approximate the full cubic curve by a single straight line
1589 immutable float dx = x4-x1;
1590 immutable float dy = y4-y1;
1592 float d2 = fabsf(((x2-x4)*dy-(y2-y4)*dx));
1593 float d3 = fabsf(((x3-x4)*dy-(y3-y4)*dx));
1595 final switch ((cast(int)(d2 > CollinearEPS)<<1)+cast(int)(d3 > CollinearEPS)) {
1596 case 0:
1597 // all collinear or p1 == p4
1598 float k = dx*dx+dy*dy;
1599 if (k == 0) {
1600 d2 = distSquared(x1, y1, x2, y2);
1601 d3 = distSquared(x4, y4, x3, y3);
1602 } else {
1603 k = 1.0f/k;
1604 float da1 = x2-x1;
1605 float da2 = y2-y1;
1606 d2 = k*(da1*dx+da2*dy);
1607 da1 = x3-x1;
1608 da2 = y3-y1;
1609 d3 = k*(da1*dx+da2*dy);
1610 if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) {
1611 // Simple collinear case, 1---2---3---4
1612 // We can leave just two endpoints
1613 return;
1615 if (d2 <= 0) d2 = distSquared(x2, y2, x1, y1);
1616 else if (d2 >= 1) d2 = distSquared(x2, y2, x4, y4);
1617 else d2 = distSquared(x2, y2, x1+d2*dx, y1+d2*dy);
1619 if (d3 <= 0) d3 = distSquared(x3, y3, x1, y1);
1620 else if (d3 >= 1) d3 = distSquared(x3, y3, x4, y4);
1621 else d3 = distSquared(x3, y3, x1+d3*dx, y1+d3*dy);
1623 if (d2 > d3) {
1624 if (d2 < tessTol) {
1625 addVertex(x2, y2/*, type*/);
1626 return;
1628 } if (d3 < tessTol) {
1629 addVertex(x3, y3/*, type*/);
1630 return;
1632 break;
1633 case 1:
1634 // p1,p2,p4 are collinear, p3 is significant
1635 if (d3*d3 <= tessTol*(dx*dx+dy*dy)) {
1636 if (angleTol < AngleTolEPS) {
1637 addVertex(x23, y23/*, type*/);
1638 return;
1639 } else {
1640 // angle condition
1641 float da1 = fabsf(atan2f(y4-y3, x4-x3)-atan2f(y3-y2, x3-x2));
1642 if (da1 >= FLT_PI) da1 = 2.0f*FLT_PI-da1;
1643 if (da1 < angleTol) {
1644 addVertex(x2, y2/*, type*/);
1645 addVertex(x3, y3/*, type*/);
1646 return;
1648 if (cuspLimit != 0) {
1649 if (da1 > cuspLimit) {
1650 addVertex(x3, y3/*, type*/);
1651 return;
1656 break;
1657 case 2:
1658 // p1,p3,p4 are collinear, p2 is significant
1659 if (d2*d2 <= tessTol*(dx*dx+dy*dy)) {
1660 if (angleTol < AngleTolEPS) {
1661 addVertex(x23, y23/*, type*/);
1662 return;
1663 } else {
1664 // angle condition
1665 float da1 = fabsf(atan2f(y3-y2, x3-x2)-atan2f(y2-y1, x2-x1));
1666 if (da1 >= FLT_PI) da1 = 2.0f*FLT_PI-da1;
1667 if (da1 < angleTol) {
1668 addVertex(x2, y2/*, type*/);
1669 addVertex(x3, y3/*, type*/);
1670 return;
1672 if (cuspLimit != 0) {
1673 if (da1 > cuspLimit) {
1674 addVertex(x2, y2/*, type*/);
1675 return;
1680 break;
1681 case 3:
1682 // regular case
1683 if ((d2+d3)*(d2+d3) <= tessTol*(dx*dx+dy*dy)) {
1684 // if the curvature doesn't exceed the distance tolerance value, we tend to finish subdivisions
1685 if (angleTol < AngleTolEPS) {
1686 addVertex(x23, y23/*, type*/);
1687 return;
1688 } else {
1689 // angle and cusp condition
1690 immutable float k = atan2f(y3-y2, x3-x2);
1691 float da1 = fabsf(k-atan2f(y2-y1, x2-x1));
1692 float da2 = fabsf(atan2f(y4-y3, x4-x3)-k);
1693 if (da1 >= FLT_PI) da1 = 2.0f*FLT_PI-da1;
1694 if (da2 >= FLT_PI) da2 = 2.0f*FLT_PI-da2;
1695 if (da1+da2 < angleTol) {
1696 // finally we can stop the recursion
1697 addVertex(x23, y23/*, type*/);
1698 return;
1700 if (cuspLimit != 0) {
1701 if (da1 > cuspLimit) {
1702 addVertex(x2, y2/*, type*/);
1703 return;
1705 if (da2 > cuspLimit) {
1706 addVertex(x3, y3/*, type*/);
1707 return;
1712 break;
1715 // continue subdivision
1716 tesselateBezierMcSeem(x1, y1, x12, y12, x123, y123, x1234, y1234, level+1/*, 0*/);
1717 tesselateBezierMcSeem(x1234, y1234, x234, y234, x34, y34, x4, y4, level+1/*, type*/);
1720 // Adaptive forward differencing for bezier tesselation.
1721 // See Lien, Sheue-Ling, Michael Shantz, and Vaughan Pratt.
1722 // "Adaptive forward differencing for rendering curves and surfaces."
1723 // ACM SIGGRAPH Computer Graphics. Vol. 21. No. 4. ACM, 1987.
1724 // original code by Taylor Holliday <taylor@audulus.com>
1725 void tesselateBezierAFD (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4/*, in int type*/) nothrow @trusted @nogc {
1726 enum AFD_ONE = (1<<10);
1728 // power basis
1729 immutable float ax = -x1+3*x2-3*x3+x4;
1730 immutable float ay = -y1+3*y2-3*y3+y4;
1731 immutable float bx = 3*x1-6*x2+3*x3;
1732 immutable float by = 3*y1-6*y2+3*y3;
1733 immutable float cx = -3*x1+3*x2;
1734 immutable float cy = -3*y1+3*y2;
1736 // Transform to forward difference basis (stepsize 1)
1737 float px = x1;
1738 float py = y1;
1739 float dx = ax+bx+cx;
1740 float dy = ay+by+cy;
1741 float ddx = 6*ax+2*bx;
1742 float ddy = 6*ay+2*by;
1743 float dddx = 6*ax;
1744 float dddy = 6*ay;
1746 //printf("dx: %f, dy: %f\n", dx, dy);
1747 //printf("ddx: %f, ddy: %f\n", ddx, ddy);
1748 //printf("dddx: %f, dddy: %f\n", dddx, dddy);
1750 int t = 0;
1751 int dt = AFD_ONE;
1753 immutable float tol = tessTol*4;
1755 while (t < AFD_ONE) {
1756 // Flatness measure.
1757 float d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
1759 // printf("d: %f, th: %f\n", d, th);
1761 // Go to higher resolution if we're moving a lot or overshooting the end.
1762 while ((d > tol && dt > 1) || (t+dt > AFD_ONE)) {
1763 // printf("up\n");
1765 // Apply L to the curve. Increase curve resolution.
1766 dx = 0.5f*dx-(1.0f/8.0f)*ddx+(1.0f/16.0f)*dddx;
1767 dy = 0.5f*dy-(1.0f/8.0f)*ddy+(1.0f/16.0f)*dddy;
1768 ddx = (1.0f/4.0f)*ddx-(1.0f/8.0f)*dddx;
1769 ddy = (1.0f/4.0f)*ddy-(1.0f/8.0f)*dddy;
1770 dddx = (1.0f/8.0f)*dddx;
1771 dddy = (1.0f/8.0f)*dddy;
1773 // Half the stepsize.
1774 dt >>= 1;
1776 // Recompute d
1777 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
1780 // Go to lower resolution if we're really flat
1781 // and we aren't going to overshoot the end.
1782 // XXX: tol/32 is just a guess for when we are too flat.
1783 while ((d > 0 && d < tol/32.0f && dt < AFD_ONE) && (t+2*dt <= AFD_ONE)) {
1784 // printf("down\n");
1786 // Apply L^(-1) to the curve. Decrease curve resolution.
1787 dx = 2*dx+ddx;
1788 dy = 2*dy+ddy;
1789 ddx = 4*ddx+4*dddx;
1790 ddy = 4*ddy+4*dddy;
1791 dddx = 8*dddx;
1792 dddy = 8*dddy;
1794 // Double the stepsize.
1795 dt <<= 1;
1797 // Recompute d
1798 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
1801 // Forward differencing.
1802 px += dx;
1803 py += dy;
1804 dx += ddx;
1805 dy += ddy;
1806 ddx += dddx;
1807 ddy += dddy;
1809 // Output a point.
1810 addVertex(px, py/*, (t > 0 ? type : 0)*/);
1812 // Advance along the curve.
1813 t += dt;
1815 // Ensure we don't overshoot.
1816 assert(t <= AFD_ONE);
1821 public void tesselateBezier (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4) {
1822 final switch (tesselator) with (AGGTesselation) {
1823 case DeCasteljau:
1824 tesselateBezierDCj(x1, y1, x2, y2, x3, y3, x4, y4, 0/*, type*/);
1825 break;
1826 case AFD:
1827 tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4/*, type*/);
1828 break;
1829 case DeCasteljauMcSeem:
1830 addVertex(x1, y1/*, 0*/);
1831 tesselateBezierMcSeem(x1, y1, x2, y2, x3, y3, x4, y4, 1/*, type*/);
1832 addVertex(x4, y4/*, type*/);
1833 break;
1837 // ////////////////////////////////////////////////////////////////////////// //
1838 public enum BaphometDims = 512; // [0..511]
1839 public void renderBaphomet (float ofsx=0.0f, float ofsy=0.0f, float scalex=1.0f, float scaley=1.0f) {
1840 auto path = cast(const(ubyte)[])baphometPath;
1841 immutable plen = path.length;
1842 uint ppos = 0;
1844 enum Command {
1845 Bounds, // always first, has 4 args (x0, y0, x1, y1)
1846 StrokeMode,
1847 FillMode,
1848 StrokeFillMode,
1849 NormalStroke,
1850 ThinStroke,
1851 MoveTo,
1852 LineTo,
1853 CubicTo, // cubic bezier
1854 EndPath,
1857 Command getCommand () nothrow @trusted @nogc {
1858 if (ppos >= plen) assert(0, "invalid path");
1859 return cast(Command)(path.ptr[ppos++]);
1862 float getFloat () nothrow @trusted @nogc {
1863 if (ppos >= plen || plen-ppos < float.sizeof) assert(0, "invalid path");
1864 version(LittleEndian) {
1865 float res = *cast(const(float)*)(&path.ptr[ppos]);
1866 ppos += cast(uint)float.sizeof;
1867 return res;
1868 } else {
1869 static assert(float.sizeof == 4);
1870 uint xp = path.ptr[ppos]|(path.ptr[ppos+1]<<8)|(path.ptr[ppos+2]<<16)|(path.ptr[ppos+3]<<24);
1871 ppos += cast(uint)float.sizeof;
1872 return *cast(const(float)*)(&xp);
1876 float scaleX (in float v) nothrow @trusted @nogc { pragma(inline, true); return ofsx+cast(float)v*scalex; }
1877 float scaleY (in float v) nothrow @trusted @nogc { pragma(inline, true); return ofsy+cast(float)v*scaley; }
1879 beginPath();
1880 float cx = 0.0f, cy = 0.0f;
1881 bool doStroke = false, doFill = false;
1882 while (ppos < plen) {
1883 auto cmd = getCommand();
1885 foreach (string mn; __traits(allMembers, Command)) {
1886 if (__traits(getMember, Command, mn) == cmd) {
1887 import core.stdc.stdio; printf("cmd=%s\n", mn.ptr);
1891 final switch (cmd) {
1892 case Command.Bounds: ppos += 4*cast(uint)float.sizeof; break;
1893 case Command.StrokeMode: doStroke = true; doFill = false; break;
1894 case Command.FillMode: doStroke = false; doFill = true; break;
1895 case Command.StrokeFillMode: doStroke = true; doFill = true; break;
1896 case Command.NormalStroke: case Command.ThinStroke: break;
1897 case Command.MoveTo:
1898 cx = scaleX(getFloat());
1899 cy = scaleY(getFloat());
1900 moveTo(cx, cy);
1901 break;
1902 case Command.LineTo:
1903 immutable float ex = scaleX(getFloat());
1904 immutable float ey = scaleY(getFloat());
1905 lineTo(ex, ey);
1906 cx = ex;
1907 cy = ey;
1908 break;
1909 case Command.CubicTo: // cubic bezier
1910 immutable float x1 = scaleX(getFloat());
1911 immutable float y1 = scaleY(getFloat());
1912 immutable float x2 = scaleX(getFloat());
1913 immutable float y2 = scaleY(getFloat());
1914 immutable float ex = scaleX(getFloat());
1915 immutable float ey = scaleY(getFloat());
1916 bezierTo(x1, y1, x2, y2, ex, ey);
1917 cx = ex;
1918 cy = ey;
1919 break;
1920 case Command.EndPath: // don't close this path
1921 if (doFill) fill();
1922 if (doStroke) stroke();
1923 //doFill = doStroke = false;
1924 beginPath();
1925 break;
1928 if (doFill) fill();
1929 if (doStroke) stroke();
1930 beginPath();
1933 private static immutable ubyte[7641] baphometPath = [
1934 0x01,0x04,0x06,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x08,0xa0,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,
1935 0x00,0x80,0xff,0x43,0xa2,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x08,0x00,0x80,0xff,
1936 0x43,0x7a,0x89,0xe5,0x42,0xa0,0x1d,0xc6,0x43,0x00,0x00,0x00,0x00,0x30,0x89,0x7f,0x43,0x00,0x00,0x00,
1937 0x00,0x08,0x7a,0x89,0xe5,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7a,0x89,0xe5,0x42,0x00,0x00,
1938 0x00,0x00,0x30,0x89,0x7f,0x43,0x08,0x00,0x00,0x00,0x00,0xa2,0x1d,0xc6,0x43,0x7a,0x89,0xe5,0x42,0x00,
1939 0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x09,0x06,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
1940 0x43,0x08,0x16,0x68,0xb3,0x43,0x72,0x87,0xdd,0x43,0x71,0x87,0xdd,0x43,0x17,0x68,0xb3,0x43,0x71,0x87,
1941 0xdd,0x43,0x30,0x89,0x7f,0x43,0x08,0x71,0x87,0xdd,0x43,0xd2,0x2f,0x18,0x43,0x16,0x68,0xb3,0x43,0x35,
1942 0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x35,0xe2,0x87,0x42,0x08,0xd1,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,
1943 0x35,0xe2,0x87,0x42,0xd2,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x08,0x35,0xe2,0x87,
1944 0x42,0x17,0x68,0xb3,0x43,0xd1,0x2f,0x18,0x43,0x72,0x87,0xdd,0x43,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
1945 0x43,0x09,0x06,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x07,0xa4,0x3f,0x7f,0x43,0x0b,0x86,0xdc,0x43,
1946 0x07,0x6c,0xb9,0xb2,0x43,0xe8,0xd1,0xca,0x42,0x07,0x6e,0x4d,0xa0,0x42,0xa9,0x10,0x9c,0x43,0x07,0xb7,
1947 0x40,0xd7,0x43,0xa9,0x10,0x9c,0x43,0x07,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x09,0x06,0x98,0x42,
1948 0x74,0x43,0xb1,0x8d,0x68,0x43,0x08,0xd7,0x24,0x79,0x43,0xba,0x83,0x6e,0x43,0xa9,0x16,0x7c,0x43,0x56,
1949 0xa1,0x76,0x43,0x74,0x2a,0x7d,0x43,0x44,0x73,0x80,0x43,0x08,0x55,0xd1,0x7e,0x43,0xe3,0xea,0x76,0x43,
1950 0xbc,0x18,0x81,0x43,0x7f,0xa8,0x6e,0x43,0x8f,0x0a,0x84,0x43,0x02,0xfc,0x68,0x43,0x09,0x06,0x92,0x29,
1951 0x8d,0x43,0x73,0xc3,0x67,0x43,0x08,0xa4,0xd9,0x8e,0x43,0xf2,0xa6,0x7a,0x43,0x8f,0x22,0x88,0x43,0x75,
1952 0x2a,0x7d,0x43,0x42,0x7f,0x82,0x43,0x08,0xc8,0x88,0x43,0x09,0x06,0xc1,0x79,0x74,0x43,0x50,0x64,0x89,
1953 0x43,0x08,0x68,0x2d,0x72,0x43,0xee,0x21,0x81,0x43,0xcd,0x97,0x55,0x43,0xe6,0xf1,0x7b,0x43,0x91,0xec,
1954 0x5d,0x43,0xa8,0xc7,0x6a,0x43,0x09,0x06,0xfa,0xa5,0x52,0x43,0x60,0x97,0x7c,0x43,0x08,0x19,0xff,0x50,
1955 0x43,0xe9,0x6e,0x8a,0x43,0xb0,0xbd,0x70,0x43,0x4c,0x51,0x82,0x43,0x04,0xeb,0x69,0x43,0x66,0x0f,0x8e,
1956 0x43,0x09,0x06,0x17,0xbf,0x71,0x43,0x2c,0x58,0x94,0x43,0x08,0x1c,0x96,0x6e,0x43,0x61,0x68,0x99,0x43,
1957 0x2d,0x3a,0x6e,0x43,0xc8,0x81,0x9e,0x43,0xb7,0x9b,0x72,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0x30,0xdb,
1958 0x82,0x43,0xdb,0xe9,0x93,0x43,0x08,0x11,0x82,0x84,0x43,0x61,0x68,0x99,0x43,0xe8,0x4a,0x84,0x43,0x8e,
1959 0xa6,0x9e,0x43,0x42,0x7f,0x82,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0xc4,0x02,0x85,0x43,0xd1,0x0b,0x92,
1960 0x43,0x08,0xd6,0xb2,0x86,0x43,0x34,0x1e,0x92,0x43,0x4f,0x58,0x87,0x43,0xa4,0xf1,0x92,0x43,0x03,0xd9,
1961 0x87,0x43,0x7b,0xc6,0x94,0x43,0x09,0x06,0x87,0x3e,0x64,0x43,0x31,0x3b,0x93,0x43,0x08,0x3b,0xbf,0x64,
1962 0x43,0x6f,0xf9,0x91,0x43,0x96,0x0b,0x67,0x43,0xc5,0x4a,0x91,0x43,0xcf,0xfe,0x6a,0x43,0x31,0x2f,0x91,
1963 0x43,0x09,0x06,0x16,0x74,0xb5,0x43,0x08,0xec,0x8e,0x43,0x08,0x1b,0x4b,0xb2,0x43,0xee,0x5d,0x8b,0x43,
1964 0x48,0x4d,0xad,0x43,0x12,0xa6,0x8a,0x43,0xf3,0xd7,0xa7,0x43,0x74,0xb8,0x8a,0x43,0x08,0x8c,0xb2,0xa0,
1965 0x43,0xcd,0xf8,0x8a,0x43,0x68,0x46,0x9b,0x43,0x79,0x8f,0x87,0x43,0x49,0xc9,0x96,0x43,0xe9,0x3e,0x82,
1966 0x43,0x08,0x60,0x5c,0x97,0x43,0xa1,0xde,0x8b,0x43,0x4e,0xa0,0x93,0x43,0x31,0x3b,0x93,0x43,0x9f,0xea,
1967 0x8d,0x43,0x27,0x8d,0x99,0x43,0x08,0x07,0xe0,0x8c,0x43,0x06,0x34,0x9b,0x43,0x38,0xe9,0x8c,0x43,0x46,
1968 0x0a,0x9e,0x43,0x3d,0xcc,0x8b,0x43,0xb2,0x06,0xa2,0x43,0x08,0xf1,0x40,0x8a,0x43,0xb0,0x12,0xa4,0x43,
1969 0x39,0xd1,0x88,0x43,0x76,0x43,0xa6,0x43,0xfa,0x06,0x88,0x43,0xa4,0x75,0xa9,0x43,0x08,0x19,0x6c,0x88,
1970 0x43,0x9f,0x9e,0xac,0x43,0x66,0xeb,0x87,0x43,0x44,0x76,0xb0,0x43,0x6b,0xce,0x86,0x43,0x3b,0xbc,0xb4,
1971 0x43,0x08,0xa9,0x8c,0x85,0x43,0x06,0xd0,0xb5,0x43,0xfa,0xee,0x83,0x43,0x74,0xa3,0xb6,0x43,0x3d,0x90,
1972 0x81,0x43,0x31,0xf6,0xb6,0x43,0x08,0x9d,0x61,0x7d,0x43,0xee,0x48,0xb7,0x43,0x3b,0x1f,0x75,0x43,0xcf,
1973 0xe3,0xb6,0x43,0xee,0x6f,0x6d,0x43,0x68,0xe2,0xb5,0x43,0x08,0xd4,0xed,0x6b,0x43,0x87,0x2f,0xb2,0x43,
1974 0x0e,0xc9,0x6b,0x43,0xa7,0x7c,0xae,0x43,0x98,0xfa,0x67,0x43,0xab,0x53,0xab,0x43,0x08,0x25,0x2c,0x64,
1975 0x43,0x33,0xa2,0xa8,0x43,0x40,0x96,0x61,0x43,0xc3,0xc2,0xa5,0x43,0x64,0xde,0x60,0x43,0xfa,0xa2,0xa2,
1976 0x43,0x08,0xb0,0x5d,0x60,0x43,0x06,0x4c,0x9f,0x43,0x9a,0xca,0x5f,0x43,0x38,0x3d,0x9b,0x43,0x3b,0x8f,
1977 0x5c,0x43,0x85,0xb0,0x98,0x43,0x08,0x42,0x36,0x51,0x43,0x3d,0xf0,0x91,0x43,0xcd,0x4f,0x49,0x43,0xdb,
1978 0xb9,0x8b,0x43,0xe0,0xdb,0x44,0x43,0x42,0x8b,0x84,0x43,0x08,0x7e,0xc9,0x44,0x43,0x8a,0x57,0x8d,0x43,
1979 0xbc,0x6c,0x0f,0x43,0x23,0x62,0x8e,0x43,0xf5,0x17,0x07,0x43,0xc5,0x3e,0x8f,0x43,0x09,0x06,0xe0,0xea,
1980 0x76,0x43,0xab,0xef,0xc5,0x43,0x08,0x12,0x00,0x79,0x43,0xab,0xcb,0xbf,0x43,0x79,0xb9,0x6d,0x43,0x7e,
1981 0x8d,0xba,0x43,0xee,0x6f,0x6d,0x43,0x98,0xeb,0xb5,0x43,0x08,0xe0,0x02,0x7b,0x43,0x5f,0x1c,0xb8,0x43,
1982 0x85,0x2c,0x82,0x43,0xe9,0x65,0xb8,0x43,0xd6,0xb2,0x86,0x43,0xc6,0x05,0xb5,0x43,0x08,0x03,0xcd,0x85,
1983 0x43,0x5a,0x39,0xb9,0x43,0xe4,0x4f,0x81,0x43,0xdb,0xd4,0xbf,0x43,0xdf,0x6c,0x82,0x43,0xbc,0x93,0xc5,
1984 0x43,0x09,0x06,0xf0,0xd0,0x22,0x43,0x5d,0x19,0x08,0x43,0x08,0xbc,0xab,0x49,0x43,0x4a,0x35,0x29,0x43,
1985 0xcb,0xf7,0x65,0x43,0xce,0x37,0x45,0x43,0x0e,0x99,0x63,0x43,0x67,0xc6,0x5c,0x43,0x09,0x06,0x05,0x94,
1986 0xab,0x43,0xc2,0x13,0x04,0x43,0x08,0x9f,0x26,0x98,0x43,0x11,0x42,0x25,0x43,0x97,0x00,0x8a,0x43,0x32,
1987 0x32,0x41,0x43,0xf5,0x2f,0x8b,0x43,0xc7,0xc0,0x58,0x43,0x09,0x06,0x8f,0x85,0x48,0x43,0xe0,0xa8,0x8c,
1988 0x43,0x08,0x55,0xaa,0x48,0x43,0xe0,0xa8,0x8c,0x43,0x6b,0x3d,0x49,0x43,0xc1,0x43,0x8c,0x43,0x31,0x62,
1989 0x49,0x43,0xc1,0x43,0x8c,0x43,0x08,0x2f,0xe3,0x2f,0x43,0xad,0xe7,0x98,0x43,0xff,0x0d,0x0d,0x43,0xad,
1990 0xf3,0x9a,0x43,0xf0,0xaf,0xcc,0x42,0x74,0x00,0x97,0x43,0x08,0xbb,0xa2,0xf7,0x42,0x93,0x4d,0x93,0x43,
1991 0x5e,0x19,0x08,0x43,0x5a,0x2a,0x87,0x43,0x23,0x6e,0x10,0x43,0x42,0x97,0x86,0x43,0x08,0xca,0xe8,0x33,
1992 0x43,0x1b,0x3c,0x80,0x43,0x80,0xe8,0x4d,0x43,0xda,0xf4,0x70,0x43,0xae,0x0e,0x4f,0x43,0x2b,0x1b,0x65,
1993 0x43,0x08,0x66,0x96,0x54,0x43,0xa3,0xe1,0x3b,0x43,0x4e,0xc4,0x19,0x43,0xa0,0x1a,0x16,0x43,0x10,0xe2,
1994 0x14,0x43,0x26,0x14,0xe0,0x42,0x08,0x5c,0x91,0x1c,0x43,0xcb,0x27,0xee,0x42,0xa9,0x40,0x24,0x43,0x71,
1995 0x3b,0xfc,0x42,0xf3,0xef,0x2b,0x43,0x8b,0x27,0x05,0x43,0x08,0xe2,0x4b,0x2c,0x43,0x48,0x86,0x07,0x43,
1996 0x79,0x62,0x2f,0x43,0x05,0xe5,0x09,0x43,0x55,0x32,0x34,0x43,0xa0,0xd2,0x09,0x43,0x08,0x74,0xa3,0x36,
1997 0x43,0x3a,0xd1,0x08,0x43,0x7e,0x81,0x38,0x43,0x09,0xd4,0x0a,0x43,0x0d,0xba,0x39,0x43,0xa0,0xea,0x0d,
1998 0x43,0x08,0x6f,0xe4,0x3d,0x43,0x43,0xc7,0x0e,0x43,0xd6,0xe5,0x3e,0x43,0xc4,0x4a,0x11,0x43,0x55,0x7a,
1999 0x40,0x43,0x59,0x72,0x13,0x43,0x08,0x55,0x92,0x44,0x43,0xbf,0x73,0x14,0x43,0x23,0x95,0x46,0x43,0xa5,
2000 0x09,0x17,0x43,0xe0,0xf3,0x48,0x43,0xfe,0x55,0x19,0x43,0x08,0xcd,0x4f,0x49,0x43,0xaa,0x10,0x1c,0x43,
2001 0x61,0x77,0x4b,0x43,0xfe,0x6d,0x1d,0x43,0x80,0xe8,0x4d,0x43,0x2b,0x94,0x1e,0x43,0x08,0x58,0xc9,0x51,
2002 0x43,0x41,0x27,0x1f,0x43,0x9b,0x82,0x53,0x43,0x35,0x72,0x20,0x43,0x53,0xf2,0x54,0x43,0x88,0xcf,0x21,
2003 0x43,0x08,0x7b,0x29,0x55,0x43,0xe8,0x0a,0x25,0x43,0xb2,0x2d,0x58,0x43,0xef,0xe8,0x26,0x43,0x9b,0xb2,
2004 0x5b,0x43,0xd0,0x8f,0x28,0x43,0x08,0x5f,0xef,0x5f,0x43,0xeb,0x11,0x2a,0x43,0xfd,0xdc,0x5f,0x43,0x6e,
2005 0x95,0x2c,0x43,0x3b,0xa7,0x60,0x43,0x2b,0xf4,0x2e,0x43,0x08,0x06,0xbb,0x61,0x43,0xfd,0xe5,0x31,0x43,
2006 0xe7,0x61,0x63,0x43,0xef,0x30,0x33,0x43,0x53,0x52,0x65,0x43,0xa3,0xb1,0x33,0x43,0x08,0x12,0xa0,0x68,
2007 0x43,0x7f,0x69,0x34,0x43,0x40,0xc6,0x69,0x43,0x64,0xff,0x36,0x43,0x7e,0x90,0x6a,0x43,0x71,0xcc,0x39,
2008 0x43,0x08,0xbc,0x5a,0x6b,0x43,0x51,0x73,0x3b,0x43,0xc1,0x49,0x6c,0x43,0xa5,0xd0,0x3c,0x43,0xe0,0xba,
2009 0x6e,0x43,0xb8,0x74,0x3c,0x43,0x08,0x6b,0x1c,0x73,0x43,0x13,0xc1,0x3e,0x43,0x40,0xf6,0x71,0x43,0xce,
2010 0x1f,0x41,0x43,0x55,0x89,0x72,0x43,0x8d,0x7e,0x43,0x43,0x08,0x68,0x2d,0x72,0x43,0x89,0xae,0x4b,0x43,
2011 0xc1,0x79,0x74,0x43,0xcb,0x78,0x4c,0x43,0x55,0xa1,0x76,0x43,0x5b,0xb1,0x4d,0x43,0x08,0xa2,0x38,0x7a,
2012 0x43,0xd1,0x56,0x4e,0x43,0x85,0xb6,0x78,0x43,0xb1,0x15,0x54,0x43,0x83,0xc7,0x77,0x43,0x89,0x0e,0x5c,
2013 0x43,0x08,0xcf,0x46,0x77,0x43,0x0f,0x81,0x5f,0x43,0x1a,0xde,0x7a,0x43,0xce,0xc7,0x5d,0x43,0x42,0x73,
2014 0x80,0x43,0x99,0xc3,0x5a,0x43,0x08,0x85,0x2c,0x82,0x43,0xf6,0xe6,0x59,0x43,0x81,0x3d,0x81,0x43,0x16,
2015 0x10,0x50,0x43,0xd6,0x8e,0x80,0x43,0x5b,0x99,0x49,0x43,0x08,0xc4,0xea,0x80,0x43,0x22,0x95,0x46,0x43,
2016 0xfa,0xe2,0x81,0x43,0xda,0xec,0x43,0x43,0x78,0x77,0x83,0x43,0xe4,0xb2,0x41,0x43,0x08,0x8a,0x27,0x85,
2017 0x43,0x86,0x77,0x3e,0x43,0x0c,0x9f,0x85,0x43,0x07,0xf4,0x3b,0x43,0x8f,0x16,0x86,0x43,0xe6,0x82,0x39,
2018 0x43,0x08,0x85,0x44,0x86,0x43,0x37,0xd9,0x35,0x43,0x1e,0x4f,0x87,0x43,0xe1,0x7b,0x34,0x43,0xdf,0x90,
2019 0x88,0x43,0xb6,0x55,0x33,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0xe5,0x31,0x43,0xfa,0x12,0x8a,0x43,0xbf,
2020 0x03,0x2d,0x43,0x19,0x78,0x8a,0x43,0x45,0x5e,0x2c,0x43,0x08,0x03,0xf1,0x8b,0x43,0xac,0x47,0x29,0x43,
2021 0x2f,0x17,0x8d,0x43,0x45,0x46,0x28,0x43,0xc8,0x21,0x8e,0x43,0x30,0xb3,0x27,0x43,0x08,0xa9,0xc8,0x8f,
2022 0x43,0xef,0xe8,0x26,0x43,0xbf,0x5b,0x90,0x43,0x5b,0xc1,0x24,0x43,0x10,0xca,0x90,0x43,0xa0,0x62,0x22,
2023 0x43,0x08,0x26,0x5d,0x91,0x43,0xbb,0xcc,0x1f,0x43,0xf0,0x70,0x92,0x43,0x78,0x13,0x1e,0x43,0x77,0xd7,
2024 0x93,0x43,0x73,0x24,0x1d,0x43,0x08,0x65,0x3f,0x96,0x43,0xce,0x58,0x1b,0x43,0xbe,0x7f,0x96,0x43,0xbf,
2025 0x8b,0x18,0x43,0x60,0x5c,0x97,0x43,0xb6,0xad,0x16,0x43,0x08,0xba,0xa8,0x99,0x43,0x78,0xcb,0x11,0x43,
2026 0x49,0xe1,0x9a,0x43,0x78,0xcb,0x11,0x43,0x01,0x51,0x9c,0x43,0x73,0xdc,0x10,0x43,0x08,0x72,0x24,0x9d,
2027 0x43,0xd2,0xff,0x0f,0x43,0x1c,0xd3,0x9d,0x43,0x07,0xec,0x0e,0x43,0xeb,0xc9,0x9d,0x43,0xe8,0x7a,0x0c,
2028 0x43,0x08,0x60,0x80,0x9d,0x43,0xd7,0xbe,0x08,0x43,0x4d,0xe8,0x9f,0x43,0x86,0x50,0x08,0x43,0x25,0xbd,
2029 0xa1,0x43,0x5b,0x2a,0x07,0x43,0x08,0x99,0x7f,0xa3,0x43,0xc9,0xf1,0x05,0x43,0x48,0x1d,0xa5,0x43,0x86,
2030 0x38,0x04,0x43,0x6c,0x71,0xa6,0x43,0x18,0x59,0x01,0x43,0x08,0x32,0x96,0xa6,0x43,0x6e,0x64,0xff,0x42,
2031 0x48,0x29,0xa7,0x43,0xed,0xcf,0xfd,0x42,0x5f,0xbc,0xa7,0x43,0x71,0x3b,0xfc,0x42,0x08,0xf3,0xe3,0xa9,
2032 0x43,0xf7,0x7d,0xf7,0x42,0xd8,0x6d,0xaa,0x43,0x45,0xe5,0xf2,0x42,0x48,0x41,0xab,0x43,0xcb,0x27,0xee,
2033 0x42,0x08,0x24,0xf9,0xab,0x43,0x52,0x6a,0xe9,0x42,0xee,0x0c,0xad,0x43,0x4c,0x8c,0xe7,0x42,0x1b,0x33,
2034 0xae,0x43,0xcc,0xf7,0xe5,0x42,0x08,0xaa,0x6b,0xaf,0x43,0xe8,0x61,0xe3,0x42,0x90,0xf5,0xaf,0x43,0xc9,
2035 0xf0,0xe0,0x42,0xe0,0x63,0xb0,0x43,0xe5,0x5a,0xde,0x42,0x08,0xaa,0x83,0xb3,0x43,0x29,0x2d,0x09,0x43,
2036 0x6a,0xfe,0x8e,0x43,0xb8,0x74,0x3c,0x43,0xd5,0x06,0x95,0x43,0xe6,0x79,0x67,0x43,0x08,0x2f,0x53,0x97,
2037 0x43,0xe9,0xb0,0x74,0x43,0xa8,0x28,0xa0,0x43,0x43,0xfd,0x76,0x43,0x83,0x28,0xad,0x43,0x17,0x59,0x81,
2038 0x43,0x08,0x3d,0xe7,0xbf,0x43,0x4b,0x8d,0x8c,0x43,0xae,0x96,0xba,0x43,0x66,0x27,0x92,0x43,0x15,0xe0,
2039 0xc7,0x43,0x6f,0x11,0x96,0x43,0x08,0x7e,0x5d,0xb2,0x43,0xdb,0x01,0x98,0x43,0x9e,0x56,0xa0,0x43,0x80,
2040 0xc1,0x97,0x43,0x69,0x2e,0x97,0x43,0x31,0x17,0x8d,0x43,0x09,0x06,0xab,0xa7,0x39,0x43,0x67,0x0f,0x0e,
2041 0x43,0x08,0xdb,0xbc,0x3b,0x43,0xe8,0x92,0x10,0x43,0xb5,0x85,0x3b,0x43,0x97,0x3c,0x14,0x43,0xab,0xa7,
2042 0x39,0x43,0x0c,0x0b,0x18,0x43,0x09,0x06,0xca,0x30,0x40,0x43,0x30,0x3b,0x13,0x43,0x08,0x17,0xc8,0x43,
2043 0x43,0xa5,0x09,0x17,0x43,0x7e,0xc9,0x44,0x43,0x1a,0xd8,0x1a,0x43,0x9d,0x22,0x43,0x43,0x8d,0xa6,0x1e,
2044 0x43,0x09,0x06,0xc8,0x78,0x4c,0x43,0xed,0xc9,0x1d,0x43,0x08,0x0b,0x32,0x4e,0x43,0x22,0xce,0x20,0x43,
2045 0x23,0xc5,0x4e,0x43,0x58,0xd2,0x23,0x43,0x0b,0x32,0x4e,0x43,0x2b,0xc4,0x26,0x43,0x09,0x06,0xec,0x08,
2046 0x58,0x43,0xc7,0xb1,0x26,0x43,0x08,0x02,0x9c,0x58,0x43,0xef,0x00,0x2b,0x43,0xd9,0x64,0x58,0x43,0x02,
2047 0xbd,0x2e,0x43,0x10,0x51,0x57,0x43,0x37,0xc1,0x31,0x43,0x09,0x06,0xcb,0xdf,0x61,0x43,0x4a,0x65,0x31,
2048 0x43,0x08,0xbe,0x2a,0x63,0x43,0xbd,0x33,0x35,0x43,0x32,0xe1,0x62,0x43,0x56,0x4a,0x38,0x43,0xde,0x83,
2049 0x61,0x43,0x3c,0xe0,0x3a,0x43,0x09,0x06,0x1c,0x7e,0x6a,0x43,0x5b,0x39,0x39,0x43,0x08,0x31,0x11,0x6b,
2050 0x43,0x0c,0xd2,0x3d,0x43,0x1c,0x7e,0x6a,0x43,0x13,0xd9,0x42,0x43,0xd9,0xc4,0x68,0x43,0xcb,0x60,0x48,
2051 0x43,0x09,0x06,0xe5,0xc1,0x73,0x43,0x16,0xf8,0x4b,0x43,0x08,0xa6,0xf7,0x72,0x43,0xb1,0xfd,0x4f,0x43,
2052 0x3b,0x07,0x71,0x43,0x4a,0x14,0x53,0x43,0xa2,0xf0,0x6d,0x43,0x7c,0x29,0x55,0x43,0x09,0x06,0x00,0x8d,
2053 0xa6,0x43,0xef,0x21,0x01,0x43,0x08,0x52,0xfb,0xa6,0x43,0xce,0xc8,0x02,0x43,0xe6,0x16,0xa7,0x43,0x51,
2054 0x4c,0x05,0x43,0x3b,0x68,0xa6,0x43,0x4c,0x75,0x08,0x43,0x09,0x06,0xde,0x20,0xa1,0x43,0x86,0x50,0x08,
2055 0x43,0x08,0xd4,0x4e,0xa1,0x43,0xd3,0xe7,0x0b,0x43,0xb5,0xe9,0xa0,0x43,0x59,0x5a,0x0f,0x43,0xba,0xcc,
2056 0x9f,0x43,0x54,0x83,0x12,0x43,0x09,0x06,0x77,0xfb,0x99,0x43,0x6c,0x16,0x13,0x43,0x08,0xde,0xfc,0x9a,
2057 0x43,0x4a,0xbd,0x14,0x43,0x06,0x34,0x9b,0x43,0xfe,0x55,0x19,0x43,0x13,0xe9,0x99,0x43,0x41,0x27,0x1f,
2058 0x43,0x09,0x06,0x46,0xce,0x93,0x43,0x26,0xa5,0x1d,0x43,0x08,0xe7,0xaa,0x94,0x43,0xbb,0xcc,0x1f,0x43,
2059 0x18,0xb4,0x94,0x43,0xa8,0x40,0x24,0x43,0xe2,0xbb,0x93,0x43,0x21,0xfe,0x28,0x43,0x09,0x06,0xb1,0x8e,
2060 0x8d,0x43,0xa8,0x58,0x28,0x43,0x08,0x19,0x90,0x8e,0x43,0x54,0x13,0x2b,0x43,0xa4,0xd9,0x8e,0x43,0x84,
2061 0x40,0x31,0x43,0x46,0xaa,0x8d,0x43,0x29,0x24,0x37,0x43,0x09,0x06,0xd6,0xbe,0x88,0x43,0xef,0x30,0x33,
2062 0x43,0x08,0x0c,0xb7,0x89,0x43,0x0e,0xa2,0x35,0x43,0xc0,0x37,0x8a,0x43,0x7a,0xaa,0x3b,0x43,0xbb,0x48,
2063 0x89,0x43,0xbb,0x7b,0x41,0x43,0x09,0x06,0x3a,0xad,0x82,0x43,0xc4,0x59,0x43,0x43,0x08,0xd2,0xb7,0x83,
2064 0x43,0x2b,0x5b,0x44,0x43,0x35,0xd6,0x85,0x43,0x48,0xf5,0x49,0x43,0x42,0x97,0x86,0x43,0xc4,0xa1,0x4f,
2065 0x43,0x09,0x06,0x9c,0xb3,0x80,0x43,0x48,0x55,0x5a,0x43,0x08,0xff,0xc5,0x80,0x43,0x09,0x73,0x55,0x43,
2066 0x93,0xe1,0x80,0x43,0x0f,0x39,0x53,0x43,0xf1,0xbe,0x7e,0x43,0x18,0xe7,0x4c,0x43,0x09,0x06,0xe0,0x02,
2067 0x7b,0x43,0x92,0xec,0x5d,0x43,0x08,0x09,0x3a,0x7b,0x43,0xf0,0xf7,0x58,0x43,0x09,0x3a,0x7b,0x43,0xe6,
2068 0x31,0x5b,0x43,0xe0,0x02,0x7b,0x43,0xa8,0x4f,0x56,0x43,0x09,0x06,0x39,0x4f,0x7d,0x43,0x3e,0x8f,0x5c,
2069 0x43,0x08,0xe9,0xe0,0x7c,0x43,0x03,0x9c,0x58,0x43,0x1e,0x2b,0x81,0x43,0x7f,0x30,0x5a,0x43,0xff,0x73,
2070 0x7d,0x43,0xf6,0xb6,0x51,0x43,0x09,0x06,0x5c,0xb8,0x52,0x43,0x28,0x21,0x87,0x43,0x08,0xae,0x3e,0x57,
2071 0x43,0x12,0x9a,0x88,0x43,0x23,0xf5,0x56,0x43,0x04,0xf1,0x8b,0x43,0x25,0xfc,0x5b,0x43,0x85,0x74,0x8e,
2072 0x43,0x08,0x2f,0xf2,0x61,0x43,0x8e,0x52,0x90,0x43,0xd9,0xdc,0x6c,0x43,0x85,0x74,0x8e,0x43,0xc6,0x20,
2073 0x69,0x43,0x3d,0xd8,0x8d,0x43,0x08,0x6d,0x8c,0x5a,0x43,0xf5,0x3b,0x8d,0x43,0x3d,0x77,0x58,0x43,0xa1,
2074 0xc6,0x87,0x43,0xf8,0xed,0x5e,0x43,0x5e,0x0d,0x86,0x43,0x09,0x06,0xde,0xcc,0x92,0x43,0xf7,0x17,0x87,
2075 0x43,0x08,0xb6,0x89,0x90,0x43,0xae,0x87,0x88,0x43,0x4a,0xa5,0x90,0x43,0xa1,0xde,0x8b,0x43,0xf9,0x2a,
2076 0x8e,0x43,0x23,0x62,0x8e,0x43,0x08,0xf5,0x2f,0x8b,0x43,0x5c,0x49,0x90,0x43,0x35,0xd6,0x85,0x43,0x8e,
2077 0x46,0x8e,0x43,0x3d,0xb4,0x87,0x43,0x47,0xaa,0x8d,0x43,0x08,0x6a,0xfe,0x8e,0x43,0xff,0x0d,0x8d,0x43,
2078 0xbb,0x6c,0x8f,0x43,0xf7,0x17,0x87,0x43,0x5c,0x31,0x8c,0x43,0xb2,0x5e,0x85,0x43,0x09,0x06,0x60,0x38,
2079 0x91,0x43,0x69,0x5d,0x7a,0x43,0x08,0x34,0x1e,0x92,0x43,0x1e,0x5b,0x89,0x43,0x04,0x63,0x7e,0x43,0x5e,
2080 0x01,0x84,0x43,0x59,0x2a,0x87,0x43,0x0d,0xcf,0x8d,0x43,0x09,0x03,0x04,0x06,0x5a,0x18,0x63,0x43,0x82,
2081 0x79,0x8b,0x43,0x08,0x25,0x2c,0x64,0x43,0x82,0x79,0x8b,0x43,0x2a,0x1b,0x65,0x43,0x9d,0xef,0x8a,0x43,
2082 0x2a,0x1b,0x65,0x43,0xc1,0x37,0x8a,0x43,0x08,0x2a,0x1b,0x65,0x43,0x17,0x89,0x89,0x43,0x25,0x2c,0x64,
2083 0x43,0x31,0xff,0x88,0x43,0x5a,0x18,0x63,0x43,0x31,0xff,0x88,0x43,0x08,0xf3,0x16,0x62,0x43,0x31,0xff,
2084 0x88,0x43,0xee,0x27,0x61,0x43,0x17,0x89,0x89,0x43,0xee,0x27,0x61,0x43,0xc1,0x37,0x8a,0x43,0x08,0xee,
2085 0x27,0x61,0x43,0x9d,0xef,0x8a,0x43,0xf3,0x16,0x62,0x43,0x82,0x79,0x8b,0x43,0x5a,0x18,0x63,0x43,0x82,
2086 0x79,0x8b,0x43,0x09,0x06,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x08,0x34,0xee,0x89,0x43,0x82,0x79,
2087 0x8b,0x43,0x85,0x5c,0x8a,0x43,0x9d,0xef,0x8a,0x43,0x85,0x5c,0x8a,0x43,0xc1,0x37,0x8a,0x43,0x08,0x85,
2088 0x5c,0x8a,0x43,0x17,0x89,0x89,0x43,0x34,0xee,0x89,0x43,0x31,0xff,0x88,0x43,0x4f,0x64,0x89,0x43,0x31,
2089 0xff,0x88,0x43,0x08,0x9c,0xe3,0x88,0x43,0x31,0xff,0x88,0x43,0x19,0x6c,0x88,0x43,0x17,0x89,0x89,0x43,
2090 0x19,0x6c,0x88,0x43,0xc1,0x37,0x8a,0x43,0x08,0x19,0x6c,0x88,0x43,0x9d,0xef,0x8a,0x43,0x9c,0xe3,0x88,
2091 0x43,0x82,0x79,0x8b,0x43,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x09,0x02,0x04,0x06,0x19,0x60,0x86,
2092 0x43,0xec,0xed,0xa3,0x43,0x08,0x35,0xd6,0x85,0x43,0x76,0x43,0xa6,0x43,0x93,0xe1,0x80,0x43,0x57,0x02,
2093 0xac,0x43,0x61,0xd8,0x80,0x43,0x87,0x17,0xae,0x43,0x08,0xa5,0x85,0x80,0x43,0xc3,0xfe,0xaf,0x43,0xce,
2094 0xbc,0x80,0x43,0x83,0x40,0xb1,0x43,0xa5,0x91,0x82,0x43,0x79,0x6e,0xb1,0x43,0x08,0x23,0x26,0x84,0x43,
2095 0x40,0x93,0xb1,0x43,0x30,0xe7,0x84,0x43,0xbe,0x1b,0xb1,0x43,0x11,0x82,0x84,0x43,0xab,0x6b,0xaf,0x43,
2096 0x08,0xb7,0x41,0x84,0x43,0x3b,0x98,0xae,0x43,0xb7,0x41,0x84,0x43,0xc3,0xf2,0xad,0x43,0xa1,0xae,0x83,
2097 0x43,0x83,0x28,0xad,0x43,0x08,0xb2,0x52,0x83,0x43,0x80,0x39,0xac,0x43,0x81,0x49,0x83,0x43,0xf0,0x00,
2098 0xab,0x43,0xe4,0x67,0x85,0x43,0x76,0x4f,0xa8,0x43,0x08,0x9c,0xd7,0x86,0x43,0xd1,0x83,0xa6,0x43,0xec,
2099 0x45,0x87,0x43,0x01,0x75,0xa2,0x43,0x19,0x60,0x86,0x43,0xec,0xed,0xa3,0x43,0x09,0x06,0xd9,0xdc,0x6c,
2100 0x43,0x14,0x25,0xa4,0x43,0x08,0xa2,0xf0,0x6d,0x43,0x9f,0x7a,0xa6,0x43,0x47,0xec,0x77,0x43,0x80,0x39,
2101 0xac,0x43,0xa9,0xfe,0x77,0x43,0xb0,0x4e,0xae,0x43,0x08,0x23,0xa4,0x78,0x43,0xea,0x35,0xb0,0x43,0xd2,
2102 0x35,0x78,0x43,0xab,0x77,0xb1,0x43,0xc1,0x79,0x74,0x43,0xa2,0xa5,0xb1,0x43,0x08,0xc6,0x50,0x71,0x43,
2103 0x68,0xca,0xb1,0x43,0xab,0xce,0x6f,0x43,0xe7,0x52,0xb1,0x43,0xea,0x98,0x70,0x43,0xd4,0xa2,0xaf,0x43,
2104 0x08,0x9d,0x19,0x71,0x43,0x96,0xd8,0xae,0x43,0x9d,0x19,0x71,0x43,0xec,0x29,0xae,0x43,0xca,0x3f,0x72,
2105 0x43,0xab,0x5f,0xad,0x43,0x08,0xa6,0xf7,0x72,0x43,0xa7,0x70,0xac,0x43,0x09,0x0a,0x73,0x43,0x17,0x38,
2106 0xab,0x43,0x44,0xcd,0x6e,0x43,0x9f,0x86,0xa8,0x43,0x08,0xd4,0xed,0x6b,0x43,0xf8,0xba,0xa6,0x43,0x31,
2107 0x11,0x6b,0x43,0x2a,0xac,0xa2,0x43,0xd9,0xdc,0x6c,0x43,0x14,0x25,0xa4,0x43,0x09,0x01,0x05,0x06,0x66,
2108 0x5d,0x7a,0x43,0x74,0xeb,0xc2,0x43,0x08,0x09,0x22,0x77,0x43,0x50,0xbb,0xc7,0x43,0xe9,0xe0,0x7c,0x43,
2109 0xf5,0x86,0xc9,0x43,0x8f,0x94,0x7a,0x43,0xc5,0x95,0xcd,0x43,0x09,0x06,0x08,0x98,0x80,0x43,0x6b,0x19,
2110 0xc3,0x43,0x08,0xb7,0x35,0x82,0x43,0x79,0xf2,0xc7,0x43,0xf1,0xbe,0x7e,0x43,0x1e,0xbe,0xc9,0x43,0x73,
2111 0x7c,0x80,0x43,0xec,0xcc,0xcd,0x43,0x09,0x06,0x28,0xab,0x7d,0x43,0xae,0xde,0xc6,0x43,0x08,0x1e,0xcd,
2112 0x7b,0x43,0x8a,0xa2,0xc9,0x43,0x30,0x89,0x7f,0x43,0x5c,0x94,0xcc,0x43,0x28,0xab,0x7d,0x43,0x42,0x2a,
2113 0xcf,0x43,0x09,0x01,0x05,0x06,0x24,0x14,0xe0,0x42,0xf5,0x77,0x97,0x43,0x08,0xf7,0x1d,0xe7,0x42,0x74,
2114 0x00,0x97,0x43,0x4d,0x93,0xec,0x42,0xdb,0xf5,0x95,0x43,0x29,0x4b,0xed,0x42,0xcd,0x34,0x95,0x43,0x09,
2115 0x06,0x29,0x7b,0xf5,0x42,0x6f,0x1d,0x98,0x43,0x08,0xe4,0xf1,0xfb,0x42,0x61,0x5c,0x97,0x43,0xdb,0x7d,
2116 0x01,0x43,0xb2,0xbe,0x95,0x43,0x55,0x23,0x02,0x43,0xe7,0xaa,0x94,0x43,0x09,0x06,0x98,0xdc,0x03,0x43,
2117 0xbe,0x8b,0x98,0x43,0x08,0x66,0xdf,0x05,0x43,0x47,0xe6,0x97,0x43,0xae,0x87,0x08,0x43,0x98,0x48,0x96,
2118 0x43,0x61,0x08,0x09,0x43,0xd6,0x06,0x95,0x43,0x09,0x06,0x31,0x0b,0x0b,0x43,0x8e,0x82,0x98,0x43,0x08,
2119 0xdb,0xc5,0x0d,0x43,0x80,0xc1,0x97,0x43,0xd6,0xee,0x10,0x43,0xa9,0xec,0x95,0x43,0x79,0xcb,0x11,0x43,
2120 0x55,0x8f,0x94,0x43,0x09,0x06,0xd1,0x2f,0x18,0x43,0xdb,0x01,0x98,0x43,0x08,0xad,0xe7,0x18,0x43,0x38,
2121 0x25,0x97,0x43,0x8a,0x9f,0x19,0x43,0x80,0xb5,0x95,0x43,0xd6,0x1e,0x19,0x43,0xe0,0xd8,0x94,0x43,0x09,
2122 0x06,0x9a,0x5b,0x1d,0x43,0x58,0x8a,0x97,0x43,0x08,0x01,0x5d,0x1e,0x43,0xf1,0x88,0x96,0x43,0x2f,0x83,
2123 0x1f,0x43,0x19,0xb4,0x94,0x43,0x19,0xf0,0x1e,0x43,0x6f,0x05,0x94,0x43,0x09,0x06,0x0b,0x53,0x24,0x43,
2124 0xae,0xdb,0x96,0x43,0x08,0x25,0xd5,0x25,0x43,0x50,0xac,0x95,0x43,0x53,0xfb,0x26,0x43,0x8a,0x7b,0x93,
2125 0x43,0x76,0x43,0x26,0x43,0xb7,0x95,0x92,0x43,0x09,0x06,0x76,0x5b,0x2a,0x43,0x47,0xda,0x95,0x43,0x08,
2126 0xf3,0xef,0x2b,0x43,0x10,0xe2,0x94,0x43,0x6d,0x95,0x2c,0x43,0xae,0xc3,0x92,0x43,0x68,0xa6,0x2b,0x43,
2127 0x47,0xc2,0x91,0x43,0x09,0x06,0x36,0xc1,0x31,0x43,0x2c,0x58,0x94,0x43,0x08,0x8c,0x1e,0x33,0x43,0x31,
2128 0x3b,0x93,0x43,0x79,0x7a,0x33,0x43,0xff,0x25,0x91,0x43,0xd9,0x9d,0x32,0x43,0xc1,0x5b,0x90,0x43,0x09,
2129 0x06,0x25,0x35,0x36,0x43,0x31,0x3b,0x93,0x43,0x08,0x3f,0xb7,0x37,0x43,0xc1,0x67,0x92,0x43,0xe0,0x93,
2130 0x38,0x43,0xae,0xb7,0x90,0x43,0x7e,0x81,0x38,0x43,0x0d,0xdb,0x8f,0x43,0x09,0x06,0xb5,0x85,0x3b,0x43,
2131 0xe4,0xaf,0x91,0x43,0x08,0xcf,0x07,0x3d,0x43,0x9d,0x13,0x91,0x43,0xbc,0x63,0x3d,0x43,0x47,0xb6,0x8f,
2132 0x43,0xe5,0x9a,0x3d,0x43,0x74,0xd0,0x8e,0x43,0x09,0x06,0xae,0xc6,0x42,0x43,0xa4,0xd9,0x8e,0x43,0x08,
2133 0xca,0x48,0x44,0x43,0xfa,0x2a,0x8e,0x43,0xa2,0x11,0x44,0x43,0x9d,0xfb,0x8c,0x43,0x55,0x92,0x44,0x43,
2134 0x0d,0xc3,0x8b,0x43,0x09,0x06,0x39,0x10,0xc3,0x43,0x34,0x36,0x96,0x43,0x08,0x92,0x44,0xc1,0x43,0xe4,
2135 0xc7,0x95,0x43,0x6f,0xf0,0xbf,0x43,0x4b,0xbd,0x94,0x43,0x47,0xb9,0xbf,0x43,0x0b,0xf3,0x93,0x43,0x09,
2136 0x06,0x8f,0x49,0xbe,0x43,0xb7,0xad,0x96,0x43,0x08,0x11,0xb5,0xbc,0x43,0x77,0xe3,0x95,0x43,0x9c,0xf2,
2137 0xba,0x43,0xfa,0x4e,0x94,0x43,0xae,0x96,0xba,0x43,0x31,0x3b,0x93,0x43,0x09,0x06,0xdb,0xb0,0xb9,0x43,
2138 0x10,0xee,0x96,0x43,0x08,0x42,0xa6,0xb8,0x43,0xc8,0x51,0x96,0x43,0x50,0x5b,0xb7,0x43,0x19,0xb4,0x94,
2139 0x43,0xf7,0x1a,0xb7,0x43,0x58,0x72,0x93,0x43,0x09,0x06,0xf2,0x2b,0xb6,0x43,0x10,0xee,0x96,0x43,0x08,
2140 0x9d,0xce,0xb4,0x43,0x04,0x2d,0x96,0x43,0xed,0x30,0xb3,0x43,0x2c,0x58,0x94,0x43,0xce,0xcb,0xb2,0x43,
2141 0xd6,0xfa,0x92,0x43,0x09,0x06,0x5a,0x09,0xb1,0x43,0x19,0xc0,0x96,0x43,0x08,0x6c,0xad,0xb0,0x43,0x77,
2142 0xe3,0x95,0x43,0x7e,0x51,0xb0,0x43,0xc0,0x73,0x94,0x43,0xd8,0x91,0xb0,0x43,0x1e,0x97,0x93,0x43,0x09,
2143 0x06,0x48,0x4d,0xad,0x43,0xbe,0x7f,0x96,0x43,0x08,0x95,0xcc,0xac,0x43,0x58,0x7e,0x95,0x43,0x4d,0x30,
2144 0xac,0x43,0x80,0xa9,0x93,0x43,0xd8,0x79,0xac,0x43,0xd6,0xfa,0x92,0x43,0x09,0x06,0x90,0xd1,0xa9,0x43,
2145 0x14,0xd1,0x95,0x43,0x08,0x83,0x10,0xa9,0x43,0xb7,0xa1,0x94,0x43,0x3b,0x74,0xa8,0x43,0xf1,0x70,0x92,
2146 0x43,0x29,0xd0,0xa8,0x43,0x1e,0x8b,0x91,0x43,0x09,0x06,0x5a,0xcd,0xa6,0x43,0x8a,0x87,0x95,0x43,0x08,
2147 0x1c,0x03,0xa6,0x43,0x23,0x86,0x94,0x43,0x5f,0xb0,0xa5,0x43,0xc1,0x67,0x92,0x43,0xe1,0x27,0xa6,0x43,
2148 0x8a,0x6f,0x91,0x43,0x09,0x06,0xd4,0x5a,0xa3,0x43,0x2c,0x58,0x94,0x43,0x08,0x29,0xac,0xa2,0x43,0x31,
2149 0x3b,0x93,0x43,0x32,0x7e,0xa2,0x43,0xff,0x25,0x91,0x43,0x83,0xec,0xa2,0x43,0x8e,0x52,0x90,0x43,0x09,
2150 0x06,0xf8,0x96,0xa0,0x43,0x1e,0x97,0x93,0x43,0x08,0xeb,0xd5,0x9f,0x43,0x7b,0xba,0x92,0x43,0x99,0x67,
2151 0x9f,0x43,0x9d,0x13,0x91,0x43,0x99,0x67,0x9f,0x43,0xfa,0x36,0x90,0x43,0x09,0x06,0xeb,0xc9,0x9d,0x43,
2152 0xc8,0x39,0x92,0x43,0x08,0xde,0x08,0x9d,0x43,0xb2,0xa6,0x91,0x43,0xe6,0xda,0x9c,0x43,0x2c,0x40,0x90,
2153 0x43,0x52,0xbf,0x9c,0x43,0x5a,0x5a,0x8f,0x43,0x09,0x06,0x37,0x3d,0x9b,0x43,0x85,0x80,0x90,0x43,0x08,
2154 0x2a,0x7c,0x9a,0x43,0xdb,0xd1,0x8f,0x43,0xf0,0xa0,0x9a,0x43,0x7d,0xa2,0x8e,0x43,0x65,0x57,0x9a,0x43,
2155 0xee,0x69,0x8d,0x43,0x09,0x02,0x04,0x06,0x2a,0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x08,0x0d,0x8a,0x31,
2156 0x42,0x9f,0x0e,0x94,0x43,0xf3,0x1f,0x34,0x42,0x3d,0xfc,0x93,0x43,0x63,0xff,0x36,0x42,0xa9,0xe0,0x93,
2157 0x43,0x08,0xb5,0x34,0x5d,0x42,0x0b,0xf3,0x93,0x43,0x6d,0xa4,0x5e,0x42,0x03,0x39,0x98,0x43,0xe7,0x31,
2158 0x5b,0x42,0x93,0x89,0x9d,0x43,0x08,0x02,0x9c,0x58,0x42,0xd4,0x5a,0xa3,0x43,0x38,0x70,0x53,0x42,0x14,
2159 0x49,0xaa,0x43,0xf8,0xed,0x5e,0x42,0x83,0x28,0xad,0x43,0x08,0xea,0x68,0x68,0x42,0x20,0x22,0xaf,0x43,
2160 0x12,0xb8,0x6c,0x42,0xb5,0x49,0xb1,0x43,0x2a,0x4b,0x6d,0x42,0x0d,0x96,0xb3,0x43,0x07,0x2a,0x4b,0x6d,
2161 0x42,0xc6,0x05,0xb5,0x43,0x08,0x87,0x6e,0x6c,0x42,0x68,0xee,0xb7,0x43,0x1c,0x66,0x66,0x42,0x31,0x0e,
2162 0xbb,0x43,0x57,0x11,0x5e,0x42,0x8f,0x49,0xbe,0x43,0x08,0x66,0x96,0x54,0x42,0xb9,0x5c,0xb8,0x43,0x2c,
2163 0x2b,0x3c,0x42,0x68,0xd6,0xb3,0x43,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x07,0x2a,0xf4,0x2e,0x42,
2164 0x61,0xa4,0xa3,0x43,0x08,0x55,0x1a,0x30,0x42,0xf0,0xd0,0xa2,0x43,0xf8,0xf6,0x30,0x42,0xb2,0x06,0xa2,
2165 0x43,0x98,0xd3,0x31,0x42,0xd6,0x4e,0xa1,0x43,0x08,0x1c,0x6f,0x38,0x42,0x2a,0x94,0x9e,0x43,0xc1,0x22,
2166 0x36,0x42,0xf5,0x9b,0x9d,0x43,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x57,
2167 0xa2,0x9b,0x43,0x08,0xab,0x8f,0x35,0x42,0x8a,0xab,0x9b,0x43,0xe9,0x71,0x3a,0x42,0xb2,0xe2,0x9b,0x43,
2168 0xb7,0x74,0x3c,0x42,0x34,0x5a,0x9c,0x43,0x08,0x23,0x7d,0x42,0x42,0x0b,0x2f,0x9e,0x43,0xe5,0x9a,0x3d,
2169 0x42,0x38,0x6d,0xa3,0x43,0x36,0xd9,0x35,0x42,0xf3,0xd7,0xa7,0x43,0x08,0x12,0x61,0x2e,0x42,0xb0,0x42,
2170 0xac,0x43,0x63,0xff,0x36,0x42,0xdd,0x74,0xaf,0x43,0x1e,0xa6,0x45,0x42,0x44,0x82,0xb2,0x43,0x08,0x74,
2171 0x1b,0x4b,0x42,0x79,0x7a,0xb3,0x43,0x10,0x21,0x4f,0x42,0x2a,0x18,0xb5,0x43,0xdb,0x4c,0x54,0x42,0x91,
2172 0x19,0xb6,0x43,0x08,0xee,0x3f,0x65,0x42,0x5f,0x28,0xba,0x43,0xa7,0xaf,0x66,0x42,0xb9,0x50,0xb6,0x43,
2173 0x14,0x58,0x5c,0x42,0xca,0xdc,0xb1,0x43,0x08,0x2c,0x8b,0x4c,0x42,0x4e,0x30,0xac,0x43,0x19,0xcf,0x48,
2174 0x42,0x2a,0xd0,0xa8,0x43,0xbc,0xab,0x49,0x42,0xa9,0x4c,0xa6,0x43,0x08,0x61,0x5f,0x47,0x42,0xfa,0xa2,
2175 0xa2,0x43,0xa7,0xaf,0x66,0x42,0x85,0x98,0x94,0x43,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x07,0x2a,
2176 0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x09,0x06,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x08,0xdc,0xe3,
2177 0xf1,0x41,0xe9,0x9e,0x92,0x43,0xd2,0xe7,0x0b,0x42,0xd6,0x06,0x95,0x43,0x2a,0xf4,0x2e,0x42,0x04,0x21,
2178 0x94,0x43,0x07,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x08,0x87,0x17,0x2e,0x42,0xc3,0x62,0x95,0x43,
2179 0xe7,0x3a,0x2d,0x42,0xf5,0x6b,0x95,0x43,0x44,0x5e,0x2c,0x42,0xf5,0x6b,0x95,0x43,0x08,0xd1,0x47,0x1c,
2180 0x42,0x19,0xc0,0x96,0x43,0x66,0xdf,0x05,0x42,0x38,0x19,0x95,0x43,0x12,0x6a,0x00,0x42,0xb2,0xbe,0x95,
2181 0x43,0x08,0xbb,0x6b,0xea,0x41,0xd6,0x12,0x97,0x43,0x2d,0x82,0xfa,0x41,0x61,0x74,0x9b,0x43,0x7e,0x72,
2182 0x06,0x42,0x8a,0xab,0x9b,0x43,0x08,0xc8,0x39,0x12,0x42,0x4e,0xd0,0x9b,0x43,0x53,0xe3,0x22,0x42,0xc3,
2183 0x86,0x9b,0x43,0x2a,0xf4,0x2e,0x42,0x57,0xa2,0x9b,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,
2184 0x08,0x01,0xa5,0x2a,0x42,0xa4,0x2d,0x9d,0x43,0x96,0x9c,0x24,0x42,0x06,0x40,0x9d,0x43,0x8a,0xb7,0x1d,
2185 0x42,0x9a,0x5b,0x9d,0x43,0x08,0x6b,0x16,0x13,0x42,0xcd,0x64,0x9d,0x43,0x42,0xc7,0x0e,0x42,0x9a,0x5b,
2186 0x9d,0x43,0x23,0x26,0x04,0x42,0xcd,0x64,0x9d,0x43,0x08,0xe6,0x91,0xeb,0x41,0x38,0x49,0x9d,0x43,0x73,
2187 0x7b,0xdb,0x41,0xf5,0x83,0x99,0x43,0x7f,0x60,0xe2,0x41,0x0b,0x0b,0x98,0x43,0x08,0x7f,0x60,0xe2,0x41,
2188 0xec,0x99,0x95,0x43,0xe3,0x5a,0xde,0x41,0xbe,0x7f,0x96,0x43,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,
2189 0x07,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x09,0x06,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x08,
2190 0xd4,0x7e,0x29,0x42,0xab,0x6b,0xaf,0x43,0x4e,0x0c,0x26,0x42,0x44,0x6a,0xae,0x43,0x38,0x79,0x25,0x42,
2191 0xd4,0x96,0xad,0x43,0x08,0x25,0xbd,0x21,0x42,0xe2,0x4b,0xac,0x43,0x49,0x35,0x29,0x42,0x9a,0x97,0xa7,
2192 0x43,0x2a,0xf4,0x2e,0x42,0x61,0xa4,0xa3,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x09,0x06,
2193 0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x08,0x86,0x20,0x80,0x43,0x57,0x41,0xe6,0x43,0x7d,0x4e,0x80,
2194 0x43,0x25,0x38,0xe6,0x43,0xa5,0x85,0x80,0x43,0xf3,0x2e,0xe6,0x43,0x08,0x35,0xca,0x83,0x43,0xd4,0xc9,
2195 0xe5,0x43,0x9c,0xd7,0x86,0x43,0x44,0x91,0xe4,0x43,0xd5,0xca,0x8a,0x43,0x91,0x1c,0xe6,0x43,0x08,0x53,
2196 0x5f,0x8c,0x43,0xf8,0x1d,0xe7,0x43,0x2f,0x17,0x8d,0x43,0x4e,0x7b,0xe8,0x43,0x92,0x29,0x8d,0x43,0x2f,
2197 0x22,0xea,0x43,0x07,0x92,0x29,0x8d,0x43,0x44,0xb5,0xea,0x43,0x08,0xfe,0x0d,0x8d,0x43,0x2a,0x4b,0xed,
2198 0x43,0xe3,0x8b,0x8b,0x43,0x55,0x7d,0xf0,0x43,0xec,0x51,0x89,0x43,0x72,0x0b,0xf4,0x43,0x08,0xcd,0xd4,
2199 0x84,0x43,0x9d,0x55,0xfb,0x43,0xc9,0xe5,0x83,0x43,0x74,0x1e,0xfb,0x43,0x73,0x94,0x84,0x43,0x5a,0x90,
2200 0xf7,0x43,0x08,0xe8,0x62,0x88,0x43,0xfd,0x30,0xee,0x43,0x39,0xc5,0x86,0x43,0xdd,0xbf,0xeb,0x43,0x35,
2201 0xbe,0x81,0x43,0x40,0xde,0xed,0x43,0x08,0x4f,0x34,0x81,0x43,0x36,0x0c,0xee,0x43,0x08,0x98,0x80,0x43,
2202 0xfd,0x30,0xee,0x43,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x40,0xec,
2203 0x43,0x08,0x35,0xbe,0x81,0x43,0x06,0xf7,0xeb,0x43,0x15,0x65,0x83,0x43,0x49,0xa4,0xeb,0x43,0x1e,0x43,
2204 0x85,0x43,0xbe,0x5a,0xeb,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0x18,0xea,0x43,0x42,0x97,0x86,0x43,0x5f,
2205 0x67,0xf4,0x43,0xa9,0x98,0x87,0x43,0xd4,0x1d,0xf4,0x43,0x08,0x5c,0x25,0x8a,0x43,0xcf,0x16,0xef,0x43,
2206 0x46,0xaa,0x8d,0x43,0x5a,0x3c,0xe9,0x43,0x19,0x6c,0x88,0x43,0x53,0x5e,0xe7,0x43,0x08,0xc4,0x02,0x85,
2207 0x43,0x96,0x0b,0xe7,0x43,0x85,0x2c,0x82,0x43,0x83,0x67,0xe7,0x43,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,
2208 0x43,0x07,0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x09,0x06,0xfd,0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,
2209 0x08,0xfa,0x6c,0x78,0x43,0xd1,0xc2,0xe0,0x43,0x25,0x5c,0x6c,0x43,0x25,0x44,0xe8,0x43,0x1d,0xe5,0x7f,
2210 0x43,0x87,0x4a,0xe6,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,0x43,0x08,0xa6,0x27,0x7b,0x43,0x91,
2211 0x28,0xe8,0x43,0xbc,0xa2,0x77,0x43,0xb0,0x8d,0xe8,0x43,0xc6,0x68,0x75,0x43,0x57,0x4d,0xe8,0x43,0x08,
2212 0xe0,0xd2,0x72,0x43,0xab,0x9e,0xe7,0x43,0x50,0x9a,0x71,0x43,0x2a,0x27,0xe7,0x43,0xea,0x98,0x70,0x43,
2213 0x57,0x35,0xe4,0x43,0x08,0x94,0x3b,0x6f,0x43,0x14,0x7c,0xe2,0x43,0xff,0x13,0x6d,0x43,0x06,0xbb,0xe1,
2214 0x43,0xcf,0xfe,0x6a,0x43,0x06,0xbb,0xe1,0x43,0x08,0x44,0x9d,0x66,0x43,0x77,0x8e,0xe2,0x43,0x3b,0xef,
2215 0x6c,0x43,0x91,0x10,0xe4,0x43,0xfd,0x24,0x6c,0x43,0xb0,0x81,0xe6,0x43,0x08,0x96,0x23,0x6b,0x43,0xee,
2216 0x57,0xe9,0x43,0xca,0x0f,0x6a,0x43,0x5f,0x37,0xec,0x43,0x55,0x71,0x6e,0x43,0x9f,0x01,0xed,0x43,0x08,
2217 0xdb,0xfb,0x75,0x43,0x3b,0xef,0xec,0x43,0x09,0x3a,0x7b,0x43,0xb0,0xa5,0xec,0x43,0x1d,0xe5,0x7f,0x43,
2218 0x91,0x40,0xec,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x08,0xa9,0x16,0x7c,0x43,0xb0,0xb1,
2219 0xee,0x43,0x47,0xec,0x77,0x43,0xd9,0xe8,0xee,0x43,0x1e,0x9d,0x73,0x43,0xcf,0x16,0xef,0x43,0x08,0x0e,
2220 0xc9,0x6b,0x43,0xee,0x7b,0xef,0x43,0x7e,0x90,0x6a,0x43,0xfd,0x30,0xee,0x43,0x01,0xfc,0x68,0x43,0x4e,
2221 0x93,0xec,0x43,0x08,0x31,0xf9,0x66,0x43,0x4e,0x87,0xea,0x43,0x31,0x11,0x6b,0x43,0xd4,0xd5,0xe7,0x43,
2222 0xd9,0xc4,0x68,0x43,0xd4,0xc9,0xe5,0x43,0x08,0xe5,0x79,0x67,0x43,0x77,0x9a,0xe4,0x43,0x44,0x9d,0x66,
2223 0x43,0xab,0x86,0xe3,0x43,0x7e,0x78,0x66,0x43,0x0b,0xaa,0xe2,0x43,0x07,0x7e,0x78,0x66,0x43,0x57,0x29,
2224 0xe2,0x43,0x08,0xa7,0xaf,0x66,0x43,0xbe,0x1e,0xe1,0x43,0x87,0x56,0x68,0x43,0x77,0x82,0xe0,0x43,0xfd,
2225 0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,0x09,0x06,0xc4,0x41,0xbf,0x43,0x85,0xc0,0x72,0x42,0x08,0x73,0xdf,
2226 0xc0,0x43,0xf4,0x76,0x72,0x42,0x97,0x33,0xc2,0x43,0x85,0xc0,0x72,0x42,0xb2,0xb5,0xc3,0x43,0x64,0x56,
2227 0x75,0x42,0x08,0x03,0x24,0xc4,0x43,0x5e,0x7f,0x78,0x42,0xfa,0x51,0xc4,0x43,0x01,0x85,0x7c,0x42,0x5c,
2228 0x64,0xc4,0x43,0xa0,0xb3,0x80,0x42,0x07,0x5c,0x64,0xc4,0x43,0x10,0x93,0x83,0x42,0x08,0xc8,0x48,0xc4,
2229 0x43,0x1c,0x78,0x8a,0x42,0x27,0x6c,0xc3,0x43,0xaf,0xcf,0x94,0x42,0x23,0x7d,0xc2,0x43,0x99,0x9c,0xa4,
2230 0x42,0x08,0x3d,0xe7,0xbf,0x43,0xfb,0xfd,0xb5,0x42,0xb3,0x9d,0xbf,0x43,0x88,0x17,0xae,0x42,0xc4,0x41,
2231 0xbf,0x43,0x69,0x76,0xa3,0x42,0x07,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x08,0x4f,0x8b,0xbf,0x43,
2232 0xed,0x81,0x91,0x42,0xe4,0xa6,0xbf,0x43,0x5d,0x61,0x94,0x42,0xfa,0x39,0xc0,0x43,0x3b,0x49,0x9d,0x42,
2233 0x08,0x2b,0x43,0xc0,0x43,0x28,0xed,0xa9,0x42,0x61,0x3b,0xc1,0x43,0x00,0x9e,0xa5,0x42,0xe4,0xb2,0xc1,
2234 0x43,0x5d,0x91,0x9c,0x42,0x08,0x78,0xce,0xc1,0x43,0xfd,0x36,0x90,0x42,0x22,0x89,0xc4,0x43,0x81,0x72,
2235 0x86,0x42,0xae,0xc6,0xc2,0x43,0xa0,0xb3,0x80,0x42,0x08,0x54,0x86,0xc2,0x43,0x58,0xd1,0x7e,0x42,0x30,
2236 0x32,0xc1,0x43,0xce,0x5e,0x7b,0x42,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x07,0xc4,0x41,0xbf,0x43,
2237 0x85,0xc0,0x72,0x42,0x09,0x06,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x08,0x35,0xfd,0xbb,0x43,0xa4,
2238 0xa1,0x5c,0x42,0x5e,0x34,0xbc,0x43,0x9d,0x2a,0x70,0x42,0x5e,0x40,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,
2239 0x4c,0x9c,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,0xef,0xbe,0x43,0x0e,0x0a,0x73,0x42,0xc4,0x41,0xbf,0x43,
2240 0x85,0xc0,0x72,0x42,0x07,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x08,0xcd,0x13,0xbf,0x43,0xe8,0xf1,
2241 0x7b,0x42,0xd6,0xe5,0xbe,0x43,0x71,0x3b,0x7c,0x42,0xdf,0xb7,0xbe,0x43,0x71,0x3b,0x7c,0x42,0x08,0x08,
2242 0xe3,0xbc,0x43,0xa4,0x61,0x7d,0x42,0x28,0x3c,0xbb,0x43,0x91,0x45,0x69,0x42,0x28,0x3c,0xbb,0x43,0x58,
2243 0x71,0x6e,0x42,0x08,0xce,0xfb,0xba,0x43,0xd5,0x35,0x78,0x42,0x59,0x45,0xbb,0x43,0x58,0x23,0x82,0x42,
2244 0xa1,0xe1,0xbb,0x43,0xd7,0xbe,0x88,0x42,0x08,0xc9,0x18,0xbc,0x43,0xaf,0x9f,0x8c,0x42,0x1e,0x76,0xbd,
2245 0x43,0x51,0x7c,0x8d,0x42,0xd6,0xe5,0xbe,0x43,0xf4,0x58,0x8e,0x42,0x08,0x9c,0x0a,0xbf,0x43,0x45,0xc7,
2246 0x8e,0x42,0x30,0x26,0xbf,0x43,0x96,0x35,0x8f,0x42,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x07,0xc4,
2247 0x41,0xbf,0x43,0x69,0x76,0xa3,0x42,0x08,0x08,0xef,0xbe,0x43,0xb1,0xd6,0x99,0x42,0xe8,0x89,0xbe,0x43,
2248 0xde,0xc5,0x8d,0x42,0xc0,0x46,0xbc,0x43,0xc2,0x5b,0x90,0x42,0x08,0x9c,0xf2,0xba,0x43,0x86,0x80,0x90,
2249 0x42,0xf2,0x43,0xba,0x43,0xe8,0x73,0x87,0x42,0x8f,0x31,0xba,0x43,0xb6,0xf4,0x7d,0x42,0x07,0x8f,0x31,
2250 0xba,0x43,0x21,0xc6,0x76,0x42,0x08,0xc0,0x3a,0xba,0x43,0x5f,0x48,0x6b,0x42,0xae,0x96,0xba,0x43,0xe3,
2251 0x83,0x61,0x42,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x09,0x06,0xea,0x74,0xea,0x43,0x61,0x44,0x93,
2252 0x43,0x08,0x24,0x5c,0xec,0x43,0x31,0x3b,0x93,0x43,0xfb,0x30,0xee,0x43,0x93,0x4d,0x93,0x43,0x0d,0xe1,
2253 0xef,0x43,0x80,0xa9,0x93,0x43,0x08,0x8f,0x58,0xf0,0x43,0xd1,0x17,0x94,0x43,0xb7,0x8f,0xf0,0x43,0x10,
2254 0xe2,0x94,0x43,0xea,0x98,0xf0,0x43,0xa9,0xec,0x95,0x43,0x07,0xea,0x98,0xf0,0x43,0x38,0x25,0x97,0x43,
2255 0x08,0x23,0x74,0xf0,0x43,0x9f,0x32,0x9a,0x43,0x5a,0x60,0xef,0x43,0x53,0xcb,0x9e,0x43,0x2d,0x3a,0xee,
2256 0x43,0xfd,0x91,0xa3,0x43,0x08,0xa2,0xf0,0xed,0x43,0xdd,0x38,0xa5,0x43,0x17,0xa7,0xed,0x43,0xbe,0xdf,
2257 0xa6,0x43,0x5a,0x54,0xed,0x43,0x9f,0x86,0xa8,0x43,0x08,0xfc,0x24,0xec,0x43,0xca,0xc4,0xad,0x43,0x48,
2258 0xa4,0xeb,0x43,0x40,0x6f,0xab,0x43,0x28,0x3f,0xeb,0x43,0x1c,0x0f,0xa8,0x43,0x08,0x1f,0x6d,0xeb,0x43,
2259 0x72,0x48,0xa3,0x43,0x67,0x09,0xec,0x43,0xd1,0x53,0x9e,0x43,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,0x43,
2260 0x07,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x08,0x7e,0x90,0xea,0x43,0x8a,0x9f,0x99,0x43,0x12,0xac,
2261 0xea,0x43,0xbc,0xa8,0x99,0x43,0xa7,0xc7,0xea,0x43,0xbc,0xa8,0x99,0x43,0x08,0x51,0x76,0xeb,0x43,0x9f,
2262 0x32,0x9a,0x43,0x5e,0x37,0xec,0x43,0x49,0xed,0x9c,0x43,0xb0,0xa5,0xec,0x43,0x2a,0xa0,0xa0,0x43,0x08,
2263 0x09,0xe6,0xec,0x43,0xd1,0x77,0xa4,0x43,0x28,0x4b,0xed,0x43,0x61,0xa4,0xa3,0x43,0xab,0xc2,0xed,0x43,
2264 0x8e,0xb2,0xa0,0x43,0x08,0x70,0xe7,0xed,0x43,0xde,0x08,0x9d,0x43,0x87,0x86,0xf0,0x43,0x2f,0x53,0x97,
2265 0x43,0x87,0x7a,0xee,0x43,0xec,0x99,0x95,0x43,0x08,0xca,0x27,0xee,0x43,0xff,0x3d,0x95,0x43,0x74,0xca,
2266 0xec,0x43,0x55,0x8f,0x94,0x43,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x07,0xea,0x74,0xea,0x43,0x61,
2267 0x44,0x93,0x43,0x09,0x06,0x05,0xd3,0xe5,0x43,0x19,0x9c,0x90,0x43,0x08,0x09,0xc2,0xe6,0x43,0xd1,0xff,
2268 0x8f,0x43,0x4d,0x6f,0xe6,0x43,0x74,0xe8,0x92,0x43,0x3b,0xd7,0xe8,0x43,0xc3,0x56,0x93,0x43,0x08,0x1f,
2269 0x61,0xe9,0x43,0x93,0x4d,0x93,0x43,0x05,0xeb,0xe9,0x43,0x93,0x4d,0x93,0x43,0xea,0x74,0xea,0x43,0x61,
2270 0x44,0x93,0x43,0x07,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x08,0x24,0x50,0xea,0x43,0xe7,0xaa,0x94,
2271 0x43,0x2d,0x22,0xea,0x43,0xe7,0xaa,0x94,0x43,0x36,0xf4,0xe9,0x43,0xe7,0xaa,0x94,0x43,0x08,0xa2,0xcc,
2272 0xe7,0x43,0xe0,0xd8,0x94,0x43,0xd4,0xc9,0xe5,0x43,0x19,0xa8,0x92,0x43,0xd4,0xc9,0xe5,0x43,0x27,0x69,
2273 0x93,0x43,0x08,0x17,0x77,0xe5,0x43,0xe0,0xd8,0x94,0x43,0x67,0xe5,0xe5,0x43,0x47,0xda,0x95,0x43,0x43,
2274 0x9d,0xe6,0x43,0xe2,0xd3,0x97,0x43,0x08,0x9d,0xdd,0xe6,0x43,0xad,0xe7,0x98,0x43,0x09,0xce,0xe8,0x43,
2275 0xff,0x55,0x99,0x43,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x07,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,
2276 0x43,0x08,0x71,0xcf,0xe9,0x43,0x53,0xb3,0x9a,0x43,0xa7,0xbb,0xe8,0x43,0xdb,0x0d,0x9a,0x43,0xc6,0x14,
2277 0xe7,0x43,0xdb,0x0d,0x9a,0x43,0x08,0x48,0x80,0xe5,0x43,0xdb,0x0d,0x9a,0x43,0x0a,0xb6,0xe4,0x43,0xc3,
2278 0x6e,0x97,0x43,0x76,0x9a,0xe4,0x43,0x74,0xf4,0x94,0x43,0x07,0x76,0x9a,0xe4,0x43,0x79,0xd7,0x93,0x43,
2279 0x08,0xd8,0xac,0xe4,0x43,0x66,0x27,0x92,0x43,0x29,0x1b,0xe5,0x43,0xe0,0xc0,0x90,0x43,0x05,0xd3,0xe5,
2280 0x43,0x19,0x9c,0x90,0x43,0x09,0x06,0x1b,0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x08,0x71,0x0b,0xf4,0x42,
2281 0x00,0x0e,0x8d,0x42,0x8c,0x0f,0x01,0x43,0x3e,0xc0,0x89,0x42,0xf3,0x28,0x06,0x43,0x48,0x9e,0x8b,0x42,
2282 0x08,0x15,0x89,0x09,0x43,0x00,0x0e,0x8d,0x42,0xe0,0x9c,0x0a,0x43,0xc1,0x8b,0x98,0x42,0xa6,0xc1,0x0a,
2283 0x43,0x02,0xa5,0xaa,0x42,0x07,0xa6,0xc1,0x0a,0x43,0xf9,0xf6,0xb0,0x42,0x08,0xa6,0xc1,0x0a,0x43,0x47,
2284 0x8e,0xb4,0x42,0x42,0xaf,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0xe0,0x9c,0x0a,0x43,0xba,0x74,0xbc,0x42,0x08,
2285 0xa1,0xd2,0x09,0x43,0x40,0x47,0xd0,0x42,0x0d,0xab,0x07,0x43,0x91,0xb5,0xd0,0x42,0x3b,0xb9,0x04,0x43,
2286 0xec,0x71,0xba,0x42,0x08,0xe5,0x5b,0x03,0x43,0xe3,0x33,0xa8,0x42,0x63,0xd8,0x00,0x43,0xce,0x70,0x9f,
2287 0x42,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x07,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,0x42,0x08,0xed,
2288 0x6f,0xed,0x42,0x73,0x24,0x9d,0x42,0xd8,0x0c,0xf5,0x42,0x99,0x6c,0x9c,0x42,0x27,0xab,0xfd,0x42,0xea,
2289 0xda,0x9c,0x42,0x08,0x36,0xca,0x03,0x43,0x2b,0x94,0x9e,0x42,0x68,0xc7,0x01,0x43,0x8f,0xbe,0xa2,0x42,
2290 0xfa,0x06,0x08,0x43,0x73,0xb4,0xb5,0x42,0x08,0x8e,0x2e,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0x9d,0xe3,0x08,
2291 0x43,0xd7,0x1e,0x99,0x42,0x28,0x15,0x05,0x43,0x32,0x3b,0x93,0x42,0x08,0x63,0xf0,0x04,0x43,0x70,0xed,
2292 0x8f,0x42,0x71,0x0b,0xf4,0x42,0x32,0x3b,0x93,0x42,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x07,0x1b,
2293 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x09,0x06,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,0x08,0x8e,0x55,
2294 0xc0,0x42,0xb8,0x4d,0x86,0x42,0x60,0xbf,0xd7,0x42,0x3e,0xf0,0x91,0x42,0x63,0xf6,0xe4,0x42,0x70,0xed,
2295 0x8f,0x42,0x08,0x7a,0x89,0xe5,0x42,0xac,0xc8,0x8f,0x42,0xcc,0xf7,0xe5,0x42,0xac,0xc8,0x8f,0x42,0x1b,
2296 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x07,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x08,0x63,0xf6,0xe4,
2297 0x42,0x3b,0x19,0x95,0x42,0xe6,0x61,0xe3,0x42,0x00,0x3e,0x95,0x42,0xf4,0x16,0xe2,0x42,0xc4,0x62,0x95,
2298 0x42,0x08,0x6e,0x74,0xd6,0x42,0x15,0xd1,0x95,0x42,0x97,0x63,0xca,0x42,0xaf,0xcf,0x94,0x42,0xfb,0x2d,
2299 0xbe,0x42,0x86,0x80,0x90,0x42,0x08,0x97,0x03,0xba,0x42,0xce,0x10,0x8f,0x42,0x5e,0x28,0xba,0x42,0x3e,
2300 0xf0,0x91,0x42,0xf2,0x4f,0xbc,0x42,0x45,0xf7,0x96,0x42,0x08,0x27,0x54,0xbf,0x42,0x73,0x24,0x9d,0x42,
2301 0xa5,0xe8,0xc0,0x42,0x86,0xe0,0xa0,0x42,0xe4,0xca,0xc5,0x42,0xed,0x11,0xaa,0x42,0x08,0x54,0xaa,0xc8,
2302 0x42,0x86,0x40,0xb1,0x42,0x59,0x81,0xc5,0x42,0xa1,0x11,0xc4,0x42,0x3e,0xe7,0xbf,0x42,0xfb,0x8d,0xce,
2303 0x42,0x08,0xb4,0x6d,0xb7,0x42,0x30,0xc2,0xd9,0x42,0x46,0xf5,0xc9,0x42,0xdf,0x53,0xd9,0x42,0x38,0x40,
2304 0xcb,0x42,0x62,0x8f,0xcf,0x42,0x08,0x7d,0xf9,0xcc,0x42,0xec,0xa1,0xc2,0x42,0x07,0x43,0xcd,0x42,0x6c,
2305 0xdd,0xb8,0x42,0x2b,0x8b,0xcc,0x42,0x92,0xf5,0xaf,0x42,0x08,0xf9,0x8d,0xce,0x42,0x41,0x57,0xa7,0x42,
2306 0x5b,0xb8,0xd2,0x42,0xae,0x2f,0xa5,0x42,0x18,0x2f,0xd9,0x42,0x13,0x2a,0xa1,0x42,0x08,0x41,0x7e,0xdd,
2307 0x42,0xe3,0x03,0xa0,0x42,0x2e,0xf2,0xe1,0x42,0x7c,0x02,0x9f,0x42,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,
2308 0x42,0x07,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x08,0x4d,0x63,0xe4,0x42,0x00,0x9e,0xa5,0x42,0xf4,
2309 0x16,0xe2,0x42,0x15,0x31,0xa6,0x42,0x99,0xca,0xdf,0x42,0x2b,0xc4,0xa6,0x42,0x08,0xc0,0x82,0xc6,0x42,
2310 0xc4,0xc2,0xa5,0x42,0x57,0xe1,0xd5,0x42,0x91,0xb5,0xd0,0x42,0x54,0xda,0xd0,0x42,0x97,0x93,0xd2,0x42,
2311 0x08,0x9c,0x3a,0xc7,0x42,0x17,0x58,0xdc,0x42,0x9c,0x0a,0xbf,0x42,0x6e,0xa4,0xde,0x42,0x90,0x25,0xb8,
2312 0x42,0xdf,0x53,0xd9,0x42,0x08,0x59,0x21,0xb5,0x42,0xf2,0xdf,0xd4,0x42,0x51,0x43,0xb3,0x42,0x91,0xb5,
2313 0xd0,0x42,0xc5,0x29,0xbb,0x42,0x0e,0x1a,0xca,0x42,0x08,0x65,0x36,0xc4,0x42,0xd0,0x07,0xbd,0x42,0x3e,
2314 0xe7,0xbf,0x42,0x37,0x09,0xbe,0x42,0x0c,0xea,0xc1,0x42,0xcd,0xd0,0xaf,0x42,0x08,0x2b,0x5b,0xc4,0x42,
2315 0x18,0x08,0xa3,0x42,0x67,0xa6,0xab,0x42,0x99,0x3c,0x94,0x42,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,
2316 0x09,];
2320 //----------------------------------------------------------------------------
2321 // Anti-Grain Geometry (AGG) - Version 2.5
2322 // A high quality rendering engine for C++
2323 // Copyright (C) 2002-2006 Maxim Shemanarev
2324 // Contact: mcseem@antigrain.com
2325 // mcseemagg@yahoo.com
2326 // http://antigrain.com
2328 // AGG is free software; you can redistribute it and/or
2329 // modify it under the terms of the GNU General Public License
2330 // as published by the Free Software Foundation; either version 2
2331 // of the License, or (at your option) any later version.
2333 // AGG is distributed in the hope that it will be useful,
2334 // but WITHOUT ANY WARRANTY; without even the implied warranty of
2335 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2336 // GNU General Public License for more details.
2338 // You should have received a copy of the GNU General Public License
2339 // along with AGG; if not, write to the Free Software
2340 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
2341 // MA 02110-1301, USA.
2342 //----------------------------------------------------------------------------
2343 private nothrow @trusted @nogc:
2346 enum PathCommand : ubyte {
2347 Stop = 0,
2348 MoveTo = 1,
2349 LineTo = 2,
2350 EndPoly = 0x0F,
2351 Mask = 0x0F,
2354 enum PathFlag : ubyte {
2355 None = 0x00,
2356 CCW = 0x10,
2357 CW = 0x20,
2358 Close = 0x40,
2359 Mask = 0xF0,
2363 bool isVertex (in uint c) pure { pragma(inline, true); return (c >= PathCommand.MoveTo && c < PathCommand.EndPoly); }
2364 bool isDrawing (in uint c) pure { pragma(inline, true); return (c >= PathCommand.LineTo && c < PathCommand.EndPoly); }
2365 bool isStop (in uint c) pure { pragma(inline, true); return (c == PathCommand.Stop); }
2366 bool isMoveTo (in uint c) pure { pragma(inline, true); return (c == PathCommand.MoveTo); }
2367 bool isLineTo (in uint c) pure { pragma(inline, true); return (c == PathCommand.LineTo); }
2368 bool isEndPoly (in uint c) pure { pragma(inline, true); return ((c&PathCommand.Mask) == PathCommand.EndPoly); }
2369 bool isClose (in uint c) pure { pragma(inline, true); return (c&~cast(uint)(PathFlag.CW|PathFlag.CCW)) == (PathCommand.EndPoly|PathFlag.Close); }
2370 bool isCW (in uint c) pure { pragma(inline, true); return ((c&PathFlag.CW) != 0); }
2371 bool isCCW (in uint c) pure { pragma(inline, true); return ((c&PathFlag.CCW) != 0); }
2372 bool isOriented (in uint c) pure { pragma(inline, true); return ((c&(PathFlag.CW|PathFlag.CCW)) != 0); }
2373 bool isClosed (in uint c) pure { pragma(inline, true); return ((c&PathFlag.Close) != 0); }
2374 uint getCloseFlag (in uint c) pure { pragma(inline, true); return (c&PathFlag.Close); }
2375 uint clearOrientation (in uint c) pure { pragma(inline, true); return (c&~cast(uint)(PathFlag.CW|PathFlag.CCW)); }
2376 uint getOrientation (in uint c) pure { pragma(inline, true); return (c&(PathFlag.CW|PathFlag.CCW)); }
2377 uint setOrientation (in uint c, uint o) pure { pragma(inline, true); return (clearOrientation(c)|o); }
2379 //enum PI = 3.14159265358979323846;
2380 //enum DBL_PI = cast(double)(0x1.921fb54442d18p+1);
2381 enum FLT_PI = cast(float)(0x1.921fb54442d18p+1);
2383 enum DEG2RAD_MULT_D = cast(double)(0x1.1df46a2529d39p-6);
2384 enum RAD2DEG_MULT_D = cast(double)(0x1.ca5dc1a63c1f8p+5);
2386 enum DEG2RAD_MULT_F = cast(float)(0x1.1df46ap-6f);
2387 enum RAD2DEG_MULT_F = cast(float)(0x1.ca5dc2p+5f);
2389 //float deg2rad (in float deg) pure nothrow @safe @nogc { pragma(inline, true); return deg*cast(float)(PI/180.0f); }
2390 //float rad2deg (in float rad) pure nothrow @safe @nogc { pragma(inline, true); return rad*cast(float)(180.0f/PI); }
2391 public T deg2rad(T) (in T deg) if (__traits(isFloating, T) && (T.sizeof == 4 || T.sizeof == 8)) {
2392 pragma(inline, true);
2393 static if (T.sizeof == 4) {
2394 return deg*DEG2RAD_MULT_F;
2395 } else {
2396 return deg*DEG2RAD_MULT_D;
2400 public T rad2deg(T) (in T rad) if (__traits(isFloating, T) && (T.sizeof == 4 || T.sizeof == 8)) {
2401 pragma(inline, true);
2402 static if (T.sizeof == 4) {
2403 return rad*RAD2DEG_MULT_F;
2404 } else {
2405 return rad*RAD2DEG_MULT_D;
2410 float calcPolygonArea(Storage) (in ref Storage st) {
2411 float sum = 0;
2412 float x = st[0].x;
2413 float y = st[0].y;
2414 float xs = x;
2415 float ys = y;
2416 foreach (immutable uint i; 1..st.length) {
2417 auto v = &st[i];
2418 sum += x*v.y-y*v.x;
2419 x = v.x;
2420 y = v.y;
2422 return (sum+x*ys-y*xs)*0.5f;
2426 void shortenPath(VertexSequence) (ref VertexSequence vs, float s, in uint closed=0) {
2427 alias VertexType = VertexSequence.ValueType;
2428 if (s > 0 && vs.length > 1) {
2429 //float d;
2430 int n = cast(int)(vs.length-2);
2431 while (n) {
2432 immutable float d = vs[n].dist;
2433 if (d > s) break;
2434 vs.removeLast();
2435 s -= d;
2436 --n;
2438 if (vs.length < 2) {
2439 vs.removeAll();
2440 } else {
2441 n = vs.length-1;
2442 VertexType* prev = &vs[n-1];
2443 VertexType* last = &vs[n];
2444 immutable float d = (prev.dist-s)/prev.dist;
2445 immutable float x = prev.x+(last.x-prev.x)*d;
2446 immutable float y = prev.y+(last.y-prev.y)*d;
2447 last.x = x;
2448 last.y = y;
2449 if (!(*prev)(*last)) vs.removeLast();
2450 vs.close(closed != 0);
2456 // ////////////////////////////////////////////////////////////////////////// //
2457 // Vertex (x, y) with the distance to the next one. The last vertex has
2458 // distance between the last and the first points if the polygon is closed
2459 // and 0.0 if it's a polyline.
2460 struct VertexDist {
2461 public nothrow @trusted @nogc:
2462 float x = 0.0f;
2463 float y = 0.0f;
2464 float dist = 0.0f;
2466 this (in float ax, in float ay) pure { x = ax; y = ay; /*dist = 0.0f;*/ }
2468 bool opCall() (in auto ref VertexDist val) {
2469 //enum VertexDistEPS = cast(double)1e-14; // Coinciding points maximal distance (Epsilon)
2470 enum VertexDistEPS = cast(float)0.00001f; // Coinciding points maximal distance (Epsilon)
2471 immutable bool ret = (dist = distance(x, y, val.x, val.y)) > VertexDistEPS;
2472 if (!ret) dist = 1.0f/VertexDistEPS;
2473 return ret;
2478 // ////////////////////////////////////////////////////////////////////////// //
2479 struct SimpleVector(T) {
2480 public nothrow @trusted @nogc:
2481 alias ValueType = T;
2482 T* pvec;
2483 uint pvecAllot, pvecSize;
2485 mixin(DisableCopyingMixin);
2487 ~this () {
2488 import core.stdc.stdlib : free;
2489 if (pvec !is null) free(pvec);
2490 pvec = null;
2491 pvecAllot = pvecSize = 0;
2494 @property uint length () const pure { pragma(inline, true); return pvecSize; }
2496 ref inout(T) opIndex (in uint idx) inout pure { pragma(inline, true);return pvec[idx]; }
2498 ref inout(T) curr (in uint idx) inout pure { pragma(inline, true);return pvec[idx]; }
2499 ref inout(T) prev (in uint idx) inout pure { pragma(inline, true);return pvec[(idx+pvecSize-1)%pvecSize]; }
2500 ref inout(T) next (in uint idx) inout pure { pragma(inline, true);return pvec[(idx+1)%pvecSize]; }
2502 void removeAll () {
2503 pragma(inline, true);
2504 pvecSize = 0;
2507 void removeLast () {
2508 pragma(inline, true);
2509 if (pvecSize > 0 ) --pvecSize;
2512 void add() (in auto ref T val) {
2513 import core.stdc.stdlib : realloc;
2514 import core.stdc.string : memcpy;
2515 if (pvecSize+1 > pvecAllot) {
2516 uint newsz = (pvecAllot|0xff)+1;
2517 pvec = cast(T*)realloc(pvec, T.sizeof*newsz);
2518 if (pvec is null) assert(0, "out of memory");
2519 pvecAllot = newsz;
2521 assert(pvecSize < pvecAllot);
2522 memcpy(pvec+pvecSize, &val, T.sizeof);
2523 ++pvecSize;
2528 // ////////////////////////////////////////////////////////////////////////// //
2529 struct VertexSequence(T) {
2530 public nothrow @trusted @nogc:
2531 SimpleVector!T me;
2532 alias me this;
2533 alias ValueType = T;
2535 mixin(DisableCopyingMixin);
2537 void add() (in auto ref T val) {
2538 import core.stdc.stdlib : realloc;
2539 import core.stdc.string : memcpy;
2540 if (pvecSize > 1) {
2541 if (!pvec[pvecSize-2](pvec[pvecSize-1])) removeLast();
2543 me.add(val);
2546 void modifyLast() (in auto ref T val) {
2547 if (pvecSize > 0) removeLast();
2548 add(val);
2551 // closed aka remove_flag
2552 void close (in bool closed) {
2553 while (pvecSize > 1) {
2554 if (pvec[pvecSize-2](pvec[pvecSize-1])) break;
2555 T t = pvec[pvecSize-1];
2556 removeLast();
2557 modifyLast(t);
2559 if (closed) {
2560 while (pvecSize > 1) {
2561 if (pvec[pvecSize-1](pvec[0])) break;
2562 removeLast();
2569 // ////////////////////////////////////////////////////////////////////////// //
2570 // process vertex source [src] with generator [dest]
2571 void feedWith(VSD, VSS) (ref VSD dest, ref VSS src) {
2572 dest.removeAll();
2573 src.rewind();
2574 float x, y;
2575 for (;;) {
2576 immutable cmd = src.vertex(&x, &y);
2577 dest.addVertex(x, y, cmd);
2578 if (isStop(cmd)) break;
2583 // ////////////////////////////////////////////////////////////////////////// //
2584 // this connects vertex source to vertex generator
2585 struct PathPoint {
2586 float x = 0.0f, y = 0.0f;
2587 bool moveto;
2588 @property bool lineto () const pure nothrow @safe @nogc { pragma(inline, true); return !moveto; }
2592 struct PathPointRange {
2593 private:
2594 SimpleVector!PathPoint pts;
2595 uint idx = 0;
2597 public nothrow @trusted @nogc:
2598 mixin(DisableCopyingMixin);
2600 this(VG) (ref VG vg) {
2601 // feed dest
2602 vg.rewind();
2603 float x, y;
2604 float sx, sy;
2605 bool first = true;
2606 for (;;) {
2607 auto cmd = vs.vertex(&x, &y);
2608 if (isStop(cmd)) break;
2609 if (isMoveTo(cmd)) {
2610 sx = x;
2611 sy = y;
2612 first = true;
2613 continue;
2615 if (isLineTo(cmd)) {
2617 import std.math : isNaN;
2618 assert(!sx.isNaN);
2619 assert(!sy.isNaN);
2621 if (first) { pts.add(PathPoint(sx, sy, true)); }
2622 pts.add(PathPoint(x, y, false));
2623 continue;
2625 if (isEndPoly(cmd)) {
2626 if (isClose(cmd) && !first) {
2627 //writeln("s=(", sx, ",", sy, "); c=(", x, ",", y, ")");
2628 pts.add(PathPoint(sx, sy, false));
2631 first = true;
2635 void rewind () { pragma(inline, true); idx = 0; }
2637 @property bool empty () const pure { pragma(inline, true); return (idx >= pts.length); }
2638 @property ref inout(PathPoint) front () inout pure { pragma(inline, true); return pts[idx]; }
2639 @property int length () const pure { pragma(inline, true); return pts.length-idx; }
2640 void popFront () { if (idx < pts.length) ++idx; }
2644 // ////////////////////////////////////////////////////////////////////////// //
2645 bool intersection (in float ax, in float ay, in float bx, in float by, in float cx, in float cy, in float dx, in float dy, float* x, float* y) /*pure*/ nothrow @trusted @nogc {
2646 //enum IntersectionEPS = cast(double)1.0e-30; // See calc_intersection
2647 enum IntersectionEPS = cast(float)1.0e-16; // See calc_intersection
2648 //version(aliced) pragma(inline, true);
2649 //import std.math : abs;
2650 import core.stdc.math : fabsf;
2651 immutable float num = (ay-cy)*(dx-cx)-(ax-cx)*(dy-cy);
2652 immutable float den = (bx-ax)*(dy-cy)-(by-ay)*(dx-cx);
2653 if (fabsf(den) < IntersectionEPS) return false;
2654 //if ((den < 0.0f ? (-den < IntersectionEPS) : (den < IntersectionEPS))) return false;
2655 immutable float r = num/den;
2656 if (x !is null) *x = ax+r*(bx-ax);
2657 if (y !is null) *y = ay+r*(by-ay);
2658 return true;
2661 float cross (in float x1, in float y1, in float x2, in float y2, in float x, in float y) pure nothrow @safe @nogc {
2662 pragma(inline, true);
2663 return (x-x2)*(y2-y1)-(y-y2)*(x2-x1);
2666 float distance (in float x1, in float y1, in float x2, in float y2) /*pure*/ nothrow @safe @nogc {
2667 pragma(inline, true);
2668 import core.stdc.math : sqrtf;
2669 immutable float dx = x2-x1;
2670 immutable float dy = y2-y1;
2671 return sqrtf(dx*dx+dy*dy);
2674 float distance() (in auto ref VertexDist v0, in auto ref VertexDist v1) /*pure*/ nothrow @safe @nogc {
2675 pragma(inline, true);
2676 import core.stdc.math : sqrtf;
2677 immutable float dx = v1.x-v0.x;
2678 immutable float dy = v1.y-v0.y;
2679 return sqrtf(dx*dx+dy*dy);
2684 * VertexConsumer API:
2685 * alias value_type = VertexType; // shoud support `VertexType(x, y)`
2686 * vc.remove_all(); -- start new polygon
2687 * vc.add(VT); -- add point to the current polygon
2689 struct StrokeCalc(VertexConsumer) {
2690 private:
2691 float mWidth = 0.5f;
2692 float mWidthAbs = 0.5f;
2693 float mWidthEps = 0.5f/1024.0f;
2694 int mWidthSign = 1;
2695 float mMiterLimit = 4.0f;
2696 float mInnerMiterLimit = 1.01f;
2697 float mApproxScale = 1.0f;
2698 LineCap mLineCap = LineCap.Butt;
2699 LineJoin mLineJoin = LineJoin.Miter;
2700 InnerJoin mInnerJoin = InnerJoin.Miter;
2702 public nothrow @trusted @nogc:
2703 alias CoordType = VertexConsumer.ValueType;
2705 mixin(DisableCopyingMixin);
2707 @property void lineCap (in LineCap lc) { pragma(inline, true); mLineCap = lc; }
2708 @property void lineJoin (in LineJoin lj) { pragma(inline, true); mLineJoin = lj; }
2709 @property void innerJoin (in InnerJoin ij) { pragma(inline, true); mInnerJoin = ij; }
2711 @property LineCap lineCap () const pure { pragma(inline, true); return mLineCap; }
2712 @property LineJoin lineJoin () const pure { pragma(inline, true); return mLineJoin; }
2713 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mInnerJoin; }
2715 @property float width () const pure { pragma(inline, true); return mWidth*2; }
2716 @property void width (in float w) {
2717 mWidth = w*0.5f;
2718 if (mWidth < 0.0f) {
2719 mWidthAbs = -mWidth;
2720 mWidthSign = -1;
2721 } else {
2722 mWidthAbs = mWidth;
2723 mWidthSign = 1;
2725 mWidthEps = mWidth/1024.0f;
2728 @property void miterLimit (in float ml) { pragma(inline, true); mMiterLimit = ml; }
2729 @property void miterLimitTheta (in float t) { pragma(inline, true); import core.stdc.math : sinf; mMiterLimit = 1.0f/sinf(t*0.5f); }
2730 @property void innerMiterLimit (in float ml) { pragma(inline, true); mInnerMiterLimit = ml; }
2731 @property void approximationScale (in float as) { pragma(inline, true); mApproxScale = as; }
2733 @property float miterLimit () const pure { pragma(inline, true); return mMiterLimit; }
2734 @property float innerMiterLimit () const pure { pragma(inline, true); return mInnerMiterLimit; }
2735 @property float approximationScale () const pure { pragma(inline, true); return mApproxScale; }
2737 void calcCap() (ref VertexConsumer vc, in auto ref VertexDist v0, in auto ref VertexDist v1, in float len) {
2738 import core.stdc.math : acosf, atan2f, cosf, sinf;
2740 vc.removeAll();
2742 float dx1 = (v1.y-v0.y)/len;
2743 float dy1 = (v1.x-v0.x)/len;
2744 float dx2 = 0.0f;
2745 float dy2 = 0.0f;
2747 dx1 *= mWidth;
2748 dy1 *= mWidth;
2750 if (mLineCap != LineCap.Round) {
2751 if (mLineCap == LineCap.Square) {
2752 dx2 = dy1*mWidthSign;
2753 dy2 = dx1*mWidthSign;
2755 addVertex(vc, v0.x-dx1-dx2, v0.y+dy1-dy2);
2756 addVertex(vc, v0.x+dx1-dx2, v0.y-dy1-dy2);
2757 } else {
2758 float da = acosf(mWidthAbs/(mWidthAbs+0.125f/mApproxScale))*2.0f;
2759 immutable int n = cast(int)(FLT_PI/da);
2760 da = FLT_PI/(n+1);
2761 addVertex(vc, v0.x-dx1, v0.y+dy1);
2762 if (mWidthSign > 0.0f) {
2763 float a1 = atan2f(dy1, -dx1);
2764 a1 += da;
2765 foreach (immutable int i; 0..n) {
2766 addVertex(vc, v0.x+cosf(a1)*mWidth, v0.y+sinf(a1)*mWidth);
2767 a1 += da;
2769 } else {
2770 float a1 = atan2f(-dy1, dx1);
2771 a1 -= da;
2772 foreach (immutable int i; 0..n) {
2773 addVertex(vc, v0.x+cosf(a1)*mWidth, v0.y+sinf(a1)*mWidth);
2774 a1 -= da;
2777 addVertex(vc, v0.x+dx1, v0.y-dy1);
2781 void calcJoin() (ref VertexConsumer vc, in auto ref VertexDist v0, in auto ref VertexDist v1, in auto ref VertexDist v2, in float len1, in float len2) {
2782 import core.stdc.math : sqrtf;
2784 immutable float dx1 = mWidth*(v1.y-v0.y)/len1;
2785 immutable float dy1 = mWidth*(v1.x-v0.x)/len1;
2786 immutable float dx2 = mWidth*(v2.y-v1.y)/len2;
2787 immutable float dy2 = mWidth*(v2.x-v1.x)/len2;
2789 vc.removeAll();
2791 float cp = cross(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
2792 if (cp != 0.0f && (cp > 0.0f) == (mWidth > 0.0f)) {
2793 // Inner join
2794 float limit = (len1 < len2 ? len1 : len2)/mWidthAbs;
2795 if (limit < mInnerMiterLimit) limit = mInnerMiterLimit;
2797 switch (mInnerJoin) {
2798 default: // inner_bevel
2799 addVertex(vc, v1.x+dx1, v1.y-dy1);
2800 addVertex(vc, v1.x+dx2, v1.y-dy2);
2801 break;
2802 case InnerJoin.Miter:
2803 calcMiter(vc, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0.0f);
2804 break;
2805 case InnerJoin.Jag:
2806 case InnerJoin.Round:
2807 cp = (dx1-dx2)*(dx1-dx2)+(dy1-dy2)*(dy1-dy2);
2808 if (cp < len1*len1 && cp < len2*len2) {
2809 calcMiter(vc, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0.0f);
2810 } else {
2811 if (mInnerJoin == InnerJoin.Jag) {
2812 addVertex(vc, v1.x+dx1, v1.y-dy1);
2813 addVertex(vc, v1.x, v1.y);
2814 addVertex(vc, v1.x+dx2, v1.y-dy2);
2815 } else {
2816 addVertex(vc, v1.x+dx1, v1.y-dy1);
2817 addVertex(vc, v1.x, v1.y);
2818 calcArc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
2819 addVertex(vc, v1.x, v1.y);
2820 addVertex(vc, v1.x+dx2, v1.y-dy2);
2823 break;
2825 } else {
2826 // Outer join
2828 // Calculate the distance between v1 and
2829 // the central point of the bevel line segment
2830 float dx = (dx1+dx2)*0.5f;
2831 float dy = (dy1+dy2)*0.5f;
2832 immutable float dbevel = sqrtf(dx*dx+dy*dy);
2834 if (mLineJoin == LineJoin.Round || mLineJoin == LineJoin.Bevel) {
2835 // This is an optimization that reduces the number of points
2836 // in cases of almost collinear segments. If there's no
2837 // visible difference between bevel and miter joins we'd rather
2838 // use miter join because it adds only one point instead of two.
2840 // Here we calculate the middle point between the bevel points
2841 // and then, the distance between v1 and this middle point.
2842 // At outer joins this distance always less than stroke width,
2843 // because it's actually the height of an isosceles triangle of
2844 // v1 and its two bevel points. If the difference between this
2845 // width and this value is small (no visible bevel) we can
2846 // add just one point.
2848 // The constant in the expression makes the result approximately
2849 // the same as in round joins and caps. You can safely comment
2850 // out this entire "if".
2851 if (mApproxScale*(mWidthAbs-dbevel) < mWidthEps) {
2852 if (intersection(v0.x+dx1, v0.y-dy1, v1.x+dx1, v1.y-dy1, v1.x+dx2, v1.y-dy2, v2.x+dx2, v2.y-dy2, &dx, &dy)) {
2853 addVertex(vc, dx, dy);
2854 } else {
2855 addVertex(vc, v1.x+dx1, v1.y-dy1);
2857 return;
2861 switch (mLineJoin) {
2862 case LineJoin.Miter:
2863 case LineJoin.MiterRevert:
2864 case LineJoin.MiterRound:
2865 calcMiter(vc, v0, v1, v2, dx1, dy1, dx2, dy2, mLineJoin, mMiterLimit, dbevel);
2866 break;
2867 case LineJoin.Round:
2868 calcArc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
2869 break;
2870 default: // Bevel join
2871 addVertex(vc, v1.x+dx1, v1.y-dy1);
2872 addVertex(vc, v1.x+dx2, v1.y-dy2);
2873 break;
2878 private:
2879 void addVertex (ref VertexConsumer vc, in float x, in float y) {
2880 pragma(inline, true);
2881 vc.add(CoordType(x, y));
2884 void calcArc (ref VertexConsumer vc, in float x, in float y, in float dx1, in float dy1, in float dx2, in float dy2) {
2885 import core.stdc.math : acosf, atan2f, cosf, sinf;
2887 float a1 = atan2f(dy1*mWidthSign, dx1*mWidthSign);
2888 float a2 = atan2f(dy2*mWidthSign, dx2*mWidthSign);
2890 immutable float da = acosf(mWidthAbs/(mWidthAbs+0.125f/mApproxScale))*2.0f;
2892 addVertex(vc, x+dx1, y+dy1);
2893 if (mWidthSign > 0.0f) {
2894 if (a1 > a2) a2 += 2.0f*FLT_PI;
2895 immutable int n = cast(int)((a2-a1)/da);
2896 immutable float daa = (a2-a1)/(n+1);
2897 a1 += daa;
2898 foreach (immutable int i; 0..n) {
2899 addVertex(vc, x+cosf(a1)*mWidth, y+sinf(a1)*mWidth);
2900 a1 += daa;
2902 } else {
2903 if (a1 < a2) a2 -= 2.0f*FLT_PI;
2904 immutable int n = cast(int)((a1-a2)/da);
2905 immutable float daa = (a1-a2)/(n+1);
2906 a1 -= daa;
2907 foreach (immutable int i; 0..n) {
2908 addVertex(vc, x+cosf(a1)*mWidth, y+sinf(a1)*mWidth);
2909 a1 -= daa;
2912 addVertex(vc, x+dx2, y+dy2);
2915 void calcMiter (ref VertexConsumer vc, in ref VertexDist v0, in ref VertexDist v1, in ref VertexDist v2,
2916 in float dx1, in float dy1, in float dx2, in float dy2, in LineJoin lj,
2917 in float mlimit, in float dbevel)
2919 float xi = v1.x;
2920 float yi = v1.y;
2921 float di = 1.0f;
2922 immutable float lim = mWidthAbs*mlimit;
2923 bool miterLimitExceeded = true; // assume the worst
2924 bool intersectionFailed = true; // assume the worst
2926 if (intersection(v0.x+dx1, v0.y-dy1, v1.x+dx1, v1.y-dy1, v1.x+dx2, v1.y-dy2, v2.x+dx2, v2.y-dy2, &xi, &yi)) {
2927 // Calculation of the intersection succeeded
2928 di = distance(v1.x, v1.y, xi, yi);
2929 if (di <= lim) {
2930 // Inside the miter limit
2931 addVertex(vc, xi, yi);
2932 miterLimitExceeded = false;
2934 intersectionFailed = false;
2935 } else {
2936 // Calculation of the intersection failed, most probably
2937 // the three points lie one straight line.
2938 // First check if v0 and v2 lie on the opposite sides of vector:
2939 // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
2940 // to the line determined by vertices v0 and v1.
2941 // This condition determines whether the next line segments continues
2942 // the previous one or goes back.
2943 immutable float x2 = v1.x+dx1;
2944 immutable float y2 = v1.y-dy1;
2945 if ((cross(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0) == (cross(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0)) {
2946 // This case means that the next segment continues
2947 // the previous one (straight line)
2948 addVertex(vc, v1.x+dx1, v1.y-dy1);
2949 miterLimitExceeded = false;
2953 if (miterLimitExceeded) {
2954 // Miter limit exceeded
2955 //------------------------
2956 switch (lj) {
2957 case LineJoin.MiterRevert:
2958 // For the compatibility with SVG, PDF, etc,
2959 // we use a simple bevel join instead of
2960 // "smart" bevel
2961 addVertex(vc, v1.x+dx1, v1.y-dy1);
2962 addVertex(vc, v1.x+dx2, v1.y-dy2);
2963 break;
2964 case LineJoin.MiterRound:
2965 calcArc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
2966 break;
2967 default:
2968 // If no miter-revert, calculate new dx1, dy1, dx2, dy2
2969 if (intersectionFailed) {
2970 immutable float mlimitM = mlimit*mWidthSign;
2971 addVertex(vc, v1.x+dx1+dy1*mlimitM, v1.y-dy1+dx1*mlimitM);
2972 addVertex(vc, v1.x+dx2-dy2*mlimitM, v1.y-dy2-dx2*mlimitM);
2973 } else {
2974 immutable float x1 = v1.x+dx1;
2975 immutable float y1 = v1.y-dy1;
2976 immutable float x2 = v1.x+dx2;
2977 immutable float y2 = v1.y-dy2;
2978 di = (lim-dbevel)/(di-dbevel);
2979 addVertex(vc, x1+(xi-x1)*di, y1+(yi-y1)*di);
2980 addVertex(vc, x2+(xi-x2)*di, y2+(yi-y2)*di);
2982 break;
2989 // ////////////////////////////////////////////////////////////////////////// //
2990 struct Stroker {
2991 public nothrow @trusted @nogc:
2992 alias VertexStorage = VertexSequence!VertexDist;
2993 alias CoordStorage = SimpleVector!AGGPoint;
2995 private:
2996 enum State {
2997 Initial,
2998 Ready,
2999 Cap1,
3000 Cap2,
3001 Outline1,
3002 CloseFirst,
3003 Outline2,
3004 OutVertices,
3005 End_poly1,
3006 End_poly2,
3007 Stop,
3010 private:
3011 StrokeCalc!CoordStorage mStroker;
3012 VertexStorage mSrcVertices;
3013 CoordStorage mOutVertices;
3014 float mShorten = 0;
3015 uint mClosed = 0;
3016 State mStatus = State.Initial;
3017 State mPrevStatus;
3018 uint mSrcVertex = 0;
3019 uint mOutVertex = 0;
3021 public:
3022 mixin(DisableCopyingMixin);
3024 @property void lineCap (LineCap lc) { pragma(inline, true); mStroker.lineCap(lc); }
3025 @property void lineJoin (LineJoin lj) { pragma(inline, true); mStroker.lineJoin(lj); }
3026 @property void innerJoin (InnerJoin ij) { pragma(inline, true); mStroker.innerJoin(ij); }
3028 @property LineCap lineCap () const pure { pragma(inline, true); return mStroker.lineCap(); }
3029 @property LineJoin lineJoin () const pure { pragma(inline, true); return mStroker.lineJoin(); }
3030 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mStroker.innerJoin(); }
3032 @property void width (float w) { pragma(inline, true); mStroker.width(w); }
3033 @property void miterLimit (float ml) { pragma(inline, true); mStroker.miterLimit(ml); }
3034 @property void miterLimitTheta (float t) { pragma(inline, true); mStroker.miterLimitTheta(t); }
3035 @property void innerMiterLimit (float ml) { pragma(inline, true); mStroker.innerMiterLimit(ml); }
3036 @property void approximationScale (float as) { pragma(inline, true); mStroker.approximationScale(as); }
3038 @property float width () const pure { pragma(inline, true); return mStroker.width(); }
3039 @property float miterLimit () const pure { pragma(inline, true); return mStroker.miterLimit(); }
3040 @property float innerMiterLimit () const pure { pragma(inline, true); return mStroker.innerMiterLimit(); }
3041 @property float approximationScale () const pure { pragma(inline, true); return mStroker.approximationScale(); }
3043 @property void shorten (float s) { pragma(inline, true); mShorten = s; }
3044 @property float shorten () const pure { pragma(inline, true); return mShorten; }
3046 // Generator interface
3047 void removeAll () {
3048 mSrcVertices.removeAll();
3049 mClosed = 0;
3050 mStatus = State.Initial;
3053 void addVertex (float x, float y, uint cmd) {
3054 mStatus = State.Initial;
3055 if (isMoveTo(cmd)) {
3056 mSrcVertices.modifyLast(VertexDist(x, y));
3057 } else {
3058 if (isVertex(cmd)) {
3059 mSrcVertices.add(VertexDist(x, y));
3060 } else {
3061 mClosed = getCloseFlag(cmd);
3066 // Vertex Source Interface
3067 void rewind () {
3068 if (mStatus == State.Initial) {
3069 mSrcVertices.close(mClosed != 0);
3070 shortenPath(mSrcVertices, mShorten, mClosed);
3071 if (mSrcVertices.length < 3) mClosed = 0;
3073 mStatus = State.Ready;
3074 mSrcVertex = 0;
3075 mOutVertex = 0;
3078 uint vertex (float* x, float* y) {
3079 uint cmd = PathCommand.LineTo;
3080 while (!isStop(cmd)) {
3081 final switch (mStatus) {
3082 case State.Initial:
3083 rewind();
3084 goto case;
3086 case State.Ready:
3087 if (mSrcVertices.length < 2+cast(uint)(mClosed != 0)) {
3088 cmd = PathCommand.Stop;
3089 break;
3091 mStatus = (mClosed ? State.Outline1 : State.Cap1);
3092 cmd = PathCommand.MoveTo;
3093 mSrcVertex = 0;
3094 mOutVertex = 0;
3095 break;
3097 case State.Cap1:
3098 mStroker.calcCap(mOutVertices, mSrcVertices[0], mSrcVertices[1], mSrcVertices[0].dist);
3099 mSrcVertex = 1;
3100 mPrevStatus = State.Outline1;
3101 mStatus = State.OutVertices;
3102 mOutVertex = 0;
3103 break;
3105 case State.Cap2:
3106 mStroker.calcCap(mOutVertices, mSrcVertices[mSrcVertices.length-1], mSrcVertices[mSrcVertices.length-2], mSrcVertices[mSrcVertices.length-2].dist);
3107 mPrevStatus = State.Outline2;
3108 mStatus = State.OutVertices;
3109 mOutVertex = 0;
3110 break;
3112 case State.Outline1:
3113 if (mClosed) {
3114 if (mSrcVertex >= mSrcVertices.length) {
3115 mPrevStatus = State.CloseFirst;
3116 mStatus = State.End_poly1;
3117 break;
3119 } else {
3120 if (mSrcVertex >= mSrcVertices.length-1) {
3121 mStatus = State.Cap2;
3122 break;
3125 mStroker.calcJoin(mOutVertices, mSrcVertices.prev(mSrcVertex), mSrcVertices.curr(mSrcVertex), mSrcVertices.next(mSrcVertex), mSrcVertices.prev(mSrcVertex).dist, mSrcVertices.curr(mSrcVertex).dist);
3126 ++mSrcVertex;
3127 mPrevStatus = mStatus;
3128 mStatus = State.OutVertices;
3129 mOutVertex = 0;
3130 break;
3132 case State.CloseFirst:
3133 mStatus = State.Outline2;
3134 cmd = PathCommand.MoveTo;
3135 goto case;
3137 case State.Outline2:
3138 if (mSrcVertex <= uint(mClosed == 0)) {
3139 mStatus = State.End_poly2;
3140 mPrevStatus = State.Stop;
3141 break;
3144 --mSrcVertex;
3145 mStroker.calcJoin(mOutVertices, mSrcVertices.next(mSrcVertex), mSrcVertices.curr(mSrcVertex), mSrcVertices.prev(mSrcVertex), mSrcVertices.curr(mSrcVertex).dist, mSrcVertices.prev(mSrcVertex).dist);
3147 mPrevStatus = mStatus;
3148 mStatus = State.OutVertices;
3149 mOutVertex = 0;
3150 break;
3152 case State.OutVertices:
3153 if (mOutVertex >= mOutVertices.length) {
3154 mStatus = mPrevStatus;
3155 } else {
3156 const(AGGPoint)* c = &mOutVertices[mOutVertex++];
3157 *x = c.x;
3158 *y = c.y;
3159 return cmd;
3161 break;
3163 case State.End_poly1:
3164 mStatus = mPrevStatus;
3165 return PathCommand.EndPoly|PathFlag.Close|PathFlag.CCW;
3167 case State.End_poly2:
3168 mStatus = mPrevStatus;
3169 return PathCommand.EndPoly|PathFlag.Close|PathFlag.CW;
3171 case State.Stop:
3172 cmd = PathCommand.Stop;
3173 break;
3176 return cmd;
3181 // ////////////////////////////////////////////////////////////////////////// //
3182 struct Contourer {
3183 public nothrow @trusted @nogc:
3184 alias VertexStorage = VertexSequence!VertexDist;
3185 alias CoordStorage = SimpleVector!AGGPoint;
3187 enum State {
3188 Initial,
3189 Ready,
3190 Outline,
3191 OutVertices,
3192 EndPoly,
3193 Stop,
3196 private:
3197 StrokeCalc!CoordStorage mStroker;
3198 float mWidth = 1;
3199 VertexStorage mSrcVertices;
3200 CoordStorage mOutVertices;
3201 State mStatus;
3202 uint mSrcVertex = 0;
3203 uint mOutVertex;
3204 uint mClosed = 0;
3205 uint mOrientation = 0;
3206 bool mAutoDetect = false;
3208 public:
3209 mixin(DisableCopyingMixin);
3211 @property void lineCap (LineCap lc) { pragma(inline, true); mStroker.lineCap(lc); }
3212 @property void lineJoin (LineJoin lj) { pragma(inline, true); mStroker.lineJoin(lj); }
3213 @property void innerJoin (InnerJoin ij) { pragma(inline, true); mStroker.innerJoin(ij); }
3215 @property LineCap lineCap () const pure { pragma(inline, true); return mStroker.lineCap(); }
3216 @property LineJoin lineJoin () const pure { pragma(inline, true); return mStroker.lineJoin(); }
3217 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mStroker.innerJoin(); }
3219 @property void width (float w) { pragma(inline, true); mStroker.width(mWidth = w); }
3220 @property void miterLimit (float ml) { pragma(inline, true); mStroker.miterLimit(ml); }
3221 @property void miterLimitTheta (float t) { pragma(inline, true); mStroker.miterLimitTheta(t); }
3222 @property void innerMiterLimit (float ml) { pragma(inline, true); mStroker.innerMiterLimit(ml); }
3223 @property void approximationScale (float as) { pragma(inline, true); mStroker.approximationScale(as); }
3225 @property float width () const pure { pragma(inline, true); return mWidth; }
3226 @property float miterLimit () const pure { pragma(inline, true); return mStroker.miterLimit(); }
3227 @property float innerMiterLimit () const pure { pragma(inline, true); return mStroker.innerMiterLimit(); }
3228 @property float approximationScale () const pure { pragma(inline, true); return mStroker.approximationScale(); }
3230 @property void autoDetectOrientation (bool v) { pragma(inline, true); mAutoDetect = v; }
3231 @property bool autoDetectOrientation () const pure { pragma(inline, true); return mAutoDetect; }
3233 // Generator interface
3234 void removeAll () {
3235 mSrcVertices.removeAll();
3236 mClosed = 0;
3237 mOrientation = 0;
3238 mStatus = State.Initial;
3241 void addVertex (float x, float y, uint cmd) {
3242 mStatus = State.Initial;
3243 if (isMoveTo(cmd)) {
3244 mSrcVertices.modifyLast(VertexDist(x, y));
3245 } else {
3246 if (isVertex(cmd)) {
3247 mSrcVertices.add(VertexDist(x, y));
3248 } else {
3249 if (isEndPoly(cmd)) {
3250 mClosed = getCloseFlag(cmd);
3251 if (mOrientation == PathFlag.None) {
3252 mOrientation = getOrientation(cmd);
3259 // Vertex Source Interface
3260 void rewind () {
3261 if (mStatus == State.Initial) {
3262 mSrcVertices.close(true);
3263 if (mAutoDetect) {
3264 if (!isOriented(mOrientation)) {
3265 mOrientation = (calcPolygonArea(mSrcVertices) > 0 ? PathFlag.CCW : PathFlag.CW);
3268 if (isOriented(mOrientation)) {
3269 mStroker.width(isCCW(mOrientation) ? mWidth : -mWidth);
3272 mStatus = State.Ready;
3273 mSrcVertex = 0;
3276 uint vertex (float* x, float* y) {
3277 uint cmd = PathCommand.LineTo;
3278 while (!isStop(cmd)) {
3279 final switch (mStatus) {
3280 case State.Initial:
3281 rewind();
3282 goto case;
3284 case State.Ready:
3285 if (mSrcVertices.length < 2+cast(uint)(mClosed != 0)) {
3286 cmd = PathCommand.Stop;
3287 break;
3289 mStatus = State.Outline;
3290 cmd = PathCommand.MoveTo;
3291 mSrcVertex = 0;
3292 mOutVertex = 0;
3293 goto case;
3295 case State.Outline:
3296 if (mSrcVertex >= mSrcVertices.length) {
3297 mStatus = State.EndPoly;
3298 break;
3300 mStroker.calcJoin(mOutVertices, mSrcVertices.prev(mSrcVertex), mSrcVertices.curr(mSrcVertex), mSrcVertices.next(mSrcVertex), mSrcVertices.prev(mSrcVertex).dist, mSrcVertices.curr(mSrcVertex).dist);
3301 ++mSrcVertex;
3302 mStatus = State.OutVertices;
3303 mOutVertex = 0;
3304 goto case;
3306 case State.OutVertices:
3307 if (mOutVertex >= mOutVertices.length) {
3308 mStatus = State.Outline;
3309 } else {
3310 const(AGGPoint)* c = &mOutVertices[mOutVertex++];
3311 *x = c.x;
3312 *y = c.y;
3313 return cmd;
3315 break;
3317 case State.EndPoly:
3318 if (!mClosed) return PathCommand.Stop;
3319 mStatus = State.Stop;
3320 return PathCommand.EndPoly|PathFlag.Close|PathFlag.CCW;
3322 case State.Stop:
3323 return PathCommand.Stop;
3326 return cmd;
3331 // ////////////////////////////////////////////////////////////////////////// //
3332 struct Dasher {
3333 private nothrow @trusted @nogc:
3334 alias VertexStorage = VertexSequence!VertexDist;
3335 enum MaxDashes = 32;
3337 enum State {
3338 Initial,
3339 Ready,
3340 Polyline,
3341 Stop,
3344 private:
3345 float[MaxDashes] mDashes = 0;
3346 float mTotalDashLen = 0;
3347 uint mNumDashes = 0;
3348 float mDashStart = 0;
3349 float mShorten = 0;
3350 float mCurrDashStart = 0;
3351 uint mCurrDash = 0;
3352 float mCurrRest = 0;
3353 const(VertexDist)* m_v1 = null;
3354 const(VertexDist)* m_v2 = null;
3356 VertexStorage mSrcVertices;
3357 uint mClosed = 0;
3358 State mStatus = State.Initial;
3359 uint mSrcVertex = 0;
3361 public:
3362 mixin(DisableCopyingMixin);
3364 @property void shorten (float s) { mShorten = s; }
3365 @property float shorten () const pure { return mShorten; }
3367 void removeAllDashes () {
3368 mTotalDashLen = 0;
3369 mNumDashes = 0;
3370 mCurrDashStart = 0;
3371 mCurrDash = 0;
3374 void addDash (float dashLen, float gapLen) {
3375 if (mNumDashes < MaxDashes) {
3376 mTotalDashLen += dashLen+gapLen;
3377 mDashes[mNumDashes++] = dashLen;
3378 mDashes[mNumDashes++] = gapLen;
3382 void dashStart (float ds) {
3383 //import std.math : abs;
3384 import core.stdc.math : fabsf;
3385 mDashStart = ds;
3386 calcDashStart(fabsf(ds));
3389 // Vertex Generator Interface
3390 void removeAll () {
3391 mStatus = State.Initial;
3392 mSrcVertices.removeAll();
3393 mClosed = 0;
3396 void addVertex (float x, float y, uint cmd) {
3397 mStatus = State.Initial;
3398 if (isMoveTo(cmd)) {
3399 mSrcVertices.modifyLast(VertexDist(x, y));
3400 } else {
3401 if (isVertex(cmd)) {
3402 mSrcVertices.add(VertexDist(x, y));
3403 } else {
3404 mClosed = getCloseFlag(cmd);
3409 // Vertex Source Interface
3410 void rewind () {
3411 if (mStatus == State.Initial) {
3412 mSrcVertices.close(mClosed != 0);
3413 shortenPath(mSrcVertices, mShorten, mClosed);
3415 mStatus = State.Ready;
3416 mSrcVertex = 0;
3419 uint vertex (float* x, float* y) {
3420 uint cmd = PathCommand.MoveTo;
3422 while (!isStop(cmd)) {
3423 final switch (mStatus) {
3424 case State.Initial:
3425 rewind();
3426 goto case;
3428 case State.Ready:
3429 if (mNumDashes < 2 || mSrcVertices.length < 2) {
3430 cmd = PathCommand.Stop;
3431 break;
3433 mStatus = State.Polyline;
3434 mSrcVertex = 1;
3435 m_v1 = &mSrcVertices[0];
3436 m_v2 = &mSrcVertices[1];
3437 mCurrRest = m_v1.dist;
3438 *x = m_v1.x;
3439 *y = m_v1.y;
3440 if (mDashStart >= 0) calcDashStart(mDashStart);
3441 return PathCommand.MoveTo;
3443 case State.Polyline:
3444 immutable float dashRest = mDashes[mCurrDash]-mCurrDashStart;
3445 cmd = (mCurrDash&1 ? PathCommand.MoveTo : PathCommand.LineTo);
3446 if (mCurrRest > dashRest) {
3447 mCurrRest -= dashRest;
3448 ++mCurrDash;
3449 if (mCurrDash >= mNumDashes) mCurrDash = 0;
3450 mCurrDashStart = 0;
3451 *x = m_v2.x-(m_v2.x-m_v1.x)*mCurrRest/m_v1.dist;
3452 *y = m_v2.y-(m_v2.y-m_v1.y)*mCurrRest/m_v1.dist;
3453 } else {
3454 mCurrDashStart += mCurrRest;
3455 *x = m_v2.x;
3456 *y = m_v2.y;
3457 ++mSrcVertex;
3458 m_v1 = m_v2;
3459 mCurrRest = m_v1.dist;
3460 if (mClosed) {
3461 if (mSrcVertex > mSrcVertices.length) {
3462 mStatus = State.Stop;
3463 } else {
3464 m_v2 = &mSrcVertices[mSrcVertex >= mSrcVertices.length ? 0 :mSrcVertex];
3466 } else {
3467 if (mSrcVertex >= mSrcVertices.length) {
3468 mStatus = State.Stop;
3469 } else {
3470 m_v2 = &mSrcVertices[mSrcVertex];
3474 return cmd;
3476 case State.Stop:
3477 cmd = PathCommand.Stop;
3478 break;
3481 return PathCommand.Stop;
3484 private:
3485 void calcDashStart (float ds) {
3486 mCurrDash = 0;
3487 mCurrDashStart = 0;
3488 while (ds > 0) {
3489 if (ds > mDashes[mCurrDash]) {
3490 ds -= mDashes[mCurrDash];
3491 ++mCurrDash;
3492 mCurrDashStart = 0;
3493 if (mCurrDash >= mNumDashes) mCurrDash = 0;
3494 } else {
3495 mCurrDashStart = ds;
3496 ds = 0;
3503 // ////////////////////////////////////////////////////////////////////////// //
3504 // Matrices and Transformations
3506 // matrix class
3507 public align(1) struct AGGMatrix {
3508 align(1):
3509 private:
3510 static immutable float[6] IdentityMat = [
3511 1.0f, 0.0f,
3512 0.0f, 1.0f,
3513 0.0f, 0.0f,
3516 public:
3517 /// Matrix values. Initial value is identity matrix.
3518 float[6] mat = [
3519 1.0f, 0.0f,
3520 0.0f, 1.0f,
3521 0.0f, 0.0f,
3524 public nothrow @trusted @nogc:
3525 /// Create Matrix with the given values.
3526 this (const(float)[] amat...) {
3527 pragma(inline, true);
3528 if (amat.length >= 6) {
3529 mat.ptr[0..6] = amat.ptr[0..6];
3530 } else {
3531 mat.ptr[0..6] = 0.0f;
3532 mat.ptr[0..amat.length] = amat[];
3536 /// Can be used to check validity of [inverted] result
3537 @property bool valid () const { import core.stdc.math : isfinite; return (isfinite(mat.ptr[0]) != 0); }
3539 /// Returns `true` if this matrix is identity matrix.
3540 @property bool isIdentity () const { version(aliced) pragma(inline, true); return (mat[] == IdentityMat[]); }
3542 /// Returns new inverse matrix.
3543 /// If inverted matrix cannot be calculated, `res.valid` fill be `false`.
3544 AGGMatrix inverted () const {
3545 AGGMatrix res = this;
3546 res.invert;
3547 return res;
3550 /// Inverts this matrix.
3551 /// If inverted matrix cannot be calculated, `this.valid` fill be `false`.
3552 ref AGGMatrix invert () {
3553 float[6] inv = void;
3554 immutable double det = cast(double)mat.ptr[0]*mat.ptr[3]-cast(double)mat.ptr[2]*mat.ptr[1];
3555 if (det > -1e-6 && det < 1e-6) {
3556 inv[] = float.nan;
3557 } else {
3558 immutable double invdet = 1.0/det;
3559 inv.ptr[0] = cast(float)(mat.ptr[3]*invdet);
3560 inv.ptr[2] = cast(float)(-mat.ptr[2]*invdet);
3561 inv.ptr[4] = cast(float)((cast(double)mat.ptr[2]*mat.ptr[5]-cast(double)mat.ptr[3]*mat.ptr[4])*invdet);
3562 inv.ptr[1] = cast(float)(-mat.ptr[1]*invdet);
3563 inv.ptr[3] = cast(float)(mat.ptr[0]*invdet);
3564 inv.ptr[5] = cast(float)((cast(double)mat.ptr[1]*mat.ptr[4]-cast(double)mat.ptr[0]*mat.ptr[5])*invdet);
3566 mat.ptr[0..6] = inv.ptr[0..6];
3567 return this;
3570 /// Sets this matrix to identity matrix.
3571 ref AGGMatrix identity () { version(aliced) pragma(inline, true); mat[] = IdentityMat[]; return this; }
3573 /// Translate this matrix.
3574 ref AGGMatrix translate (in float tx, in float ty) {
3575 version(aliced) pragma(inline, true);
3576 return this.mul(Translated(tx, ty));
3579 /// Scale this matrix.
3580 ref AGGMatrix scale (in float sx, in float sy) {
3581 version(aliced) pragma(inline, true);
3582 return this.mul(Scaled(sx, sy));
3585 /// Rotate this matrix.
3586 ref AGGMatrix rotate (in float a) {
3587 version(aliced) pragma(inline, true);
3588 return this.mul(Rotated(a));
3591 /// Skew this matrix by X axis.
3592 ref AGGMatrix skewX (in float a) {
3593 version(aliced) pragma(inline, true);
3594 return this.mul(SkewedX(a));
3597 /// Skew this matrix by Y axis.
3598 ref AGGMatrix skewY (in float a) {
3599 version(aliced) pragma(inline, true);
3600 return this.mul(SkewedY(a));
3603 /// Skew this matrix by both axes.
3604 ref AGGMatrix skewY (in float ax, in float ay) {
3605 version(aliced) pragma(inline, true);
3606 return this.mul(SkewedXY(ax, ay));
3609 /// Transform point with this matrix. `null` destinations are allowed.
3610 /// [sx] and [sy] is the source point. [dx] and [dy] may point to the same variables.
3611 void point (float* dx, float* dy, float sx, float sy) {
3612 version(aliced) pragma(inline, true);
3613 if (dx !is null) *dx = sx*mat.ptr[0]+sy*mat.ptr[2]+mat.ptr[4];
3614 if (dy !is null) *dy = sx*mat.ptr[1]+sy*mat.ptr[3]+mat.ptr[5];
3617 /// Transform point with this matrix.
3618 void point (ref float x, ref float y) {
3619 version(aliced) pragma(inline, true);
3620 immutable float nx = x*mat.ptr[0]+y*mat.ptr[2]+mat.ptr[4];
3621 immutable float ny = x*mat.ptr[1]+y*mat.ptr[3]+mat.ptr[5];
3622 x = nx;
3623 y = ny;
3626 /// Sets this matrix to the result of multiplication of `this` and [s] (this * S).
3627 ref AGGMatrix mul() (in auto ref AGGMatrix s) {
3628 immutable float t0 = mat.ptr[0]*s.mat.ptr[0]+mat.ptr[1]*s.mat.ptr[2];
3629 immutable float t2 = mat.ptr[2]*s.mat.ptr[0]+mat.ptr[3]*s.mat.ptr[2];
3630 immutable float t4 = mat.ptr[4]*s.mat.ptr[0]+mat.ptr[5]*s.mat.ptr[2]+s.mat.ptr[4];
3631 mat.ptr[1] = mat.ptr[0]*s.mat.ptr[1]+mat.ptr[1]*s.mat.ptr[3];
3632 mat.ptr[3] = mat.ptr[2]*s.mat.ptr[1]+mat.ptr[3]*s.mat.ptr[3];
3633 mat.ptr[5] = mat.ptr[4]*s.mat.ptr[1]+mat.ptr[5]*s.mat.ptr[3]+s.mat.ptr[5];
3634 mat.ptr[0] = t0;
3635 mat.ptr[2] = t2;
3636 mat.ptr[4] = t4;
3637 return this;
3640 /// Sets this matrix to the result of multiplication of [s] and `this` (S * this).
3641 /// Sets the transform to the result of multiplication of two transforms, of A = B*A.
3642 /// Group: matrices
3643 ref AGGMatrix premul() (in auto ref AGGMatrix s) {
3644 AGGMatrix s2 = s;
3645 s2.mul(this);
3646 mat[] = s2.mat[];
3647 return this;
3650 /// Multiply this matrix by [s], return result as new matrix.
3651 /// Performs operations in this left-to-right order.
3652 AGGMatrix opBinary(string op="*") (in auto ref AGGMatrix s) const {
3653 version(aliced) pragma(inline, true);
3654 AGGMatrix res = this;
3655 res.mul(s);
3656 return res;
3659 /// Multiply this matrix by [s].
3660 /// Performs operations in this left-to-right order.
3661 ref AGGMatrix opOpAssign(string op="*") (in auto ref AGGMatrix s) {
3662 version(aliced) pragma(inline, true);
3663 return this.mul(s);
3666 float scaleX () const { pragma(inline, true); import core.stdc.math : sqrtf; return sqrtf(mat.ptr[0]*mat.ptr[0]+mat.ptr[2]*mat.ptr[2]); } /// Returns x scaling of this matrix.
3667 float scaleY () const { pragma(inline, true); import core.stdc.math : sqrtf; return sqrtf(mat.ptr[1]*mat.ptr[1]+mat.ptr[3]*mat.ptr[3]); } /// Returns y scaling of this matrix.
3668 float rotation () const { pragma(inline, true); import core.stdc.math : atan2f; return atan2f(mat.ptr[1], mat.ptr[0]); } /// Returns rotation of this matrix.
3669 float tx () const { pragma(inline, true); return mat.ptr[4]; } /// Returns x translation of this matrix.
3670 float ty () const { pragma(inline, true); return mat.ptr[5]; } /// Returns y translation of this matrix.
3672 ref AGGMatrix scaleX (in float v) { pragma(inline, true); return scaleRotateTransform(v, scaleY, rotation, tx, ty); } /// Sets x scaling of this matrix.
3673 ref AGGMatrix scaleY (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, v, rotation, tx, ty); } /// Sets y scaling of this matrix.
3674 ref AGGMatrix rotation (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, scaleY, v, tx, ty); } /// Sets rotation of this matrix.
3675 ref AGGMatrix tx (in float v) { pragma(inline, true); mat.ptr[4] = v; return this; } /// Sets x translation of this matrix.
3676 ref AGGMatrix ty (in float v) { pragma(inline, true); mat.ptr[5] = v; return this; } /// Sets y translation of this matrix.
3678 /// Utility function to be used in `setXXX()`.
3679 /// This is the same as doing: `mat.identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
3680 ref AGGMatrix scaleRotateTransform (in float xscale, in float yscale, in float a, in float tx, in float ty) {
3681 import core.stdc.math : sinf, cosf;
3682 immutable float cs = cosf(a), sn = sinf(a);
3683 mat.ptr[0] = xscale*cs; mat.ptr[1] = yscale*sn;
3684 mat.ptr[2] = xscale*-sn; mat.ptr[3] = yscale*cs;
3685 mat.ptr[4] = tx; mat.ptr[5] = ty;
3686 return this;
3689 /// This is the same as doing: `mat.identity.rotate(a).translate(tx, ty)`, only faster
3690 ref AGGMatrix rotateTransform (in float a, in float tx, in float ty) {
3691 import core.stdc.math : sinf, cosf;
3692 immutable float cs = cosf(a), sn = sinf(a);
3693 mat.ptr[0] = cs; mat.ptr[1] = sn;
3694 mat.ptr[2] = -sn; mat.ptr[3] = cs;
3695 mat.ptr[4] = tx; mat.ptr[5] = ty;
3696 return this;
3699 /// Returns new identity matrix.
3700 static AGGMatrix Identity () { pragma(inline, true); return AGGMatrix.init; }
3702 /// Returns new translation matrix.
3703 static AGGMatrix Translated (in float tx, in float ty) {
3704 version(aliced) pragma(inline, true);
3705 AGGMatrix res = void;
3706 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
3707 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
3708 res.mat.ptr[4] = tx; res.mat.ptr[5] = ty;
3709 return res;
3712 /// Returns new scaling matrix.
3713 static AGGMatrix Scaled (in float sx, in float sy) {
3714 version(aliced) pragma(inline, true);
3715 AGGMatrix res = void;
3716 res.mat.ptr[0] = sx; res.mat.ptr[1] = 0.0f;
3717 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = sy;
3718 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3719 return res;
3722 /// Returns new rotation matrix. Angle is specified in radians.
3723 static AGGMatrix Rotated (in float a) {
3724 version(aliced) pragma(inline, true);
3725 import core.stdc.math : sinf, cosf;
3726 immutable float cs = cosf(a), sn = sinf(a);
3727 AGGMatrix res = void;
3728 res.mat.ptr[0] = cs; res.mat.ptr[1] = sn;
3729 res.mat.ptr[2] = -sn; res.mat.ptr[3] = cs;
3730 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3731 return res;
3734 /// Returns new x-skewing matrix. Angle is specified in radians.
3735 static AGGMatrix SkewedX (in float a) {
3736 version(aliced) pragma(inline, true);
3737 import core.stdc.math : tanf;
3738 AGGMatrix res = void;
3739 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
3740 res.mat.ptr[2] = tanf(a); res.mat.ptr[3] = 1.0f;
3741 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3742 return res;
3745 /// Returns new y-skewing matrix. Angle is specified in radians.
3746 static AGGMatrix SkewedY (in float a) {
3747 version(aliced) pragma(inline, true);
3748 import core.stdc.math : tanf;
3749 AGGMatrix res = void;
3750 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = tanf(a);
3751 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
3752 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3753 return res;
3756 /// Returns new xy-skewing matrix. Angles are specified in radians.
3757 static AGGMatrix SkewedXY (in float ax, in float ay) {
3758 version(aliced) pragma(inline, true);
3759 import core.stdc.math : tanf;
3760 AGGMatrix res = void;
3761 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = tanf(ay);
3762 res.mat.ptr[2] = tanf(ax); res.mat.ptr[3] = 1.0f;
3763 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3764 return res;
3767 /// Utility function to be used in `setXXX()`.
3768 /// This is the same as doing: `AGGMatrix.Identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
3769 static AGGMatrix ScaledRotatedTransformed (in float xscale, in float yscale, in float a, in float tx, in float ty) {
3770 AGGMatrix res = void;
3771 res.scaleRotateTransform(xscale, yscale, a, tx, ty);
3772 return res;
3775 /// This is the same as doing: `AGGMatrix.Identity.rotate(a).translate(tx, ty)`, only faster
3776 static AGGMatrix RotatedTransformed (in float a, in float tx, in float ty) {
3777 AGGMatrix res = void;
3778 res.rotateTransform(a, tx, ty);
3779 return res;
3782 public:
3783 // premultiplies current coordinate system by specified matrix
3784 ref AGGMatrix transform() (in auto ref AGGMatrix mt) { pragma(inline, true); this *= mt; return this; }
3786 // translates current coordinate system
3787 ref AGGMatrix translatePre (in float x, in float y) { pragma(inline, true); return premul(AGGMatrix.Translated(x, y)); }
3789 // rotates current coordinate system; angle is specified in radians
3790 ref AGGMatrix rotatePre (in float angle) { pragma(inline, true); return premul(AGGMatrix.Rotated(angle)); }
3792 // skews the current coordinate system along X axis; angle is specified in radians
3793 ref AGGMatrix skewXPre (in float angle) { pragma(inline, true); return premul(AGGMatrix.SkewedX(angle)); }
3795 // skews the current coordinate system along Y axis; angle is specified in radians
3796 ref AGGMatrix skewYPre (in float angle) { pragma(inline, true); return premul(AGGMatrix.SkewedY(angle)); }
3798 // scales the current coordinate system
3799 ref AGGMatrix scalePre (in float x, in float y) { pragma(inline, true); return premul(AGGMatrix.Scaled(x, y)); }