1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3 
4 #include "wx/wxprec.h"      // for compilers that support precompilation
5 #ifndef WX_PRECOMP
6     #include "wx/wx.h"      // for all others include the necessary headers
7 #endif
8 
9 #include "wx/rawbmp.h"      // for wxAlphaPixelData
10 
11 #include "wxgolly.h"        // for mainptr, viewptr
12 #include "wxmain.h"         // for mainptr->...
13 #include "wxview.h"         // for viewptr->...
14 #include "wxlayer.h"        // for currlayer->...
15 #include "wxprefs.h"        // for showoverlay
16 #include "wxutils.h"        // for Warning
17 
18 #include "wxoverlay.h"
19 
20 #include <vector>           // for std::vector
21 #include <cstdio>           // for FILE*, etc
22 #include <math.h>           // for sin, cos, log, sqrt and atn2
23 #include <stdlib.h>         // for malloc, free
24 #include <string.h>         // for strchr, strtok
25 #include <ctype.h>          // for isspace, isdigit
26 
27 // Endian definitions
28 // Note: for big endian platforms you must compile with BIGENDIAN defined
29 #ifdef BIGENDIAN
30 
31 // big endian 32bit pixel component order is RGBA
32 
33 // masks to isolate components in pixel
34 #define RMASK   0xff000000
35 #define GMASK   0x00ff0000
36 #define BMASK   0x0000ff00
37 #define AMASK   0x000000ff
38 #define RBMASK  0xff00ff00
39 #define RBGMASK 0xffffff00
40 
41 // shift RB components right and left to avoid overflow
42 // R.B. becomes .R.B
43 #define RBRIGHT(x) ((x)>>8)
44 // .R.B becomes R.B.
45 #define RBLEFT(x)  ((x)<<8)
46 
47 // get components as byte from pixel
48 #define RED2BYTE(x)   ((x)>>24)
49 #define GREEN2BYTE(x) (((x)&GMASK)>>16)
50 #define BLUE2BYTE(x)  (((x)&BMASK)>>8)
51 #define ALPHA2BYTE(x) ((x)&AMASK)
52 
53 // set components from bytes in pixel
54 #define BYTE2RED(x)   ((x)<<24)
55 #define BYTE2GREEN(x) ((x)<<16)
56 #define BYTE2BLUE(x)  ((x)<<8)
57 #define BYTE2ALPHA(x) (x)
58 
59 #else
60 
61 // little endian 32bit pixel component order is ABGR
62 
63 // masks to isolate components in pixel
64 #define RMASK   0x000000ff
65 #define GMASK   0x0000ff00
66 #define BMASK   0x00ff0000
67 #define AMASK   0xff000000
68 #define RBMASK  0x00ff00ff
69 #define RGBMASK 0x00ffffff
70 
71 // shift RB components right and left to avoid overflow
72 // not required for little endian
73 #define RBRIGHT(x) (x)
74 #define RBLEFT(x)  (x)
75 
76 // get components as byte from pixel
77 #define RED2BYTE(x)   ((x)&RMASK)
78 #define GREEN2BYTE(x) (((x)&GMASK)>>8)
79 #define BLUE2BYTE(x)  (((x)&BMASK)>>16)
80 #define ALPHA2BYTE(x) ((x)>>24)
81 
82 // set components from bytes in pixel
83 #define BYTE2RED(x)   (x)
84 #define BYTE2GREEN(x) ((x)<<8)
85 #define BYTE2BLUE(x)  ((x)<<16)
86 #define BYTE2ALPHA(x) ((x)<<24)
87 
88 #endif
89 
90 // alpha blend source with opaque destination
91 #define ALPHABLENDOPAQUEDEST(source, dest, resultptr, alpha, invalpha) \
92     { \
93         const unsigned int _newrb = (alpha * RBRIGHT(source & RBMASK) + invalpha * RBRIGHT(dest & RBMASK)) >> 8; \
94         const unsigned int _newg  = (alpha *        (source & GMASK)  + invalpha *        (dest & GMASK))  >> 8; \
95         *resultptr = (RBLEFT(_newrb) & RBMASK) | (_newg & GMASK) | AMASK; \
96     }
97 
98 // alpha blend source with translucent destination
99 #define ALPHABLENDTRANSDEST(source, dest, resultptr, alpha, invalpha) \
100     { \
101         const unsigned int _destinva = (ALPHA2BYTE(dest) * invalpha) >> 8; \
102         const unsigned int _outa = alpha + _destinva; \
103         const unsigned int _newr = (alpha * RED2BYTE(source)   + _destinva * RED2BYTE(dest))   / _outa; \
104         const unsigned int _newg = (alpha * GREEN2BYTE(source) + _destinva * GREEN2BYTE(dest)) / _outa; \
105         const unsigned int _newb = (alpha * BLUE2BYTE(source)  + _destinva * BLUE2BYTE(dest))  / _outa; \
106         *resultptr = BYTE2RED(_newr) | BYTE2GREEN(_newg) | BYTE2BLUE(_newb) | BYTE2ALPHA(_outa - 1); \
107     }
108 
109 // alpha blend source with destination
110 #define ALPHABLEND(source, dest, resultptr, alpha, invalpha) \
111     if ((dest & AMASK) == AMASK) { \
112         ALPHABLENDOPAQUEDEST(source, dest, resultptr, alpha, invalpha); \
113     } else { \
114         ALPHABLENDTRANSDEST(source, dest, resultptr, alpha, invalpha); \
115     }
116 
117 // alpha blend premultiplied source with opaque destination
118 #define ALPHABLENDPREOPAQUEDEST(sourcearb, sourceag, dest, resultptr, invalpha) \
119     { \
120         const unsigned int _newrb = (sourcearb + invalpha * RBRIGHT(dest & RBMASK)) >> 8; \
121         const unsigned int _newg  = (sourceag  + invalpha *        (dest & GMASK))  >> 8; \
122         *resultptr = (RBLEFT(_newrb) & RBMASK) | (_newg & GMASK) | AMASK; \
123     }
124 
125 // alpha blend premultiplied source with destination
126 #define ALPHABLENDPRE(source, sourcearb, sourceag, dest, resultptr, alpha, invalpha) \
127     if ((dest & AMASK) == AMASK) { \
128         ALPHABLENDPREOPAQUEDEST(sourcearb, sourceag, dest, resultptr, invalpha); \
129     } else { \
130         ALPHABLENDTRANSDEST(source, dest, resultptr, alpha, invalpha); \
131     }
132 
133 // -----------------------------------------------------------------------------
134 
Clip(int w,int h,bool use_calloc)135 Clip::Clip(int w, int h, bool use_calloc) {
136     cwd = w;
137     cht = h;
138     if (use_calloc) {
139         cdata = (unsigned char*) calloc(cwd * cht * 4, sizeof(*cdata));
140     } else {
141         cdata = (unsigned char*) malloc(cwd * cht * 4 * sizeof(*cdata));
142     }
143     rowindex = NULL;
144     // set bounding box to clip extent
145     xbb = 0;
146     ybb = 0;
147     wbb = w;
148     hbb = h;
149     cdatabb = cdata;
150 }
151 
~Clip()152 Clip::~Clip() {
153     if (cdata) {
154         free(cdata);
155         cdata = NULL;
156     }
157     RemoveIndex();
158 }
159 
160 // compute non-transparent pixel bounding box
ComputeBoundingBox()161 void Clip::ComputeBoundingBox() {
162     unsigned int *clipdata = (unsigned int*)cdata;
163 
164     // discard transparent top rows
165     int x, y;
166     for (y = 0; y < cht; y++) {
167         // use row index if available
168         if (rowindex) {
169             if (rowindex[y] != alpha0) break;
170         } else {
171             // otherwise look along the row for non-zero alpha
172             x = 0;
173             while (x < cwd && (!(*clipdata++ & AMASK))) {
174                 x++;
175             }
176             if (x < cwd) break;
177         }
178     }
179     ybb = y;
180     hbb = cht - y;
181 
182     // discard transparent bottom rows
183     if (hbb > 0) {
184         clipdata = ((unsigned int*)cdata) + cwd * cht;
185         for (y = cht - 1; y > ybb; y--) {
186             // use row index if available
187             if (rowindex) {
188                 if (rowindex[y] != alpha0) break;
189             } else {
190                 // otherwise look along the row for non-zero alpha
191                 x = 0;
192                 while (x < cwd && (!(*--clipdata & AMASK))) {
193                     x++;
194                 }
195                 if (x < cwd) break;
196             }
197         }
198         y = cht - 1 - y;
199         hbb -= y;
200 
201         // discard transparent left columns
202         clipdata = (unsigned int*)cdata;
203         for (x = 0; x < cwd; x++) {
204             y = 0;
205             unsigned int *rowdata = clipdata;
206             while (y < cht && (!(*rowdata & AMASK))) {
207                 y++;
208                 rowdata += cwd;
209             }
210             if (y < cht) break;
211             clipdata++;
212         }
213         xbb = x;
214         wbb = cwd - x;
215 
216         // discard transparent right columns
217         if (wbb > 0) {
218             clipdata = ((unsigned int*)cdata) + cwd;
219             for (x = cwd - 1; x > xbb; x--) {
220                 y = 0;
221                 clipdata--;
222                 unsigned int *rowdata = clipdata;
223                 while (y < cht && (!(*rowdata & AMASK))) {
224                     y++;
225                     rowdata += cwd;
226                 }
227                 if (y < cht) break;
228             }
229             x = cwd - 1 - x;
230             wbb -= x;
231         }
232     }
233 
234     // compute top left pixel in bounding box
235     cdatabb = cdata + (ybb * cwd + xbb) * 4;
236 }
237 
238 // add row index to the clip
239 // if there are no rows that can be optimized the index will not be created
AddIndex()240 void Clip::AddIndex() {
241     if (!rowindex) {
242         // allocate the index
243         rowindex = (rowtype*)malloc(cht * sizeof(*rowindex));
244     }
245     unsigned int *lp = (unsigned int*)cdata;
246     unsigned int alpha;
247     unsigned int first;
248     bool bothrow = false;
249     int j;
250 
251     // check each row
252     int numopt = 0;
253     for (int i = 0; i < cht; i++) {
254         // check what type of pixels the row contains
255         first = *lp & AMASK;
256         alpha = first;
257         lp++;
258         j = 1;
259         bothrow = false;
260         // check for all transparent or all opaque
261         if (first == 0 || first == AMASK) {
262             while (j < cwd && alpha == first) {
263                 alpha = *lp++ & AMASK;
264                 j++;
265             }
266             if (j < cwd) {
267                 // that failed so check for a mix of transparent and opaque
268                 while (j < cwd && (alpha == 0 || alpha == AMASK)) {
269                     alpha = *lp++ & AMASK;
270                     j++;
271                 }
272                 if (j == cwd) bothrow = true;
273             }
274         }
275 
276         // set this row's flag
277        if (bothrow) {
278             numopt++;
279             rowindex[i] = both;
280         } else if (alpha == 0 && first == 0) {
281             numopt++;
282             rowindex[i] = alpha0;
283         } else if (alpha == AMASK && first == AMASK) {
284             numopt++;
285             rowindex[i] = opaque;
286         } else {
287             rowindex[i] = mixed;
288         }
289         lp += cwd - j;
290     }
291 
292     // compute non-zero alpha bounding box
293     ComputeBoundingBox();
294 
295     // remove the index if there were no optimized rows
296     if (numopt == 0) RemoveIndex();
297 }
298 
299 // remove row index from the clip
RemoveIndex()300 void Clip::RemoveIndex() {
301     if (rowindex) {
302         free(rowindex);
303         rowindex = NULL;
304     }
305 };
306 
307 // -----------------------------------------------------------------------------
308 
309 const int clipbatch = 16;    // clip batch size for allocation
310 
ClipManager()311 ClipManager::ClipManager() {
312     Clear();
313     lsize = clipbatch;
314     esize = clipbatch;
315     osize = clipbatch;
316     hsize = clipbatch;
317     lcliplist = (const Clip**)malloc(lsize * sizeof(*lcliplist));
318     ecliplist = (const Clip**)malloc(esize * sizeof(*ecliplist));
319     ocliplist = (const Clip**)malloc(osize * sizeof(*ocliplist));
320     hcliplist = (const Clip**)malloc(hsize * sizeof(*hcliplist));
321 }
322 
~ClipManager()323 ClipManager::~ClipManager() {
324     if (lcliplist) {
325         free(lcliplist);
326         lcliplist = NULL;
327     }
328     if (ecliplist) {
329         free(ecliplist);
330         ecliplist = NULL;
331     }
332     if (ocliplist) {
333         free(ocliplist);
334         ocliplist = NULL;
335     }
336     if (hcliplist) {
337         free(hcliplist);
338         hcliplist = NULL;
339     }
340 }
341 
Clear()342 void ClipManager::Clear() {
343     lclips = 0;
344     eclips = 0;
345     oclips = 0;
346     hclips = 0;
347     lclip = NULL;
348     eclip = NULL;
349     oclip = NULL;
350     sclip = NULL;
351     pclip = NULL;
352     aclip = NULL;
353     lnaclip = NULL;
354     snaclip = NULL;
355     elnaclip = NULL;
356     olnaclip = NULL;
357     hclip = NULL;
358     hnaclip = NULL;
359 }
360 
AddLiveClip(const Clip * liveclip)361 void ClipManager::AddLiveClip(const Clip *liveclip) {
362     if (lclips == lsize) {
363         // allocate more memory
364         lsize += clipbatch;
365         lcliplist = (const Clip**)realloc(lcliplist, lsize * sizeof(*lcliplist));
366     }
367     lcliplist[lclips++] = liveclip;
368 }
369 
AddEvenClip(const Clip * clip)370 void ClipManager::AddEvenClip(const Clip *clip) {
371     if (eclips == esize) {
372         // allocate more memory
373         esize += clipbatch;
374         ecliplist = (const Clip**)realloc(ecliplist, esize * sizeof(*ecliplist));
375     }
376     ecliplist[eclips++] = clip;
377 }
378 
AddOddClip(const Clip * clip)379 void ClipManager::AddOddClip(const Clip *clip) {
380     if (oclips == osize) {
381         // allocate more memory
382         osize += clipbatch;
383         ocliplist = (const Clip**)realloc(ocliplist, osize * sizeof(*ocliplist));
384     }
385     ocliplist[oclips++] = clip;
386 }
387 
AddHistoryClip(const Clip * clip)388 void ClipManager::AddHistoryClip(const Clip *clip) {
389     if (hclips == hsize) {
390         // allocate more memory
391         hsize += clipbatch;
392         hcliplist = (const Clip**)realloc(hcliplist, hsize * sizeof(*hcliplist));
393     }
394     hcliplist[hclips++] = clip;
395 }
396 
GetLiveClips(int * numclips)397 const Clip **ClipManager::GetLiveClips(int *numclips) {
398     *numclips = lclips;
399     return lcliplist;
400 }
401 
GetEvenClips(int * numclips)402 const Clip **ClipManager::GetEvenClips(int *numclips) {
403     *numclips = eclips;
404     return ecliplist;
405 }
406 
GetOddClips(int * numclips)407 const Clip **ClipManager::GetOddClips(int *numclips) {
408     *numclips = oclips;
409     return ocliplist;
410 }
411 
GetHistoryClips(int * numclips)412 const Clip **ClipManager::GetHistoryClips(int *numclips) {
413     *numclips = hclips;
414     return hcliplist;
415 }
416 
SetLiveClip(const Clip * liveclip)417 void ClipManager::SetLiveClip(const Clip *liveclip) {
418     lclip = liveclip;
419 }
420 
SetOddClip(const Clip * oddclip)421 void ClipManager::SetOddClip(const Clip *oddclip) {
422     oclip = oddclip;
423 }
424 
SetEvenClip(const Clip * evenclip)425 void ClipManager::SetEvenClip(const Clip *evenclip) {
426     eclip = evenclip;
427 }
428 
SetSelectClip(const Clip * selectclip)429 void ClipManager::SetSelectClip(const Clip *selectclip) {
430     sclip = selectclip;
431 }
432 
SetPasteClip(const Clip * pasteclip)433 void ClipManager::SetPasteClip(const Clip *pasteclip) {
434     pclip = pasteclip;
435 }
436 
SetLiveNotActiveClip(const Clip * livenaclip)437 void ClipManager::SetLiveNotActiveClip(const Clip *livenaclip) {
438     lnaclip = livenaclip;
439 }
440 
SetSelectNotActiveClip(const Clip * selectnaclip)441 void ClipManager::SetSelectNotActiveClip(const Clip *selectnaclip) {
442     snaclip = selectnaclip;
443 }
444 
SetEvenLiveNotActiveClip(const Clip * evennaclip)445 void ClipManager::SetEvenLiveNotActiveClip(const Clip *evennaclip) {
446     elnaclip = evennaclip;
447 }
448 
SetOddLiveNotActiveClip(const Clip * oddnaclip)449 void ClipManager::SetOddLiveNotActiveClip(const Clip *oddnaclip) {
450     olnaclip = oddnaclip;
451 }
452 
SetActiveClip(const Clip * activeclip)453 void ClipManager::SetActiveClip(const Clip *activeclip) {
454     aclip = activeclip;
455 }
456 
SetHistoryClip(const Clip * historyclip)457 void ClipManager::SetHistoryClip(const Clip *historyclip) {
458     hclip = historyclip;
459 }
460 
SetHistoryNotActiveClip(const Clip * historynaclip)461 void ClipManager::SetHistoryNotActiveClip(const Clip *historynaclip) {
462     hnaclip = historynaclip;
463 }
464 
GetLiveClip(int * clipwd)465 const Clip *ClipManager::GetLiveClip(int *clipwd) {
466     if (lclip && clipwd) *clipwd = lclip->cwd;
467     return lclip;
468 }
469 
GetOddClip(int * clipwd)470 const Clip *ClipManager::GetOddClip(int *clipwd) {
471     if (oclip && clipwd) *clipwd = oclip->cwd;
472     return oclip;
473 }
474 
GetEvenClip(int * clipwd)475 const Clip *ClipManager::GetEvenClip(int *clipwd) {
476     if (eclip && clipwd) *clipwd = eclip->cwd;
477     return eclip;
478 }
479 
GetSelectClip(int * clipwd)480 const Clip *ClipManager::GetSelectClip(int *clipwd) {
481     if (sclip && clipwd) *clipwd = sclip->cwd;
482     return sclip;
483 }
484 
GetPasteClip(int * clipwd)485 const Clip *ClipManager::GetPasteClip(int *clipwd) {
486     if (pclip && clipwd) *clipwd = pclip->cwd;
487     return pclip;
488 }
489 
GetActiveClip(int * clipwd)490 const Clip *ClipManager::GetActiveClip(int *clipwd) {
491     if (aclip && clipwd) *clipwd = aclip->cwd;
492     return aclip;
493 }
494 
GetLiveNotActiveClip(int * clipwd)495 const Clip *ClipManager::GetLiveNotActiveClip(int *clipwd) {
496     if (lnaclip && clipwd) *clipwd = lnaclip->cwd;
497     return lnaclip;
498 }
499 
GetSelectNotActiveClip(int * clipwd)500 const Clip *ClipManager::GetSelectNotActiveClip(int *clipwd) {
501     if (snaclip && clipwd) *clipwd = snaclip->cwd;
502     return snaclip;
503 }
504 
GetEvenLiveNotActiveClip(int * clipwd)505 const Clip *ClipManager::GetEvenLiveNotActiveClip(int *clipwd) {
506     if (elnaclip && clipwd) *clipwd = elnaclip->cwd;
507     return elnaclip;
508 }
509 
GetOddLiveNotActiveClip(int * clipwd)510 const Clip *ClipManager::GetOddLiveNotActiveClip(int *clipwd) {
511     if (olnaclip && clipwd) *clipwd = olnaclip->cwd;
512     return olnaclip;
513 }
514 
GetHistoryClip(int * clipwd)515 const Clip *ClipManager::GetHistoryClip(int *clipwd) {
516     if (hclip && clipwd) *clipwd = hclip->cwd;
517     return hclip;
518 }
519 
GetHistoryNotActiveClip(int * clipwd)520 const Clip *ClipManager::GetHistoryNotActiveClip(int *clipwd) {
521     if (hnaclip && clipwd) *clipwd = hnaclip->cwd;
522     return hnaclip;
523 }
524 
525 // -----------------------------------------------------------------------------
526 
Table()527 Table::Table() {
528     nkeys = 0;
529     keys = NULL;
530     values = NULL;
531     exists = NULL;
532 }
533 
~Table()534 Table::~Table() {
535     FreeMemory();
536 }
537 
SetSize(int sz)538 bool Table::SetSize(int sz) {
539     // reallocate memory
540     FreeMemory();
541     size = sz;
542     nkeys = 0;
543     return AllocateMemory();
544 }
545 
Clear()546 void Table::Clear() {
547     // clear the table
548     if (nkeys > 0) {
549         ClearKeys();
550         memset(values, 0, size * sizeof(*values));
551     }
552 }
553 
ClearKeys()554 void Table::ClearKeys() {
555     // zero the number of keys and exists flags
556     nkeys = 0;
557     memset(exists, 0, size * sizeof(*exists));
558 }
559 
GetKeys(int * numkeys)560 const int *Table::GetKeys(int *numkeys) {
561     // return the number of keys
562     if (numkeys) *numkeys = nkeys;
563 
564     // return the list of keys
565     return keys;
566 }
567 
GetNumKeys()568 const int Table::GetNumKeys() {
569     // return the number of keys
570     return nkeys;
571 }
572 
GetValues()573 const unsigned char *Table::GetValues() {
574     // return the list of values
575     return values;
576 }
577 
SetValue(const int key,const unsigned char value)578 void Table::SetValue(const int key, const unsigned char value) {
579     // check if the key exists
580     if (!exists[key]) {
581         // create a new key
582         keys[nkeys++] = key;
583 
584         // mark the key as exists
585         exists[key] = true;
586     }
587 
588     // set the value at the key
589     values[key] = value;
590 }
591 
SetTo1(const int key)592 void Table::SetTo1(const int key) {
593     // check if the key exists
594     if (!exists[key]) {
595         // create a new key
596         keys[nkeys++] = key;
597 
598         // mark the key as exists
599         exists[key] = true;
600     }
601 
602     // set the value at the key
603     values[key] = 1;
604 }
605 
AddToValue(const int key,const unsigned char amount)606 void Table::AddToValue(const int key, const unsigned char amount) {
607     // check if the key exists
608     if (!exists[key]) {
609         // create a new key
610         keys[nkeys++] = key;
611 
612         // set the value to the amount
613         values[key] = amount;
614 
615         // mark the key as exists
616         exists[key] = true;
617     } else {
618         // add the amount to the value at the key
619         values[key] += amount;
620     }
621 }
622 
DecrementTo1(const int key)623 void Table::DecrementTo1(const int key) {
624     // decrement the value if above 1
625     if (values[key] > 1) values[key]--;
626 }
627 
SortKeys()628 void Table::SortKeys() {
629     int *key = keys;
630     char *exist = exists;
631     int *lastkey = key + nkeys;
632     // sort keys into ascending order
633     while (key < lastkey) {
634         if (*exist) {
635             *key++ = exist - exists;
636         }
637         exist++;
638     }
639 }
640 
Copy(const Table & from)641 void Table::Copy(const Table &from) {
642     if (from.size != size) {
643         SetSize(from.size);
644     }
645     nkeys = from.nkeys;
646     memcpy(values, from.values, size * sizeof(*values));
647     memcpy(exists, from.exists, size * sizeof(*exists));
648     // create keys in ascending order from exists flags
649     SortKeys();
650 }
651 
FreeMemory()652 void Table::FreeMemory() {
653     if (values) {
654         free(values);
655         values = NULL;
656     }
657     if (keys) {
658         free(keys);
659         keys = NULL;
660     }
661     if (exists) {
662         free(exists);
663         exists = NULL;
664     }
665 }
666 
AllocateMemory()667 bool Table::AllocateMemory() {
668     // allocate keys
669     keys = (int*)malloc(size * sizeof(*keys));
670 
671     // allocate and clear values and key exists
672     values = (unsigned char*)calloc(size, sizeof(*values));
673     exists = (char*)calloc(size, sizeof(*exists));
674 
675     // check allocation succeeded
676     if (keys == NULL || values == NULL || exists == NULL) {
677         FreeMemory();
678         return false;
679     }
680     return true;
681 }
682 
683 // -----------------------------------------------------------------------------
684 
685 // some useful macros:
686 
687 #if !wxCHECK_VERSION(2,9,0)
688     #define wxImageResizeQuality int
689 #endif
690 
691 #ifdef __WXMSW__
692     #define round(x) int( (x) < 0 ? (x)-0.5 : (x)+0.5 )
693     #define remainder(n,d) ( (n) - round((n)/(d)) * (d) )
694 #endif
695 
696 #define PixelInOverlay(x,y) \
697     (((unsigned int)(x)) < (unsigned int)ovwd && ((unsigned int)(y)) < (unsigned int)ovht)
698 
699 #define PixelInTarget(x,y) \
700     (((unsigned int)(x)) < (unsigned int)wd && ((unsigned int)(y)) < (unsigned int)ht)
701 
702 #define RectOutsideTarget(x,y,w,h) \
703     (x >= wd || x + w <= 0 || \
704      y >= ht || y + h <= 0)
705 
706 #define RectInsideTarget(x,y,w,h) \
707     (x >= 0 && x + w <= wd && \
708      y >= 0 && y + h <= ht)
709 
710 // -----------------------------------------------------------------------------
711 
712 Overlay *curroverlay = NULL;        // pointer to current overlay
713 
714 const char *no_overlay = "overlay has not been created";
715 const char *no_cellview = "overlay does not have a cell view";
716 
717 const int cellviewmaxsize = 4096;   // maximum dimension for cell view
718 const int cellviewmultiple = 16;    // cellview dimensions must be a multiple of this value
719 
720 // for camera
721 const double camminzoom = 0.0625;   // minimum zoom
722 const double cammaxzoom = 32.0;     // maximum zoom
723 
724 // for theme
725 const int aliveStart = 64;          // new cell color index
726 const int aliveEnd = 127;           // cell alive longest color index
727 const int deadStart = 63;           // cell just died color index
728 const int deadEnd = 1;              // cell dead longest color index
729 
730 // for stars
731 const int numStars = 10000;         // number of stars in starfield
732 const int starMaxX = 8192;
733 const int starMaxY = 8192;
734 const int starMaxZ = 1024;
735 const double degToRad = M_PI / 180;
736 const double radToDeg = 180 / M_PI;
737 
738 // for replace
739 const int matchany = -1;            // match any component value
740 
741 #ifdef __WXMAC__
742     // on Mac we'll need to increase the line height of text by 1 or 2 pixels to avoid
743     // a GetTextExtent bug that clips the bottom pixels of descenders like "gjpqy"
744     static int extraht;
745 #endif
746 
747 #ifdef ENABLE_SOUND
748     static ISoundEngine *engine = NULL;
749 #endif
750 
751 // -----------------------------------------------------------------------------
752 
Overlay()753 Overlay::Overlay()
754 {
755     pixmap = NULL;
756     ovpixmap = NULL;
757     cellview = NULL;
758     cellview1 = NULL;
759     zoomview = NULL;
760     starx = NULL;
761     stary = NULL;
762     starz = NULL;
763     renderclip = NULL;
764     #ifdef ENABLE_SOUND
765     // initialize sound engine once (avoids "Could not add IO Proc for CoreAudio" warnings in Mac console)
766     if (engine == NULL) {
767         engine = createIrrKlangDevice(ESOD_AUTO_DETECT, ESEO_MULTI_THREADED | ESEO_LOAD_PLUGINS | ESEO_USE_3D_BUFFERS);
768         if (!engine) Warning(_("Unable to initialize sound!"));
769     }
770     #endif
771 
772     // 3D
773     stepsize = 1;
774     depthshading = false;
775     celltype = cube;
776     gridsize = 0;
777     showhistory = 0;
778     fadehistory = false;
779     modN = NULL;
780     modNN = NULL;
781     xyz = NULL;
782     xaxis = NULL;
783     yaxis = NULL;
784     zaxis = NULL;
785 }
786 
787 // -----------------------------------------------------------------------------
788 
~Overlay()789 Overlay::~Overlay()
790 {
791     DeleteOverlay();
792 }
793 
794 // -----------------------------------------------------------------------------
795 
DeleteOverlay()796 void Overlay::DeleteOverlay()
797 {
798     if (ovpixmap) {
799         free(ovpixmap);
800         ovpixmap = NULL;
801     }
802     pixmap = NULL;
803 
804     // delete clips
805     std::map<std::string,Clip*>::iterator it;
806     for (it = clips.begin(); it != clips.end(); ++it) {
807         delete it->second;
808     }
809     clips.clear();
810 
811     #ifdef ENABLE_SOUND
812     // stop any sound playback and delete cached sounds
813     if (engine) {
814         // delete sounds
815         std::map<std::string,ISound*>::iterator itsnd;
816         for (itsnd = sounds.begin(); itsnd != sounds.end(); ++itsnd) {
817             itsnd->second->drop();
818         }
819         sounds.clear();
820     }
821     #endif
822 
823     // delete cellview
824     DeleteCellView();
825 
826     // free div table
827     FreeDivTable();
828 
829     // clear axis flags
830     FreeAxisFlags();
831 }
832 
833 // -----------------------------------------------------------------------------
834 
DeleteStars()835 void Overlay::DeleteStars()
836 {
837     if (starx) {
838         free(starx);
839         starx = NULL;
840     }
841     if (stary) {
842         free(stary);
843         stary = NULL;
844     }
845     if (starz) {
846         free(starz);
847         starz = NULL;
848     }
849 }
850 
851 // -----------------------------------------------------------------------------
852 
DeleteCellView()853 void Overlay::DeleteCellView()
854 {
855     if (cellview) {
856         free(cellview);
857         cellview = NULL;
858     }
859     if (cellview1) {
860         free(cellview1);
861         cellview1 = NULL;
862     }
863     if (zoomview) {
864         free(zoomview);
865         zoomview = NULL;
866     }
867     DeleteStars();
868 }
869 
870 // -----------------------------------------------------------------------------
871 
SetRGBA(unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha,unsigned int * rgba)872 void Overlay::SetRGBA(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha, unsigned int *rgba)
873 {
874     unsigned char *rgbaptr = (unsigned char *)rgba;
875     *rgbaptr++ = red;
876     *rgbaptr++ = green;
877     *rgbaptr++ = blue;
878     *rgbaptr   = alpha;
879 }
880 
881 // -----------------------------------------------------------------------------
882 
GetRGBA(unsigned char * red,unsigned char * green,unsigned char * blue,unsigned char * alpha,unsigned int rgba)883 void Overlay::GetRGBA(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *alpha, unsigned int rgba)
884 {
885     unsigned char *rgbaptr = (unsigned char *)&rgba;
886     *red   = *rgbaptr++;
887     *green = *rgbaptr++;
888     *blue  = *rgbaptr++;
889     *alpha = *rgbaptr;
890 }
891 
892 // -----------------------------------------------------------------------------
893 
RefreshCellViewWithTheme()894 void Overlay::RefreshCellViewWithTheme()
895 {
896     // refresh the cellview for a 2 state pattern using LifeViewer theme
897     unsigned char *cellviewptr = cellview;
898     unsigned char *cellviewptr1 = cellview1;
899     const unsigned char *end = cellview + (cellwd * cellht);
900 
901     // get the cells in the cell view
902     lifealgo *algo = currlayer->algo;
903     algo->getcells(cellviewptr1, cellx, celly, cellwd, cellht);
904 
905     // update based on the theme
906     while (cellviewptr < end) {
907         const unsigned char state = *cellviewptr;
908         if (*cellviewptr1++) {
909             // new cell is alive
910             if (state >= aliveStart) {
911                 // cell was already alive
912                 if (state < aliveEnd) *cellviewptr = state + 1;
913             } else {
914                 // cell just born
915                 *cellviewptr = aliveStart;
916             }
917         } else {
918             // new cell is dead
919             if (state >= aliveStart) {
920                 // cell just died
921                 *cellviewptr = deadStart;
922             } else {
923                 // cell is decaying
924                 if (state > deadEnd) *cellviewptr = state - 1;
925             }
926         }
927         cellviewptr++;
928     }
929 }
930 
931 // -----------------------------------------------------------------------------
932 
RefreshCellView()933 void Overlay::RefreshCellView()
934 {
935     lifealgo *algo = currlayer->algo;
936 
937     // read cells into the buffer
938     algo->getcells(cellview, cellx, celly, cellwd, cellht);
939 }
940 
941 // -----------------------------------------------------------------------------
942 
GetPatternColors()943 void Overlay::GetPatternColors()
944 {
945     unsigned long *rgba = (unsigned long *)cellRGBA;
946 
947     // read pattern colors
948     const int numicons = currlayer->numicons;
949     const unsigned char *cellr = currlayer->cellr;
950     const unsigned char *cellg = currlayer->cellg;
951     const unsigned char *cellb = currlayer->cellb;
952 
953     for (int i = 0; i <= numicons; i++) {
954         *rgba++ = BYTE2RED(cellr[i]) | BYTE2GREEN(cellg[i]) | BYTE2BLUE(cellb[i]) | AMASK;
955     }
956 
957     // read border color from View Settings
958     unsigned char borderr = borderrgb->Red();
959     unsigned char borderg = borderrgb->Green();
960     unsigned char borderb = borderrgb->Blue();
961     unsigned char alpha = 255; // opaque
962     SetRGBA(borderr, borderg, borderb, alpha, &borderRGBA);
963 }
964 
965 // -----------------------------------------------------------------------------
966 
GetThemeColors(double brightness)967 void Overlay::GetThemeColors(double brightness)
968 {
969     unsigned char *rgb = (unsigned char *)cellRGBA;
970 
971     // cell born color
972     unsigned char aliveStartR, aliveStartG, aliveStartB, aliveStartA;
973 
974     // cell alive long time color
975     unsigned char aliveEndR, aliveEndG, aliveEndB;
976 
977     // cell just died color
978     unsigned char deadStartR, deadStartG, deadStartB, deadStartA;
979 
980     // cell dead long time color
981     unsigned char deadEndR, deadEndG, deadEndB;
982 
983     // cell never occupied color
984     unsigned char unoccupiedR, unoccupiedG, unoccupiedB, unoccupiedA;
985 
986     // get the color rgb components
987     GetRGBA(&aliveStartR, &aliveStartG, &aliveStartB, &aliveStartA, aliveStartRGBA);
988     GetRGBA(&aliveEndR, &aliveEndG, &aliveEndB, &aliveStartA, aliveEndRGBA);
989     GetRGBA(&deadStartR, &deadStartG, &deadStartB, &deadStartA, deadStartRGBA);
990     GetRGBA(&deadEndR, &deadEndG, &deadEndB, &deadStartA, deadEndRGBA);
991     GetRGBA(&unoccupiedR, &unoccupiedG, &unoccupiedB, &unoccupiedA, unoccupiedRGBA);
992 
993     // set never occupied cell color
994     *rgb++ = unoccupiedR;
995     *rgb++ = unoccupiedG;
996     *rgb++ = unoccupiedB;
997     *rgb++ = unoccupiedA;
998 
999     // set decaying colors
1000     for (int i = deadEnd; i <= deadStart; i++) {
1001         const double weight = 1 - ((double)(i - deadEnd) / (deadStart - deadEnd));
1002         *rgb++ = deadStartR * (1 - weight) + deadEndR * weight;
1003         *rgb++ = deadStartG * (1 - weight) + deadEndG * weight;
1004         *rgb++ = deadStartB * (1 - weight) + deadEndB * weight;
1005         *rgb++ = deadStartA;
1006     }
1007 
1008     // set living colors
1009     for (int i = aliveStart; i <= aliveEnd; i++) {
1010         const double weight = 1 - ((double)(i - aliveStart) / (aliveEnd - aliveStart));
1011         *rgb++ = (aliveStartR * weight + aliveEndR * (1 - weight)) * brightness;
1012         *rgb++ = (aliveStartG * weight + aliveEndG * (1 - weight)) * brightness;
1013         *rgb++ = (aliveStartB * weight + aliveEndB * (1 - weight)) * brightness;
1014         *rgb++ = aliveStartA;
1015     }
1016 
1017     // read border color from View Settings
1018     unsigned char borderr = borderrgb->Red();
1019     unsigned char borderg = borderrgb->Green();
1020     unsigned char borderb = borderrgb->Blue();
1021     SetRGBA(borderr, borderg, borderb, bordera, &borderRGBA);
1022 }
1023 
1024 // -----------------------------------------------------------------------------
1025 
UpdateZoomView(unsigned char * source,unsigned char * dest,const unsigned int step)1026 void Overlay::UpdateZoomView(unsigned char *source, unsigned char *dest, const unsigned int step)
1027 {
1028     unsigned char state;
1029     unsigned char max;
1030     const unsigned int halfstep = step >> 1;
1031     const unsigned int ystep = step * cellwd;
1032     unsigned char *row1 = source;
1033     unsigned char *row2 = source + halfstep * cellwd;
1034 
1035     for (unsigned int h = 0; h < cellht; h += step) {
1036         for (unsigned int w = 0; w < cellwd; w += step) {
1037             // find the maximum state value in each 2x2 block
1038             max = row1[w];
1039             state = row1[w + halfstep];
1040             if (state > max) max = state;
1041             state = row2[w];
1042             if (state > max) max = state;
1043             state = row2[w + halfstep];
1044             if (state > max) max = state;
1045             dest[w] = max;
1046         }
1047 
1048         // update row pointers
1049         row1 += ystep;
1050         row2 += ystep;
1051         dest += ystep;
1052     }
1053 }
1054 
1055 // -----------------------------------------------------------------------------
1056 
DoDrawCells()1057 const char *Overlay::DoDrawCells()
1058 {
1059     if (cellview == NULL) return OverlayError(no_cellview);
1060 
1061     int mask = 0;
1062     unsigned char *cells = cellview;
1063     unsigned char *source = cellview;
1064     unsigned char *dest = zoomview;
1065 
1066     // check for zoom < 1
1067     if (camzoom < 1) {
1068         int negzoom = (1 / camzoom) - 0.001;
1069         int step = 2;
1070         do {
1071             UpdateZoomView(source, dest, step);
1072 
1073             // next zoom level
1074             step <<= 1;
1075             negzoom >>= 1;
1076             mask = (mask << 1) | 1;
1077 
1078             // update source and destination
1079             source = dest;
1080             dest = zoomview + (step >> 1) - 1;
1081         } while (negzoom >= 1);
1082 
1083         // source is the zoom view
1084         cells = source;
1085     }
1086 
1087     // check for hex
1088     double angle = camangle;
1089     if (ishex) {
1090         angle = 0;
1091     }
1092 
1093     // pick renderer based on whether camera is rotated
1094     if (angle == 0) {
1095         DrawCellsNoRotate(cells, ~mask);
1096     } else {
1097         DrawCellsRotate(cells, ~mask, angle);
1098     }
1099 
1100     // draw stars if enabled
1101     if (stars) {
1102         DrawStars(angle);
1103     }
1104 
1105     // mark target clip as changed
1106     DisableTargetClipIndex();
1107 
1108     return NULL;
1109 }
1110 
1111 // -----------------------------------------------------------------------------
1112 
DrawCellsRotate(unsigned char * cells,int mask,double angle)1113 void Overlay::DrawCellsRotate(unsigned char *cells, int mask, double angle)
1114 {
1115     // convert depth to actual depth
1116     const double depth = camlayerdepth / 2 + 1;
1117 
1118     // check pixel brightness depending on layers
1119     double brightness = 1;
1120     double brightinc = 0;
1121 
1122     // check whether to draw layers
1123     int layertarget = 0;
1124     if (theme && camlayers > 1 && depth > 1) {
1125         brightness = 0.6;
1126         brightinc = 0.4 / (camlayers - 1);
1127         layertarget = camlayers;
1128     }
1129 
1130     // refresh the cell view
1131     if (theme) {
1132         // using theme colors
1133         GetThemeColors(brightness);
1134     } else {
1135         // using standard pattern colors
1136         GetPatternColors();
1137     }
1138 
1139     // compute deltas in horizontal and vertical direction based on rotation
1140     double dxy = sin(angle / 180 * M_PI) / camzoom;
1141     double dyy = cos(angle / 180 * M_PI) / camzoom;
1142 
1143     double sy = -((wd / 2) * (-dxy) + (ht / 2) * dyy) + camy;
1144     double sx = -((wd / 2) * dyy + (ht / 2) * dxy) + camx;
1145 
1146     unsigned char state;
1147     unsigned int *overlayptr = (unsigned int *)pixmap;
1148     double x, y;
1149 
1150     // draw each pixel
1151     y = sy;
1152     const int height = ht;
1153     const int width = wd;
1154     for (int h = 0; h < height; h++) {
1155         x = sx;
1156 
1157         // offset if hex rule
1158         if (ishex) {
1159             x += 0.5 * (int)y;
1160         }
1161 
1162         // check if entire row in on the grid
1163         unsigned int ix = (((int)x) & mask);
1164         unsigned int iy = (((int)y) & mask);
1165         unsigned int tx = (((int)(x + dyy * wd)) & mask);
1166         unsigned int ty = (((int)(y - dxy * wd)) & mask);
1167         if (ix < cellwd && iy < cellht && tx < cellwd && ty < cellht) {
1168             for (int w = 0; w < width; w++) {
1169                 ix = (((int)x) & mask);
1170                 iy = (((int)y) & mask);
1171                 state = cells[cellwd * iy + ix];
1172                 *overlayptr++ = cellRGBA[state];
1173                 x += dyy;
1174                 y -= dxy;
1175             }
1176         } else {
1177             for (int w = 0; w < width; w++) {
1178                 ix = (((int)x) & mask);
1179                 iy = (((int)y) & mask);
1180 
1181                 // check if pixel is in the cell view
1182                 if (ix < cellwd && iy < cellht) {
1183                     state = cells[cellwd * iy + ix];
1184                     *overlayptr++ = cellRGBA[state];
1185                 } else {
1186                     *overlayptr++ = borderRGBA;
1187                 }
1188 
1189                 // update row position
1190                 x += dyy;
1191                 y -= dxy;
1192             }
1193         }
1194         // update column position
1195         sx += dxy;
1196         sy += dyy;
1197         y = sy;
1198     }
1199 
1200     // draw grid lines if enabled
1201     if (grid && angle == 0 && camzoom >= 4) {
1202         DrawGridLines();
1203     }
1204 
1205     // draw any layers
1206     if (theme) {
1207         double layerzoom = camzoom;
1208         int zoomlevel;
1209 
1210         for (int i = 1; i < layertarget; i++) {
1211             unsigned char transparenttarget = (i * ((aliveEnd + 1) / camlayers));
1212 
1213             // update brightness
1214             brightness += brightinc;
1215             GetThemeColors(brightness);
1216 
1217             // adjust zoom for next level
1218             dxy /= depth;
1219             dyy /= depth;
1220             layerzoom *= depth;
1221             cells = cellview;
1222             zoomlevel = 0;
1223             mask = ~0;
1224 
1225             // compute which zoomview level to use for this layer
1226             if (layerzoom < 0.125) {
1227                 zoomlevel = 8;
1228             } else {
1229                 if (layerzoom < 0.25) {
1230                     zoomlevel = 4;
1231                 } else {
1232                     if (layerzoom < 0.5) {
1233                         zoomlevel = 2;
1234                     } else {
1235                         if (layerzoom < 1) {
1236                             zoomlevel = 1;
1237                         }
1238                     }
1239                 }
1240             }
1241 
1242             // setup the mask for the zoom level
1243             if (zoomlevel > 0) {
1244                 mask = ~((zoomlevel << 1) - 1);
1245                 cells = zoomview + zoomlevel - 1;
1246             }
1247 
1248             sy = -((wd / 2) * (-dxy) + (ht / 2) * dyy) + camy;
1249             sx = -((wd / 2) * dyy + (ht / 2) * dxy) + camx;
1250 
1251             overlayptr = (unsigned int *)pixmap;
1252 
1253             // draw each pixel
1254             y = sy;
1255             for (int h = 0; h < ht; h++) {
1256                 x = sx;
1257 
1258                 // offset if hex rule
1259                 if (ishex) {
1260                     x += 0.5 * (int)y;
1261                 }
1262 
1263                 // check if entire row in on the grid
1264                 unsigned int ix = (((int)x) & mask);
1265                 unsigned int iy = (((int)y) & mask);
1266                 unsigned int tx = (((int)(x + dyy * wd)) & mask);
1267                 unsigned int ty = (((int)(y - dxy * wd)) & mask);
1268                 if (ix < cellwd && iy < cellht && tx < cellwd && ty < cellht) {
1269                     for (int w = 0; w < wd; w++) {
1270                         ix = (((int)x) & mask);
1271                         iy = (((int)y) & mask);
1272                         state = cells[cellwd * iy + ix];
1273                         if (state >= transparenttarget) {
1274                             *overlayptr = cellRGBA[state];
1275                         }
1276                         overlayptr++;
1277                         x += dyy;
1278                         y -= dxy;
1279                     }
1280                 } else {
1281                     for (int w = 0; w < wd; w++) {
1282                         ix = (((int)x) & mask);
1283                         iy = (((int)y) & mask);
1284 
1285                         // check if pixel is on the grid
1286                         if (ix < cellwd && iy < cellht) {
1287                             state = cells[cellwd * iy + ix];
1288 
1289                             // check if it is transparent
1290                             if (state >= transparenttarget) {
1291                                 // draw the pixel
1292                                 *overlayptr = cellRGBA[state];
1293                             }
1294                         }
1295                         overlayptr++;
1296 
1297                         // update row position
1298                         x += dyy;
1299                         y -= dxy;
1300                     }
1301                 }
1302 
1303                 // update column position
1304                 sx += dxy;
1305                 sy += dyy;
1306                 y = sy;
1307             }
1308         }
1309     }
1310 }
1311 
1312 // -----------------------------------------------------------------------------
1313 
DrawCellsNoRotate(unsigned char * cells,int mask)1314 void Overlay::DrawCellsNoRotate(unsigned char *cells, int mask)
1315 {
1316     // convert depth to actual depth
1317     const double depth = camlayerdepth / 2 + 1;
1318 
1319     // check pixel brightness depending on layers
1320     double brightness = 1;
1321     double brightinc = 0;
1322 
1323     // check whether to draw layers
1324     int layertarget = 0;
1325     if (theme && camlayers > 1 && depth > 1) {
1326         brightness = 0.6;
1327         brightinc = 0.4 / (camlayers - 1);
1328         layertarget = camlayers;
1329     }
1330 
1331     // refresh the cell view
1332     if (theme) {
1333         // using theme colors
1334         GetThemeColors(brightness);
1335     } else {
1336         // using standard pattern colors
1337         GetPatternColors();
1338     }
1339 
1340     // compute deltas in horizontal and vertical direction
1341     double dyy = 1 / camzoom;
1342 
1343     double sy = -((ht / 2) * dyy) + camy;
1344     double sx = -((wd / 2) * dyy) + camx;
1345 
1346     unsigned char state;
1347     unsigned int  *overlayptr = (unsigned int *)pixmap;
1348     unsigned char *rowptr;
1349     double x, y;
1350 
1351     // draw each pixel
1352     y = sy;
1353     for (int h = 0; h < ht; h++) {
1354         unsigned int iy = (((int)y) & mask);
1355 
1356         // clip to the grid
1357         if (iy < cellht) {
1358             // get the row
1359             rowptr = cells + cellwd * iy;
1360             x = sx;
1361 
1362             // offset if hex rule
1363             if (ishex) {
1364                 x += 0.5 * (int)y;
1365             }
1366 
1367             // check if the whole row is on the grid
1368             unsigned int ix = (((int)x) & mask);
1369             unsigned int tx = (((int)(x + dyy * wd)) & mask);
1370             if (ix < cellwd && tx < cellwd) {
1371                 for (int w = 0; w < wd; w++) {
1372                     ix = (((int)x) & mask);
1373                     state = rowptr[ix];
1374                     *overlayptr++ = cellRGBA[state];
1375                     x += dyy;
1376                 }
1377             } else {
1378                 for (int w = 0; w < wd; w++) {
1379                     // check if pixel is in the cell view
1380                     ix = (((int)x) & mask);
1381                     if (ix < cellwd) {
1382                         state = rowptr[ix];
1383                         *overlayptr++ = cellRGBA[state];
1384                     } else {
1385                         *overlayptr++ = borderRGBA;
1386                     }
1387 
1388                     // update row position
1389                     x += dyy;
1390                 }
1391             }
1392         } else {
1393             // draw off grid row
1394             for (int w = 0; w < wd; w++) {
1395                 // draw pixel
1396                 *overlayptr++ = borderRGBA;
1397             }
1398         }
1399 
1400         // update column position
1401         sy += dyy;
1402         y = sy;
1403     }
1404 
1405     // draw grid lines if enabled
1406     if (grid && camzoom >= 4) {
1407         DrawGridLines();
1408     }
1409 
1410     // draw any layers
1411     if (theme) {
1412         double layerzoom = camzoom;
1413         int zoomlevel;
1414 
1415         for (int i = 1; i < layertarget; i++) {
1416             unsigned char transparenttarget = (i * ((aliveEnd + 1) / camlayers));
1417 
1418             // update brightness
1419             brightness += brightinc;
1420             GetThemeColors(brightness);
1421 
1422             // adjust zoom for next level
1423             dyy /= depth;
1424             layerzoom *= depth;
1425             cells = cellview;
1426             zoomlevel = 0;
1427             mask = ~0;
1428 
1429             // compute which zoomview level to use for this layer
1430             if (layerzoom < 0.125) {
1431                 zoomlevel = 8;
1432             } else {
1433                 if (layerzoom < 0.25) {
1434                     zoomlevel = 4;
1435                 } else {
1436                     if (layerzoom < 0.5) {
1437                         zoomlevel = 2;
1438                     } else {
1439                         if (layerzoom < 1) {
1440                             zoomlevel = 1;
1441                         }
1442                     }
1443                 }
1444             }
1445 
1446             // setup the mask for the zoom level
1447             if (zoomlevel > 0) {
1448                 mask = ~((zoomlevel << 1) - 1);
1449                 cells = zoomview + zoomlevel - 1;
1450             }
1451 
1452             sy = -((ht / 2) * dyy) + camy;
1453             sx = -((wd / 2) * dyy) + camx;
1454 
1455             overlayptr = (unsigned int *)pixmap;
1456 
1457             // draw each pixel
1458             y = sy;
1459             for (int h = 0; h < ht; h++) {
1460                 unsigned int iy = (((int)y) & mask);
1461 
1462                 // clip to the grid
1463                 if (iy < cellht) {
1464                     // get the row
1465                     rowptr = cells + cellwd * iy;
1466                     x = sx;
1467 
1468                     // offset if hex rule
1469                     if (ishex) {
1470                         x += 0.5 * (int)y;
1471                     }
1472 
1473                     // check if the whole row is on the grid
1474                     unsigned int ix = (((int)x) & mask);
1475                     unsigned int tx = (((int)(x + dyy * wd)) & mask);
1476                     if (ix < cellwd && tx < cellwd) {
1477                         for (int w = 0; w < wd; w++) {
1478                             ix = (((int)x) & mask);
1479                             state = rowptr[ix];
1480                             if (state >= transparenttarget) {
1481                                 *overlayptr = cellRGBA[state];
1482                             }
1483                             overlayptr++;
1484                             x += dyy;
1485                         }
1486                     } else {
1487                         for (int w = 0; w < wd; w++) {
1488                             // check if pixel is on the grid
1489                             ix = (((int)x) & mask);
1490                             if (ix < cellwd) {
1491                                 state = rowptr[ix];
1492                                 // check if it is transparent
1493                                 if (state >= transparenttarget) {
1494                                     // draw the pixel
1495                                     *overlayptr = cellRGBA[state];
1496                                 }
1497                             }
1498                             overlayptr++;
1499                             // update row position
1500                             x += dyy;
1501                         }
1502                     }
1503                 } else {
1504                     // draw off grid row
1505                     for (int w = 0; w < wd; w++) {
1506                         // draw pixel
1507                         *overlayptr++ = borderRGBA;
1508                     }
1509                 }
1510 
1511                 // update column position
1512                 sy += dyy;
1513                 y = sy;
1514             }
1515         }
1516     }
1517 }
1518 
1519 // -----------------------------------------------------------------------------
1520 
DoUpdateCells()1521 const char *Overlay::DoUpdateCells()
1522 {
1523     if (cellview == NULL) return OverlayError(no_cellview);
1524 
1525     // check if themes are used
1526     if (theme) {
1527         RefreshCellViewWithTheme();
1528     } else {
1529         RefreshCellView();
1530     }
1531 
1532     return NULL;
1533 }
1534 
1535 // -----------------------------------------------------------------------------
1536 
DrawVLine(int x,int y1,int y2,unsigned int color)1537 void Overlay::DrawVLine(int x, int y1, int y2, unsigned int color)
1538 {
1539     // check the line is on the display
1540     if (x < 0 || x >= wd) {
1541         return;
1542     }
1543 
1544     // clip the line to the display
1545     if (y1 < 0) {
1546         y1 = 0;
1547     } else {
1548         if (y1 >= ht) {
1549             y1 = ht - 1;
1550         }
1551     }
1552     if (y2 < 0) {
1553         y2 = 0;
1554     } else {
1555         if (y2 >= ht) {
1556             y2 = ht - 1;
1557         }
1558     }
1559 
1560     // ensure the y coordinates are in ascending order
1561     if (y1 > y2) {
1562         int temp = y1;
1563         y1 = y2;
1564         y2 = temp;
1565     }
1566 
1567     // get the starting pixel
1568     unsigned int *pix = ((unsigned int*)pixmap) + y1 * wd + x;
1569 
1570     // draw down the column
1571     while (y1 <= y2) {
1572         *pix = color;
1573         pix += wd;
1574         y1++;
1575     }
1576 }
1577 
1578 // -----------------------------------------------------------------------------
1579 
DrawHLine(int x1,int x2,int y,unsigned int color)1580 void Overlay::DrawHLine(int x1, int x2, int y, unsigned int color)
1581 {
1582     // check the line is on the display
1583     if (y < 0 || y >= ht) {
1584         return;
1585     }
1586 
1587     // clip the line to the display
1588     if (x1 < 0) {
1589         x1 = 0;
1590     } else {
1591         if (x1 >= wd) {
1592             x1 = wd - 1;
1593         }
1594     }
1595     if (x2 < 0) {
1596         x2 = 0;
1597     } else {
1598         if (x2 >= wd) {
1599             x2 = wd - 1;
1600         }
1601     }
1602 
1603     // ensure the x coordinates are in ascending order
1604     if (x1 > x2) {
1605         int temp = x1;
1606         x1 = x2;
1607         x2 = temp;
1608     }
1609 
1610     // get the starting pixel
1611     unsigned int *pix = ((unsigned int*)pixmap) + y * wd + x1;
1612 
1613     // draw along the row
1614     while (x1 <= x2) {
1615         *pix++ = color;
1616         x1++;
1617     }
1618 }
1619 
1620 // -----------------------------------------------------------------------------
1621 
DrawGridLines()1622 void Overlay::DrawGridLines()
1623 {
1624     double x, y;
1625     unsigned char shade;
1626     bool light = false;
1627 
1628     // check if background is light or dark
1629     unsigned char red, green, blue, alpha;
1630     GetRGBA(&red, &green, &blue, &alpha, cellRGBA[0]);
1631     if ((red + green + blue) / 3 >= 128) {
1632         light = true;
1633     }
1634 
1635     // check if custom grid line color is defined
1636     if (!customgridcolor) {
1637         // no custom grid color defined to base it on background color
1638         shade = light ? 229 : 80;
1639         SetRGBA(shade, shade, shade, 255, &gridRGBA);
1640     }
1641 
1642     // check if custom major grid line color is defined
1643     if (!customgridmajorcolor) {
1644         // no custom grid color defined to base it on background color
1645         shade = light ? 209 : 112;
1646         SetRGBA(shade, shade, shade, 255, &gridmajorRGBA);
1647     }
1648 
1649     // compute single cell offset
1650     double xoff = remainder(((cellwd / 2 - camx + 0.5) * camzoom) + (wd / 2), camzoom);
1651     double yoff = remainder(((cellht / 2 - camy + 0.5) * camzoom) + (ht / 2), camzoom);
1652 
1653     // draw twice if major grid lines enabled
1654     int loop = 1;
1655     if (gridmajor > 0) {
1656         loop = 2;
1657     }
1658 
1659     // start drawing the grid lines
1660     unsigned int targetRGBA = gridRGBA;
1661     unsigned int drawRGBA = gridRGBA;
1662     int gridlineNum;
1663     int vlineNum;
1664 
1665     while (loop) {
1666         // compute grid line vertical offset
1667         gridlineNum = floor(-(wd / 2 / camzoom) - (cellwd / 2 - camx));
1668 
1669         // draw vertical lines
1670         for (x = 0; x <= wd * camzoom; x += camzoom) {
1671             if (gridmajor > 0) {
1672                 // choose whether to use major or minor color
1673                 if (gridlineNum % gridmajor == 0) {
1674                     drawRGBA = gridmajorRGBA;
1675                 } else {
1676                     drawRGBA = gridRGBA;
1677                 }
1678             }
1679             gridlineNum++;
1680 
1681             // check whether to draw the line
1682             if (drawRGBA == targetRGBA) {
1683                 // check for hex display
1684                 if (ishex) {
1685                     vlineNum = (int)(-(ht / 2 / camzoom) - (cellht / 2 - camy));
1686 
1687                     // draw staggered vertical line
1688                     for (y = yoff - camzoom; y <= ht + camzoom; y += camzoom) {
1689                         if ((vlineNum & 1) != 0) {
1690                             DrawVLine(round(x + xoff + camzoom / 2), round(y + camzoom / 2), round(y + camzoom / 2 +  camzoom - 1), drawRGBA);
1691                         } else {
1692                             DrawVLine(round(x + xoff + camzoom), round(y + camzoom / 2), round(y + camzoom / 2 + camzoom - 1), drawRGBA);
1693                         }
1694                         vlineNum++;
1695                     }
1696                 } else {
1697                     DrawVLine(round(x + xoff + camzoom / 2), 0, ht - 1, drawRGBA);
1698                 }
1699             }
1700         }
1701 
1702         // compute grid line horizontal offset
1703         gridlineNum = (int)(-(ht / 2 / camzoom) - (cellht / 2 - camy));
1704 
1705         // draw horizontal lines
1706         for (y = 0; y <= ht + camzoom; y += camzoom) {
1707             if (gridmajor > 0) {
1708                 // choose whether to use major or minor color
1709                 if (gridlineNum % gridmajor == 0) {
1710                     drawRGBA = gridmajorRGBA;
1711                 } else {
1712                     drawRGBA = gridRGBA;
1713                 }
1714             }
1715             gridlineNum++;
1716 
1717             // check whether to draw the line
1718             if (drawRGBA == targetRGBA) {
1719                 DrawHLine(0, wd - 1, round(y + yoff + camzoom / 2), drawRGBA);
1720             }
1721         }
1722 
1723         // next iteration so switch to major color
1724         loop--;
1725         targetRGBA = gridmajorRGBA;
1726     }
1727 }
1728 
1729 // -----------------------------------------------------------------------------
1730 
CreateStars()1731 void Overlay::CreateStars()
1732 {
1733     int i;
1734     double curx, cury, curz;
1735 
1736     // allocate the stars
1737     if (starx == NULL) {
1738         starx = (double *)malloc(numStars * sizeof(*starx));
1739     }
1740     if (stary == NULL) {
1741         stary = (double *)malloc(numStars * sizeof(*stary));
1742     }
1743     if (starz == NULL) {
1744         starz = (double *)malloc(numStars * sizeof(*starz));
1745     }
1746 
1747     // compute radius^2 of the starfield
1748     int radius2 = (starMaxX * starMaxX) + (starMaxY * starMaxY);
1749     double id;
1750 
1751     // create random stars using fixed seed
1752     srand(52315);
1753 
1754     for (i = 0; i < numStars; i++) {
1755         // get the next z coordinate based on the star number
1756         // (more stars nearer the camera)
1757         id = (double)i;
1758         curz = ((id / numStars) * (id / numStars) * (id / numStars) * (id / numStars) * starMaxZ) + 1;
1759 
1760         // pick a random 2d position and ensure it is within the radius
1761         do {
1762             curx = 3 * ((((double)rand()) / RAND_MAX * starMaxX) - (starMaxX / 2));
1763             cury = 3 * ((((double)rand()) / RAND_MAX * starMaxY) - (starMaxY / 2));
1764         } while (((curx * curx) + (cury * cury)) > radius2);
1765 
1766         // save the star position
1767         starx[i] = curx;
1768         stary[i] = cury;
1769         starz[i] = curz;
1770     }
1771 
1772     // create random seed
1773     srand(time(0));
1774 }
1775 
1776 // -----------------------------------------------------------------------------
1777 
DrawStars(double angle)1778 void Overlay::DrawStars(double angle)
1779 {
1780     int offset;
1781     unsigned int *pixmapRGBA = (unsigned int*)pixmap;
1782 
1783     // get the unoccupied cell pixel color
1784     unsigned int blankRGBA = cellRGBA[0];
1785     unsigned char *blankCol = (unsigned char*)&blankRGBA;
1786     unsigned char blankR = *blankCol++;
1787     unsigned char blankG = *blankCol++;
1788     unsigned char blankB = *blankCol++;
1789 
1790     // get the star color components
1791     unsigned char *starCol = (unsigned char*)&starRGBA;
1792     unsigned char starR = *starCol++;
1793     unsigned char starG = *starCol++;
1794     unsigned char starB = *starCol++;
1795 
1796     unsigned int pixelRGBA;
1797     unsigned char *pixelCol = (unsigned char*)&pixelRGBA;
1798     pixelCol[3] = 255;
1799     unsigned char red, green, blue;
1800 
1801     // check if stars have been allocated
1802     if (starx == NULL) {
1803         CreateStars();
1804     }
1805 
1806     // update each star
1807     for (int i = 0; i < numStars; i++) {
1808         // get the 2d part of 3d position
1809         double x = starx[i] - camx;
1810         double y = stary[i] - camy;
1811 
1812         // check if angle is non zero
1813         if (angle != 0) {
1814             // compute radius
1815             double radius = sqrt((x * x) + (y * y));
1816 
1817             // get angle
1818             double theta = atan2(y, x) * radToDeg;
1819 
1820             // add current rotation
1821             theta += angle;
1822             if (theta < 0) {
1823                 theta += 360;
1824             } else {
1825                 if (theta >= 360) {
1826                     theta -= 360;
1827                 }
1828             }
1829 
1830             // compute rotated position
1831             x = radius * cos(theta * degToRad);
1832             y = radius * sin(theta * degToRad);
1833         }
1834 
1835         // create the 2d position
1836         double z = (starz[i] / camzoom) * 2;
1837         int ix = (int)(x / z) + wd / 2;
1838         int iy = (int)(y / z) + ht / 2;
1839 
1840         // check the star and halo are on the display
1841         if (ix > 0 && ix < (wd - 1) && iy > 0 && iy < (ht - 1)) {
1842             // compute the star brightness
1843             z = 1536 / z;
1844             if (z > 255) {
1845                 z = 255;
1846             }
1847             z = z / 255;
1848 
1849             // check if pixel is blank
1850             offset = ix + iy * wd;
1851             if (pixmapRGBA[offset] == blankRGBA) {
1852                 // compute the color components
1853                 red = blankR + (starR - blankR) * z;
1854                 green = blankG + (starG - blankG) * z;
1855                 blue = blankB + (starB - blankB) * z;
1856                 pixelCol[0] = red;
1857                 pixelCol[1] = green;
1858                 pixelCol[2] = blue;
1859                 pixmapRGBA[offset] = pixelRGBA;
1860             }
1861 
1862             // use a dimmer color for the halo
1863             red = blankR + (starR - blankR) * z;
1864             green = blankG + (starG - blankG) * z;
1865             blue = blankB + (starB - blankB) * z;
1866             pixelCol[0] = red;
1867             pixelCol[1] = green;
1868             pixelCol[2] = blue;
1869 
1870             // left halo
1871             offset -= 1;
1872             if (pixmapRGBA[offset] == blankRGBA) {
1873                 pixmapRGBA[offset] = pixelRGBA;
1874             }
1875 
1876             // right halo
1877             offset += 2;
1878             if (pixmapRGBA[offset] == blankRGBA) {
1879                 pixmapRGBA[offset] = pixelRGBA;
1880             }
1881 
1882             // top halo
1883             offset -= (1 + wd);
1884             if (pixmapRGBA[offset] == blankRGBA) {
1885                 pixmapRGBA[offset] = pixelRGBA;
1886             }
1887 
1888             // bottom halo
1889             offset += (wd + wd);
1890             if (pixmapRGBA[offset] == blankRGBA) {
1891                 pixmapRGBA[offset] = pixelRGBA;
1892             }
1893         }
1894     }
1895 }
1896 
1897 // -----------------------------------------------------------------------------
1898 
DoCellView(const char * args)1899 const char *Overlay::DoCellView(const char *args)
1900 {
1901     // check the arguments are valid
1902     int x, y, w, h;
1903     if (sscanf(args, " %d %d %d %d", &x, &y, &w, &h) != 4) {
1904         return OverlayError("cellview command requires 4 arguments");
1905     }
1906 
1907     if (w < cellviewmultiple) return OverlayError("width of cellview must be >= 16");
1908     if (h < cellviewmultiple) return OverlayError("height of cellview must be >= 16");
1909 
1910     if (w > cellviewmaxsize) return OverlayError("width of cellview too big");
1911     if (h > cellviewmaxsize) return OverlayError("height of cellview too big");
1912 
1913     if ((w & (cellviewmultiple - 1)) != 0) return OverlayError("width of cellview must be a multiple of 16");
1914     if ((h & (cellviewmultiple - 1)) != 0) return OverlayError("height of cellview  must be a multiple of 16");
1915 
1916     // delete any existing cellview
1917     DeleteCellView();
1918 
1919     // use calloc so all cells will be in state 0
1920     cellview = (unsigned char*) calloc(w * h, sizeof(*cellview));
1921     if (cellview == NULL) return OverlayError("not enough memory to create cellview");
1922     cellview1 = (unsigned char*) calloc(w * h, sizeof(*cellview1));
1923     if (cellview1 == NULL) return OverlayError("not enough memory to create cellview");
1924 
1925     // allocate the zoom view
1926     zoomview = (unsigned char*) calloc(w * h, sizeof(*zoomview));
1927     if (zoomview == NULL) return OverlayError("not enough memory to create cellview");
1928 
1929     // save the arguments
1930     cellwd = w;
1931     cellht = h;
1932     cellx = x;
1933     celly = y;
1934 
1935     // set the default camera position to the center
1936     camx = w / 2;
1937     camy = h / 2;
1938 
1939     // set default angle
1940     camangle = 0;
1941 
1942     // set default zoom
1943     camzoom = 1;
1944 
1945     // set default layers
1946     camlayers = 1;
1947     camlayerdepth = 0.05;
1948 
1949     // initialize ishex to false and let scripts use the hexrule() function
1950     // from the oplus package to determine if the current rule uses a
1951     // hexagonal neighborhood
1952     ishex = false;
1953 
1954     // use standard pattern colors
1955     theme = false;
1956 
1957     // disable grid and set default grid major interval
1958     grid = false;
1959     gridmajor = 10;
1960     customgridcolor = false;
1961     customgridmajorcolor = false;
1962 
1963     // disable stars and set star color to opaque whie
1964     stars = false;
1965     SetRGBA(255, 255, 255, 255, &starRGBA);
1966 
1967     // populate cellview
1968     DoUpdateCells();
1969 
1970     return NULL;
1971 }
1972 
1973 // -----------------------------------------------------------------------------
1974 
CamZoom(const char * args)1975 const char *Overlay::CamZoom(const char *args)
1976 {
1977     // check the argument is valid
1978     double zoom;
1979     if (sscanf(args, " %lf", &zoom) != 1) {
1980         return OverlayError("camera zoom command requires 1 argument");
1981     }
1982 
1983     if (zoom < camminzoom) return OverlayError("camera zoom too small");
1984     if (zoom > cammaxzoom) return OverlayError("camera zoom too big");
1985 
1986     // save the new value
1987     camzoom = zoom;
1988 
1989     return NULL;
1990 }
1991 
1992 // -----------------------------------------------------------------------------
1993 
CamAngle(const char * args)1994 const char *Overlay::CamAngle(const char *args)
1995 {
1996     // check the argument is valid
1997     double angle;
1998     if (sscanf(args, " %lf", &angle) != 1) {
1999         return OverlayError("camera angle command requires 1 argument");
2000     }
2001 
2002     if (angle < 0) return OverlayError("camera angle too small");
2003     if (angle > 360) return OverlayError("camera angle too big");
2004 
2005     // save the new value
2006     camangle = angle;
2007 
2008     return NULL;
2009 }
2010 
2011 // -----------------------------------------------------------------------------
2012 
CamXY(const char * args)2013 const char *Overlay::CamXY(const char *args)
2014 {
2015     // check the arguments are valid
2016     double x;
2017     double y;
2018     if (sscanf(args, " %lf %lf", &x, &y) != 2) {
2019         return OverlayError("camera xy command requires 2 arguments");
2020     }
2021 
2022     // save the new values
2023     camx = x;
2024     camy = y;
2025 
2026     return NULL;
2027 }
2028 
2029 // -----------------------------------------------------------------------------
2030 
DoCamera(const char * args)2031 const char *Overlay::DoCamera(const char *args)
2032 {
2033     if (cellview == NULL) return OverlayError(no_cellview);
2034 
2035     if (strncmp(args, "xy ", 3) == 0)    return CamXY(args+3);
2036     if (strncmp(args, "angle ", 6) == 0) return CamAngle(args+6);
2037     if (strncmp(args, "zoom ", 5) == 0)  return CamZoom(args+5);
2038 
2039     return OverlayError("unknown camera command");
2040 }
2041 
2042 // -----------------------------------------------------------------------------
2043 
CellOptionLayers(const char * args)2044 const char *Overlay::CellOptionLayers(const char *args)
2045 {
2046     // check the argument is valid
2047     int howmany;
2048 
2049     if (sscanf(args, " %d", &howmany) != 1) {
2050         return OverlayError("celloption layers command requires 1 argument");
2051     }
2052 
2053     if (howmany < 1) return OverlayError("celloption layers must be at least 1");
2054     if (howmany > 10) return OverlayError("celloption layers is too big");
2055 
2056     // save the new values
2057     camlayers = howmany;
2058 
2059     return NULL;
2060 }
2061 
2062 // -----------------------------------------------------------------------------
2063 
CellOptionDepth(const char * args)2064 const char *Overlay::CellOptionDepth(const char *args)
2065 {
2066     // check the argument is valid
2067     double depth;
2068     if (sscanf(args, " %lf", &depth) != 1) {
2069         return OverlayError("celloption depth command requires 1 argument");
2070     }
2071 
2072     if (depth < 0 || depth > 1) return OverlayError("celloption depth is out of range");
2073 
2074     // save the new values
2075     camlayerdepth = depth;
2076 
2077     return NULL;
2078 }
2079 
2080 // -----------------------------------------------------------------------------
2081 
CellOptionHex(const char * args)2082 const char *Overlay::CellOptionHex(const char *args)
2083 {
2084     int mode;
2085     if (sscanf(args, "%d", &mode) != 1) {
2086         return OverlayError("celloption hex command requires 1 argument");
2087     }
2088 
2089     ishex = mode == 1;
2090 
2091     return NULL;
2092 }
2093 
2094 // -----------------------------------------------------------------------------
2095 
CellOptionGrid(const char * args)2096 const char *Overlay::CellOptionGrid(const char *args)
2097 {
2098     int mode;
2099     if (sscanf(args, "%d", &mode) != 1) {
2100         return OverlayError("celloption grid command requires 1 argument");
2101     }
2102 
2103     grid = mode == 1;
2104 
2105     return NULL;
2106 }
2107 
2108 // -----------------------------------------------------------------------------
2109 
CellOptionGridMajor(const char * args)2110 const char *Overlay::CellOptionGridMajor(const char *args)
2111 {
2112     int major;
2113     if (sscanf(args, "%d", &major) != 1) {
2114         return OverlayError("celloption grid command requires 1 argument");
2115     }
2116 
2117     if (major < 0 || major > 16) return OverlayError("celloption major is out of range");
2118 
2119     gridmajor = major;
2120 
2121     return NULL;
2122 }
2123 
2124 // -----------------------------------------------------------------------------
2125 
CellOptionStars(const char * args)2126 const char *Overlay::CellOptionStars(const char *args)
2127 {
2128     int mode;
2129     if (sscanf(args, "%d", &mode) != 1) {
2130         return OverlayError("celloption stars command requires 1 argument");
2131     }
2132 
2133     stars = mode == 1;
2134 
2135     return NULL;
2136 }
2137 
2138 // -----------------------------------------------------------------------------
2139 
DoCellOption(const char * args)2140 const char *Overlay::DoCellOption(const char *args)
2141 {
2142     if (cellview == NULL) return OverlayError(no_cellview);
2143 
2144     if (strncmp(args, "hex ", 3) == 0)        return CellOptionHex(args+3);
2145     if (strncmp(args, "depth ", 6) == 0)      return CellOptionDepth(args+6);
2146     if (strncmp(args, "layers ", 7) == 0)     return CellOptionLayers(args+7);
2147     if (strncmp(args, "grid ", 5) == 0)       return CellOptionGrid(args+5);
2148     if (strncmp(args, "gridmajor ", 10) == 0) return CellOptionGridMajor(args+10);
2149     if (strncmp(args, "stars ", 6) == 0)      return CellOptionStars(args+6);
2150 
2151     return OverlayError("unknown celloption command");
2152 }
2153 
2154 // -----------------------------------------------------------------------------
2155 
DoTheme(const char * args)2156 const char *Overlay::DoTheme(const char *args)
2157 {
2158     if (cellview == NULL) return OverlayError(no_cellview);
2159 
2160     // check the arguments are valid
2161     int asr, asg, asb, aer, aeg, aeb, dsr, dsg, dsb, der, deg, deb, ur, ug, ub;
2162 
2163     // default alpha values to opaque
2164     int aa = 255;
2165     int da = 255;
2166     int ua = 255;
2167     int ba = 255;
2168 
2169     // whether theme is disabled
2170     int disable = 0;
2171 
2172     // argument count
2173     int count = 0;
2174 
2175     // check for 19 argument version
2176     count = sscanf(args, " %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
2177         &asr, &asg, &asb, &aer, &aeg, &aeb, &dsr, &dsg, &dsb, &der, &deg, &deb, &ur, &ug, &ub, &aa, &da, &ua, &ba);
2178     if (count != 19) {
2179         // check for 15 argument version
2180         if (count != 15) {
2181             // check for single argument version
2182             if (count == 1) {
2183                 disable = asr;
2184                 if (disable != -1) {
2185                     return OverlayError("theme command single argument must be -1");
2186                 }
2187             } else {
2188                 return OverlayError("theme command requires single argument -1, or 15 or 19 rgb components");
2189             }
2190         }
2191     }
2192 
2193     if (disable != -1) {
2194         if (asr < 0 || asr > 255 ||
2195             asg < 0 || asg > 255 ||
2196             asb < 0 || asb > 255 ) {
2197             return OverlayError("theme alivestart values must be from 0 to 255");
2198         }
2199         if (aer < 0 || aer > 255 ||
2200             aeg < 0 || aeg > 255 ||
2201             aeb < 0 || aeb > 255 ) {
2202             return OverlayError("theme aliveend values must be from 0 to 255");
2203         }
2204         if (dsr < 0 || dsr > 255 ||
2205             dsg < 0 || dsg > 255 ||
2206             dsb < 0 || dsb > 255 ) {
2207             return OverlayError("theme deadstart values must be from 0 to 255");
2208         }
2209         if (der < 0 || der > 255 ||
2210             deg < 0 || deg > 255 ||
2211             deb < 0 || deb > 255 ) {
2212             return OverlayError("theme deadend values must be from 0 to 255");
2213         }
2214         if (ur < 0 || ur > 255 ||
2215             ug < 0 || ug > 255 ||
2216             ub < 0 || ub > 255 ) {
2217             return OverlayError("theme unnocupied values must be from 0 to 255");
2218         }
2219         if (aa < 0 || aa > 255) {
2220             return OverlayError("theme alive alpha must be from 0 to 255");
2221         }
2222         if (da < 0 || da > 255) {
2223             return OverlayError("theme dead alpha must be from 0 to 255");
2224         }
2225         if (ua < 0 || ua > 255) {
2226             return OverlayError("theme unoccupied alpha must be from 0 to 255");
2227         }
2228         if (ba < 0 || ba > 255) {
2229             return OverlayError("theme border alpha must be from 0 to 255");
2230         }
2231     }
2232 
2233     // save the new values
2234     if (disable == -1) {
2235         theme = false;
2236     } else {
2237         theme = true;
2238         SetRGBA(asr, asg, asb, aa, &aliveStartRGBA);
2239         SetRGBA(aer, aeg, aeb, aa, &aliveEndRGBA);
2240         SetRGBA(dsr, dsg, dsb, da, &deadStartRGBA);
2241         SetRGBA(der, deg, deb, da, &deadEndRGBA);
2242         SetRGBA(ur, ug, ub, ua, &unoccupiedRGBA);
2243         bordera = ba;
2244     }
2245 
2246     return NULL;
2247 }
2248 
2249 // -----------------------------------------------------------------------------
2250 
SetRenderTarget(unsigned char * pix,int pwd,int pht,Clip * clip)2251 void Overlay::SetRenderTarget(unsigned char *pix, int pwd, int pht, Clip *clip)
2252 {
2253     pixmap = pix;
2254     wd = pwd;
2255     ht = pht;
2256     renderclip = clip;
2257 }
2258 
2259 // -----------------------------------------------------------------------------
2260 
DoResize(const char * args)2261 const char *Overlay::DoResize(const char *args)
2262 {
2263     if (pixmap == NULL) return OverlayError(no_overlay);
2264 
2265     // don't set wd and ht until we've checked the args are valid
2266     int w, h, oldw, oldh;
2267     bool isclip = false;
2268     int namepos;
2269     char dummy;
2270     if (sscanf(args, " %d %d %n%c", &w, &h, &namepos, &dummy) != 3) {
2271         if (sscanf(args, " %d %d", &w, &h) != 2) {
2272             return OverlayError("create command requires 2 or 3 arguments");
2273         }
2274     } else {
2275         isclip = true;
2276     }
2277 
2278     // check whether resizing clip or overlay
2279     if (isclip) {
2280         // resize clip
2281         if (w <= 0) return OverlayError("width of clip must be > 0");
2282         if (h <= 0) return OverlayError("height of clip must be > 0");
2283 
2284         // get clip name
2285         std::string name = args + namepos;
2286 
2287         // check if the clip exists
2288         std::map<std::string,Clip*>::iterator it;
2289         it = clips.find(name);
2290         if (it == clips.end()) {
2291             static std::string msg;
2292             msg = "unknown resize clip (";
2293             msg += name;
2294             msg += ")";
2295             return OverlayError(msg.c_str());
2296         }
2297 
2298         // get the clip dimensions
2299         oldw = it->second->cwd;
2300         oldh = it->second->cht;
2301 
2302         // delete the clip
2303         delete it->second;
2304         clips.erase(it);
2305 
2306         // allocate the resized clip with calloc
2307         Clip *newclip = new Clip(w, h, true);
2308         if (newclip == NULL || newclip->cdata == NULL) {
2309             delete newclip;
2310             return OverlayError("not enough memory to resize clip");
2311         }
2312 
2313         // save named clip
2314         clips[name] = newclip;
2315 
2316         // check if the clip is the render target
2317         if (targetname == name) {
2318             SetRenderTarget(newclip->cdata, newclip->cwd, newclip->cht, newclip);
2319         }
2320     } else {
2321         // resize overlay
2322         if (w <= 0) return OverlayError("width of overlay must be > 0");
2323         if (h <= 0) return OverlayError("height of overlay must be > 0");
2324 
2325         // given width and height are ok
2326         oldw = ovwd;
2327         oldh = ovht;
2328         ovwd = w;
2329         ovht = h;
2330 
2331         // free the previous pixmap
2332         free(ovpixmap);
2333 
2334         // create the new pixmap
2335         ovpixmap = (unsigned char*) calloc(ovwd * ovht * 4, sizeof(*ovpixmap));
2336         if (ovpixmap == NULL) return OverlayError("not enough memory to resize overlay");
2337 
2338         // check if overlay is the render target
2339         if (targetname == "") {
2340             SetRenderTarget(ovpixmap, ovwd, ovht, NULL);
2341         }
2342     }
2343 
2344     // return old dimensions
2345     static char result[32];
2346     sprintf(result, "%d %d", oldw, oldh);
2347     return result;
2348 }
2349 
2350 // -----------------------------------------------------------------------------
2351 
DoCreate(const char * args)2352 const char *Overlay::DoCreate(const char *args)
2353 {
2354     // don't set wd and ht until we've checked the args are valid
2355     int w, h;
2356     bool isclip = false;
2357     int namepos;
2358     char dummy;
2359     if (sscanf(args, " %d %d %n%c", &w, &h, &namepos, &dummy) != 3) {
2360         if (sscanf(args, " %d %d", &w, &h) != 2) {
2361             return OverlayError("create command requires 2 or 3 arguments");
2362         }
2363     } else {
2364         isclip = true;
2365     }
2366 
2367     // check whether creating clip or overlay
2368     if (isclip) {
2369         // create clip
2370         if (w <= 0) return OverlayError("width of clip must be > 0");
2371         if (h <= 0) return OverlayError("height of clip must be > 0");
2372 
2373         std::string name = args + namepos;
2374 
2375         // delete any existing clip data with the given name
2376         std::map<std::string,Clip*>::iterator it;
2377         it = clips.find(name);
2378         if (it != clips.end()) {
2379             delete it->second;
2380             clips.erase(it);
2381         }
2382 
2383         // allocate the clip with calloc
2384         Clip *newclip = new Clip(w, h, true);
2385         if (newclip == NULL || newclip->cdata == NULL) {
2386             delete newclip;
2387             return OverlayError("not enough memory to create clip");
2388         }
2389 
2390         // create named clip
2391         clips[name] = newclip;
2392     } else {
2393         // creating overlay
2394         if (w <= 0) return OverlayError("width of overlay must be > 0");
2395         if (h <= 0) return OverlayError("height of overlay must be > 0");
2396 
2397         // given width and height are ok
2398         ovwd = w;
2399         ovht = h;
2400 
2401         // delete any existing pixmap
2402         DeleteOverlay();
2403 
2404         // use calloc so all pixels will be 100% transparent (alpha = 0)
2405         ovpixmap = (unsigned char*) calloc(ovwd * ovht * 4, sizeof(*ovpixmap));
2406         if (ovpixmap == NULL) return OverlayError("not enough memory to create overlay");
2407 
2408         // initialize RGBA values to opaque white
2409         r = g = b = a = 255;
2410         SetRGBA(r, g, b, a, &rgbadraw);
2411 
2412         // don't do alpha blending initially
2413         alphablend = 0;
2414 
2415         only_draw_overlay = false;
2416 
2417         // initial position of overlay is in top left corner of current layer
2418         pos = topleft;
2419 
2420         ovcursor = wxSTANDARD_CURSOR;
2421         cursname = "arrow";
2422 
2423         // identity transform
2424         axx = 1;
2425         axy = 0;
2426         ayx = 0;
2427         ayy = 1;
2428         identity = true;
2429 
2430         // initialize current font used by text command
2431         currfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
2432         fontname = "default";
2433         fontsize = 10;
2434         #ifdef __WXMAC__
2435             // need to increase Mac font size by 25% to match text size on Win/Linux
2436             currfont.SetPointSize(int(fontsize * 1.25 + 0.5));
2437             extraht = 1;
2438         #else
2439             currfont.SetPointSize(fontsize);
2440         #endif
2441 
2442         // default text alignment
2443         align = left;
2444 
2445         // default text background
2446         textbgRGBA = 0;
2447 
2448         // default width for lines and ellipses
2449         linewidth = 1;
2450 
2451         // make sure the Show Overlay option is ticked
2452         if (!showoverlay) {
2453             mainptr->ToggleOverlay();
2454         } else {
2455             // enable Save Overlay
2456             mainptr->UpdateMenuItems();
2457         }
2458 
2459         // set overlay as render target
2460         SetRenderTarget(ovpixmap, ovwd, ovht, NULL);
2461         targetname = "";
2462     }
2463 
2464     return NULL;
2465 }
2466 
2467 // -----------------------------------------------------------------------------
2468 
PointInOverlay(int vx,int vy,int * ox,int * oy)2469 bool Overlay::PointInOverlay(int vx, int vy, int *ox, int *oy)
2470 {
2471     if (ovpixmap == NULL) return false;
2472 
2473     int viewwd, viewht;
2474     viewptr->GetClientSize(&viewwd, &viewht);
2475     if (viewwd <= 0 || viewht <= 0) return false;
2476 
2477     int x = 0;
2478     int y = 0;
2479     switch (pos) {
2480         case topleft:
2481             break;
2482         case topright:
2483             x = viewwd - ovwd;
2484             break;
2485         case bottomright:
2486             x = viewwd - ovwd;
2487             y = viewht - ovht;
2488             break;
2489         case bottomleft:
2490             y = viewht - ovht;
2491             break;
2492         case middle:
2493             x = (viewwd - ovwd) / 2;
2494             y = (viewht - ovht) / 2;
2495             break;
2496     }
2497 
2498     if (vx < x) return false;
2499     if (vy < y) return false;
2500     if (vx >= x + ovwd) return false;
2501     if (vy >= y + ovht) return false;
2502 
2503     *ox = vx - x;
2504     *oy = vy - y;
2505 
2506     return true;
2507 }
2508 
2509 // -----------------------------------------------------------------------------
2510 
DoPosition(const char * args)2511 const char *Overlay::DoPosition(const char *args)
2512 {
2513     if (pixmap == NULL) return OverlayError(no_overlay);
2514 
2515     if (strncmp(args+1, "topleft", 7) == 0) {
2516         pos = topleft;
2517 
2518     } else if (strncmp(args+1, "topright", 8) == 0) {
2519         pos = topright;
2520 
2521     } else if (strncmp(args+1, "bottomright", 11) == 0) {
2522         pos = bottomright;
2523 
2524     } else if (strncmp(args+1, "bottomleft", 10) == 0) {
2525         pos = bottomleft;
2526 
2527     } else if (strncmp(args+1, "middle", 6) == 0) {
2528         pos = middle;
2529 
2530     } else {
2531         return OverlayError("unknown position");
2532     }
2533 
2534     return NULL;
2535 }
2536 
2537 // -----------------------------------------------------------------------------
2538 
DecodeReplaceArg(const char * arg,int * find,bool * negfind,int * replace,int * invreplace,int * delta,int component)2539 const char *Overlay::DecodeReplaceArg(const char *arg, int *find, bool *negfind, int *replace, int *invreplace, int *delta, int component) {
2540     // argument is a string defining the find component value, optional replacement specification and
2541     // optional postfix value adjustment
2542     // find part is one of:
2543     //     *       (match any value)
2544     //     0..255  (match specific value)
2545     //     !0..255 (match values other than this value)
2546     // optional replacement part is one of:
2547     //     r       (replace with red component)
2548     //     g       (replace with green component)
2549     //     b       (replace with blue component)
2550     //     a       (replace with alpha component)
2551     //     #       (leave this component unchanged)
2552     // optional postfix is one of:
2553     //     -       (value should be inverted: v -> 255-v)
2554     //     --      (value should be decremented)
2555     //     ++      (value should be incremented)
2556     //     -0..255 (value should have constant subtracted from it)
2557     //     +0..255 (value should have constant added to it)
2558     char *p     = (char*)arg;
2559     *find       = 0;
2560     *negfind    = false;
2561     *replace    = 0;
2562     *invreplace = 0;
2563 
2564     // decode find
2565     if (*p == '*') {
2566         // match any
2567         *find = matchany;
2568         p++;
2569     } else {
2570         if (*p == '!') {
2571             // invert match
2572             *negfind = true;
2573             p++;
2574         }
2575         // read value
2576         while (*p >= '0' && *p <= '9') {
2577             *find = 10 * (*find) + *p - '0';
2578             p++;
2579         }
2580         if (*find < 0 || *find > 255) {
2581             return "replace argument is out of range";
2582         }
2583     }
2584 
2585     // decode optional replacement
2586     if (*p != '\0') {
2587         const char *valid = "rgba#";
2588         char *match = strchr((char*)valid, *p);
2589         if (match) {
2590             *replace = match - valid + 1;
2591             if (*replace == 5) *replace = component;
2592             p++;
2593         } else {
2594             if (*p != '-') return "replace argument postfix is invalid";
2595         }
2596         // check for invert, increment or decrement
2597         if (*p == '-') {
2598             p++;
2599             if (*p == '-') {
2600                 *delta = -1;
2601                 p++;
2602             } else {
2603                 if (*p >= '0' && *p <= '9') {
2604                     while (*p >= '0' && *p <= '9') {
2605                         *delta = 10 * (*delta) + *p - '0';
2606                         p++;
2607                     }
2608                     if (*delta < 0 || *delta > 255) {
2609                         return "replace delta is out of range";
2610                     }
2611                     *delta = -*delta;
2612                 } else {
2613                     *invreplace = 255;
2614                 }
2615             }
2616         } else {
2617             if (*p == '+') {
2618                 p++;
2619                 if (*p == '+') {
2620                     *delta = 1;
2621                     p++;
2622                 } else {
2623                     if (*p >= '0' && *p <= '9') {
2624                         while (*p >= '0' && *p <= '9') {
2625                             *delta = 10 * (*delta) + *p - '0';
2626                             p++;
2627                         }
2628                         if (*delta < 0 || *delta > 255) {
2629                             return "replace delta is out of range";
2630                         }
2631                     } else {
2632                         p--;
2633                     }
2634                 }
2635             }
2636         }
2637     }
2638 
2639     // any trailing characters are invalid
2640     if (*p != '\0') {
2641         return "replace argument postix is invalid";
2642     }
2643     return NULL;
2644 }
2645 
2646 // -----------------------------------------------------------------------------
2647 
DoReplace(const char * args)2648 const char *Overlay::DoReplace(const char *args)
2649 {
2650     if (pixmap == NULL) return OverlayError(no_overlay);
2651 
2652     // allocate memory for the arguments
2653     char *buffer = (char*)malloc(strlen(args) + 1);
2654     strcpy(buffer, args);
2655 
2656     // check the arguments exist
2657     const char *delim = " ";
2658     const char *arg1 = strtok(buffer, delim);
2659     const char *arg2 = strtok(NULL, delim);
2660     const char *arg3 = strtok(NULL, delim);
2661     const char *arg4 = strtok(NULL, delim);
2662     if (arg1 == NULL || arg2 == NULL || arg3 == NULL || arg4 == NULL) {
2663         free(buffer);
2664         return OverlayError("replace command requires 4 arguments");
2665     }
2666 
2667     // decode the r g b a arguments
2668     int findr = 0;
2669     int findg = 0;
2670     int findb = 0;
2671     int finda = 0;
2672     int replacer = 0;
2673     int replaceg = 0;
2674     int replaceb = 0;
2675     int replacea = 0;
2676     bool negr = false;
2677     bool negg = false;
2678     bool negb = false;
2679     bool nega = false;
2680     int invr = 0;
2681     int invg = 0;
2682     int invb = 0;
2683     int inva = 0;
2684     int deltar = 0;
2685     int deltag = 0;
2686     int deltab = 0;
2687     int deltaa = 0;
2688     const char *error = DecodeReplaceArg(arg1, &findr, &negr, &replacer, &invr, &deltar, 1);
2689     if (error) { free(buffer); return OverlayError(error); }
2690     error = DecodeReplaceArg(arg2, &findg, &negg, &replaceg, &invg, &deltag, 2);
2691     if (error) { free(buffer); return OverlayError(error); }
2692     error = DecodeReplaceArg(arg3, &findb, &negb, &replaceb, &invb, &deltab, 3);
2693     if (error) { free(buffer); return OverlayError(error); }
2694     error = DecodeReplaceArg(arg4, &finda, &nega, &replacea, &inva, &deltaa, 4);
2695     if (error) { free(buffer); return OverlayError(error); }
2696     free(buffer);
2697 
2698     // get the current render target
2699     unsigned char *clipdata = pixmap;
2700     const int w = wd;
2701     const int h = ht;
2702     const int allpixels = w * h;
2703 
2704     // check that negation is correctly used
2705     if (negg || negb || (nega && negr)) {
2706         return OverlayError("replace ! may only be at start or before alpha");
2707     }
2708 
2709     // mark target clip as changed
2710     DisableTargetClipIndex();
2711 
2712     unsigned char clipr, clipg, clipb, clipa;
2713     const int bytebits = 8;
2714     const int remainbits = 32 - bytebits;
2715 
2716     // count how many pixels are replaced
2717     int numchanged = 0;
2718     static char result[16];
2719 
2720     const bool allwild = (findr == matchany && findg == matchany && findb == matchany && finda == matchany);
2721     const bool zerodelta = (deltar == 0 && deltag == 0 && deltab == 0 && deltaa == 0);
2722     const bool zeroinv = (invr == 0 && invg == 0 && invb == 0 && inva == 0);
2723     const bool fixedreplace = (replacer == 0 && replaceg == 0 && replaceb == 0 && replacea == 0);
2724     const bool destreplace = (replacer == 1 && replaceg == 2 && replaceb == 3 && replacea == 4);
2725 
2726     // some specific common use cases are optimized for performance followed by the general purpose routine
2727     // the optimized versions are typically an order of magnitude faster
2728 
2729     // optimization case 1: fixed find and replace
2730     if ((findr != matchany && findg != matchany && findb != matchany && finda != matchany) &&
2731         fixedreplace && !nega && zeroinv && zerodelta) {
2732         // use 32 bit colors
2733         unsigned int *cdata = (unsigned int*)clipdata;
2734         unsigned int findcol = 0;
2735         const unsigned int replacecol = rgbadraw;
2736         SetRGBA(findr, findg, findb, finda, &findcol);
2737 
2738         // check for not equals case
2739         if (negr) {
2740             for (int i = 0; i < allpixels; i++) {
2741                 if (*cdata != findcol) {
2742                     *cdata = replacecol;
2743                     numchanged++;
2744                 }
2745                 cdata++;
2746             }
2747         } else {
2748             for (int i = 0; i < allpixels; i++) {
2749                 if (*cdata == findcol) {
2750                     *cdata = replacecol;
2751                     numchanged++;
2752                 }
2753                 cdata++;
2754             }
2755         }
2756 
2757         // return number of pixels replaced
2758         sprintf(result, "%d", numchanged);
2759         return result;
2760     }
2761 
2762     // optimization case 2: match pixels with different alpha and fixed replace
2763     if (zerodelta && fixedreplace && zeroinv && nega &&
2764         ((findr != matchany && findg != matchany && findb != matchany) || (findr == matchany && findg == matchany && findb == matchany))) {
2765         unsigned int *cdata = (unsigned int*)clipdata;
2766         const unsigned int replacecol = rgbadraw;
2767         if (findr != matchany) {
2768             // fixed match
2769             for (int i = 0; i < allpixels; i++) {
2770                 if (clipdata[0] == findr && clipdata[1] == findg && clipdata[2] == findb && clipdata[3] != finda) {
2771                     *cdata = replacecol;
2772                     numchanged++;
2773                 }
2774                 cdata++;
2775                 clipdata += 4;
2776             }
2777         } else {
2778             // r g b wildcard match
2779             clipdata += 3;
2780             for (int i = 0; i < allpixels; i++) {
2781                 if (*clipdata != finda) {
2782                     *cdata = replacecol;
2783                     numchanged++;
2784                 }
2785                 cdata++;
2786                 clipdata += 4;
2787             }
2788         }
2789 
2790         // return number of pixels replaced
2791         sprintf(result, "%d", numchanged);
2792         return result;
2793     }
2794 
2795     // optimization case 3: fill
2796     if (allwild && zerodelta && zeroinv && fixedreplace) {
2797         // fill clip with current RGBA
2798         unsigned int *cdata = (unsigned int*)clipdata;
2799         const unsigned int replacecol = rgbadraw;
2800         for (int i = 0; i < allpixels; i++) {
2801             if (*cdata != replacecol) {
2802                 *cdata = replacecol;
2803                 numchanged++;
2804             }
2805             cdata++;
2806         }
2807 
2808         // return number of pixels replaced
2809         sprintf(result, "%d", numchanged);
2810         return result;
2811     }
2812 
2813     // optimization case 4: no-op
2814     if (allwild && zerodelta && zeroinv && destreplace) {
2815         // return number of pixels replaced
2816         sprintf(result, "%d", numchanged);
2817         return result;
2818     }
2819 
2820     // optimization case 5: set constant alpha value on every pixel
2821     if (allwild && zerodelta && zeroinv &&
2822         (replacer == 1 && replaceg == 2 && replaceb == 3 && replacea == 0)) {
2823         // set alpha
2824         clipdata += 3;
2825         for (int i = 0; i < allpixels; i++) {
2826             if (*clipdata != a) {
2827                 *clipdata = a;
2828                 numchanged++;
2829             }
2830             clipdata += 4;
2831         }
2832 
2833         // return number of pixels replaced
2834         sprintf(result, "%d", numchanged);
2835         return result;
2836     }
2837 
2838     // optimization case 6: invert one or more rgba components
2839     if (allwild && zerodelta && !zeroinv && destreplace) {
2840         // invert specified components of every pixel
2841         unsigned int *cdata = (unsigned int*)clipdata;
2842         unsigned int invmask = 0;
2843         SetRGBA(invr, invg, invb, inva, &invmask);
2844 
2845         for (int i = 0; i < allpixels; i++) {
2846             *cdata = *cdata ^ invmask;
2847             cdata++;
2848         }
2849 
2850         // return number of pixels replaced
2851         numchanged = allpixels;
2852         sprintf(result, "%d", numchanged);
2853         return result;
2854     }
2855 
2856     // optimization case 7: offset only alpha value
2857     if (allwild && zeroinv && destreplace && deltar == 0 && deltag == 0 && deltab == 0 && deltaa != 0) {
2858         // offset alpha value of every pixel
2859         bool changed;
2860         int value, orig;
2861         unsigned int clamp;
2862 
2863         clipdata += 3;
2864         for (int i = 0; i < allpixels; i++) {
2865             changed = false;
2866             orig = *clipdata;
2867             value = orig + deltaa;
2868             clamp = value >> bytebits;
2869             if (clamp) { value = ~clamp >> remainbits; }
2870             changed = value != orig;
2871             if (changed) {
2872                 *clipdata = value;
2873                 numchanged++;
2874             }
2875             clipdata += 4;
2876         }
2877 
2878         // return number of pixels replaced
2879         sprintf(result, "%d", numchanged);
2880         return result;
2881     }
2882 
2883     // optimization case 8: offset one or more rgba components
2884     if (allwild && zeroinv && destreplace && !zerodelta) {
2885         // offset rgba values of every pixel
2886         bool changed;
2887         int value, orig;
2888         unsigned int clamp;
2889 
2890         for (int i = 0; i < allpixels; i++) {
2891             changed = false;
2892 
2893             // change r if required
2894             if (deltar) {
2895                 orig = clipdata[0];
2896                 value = orig + deltar;
2897                 clamp = value >> bytebits;
2898                 if (clamp) { value = ~clamp >> remainbits; }
2899                 changed = value != orig;
2900                 if (changed) {
2901                     clipdata[0] = value;
2902                 }
2903             }
2904             // change g if required
2905             if (deltag) {
2906                 orig = clipdata[1];
2907                 value = orig + deltag;
2908                 clamp = value >> bytebits;
2909                 if (clamp) { value = ~clamp >> remainbits; }
2910                 changed = value != orig;
2911                 if (changed) {
2912                     clipdata[1] = value;
2913                 }
2914             }
2915             // change b if required
2916             if (deltab) {
2917                 orig = clipdata[2];
2918                 value = orig + deltab;
2919                 clamp = value >> bytebits;
2920                 if (clamp) { value = ~clamp >> remainbits; }
2921                 changed = value != orig;
2922                 if (changed) {
2923                     clipdata[2] = value;
2924                 }
2925             }
2926             // change a if required
2927             if (deltaa) {
2928                 orig = clipdata[3];
2929                 value = orig + deltaa;
2930                 clamp = value >> bytebits;
2931                 if (clamp) { value = ~clamp >> remainbits; }
2932                 changed = value != orig;
2933                 if (changed) {
2934                     clipdata[3] = value;
2935                 }
2936             }
2937             // count number changed
2938             if (changed) {
2939                 numchanged++;
2940             }
2941             clipdata += 4;
2942         }
2943 
2944         // return number of pixels replaced
2945         sprintf(result, "%d", numchanged);
2946         return result;
2947     }
2948 
2949     // optimization case 9: convert RGBA to ABGR
2950     if (allwild && zeroinv && zerodelta && replacer == 4 && replaceg == 3 && replaceb == 2 && replacea == 1) {
2951         unsigned int *cdata = (unsigned int*)clipdata;
2952         unsigned int c;
2953         for (int i = 0; i < allpixels; i++) {
2954             c = *cdata;
2955             *cdata++ = BYTE2RED(ALPHA2BYTE(c)) | BYTE2GREEN(BLUE2BYTE(c)) | BYTE2BLUE(GREEN2BYTE(c)) | BYTE2ALPHA(RED2BYTE(c));
2956         }
2957 
2958         // return number changed
2959         numchanged = allpixels;
2960         sprintf(result, "%d", numchanged);
2961         return result;
2962     }
2963 
2964     // general case
2965     bool matchr, matchg, matchb, matcha, matchpixel;
2966     int value = 0;
2967     bool changed = false;
2968     unsigned int clamp;
2969     for (int i = 0; i < allpixels; i++) {
2970         // read the clip pixel
2971         clipr = clipdata[0];
2972         clipg = clipdata[1];
2973         clipb = clipdata[2];
2974         clipa = clipdata[3];
2975 
2976         // check if the pixel components match
2977         matchr = (findr == matchany) || (findr == clipr);
2978         matchg = (findg == matchany) || (findg == clipg);
2979         matchb = (findb == matchany) || (findb == clipb);
2980         matcha = (finda == matchany) || (finda == clipa);
2981 
2982         // check for negative components
2983         if (negr) {
2984             matchpixel = !(matchr && matchg && matchb && matcha);
2985         } else {
2986             if (nega) {
2987                 matchpixel = (matchr && matchg && matchb && !matcha);
2988             } else {
2989                 matchpixel = (matchr && matchg && matchb && matcha);
2990             }
2991         }
2992 
2993         // did pixel match
2994         changed = false;
2995         if (matchpixel) {
2996             // match made so process r component
2997             switch (replacer) {
2998                 case 0:
2999                     // use current RGBA r component
3000                     value = r ^ invr;
3001                     break;
3002                 case 1:
3003                     // use clip r component
3004                     value = clipr ^ invr;
3005                     break;
3006                 case 2:
3007                     // use clip g component
3008                     value = clipg ^ invr;
3009                     break;
3010                 case 3:
3011                     // use clip b component
3012                     value = clipb ^ invr;
3013                     break;
3014                 case 4:
3015                     // use clip a component
3016                     value = clipa ^ invr;
3017                     break;
3018             }
3019             if (deltar) {
3020                 value += deltar;
3021                 clamp = value >> bytebits;
3022                 if (clamp) { value = ~clamp >> remainbits; }
3023             }
3024             if (value != clipr) {
3025                 *clipdata = value;
3026                 changed = true;
3027             }
3028             clipdata++;
3029 
3030             // g component
3031             switch (replaceg) {
3032                 case 0:
3033                     // use current RGBA g component
3034                     value = g ^ invg;
3035                     break;
3036                 case 1:
3037                     // use clip r component
3038                     value = clipr ^ invg;
3039                     break;
3040                 case 2:
3041                     // use clip g component
3042                     value = clipg ^ invg;
3043                     break;
3044                 case 3:
3045                     // use clip b component
3046                     value = clipb ^ invg;
3047                     break;
3048                 case 4:
3049                     // use clip a component
3050                     value = clipa ^ invg;
3051                     break;
3052             }
3053             if (deltag) {
3054                 value += deltag;
3055                 clamp = value >> bytebits;
3056                 if (clamp) { value = ~clamp >> remainbits; }
3057             }
3058             if (value != clipg) {
3059                 *clipdata = value;
3060                 changed = true;
3061             }
3062             clipdata++;
3063 
3064             // b component
3065             switch (replaceb) {
3066                 case 0:
3067                     // use current RGBA b component
3068                     value = b ^ invb;
3069                     break;
3070                 case 1:
3071                     // use clip r component
3072                     value = clipr ^ invb;
3073                     break;
3074                 case 2:
3075                     // use clip g component
3076                     value = clipr ^ invb;
3077                     break;
3078                 case 3:
3079                     // use clip b component
3080                     value = clipb ^ invb;
3081                     break;
3082                 case 4:
3083                     // use clip a component
3084                     value = clipa ^ invb;
3085                     break;
3086             }
3087             if (deltab) {
3088                 value += deltab;
3089                 clamp = value >> bytebits;
3090                 if (clamp) { value = ~clamp >> remainbits; }
3091             }
3092             if (value != clipb) {
3093                 *clipdata = value;
3094                 changed = true;
3095             }
3096             clipdata++;
3097 
3098             // a component
3099             switch (replacea) {
3100                 case 0:
3101                     // use current RGBA a component
3102                     value = a ^ inva;
3103                     break;
3104                 case 1:
3105                     // use clip r component
3106                     value = clipr ^ inva;
3107                     break;
3108                 case 2:
3109                     // use clip g component
3110                     value = clipg ^ inva;
3111                     break;
3112                 case 3:
3113                     // use clip b component
3114                     value = clipb ^ inva;
3115                     break;
3116                 case 4:
3117                     // use clip a component
3118                     value = clipa ^ inva;
3119                     break;
3120             }
3121             if (deltaa) {
3122                 value += deltaa;
3123                 clamp = value >> bytebits;
3124                 if (clamp) { value = ~clamp >> remainbits; }
3125             }
3126             if (value != clipa) {
3127                 *clipdata = value;
3128                 changed = true;
3129             }
3130             clipdata++;
3131 
3132             // check if pixel changed
3133             if (changed) {
3134                 numchanged++;
3135             }
3136         } else {
3137             // no match so skip pixel
3138             clipdata += 4;
3139         }
3140     }
3141 
3142     // return number of pixels replaced
3143     sprintf(result, "%d", numchanged);
3144     return result;
3145 }
3146 
3147 // -----------------------------------------------------------------------------
3148 
DoSetRGBA(const char * cmd,lua_State * L,int n,int * nresults)3149 const char *Overlay::DoSetRGBA(const char *cmd, lua_State *L, int n, int *nresults)
3150 {
3151     if (pixmap == NULL) return OverlayError(no_overlay);
3152 
3153     // check if there are arguments
3154     int valid = false;
3155     int i = 2;
3156 
3157     if (n > 1) {
3158         // attempt to read the arguments
3159         lua_rawgeti(L, 1, i++);
3160         int a1 = (int)lua_tonumberx(L, -1, &valid);
3161         lua_pop(L, 1);
3162         if (!valid) return OverlayError("rgba command has illegal red argument");
3163         lua_rawgeti(L, 1, i++);
3164         int a2 = (int)lua_tonumberx(L, -1, &valid);
3165         lua_pop(L, 1);
3166         if (!valid) return OverlayError("rgba command has illegal green argument");
3167         lua_rawgeti(L, 1, i++);
3168         int a3 = (int)lua_tonumberx(L, -1, &valid);
3169         lua_pop(L, 1);
3170         if (!valid) return OverlayError("rgba command has illegal blue argument");
3171         lua_rawgeti(L, 1, i++);
3172         int a4 = (int)lua_tonumberx(L, -1, &valid);
3173         lua_pop(L, 1);
3174         if (!valid) return OverlayError("rgba command has illegal alpha argument");
3175 
3176         // validate argument range
3177         if (a1 < 0 || a1 > 255 ||
3178             a2 < 0 || a2 > 255 ||
3179             a3 < 0 || a3 > 255 ||
3180             a4 < 0 || a4 > 255) {
3181             return OverlayError("rgba values must be from 0 to 255");
3182         }
3183 
3184         // return the current rgba values as a table on the Lua stack
3185         // {"rgba", r, g, b, a}
3186         lua_newtable(L);
3187         i = 1;
3188         lua_pushstring(L, cmd);
3189         lua_rawseti(L, -2, i++);
3190         lua_pushinteger(L, r);
3191         lua_rawseti(L, -2, i++);
3192         lua_pushinteger(L, g);
3193         lua_rawseti(L, -2, i++);
3194         lua_pushinteger(L, b);
3195         lua_rawseti(L, -2, i++);
3196         lua_pushinteger(L, a);
3197         lua_rawseti(L, -2, i++);
3198         *nresults = 1;  // 1 result: the table
3199 
3200         // set the new values
3201         r = (unsigned char)a1;
3202         g = (unsigned char)a2;
3203         b = (unsigned char)a3;
3204         a = (unsigned char)a4;
3205         SetRGBA(r, g, b, a, &rgbadraw);
3206     } else {
3207         return OverlayError("rgba command requires 4 arguments");
3208     }
3209 
3210     return NULL;
3211 }
3212 
3213 // -----------------------------------------------------------------------------
3214 
DoSetRGBA(const char * args)3215 const char *Overlay::DoSetRGBA(const char *args)
3216 {
3217     if (pixmap == NULL) return OverlayError(no_overlay);
3218 
3219     int a1, a2, a3, a4;
3220     if (sscanf(args, " %d %d %d %d", &a1, &a2, &a3, &a4) != 4) {
3221         return OverlayError("rgba command requires 4 arguments");
3222     }
3223 
3224     if (a1 < 0 || a1 > 255 ||
3225         a2 < 0 || a2 > 255 ||
3226         a3 < 0 || a3 > 255 ||
3227         a4 < 0 || a4 > 255) {
3228         return OverlayError("rgba values must be from 0 to 255");
3229     }
3230 
3231     unsigned char oldr = r;
3232     unsigned char oldg = g;
3233     unsigned char oldb = b;
3234     unsigned char olda = a;
3235 
3236     r = (unsigned char) a1;
3237     g = (unsigned char) a2;
3238     b = (unsigned char) a3;
3239     a = (unsigned char) a4;
3240     SetRGBA(r, g, b, a, &rgbadraw);
3241 
3242     // return old values
3243     static char result[16];
3244     sprintf(result, "%hhu %hhu %hhu %hhu", oldr, oldg, oldb, olda);
3245     return result;
3246 }
3247 
3248 // -----------------------------------------------------------------------------
3249 
DrawPixel(int x,int y)3250 void Overlay::DrawPixel(int x, int y)
3251 {
3252     // caller must guarantee that pixel is within pixmap
3253     if (alphablend && a < 255) {
3254         // do nothing if source pixel is transparent
3255         if (a) {
3256             unsigned int *lp = ((unsigned int*)pixmap) + y * wd + x;
3257             const unsigned int alpha = a + 1;
3258             const unsigned int invalpha = 256 - a;
3259             const unsigned int dest = *lp;
3260             ALPHABLEND(rgbadraw, dest, lp, alpha, invalpha);
3261         }
3262     } else {
3263         unsigned int *lp = ((unsigned int*)pixmap) + y * wd + x;
3264         *lp = rgbadraw;
3265     }
3266 }
3267 
3268 // -----------------------------------------------------------------------------
3269 
GetCoordinatePair(char * args,int * x,int * y)3270 const char *Overlay::GetCoordinatePair(char *args, int *x, int *y)
3271 {
3272     // attempt to decode integers
3273     char c = *args++;
3274     bool sign = false;
3275     int newx = 0;
3276     int newy = 0;
3277 
3278     // skip whitespace
3279     while (c == ' ') {
3280         c = *args++;
3281     }
3282     if (!c) return NULL;
3283 
3284     // check for sign
3285     if (c == '-') {
3286         sign = true;
3287         c = *args++;
3288     }
3289     if (!c) return NULL;
3290 
3291     // read digits
3292     while (c >= '0' && c <= '9') {
3293         newx = 10 * newx + (c - '0');
3294         c = *args++;
3295     }
3296     if (sign) newx = -newx;
3297 
3298     // check for end of word
3299     if (c && c != ' ') return NULL;
3300 
3301     // skip whitespace
3302     while (c == ' ') {
3303         c = *args++;
3304     }
3305     if (!c) return NULL;
3306 
3307     // check for sign
3308     sign = false;
3309     if (c == '-') {
3310         sign = true;
3311         c = *args++;
3312     }
3313 
3314     // read digits
3315     while (c >= '0' && c <= '9') {
3316         newy = 10 * newy + (c - '0');
3317         c = *args++;
3318     }
3319     if (sign) newy = -newy;
3320 
3321     // check for end of word
3322     if (c && c != ' ') return NULL;
3323     while (c == ' ') {
3324         c = *args++;
3325     }
3326     args--;
3327 
3328     // return coordinates
3329     *x = newx;
3330     *y = newy;
3331     return args;
3332 }
3333 
3334 // -----------------------------------------------------------------------------
3335 
DoSetPixel(lua_State * L,int n,int * nresults)3336 const char *Overlay::DoSetPixel(lua_State *L, int n, int *nresults)
3337 {
3338     if (pixmap == NULL) return OverlayError(no_overlay);
3339 
3340     // check if there are arguments
3341     // note: it is possible that n > 1 and arguments have nil values
3342     int valid = false;
3343     int i = 2;
3344     int type = -1;
3345 
3346     // mark target clip as changed
3347     DisableTargetClipIndex();
3348 
3349     // check for alpha blending
3350     if (alphablend && a < 255) {
3351         // use alpha blending
3352         // do nothing if source pixel is transparent
3353         if (a) {
3354             // compute pixel alpha
3355             const unsigned int alpha = a + 1;
3356             const unsigned int invalpha = 256 - a;
3357             const unsigned int sourcearb = alpha * RBRIGHT(rgbadraw & RBMASK);
3358             const unsigned int sourceag =  alpha *        (rgbadraw & GMASK);
3359             if (alphablend == 1) {
3360                 // full alpha blend
3361                 do {
3362                     // get next pixel coordinate
3363                     lua_rawgeti(L, 1, i++);
3364                     int x = (int)lua_tonumberx(L, -1, &valid);
3365                     if (!valid) break;
3366                     lua_pop(L, 1);
3367                     lua_rawgeti(L, 1, i++);
3368                     int y = (int)lua_tonumberx(L, -1, &valid);
3369                     if (!valid) break;
3370                     lua_pop(L, 1);
3371 
3372                     // ignore pixel if outside pixmap edges
3373                     if (PixelInTarget(x, y)) {
3374                         unsigned int *lp = ((unsigned int*)pixmap) + y * wd + x;
3375                         const unsigned int dest = *lp;
3376                         ALPHABLENDPRE(rgbadraw, sourcearb, sourceag, dest, lp, alpha, invalpha);
3377                     }
3378                 } while (i <= n);
3379             } else {
3380                 // fast alpha blend (opaque destination)
3381                 do {
3382                     // get next pixel coordinate
3383                     lua_rawgeti(L, 1, i++);
3384                     int x = (int)lua_tonumberx(L, -1, &valid);
3385                     if (!valid) break;
3386                     lua_pop(L, 1);
3387                     lua_rawgeti(L, 1, i++);
3388                     int y = (int)lua_tonumberx(L, -1, &valid);
3389                     if (!valid) break;
3390                     lua_pop(L, 1);
3391 
3392                     // ignore pixel if outside pixmap edges
3393                     if (PixelInTarget(x, y)) {
3394                         unsigned int *lp = ((unsigned int*)pixmap) + y * wd + x;
3395                         const unsigned int dest = *lp;
3396                         ALPHABLENDPREOPAQUEDEST(sourcearb, sourceag, dest, lp, invalpha);
3397                     }
3398                 } while (i <= n);
3399             }
3400 
3401             // check if loop terminated because of failed number conversion
3402             if (!valid) {
3403                 // get the type of the argument
3404                 type = lua_type(L, -1);
3405                 lua_pop(L, 1);
3406             }
3407         }
3408     } else {
3409         // use fast copy
3410         unsigned int rgba = rgbadraw;
3411         unsigned int *lpixmap = (unsigned int*)pixmap;
3412         do {
3413             // get next pixel coordinate
3414             lua_rawgeti(L, 1, i++);
3415             int x = (int)lua_tonumberx(L, -1, &valid);
3416             if (!valid) break;
3417             lua_pop(L, 1);
3418             lua_rawgeti(L, 1, i++);
3419             int y = (int)lua_tonumberx(L, -1, &valid);
3420             if (!valid) break;
3421             lua_pop(L, 1);
3422 
3423             // ignore pixel if outside pixmap edges
3424             if (PixelInTarget(x, y)) {
3425                 *(lpixmap + y*wd + x) = rgba;
3426             }
3427         } while (i <= n);
3428 
3429         // check if loop terminated because of failed number conversion
3430         if (!valid) {
3431             // get the type of the argument
3432             type = lua_type(L, -1);
3433             lua_pop(L, 1);
3434         }
3435     }
3436 
3437     // check if there were errors
3438     if (!valid) {
3439         // check if the argument number is a multiple of 2 and the argument is nil
3440         if ((((i - 3) & 1) == 0) && (type == LUA_TNIL)) {
3441             // command was valid
3442             valid = true;
3443         }
3444     }
3445 
3446     if (!valid) {
3447         // return appropriate error message
3448         switch ((i - 3) & 1) {
3449             case 0:
3450                 return OverlayError("set command has illegal x");
3451                 break;
3452             case 1:
3453                 return OverlayError("set command has illegal y");
3454                 break;
3455         }
3456     }
3457     return NULL;
3458 }
3459 
3460 // -----------------------------------------------------------------------------
3461 
DoSetPixel(const char * args)3462 const char *Overlay::DoSetPixel(const char *args)
3463 {
3464     if (pixmap == NULL) return OverlayError(no_overlay);
3465 
3466     int x = 0;
3467     int y = 0;
3468     // get the first pixel coordinates (mandatory)
3469     args = GetCoordinatePair((char*)args, &x, &y);
3470     if (!args) return OverlayError("set command requires coordinate pairs");
3471 
3472     // mark target clip as changed
3473     DisableTargetClipIndex();
3474 
3475     // ignore pixel if outside pixmap edges
3476     if (PixelInTarget(x, y)) DrawPixel(x, y);
3477 
3478     // read any further coordinates
3479     while (*args) {
3480         args = GetCoordinatePair((char*)args, &x, &y);
3481         if (!args) return OverlayError("set command has illegal coordinates");
3482         if (PixelInTarget(x, y)) DrawPixel(x, y);
3483     }
3484 
3485     return NULL;
3486 }
3487 
3488 // -----------------------------------------------------------------------------
3489 
DoGet(lua_State * L,int n,int * nresults)3490 const char *Overlay::DoGet(lua_State *L, int n, int *nresults)
3491 {
3492     if (pixmap == NULL) return "";
3493 
3494     // check if there are arguments
3495     int valid = false;
3496     int i = 2;
3497 
3498     if (n > 1) {
3499         // attempt to read the arguments
3500         lua_rawgeti(L, 1, i++);
3501         int x = (int)lua_tonumberx(L, -1, &valid);
3502         lua_pop(L,1);
3503         if (!valid) return OverlayError("get command has illegal x argument");
3504         lua_rawgeti(L, 1, i++);
3505         int y = (int)lua_tonumberx(L, -1, &valid);
3506         lua_pop(L,1);
3507         if (!valid) return OverlayError("get command has illegal y argument");
3508 
3509         // check if x,y is outside pixmap
3510         if (!PixelInTarget(x, y)) {
3511             // return -1 for all components to indicate outside pixmap
3512             lua_pushinteger(L, -1);
3513             lua_pushinteger(L, -1);
3514             lua_pushinteger(L, -1);
3515             lua_pushinteger(L, -1);
3516         } else {
3517             // get and return the pixel rgba values
3518             unsigned char *p = pixmap + y*wd*4 + x*4;
3519             lua_pushinteger(L, p[0]);
3520             lua_pushinteger(L, p[1]);
3521             lua_pushinteger(L, p[2]);
3522             lua_pushinteger(L, p[3]);
3523         }
3524         *nresults = 4;
3525     } else {
3526         return OverlayError("get command requires 2 arguments");
3527     }
3528     return NULL;
3529 }
3530 
3531 // -----------------------------------------------------------------------------
3532 
DoGetPixel(const char * args)3533 const char *Overlay::DoGetPixel(const char *args)
3534 {
3535     if (pixmap == NULL) return "";
3536 
3537     int x, y;
3538     if (sscanf(args, "%d %d", &x, &y) != 2) {
3539         return OverlayError("get command requires 2 arguments");
3540     }
3541 
3542     // check if x,y is outside pixmap
3543     if (!PixelInTarget(x, y)) return "";
3544 
3545     unsigned char *p = pixmap + y*wd*4 + x*4;
3546     static char result[16];
3547     sprintf(result, "%hhu %hhu %hhu %hhu", p[0], p[1], p[2], p[3]);
3548     return result;
3549 }
3550 
3551 // -----------------------------------------------------------------------------
3552 
TransparentPixel(int x,int y)3553 bool Overlay::TransparentPixel(int x, int y)
3554 {
3555     if (ovpixmap == NULL) return false;
3556 
3557     // check if x,y is outside pixmap
3558     if (!PixelInOverlay(x, y)) return false;
3559 
3560     unsigned char *p = ovpixmap + y*ovwd*4 + x*4;
3561 
3562     // return true if alpha value is 0
3563     return p[3] == 0;
3564 }
3565 
3566 // -----------------------------------------------------------------------------
3567 
SetOverlayCursor()3568 void Overlay::SetOverlayCursor()
3569 {
3570     if (cursname == "current") {
3571         // currlayer->curs might have changed
3572         ovcursor = currlayer->curs;
3573     }
3574     viewptr->SetCursor(*ovcursor);
3575 }
3576 
3577 // -----------------------------------------------------------------------------
3578 
DoCursor(const char * args)3579 const char *Overlay::DoCursor(const char *args)
3580 {
3581     if (pixmap == NULL) return OverlayError(no_overlay);
3582 
3583     if (strncmp(args+1, "arrow", 5) == 0) {
3584         ovcursor = wxSTANDARD_CURSOR;
3585 
3586     } else if (strncmp(args+1, "current", 7) == 0) {
3587         ovcursor = currlayer->curs;
3588 
3589     } else if (strncmp(args+1, "pencil", 6) == 0) {
3590         ovcursor = curs_pencil;
3591 
3592     } else if (strncmp(args+1, "pick", 4) == 0) {
3593         ovcursor = curs_pick;
3594 
3595     } else if (strncmp(args+1, "cross", 5) == 0) {
3596         ovcursor = curs_cross;
3597 
3598     } else if (strncmp(args+1, "hand", 4) == 0) {
3599         ovcursor = curs_hand;
3600 
3601     } else if (strncmp(args+1, "zoomin", 6) == 0) {
3602         ovcursor = curs_zoomin;
3603 
3604     } else if (strncmp(args+1, "zoomout", 7) == 0) {
3605         ovcursor = curs_zoomout;
3606 
3607     } else if (strncmp(args+1, "wait", 4) == 0) {
3608         ovcursor = curs_wait;
3609 
3610     } else if (strncmp(args+1, "hidden", 6) == 0) {
3611         ovcursor = curs_hidden;
3612 
3613     } else {
3614         return OverlayError("unknown cursor");
3615     }
3616 
3617     std::string oldcursor = cursname;
3618     cursname = args+1;
3619 
3620     viewptr->CheckCursor(mainptr->infront);
3621 
3622     // return old cursor name
3623     static std::string result;
3624     result = oldcursor;
3625     return result.c_str();
3626 }
3627 
3628 // -----------------------------------------------------------------------------
3629 
CheckCursor()3630 void Overlay::CheckCursor()
3631 {
3632     // the cursor needs to be checked if the pixmap data has changed, but that's
3633     // highly likely if we call this routine at the end of DrawOverlay
3634     viewptr->CheckCursor(mainptr->infront);
3635 }
3636 
3637 // -----------------------------------------------------------------------------
3638 
DoGetXY()3639 const char *Overlay::DoGetXY()
3640 {
3641     if (pixmap == NULL) return "";
3642     if (!mainptr->infront) return "";
3643 
3644     wxPoint pt = viewptr->ScreenToClient( wxGetMousePosition() );
3645 
3646     int ox, oy;
3647     if (PointInOverlay(pt.x, pt.y, &ox, &oy)) {
3648         static char result[32];
3649         sprintf(result, "%d %d", ox, oy);
3650         return result;
3651     } else {
3652         return "";
3653     }
3654 }
3655 
3656 // -----------------------------------------------------------------------------
3657 
LineOptionWidth(const char * args)3658 const char *Overlay::LineOptionWidth(const char *args)
3659 {
3660     int w, oldwidth;
3661     if (sscanf(args, " %d", &w) != 1) {
3662         return OverlayError("lineoption width command requires 1 argument");
3663     }
3664 
3665     if (w < 1) return OverlayError("line width must be > 0");
3666     if (w > 10000) return OverlayError("line width must be <= 10000");
3667 
3668     oldwidth = linewidth;
3669     linewidth = w;
3670 
3671     static char result[32];
3672     sprintf(result, "%d", oldwidth);
3673     return result;
3674 }
3675 
3676 // -----------------------------------------------------------------------------
3677 
DoLineOption(const char * args)3678 const char *Overlay::DoLineOption(const char *args)
3679 {
3680     if (pixmap == NULL) return OverlayError(no_overlay);
3681 
3682     if (strncmp(args, "width ", 6) == 0) return LineOptionWidth(args+6);
3683 
3684     return OverlayError("unknown lineoption command");
3685 }
3686 
3687 // -----------------------------------------------------------------------------
3688 
DrawAAPixel(int x,int y,double opac)3689 void Overlay::DrawAAPixel(int x, int y, double opac)
3690 {
3691     if (PixelInTarget(x, y)) {
3692         unsigned char newalpha = 255-int(opac);
3693         if (newalpha == 0) return;
3694 
3695         if (!alphablend) {
3696             // only true in DrawThickEllipse
3697             if (newalpha > 127) {
3698                 // don't adjust current alpha value
3699                 DrawPixel(x, y);
3700             }
3701             return;
3702         }
3703 
3704         // temporarily adjust current alpha value
3705         unsigned char olda = a;
3706         if (a < 255) newalpha = int(newalpha * a / 255);
3707         a = newalpha;
3708 
3709         DrawPixel(x, y);
3710 
3711         // restore alpha
3712         a = olda;
3713     }
3714 }
3715 
3716 // -----------------------------------------------------------------------------
3717 
3718 // need an adjustment for thick lines when linewidth is an even number
3719 static double even_w;
3720 
PerpendicularX(int x0,int y0,int dx,int dy,int xstep,int ystep,int einit,int winit,double w,double D2)3721 void Overlay::PerpendicularX(int x0, int y0, int dx, int dy, int xstep, int ystep,
3722                              int einit, int winit, double w, double D2)
3723 {
3724     // dx > dy
3725     int threshold = dx - 2*dy;
3726     int E_diag = -2*dx;
3727     int E_square = 2*dy;
3728     int x = x0;
3729     int y = y0;
3730     int err = einit;
3731     int tk = dx+dy-winit;
3732 
3733     // draw top/bottom half of line
3734     int q = 0;
3735     while (tk <= even_w) {
3736         if (alphablend) {
3737             double alfa = 255 * (w - tk) / D2;
3738             if (alfa < 255) {
3739                 if (even_w != w) alfa = 128;
3740                 DrawAAPixel(x, y, 255-alfa);
3741             } else {
3742                 if (PixelInTarget(x, y)) DrawPixel(x, y);
3743             }
3744         } else {
3745             if (PixelInTarget(x, y)) DrawPixel(x, y);
3746         }
3747         if (err >= threshold) {
3748             x += xstep;
3749             err += E_diag;
3750             tk += 2*dy;
3751         }
3752         err += E_square;
3753         y += ystep;
3754         tk += 2*dx;
3755         q++;
3756     }
3757 
3758     y = y0;
3759     x = x0;
3760     err = -einit;
3761     tk = dx+dy+winit;
3762 
3763     // draw other half of line
3764     int p = 0;
3765     while (tk <= w) {
3766         if (p > 0) {
3767             if (alphablend) {
3768                 double alfa = 255 * (w - tk) / D2;
3769                 if (alfa < 255) {
3770                     if (even_w != w) alfa = 128;
3771                     DrawAAPixel(x, y, 255-alfa);
3772                 } else {
3773                     if (PixelInTarget(x, y)) DrawPixel(x, y);
3774                 }
3775             } else {
3776                 if (PixelInTarget(x, y)) DrawPixel(x, y);
3777             }
3778         }
3779         if (err > threshold) {
3780             x -= xstep;
3781             err += E_diag;
3782             tk += 2*dy;
3783         }
3784         err += E_square;
3785         y -= ystep;
3786         tk += 2*dx;
3787         p++;
3788     }
3789 
3790     if (q == 0 && p < 2) {
3791         // needed for very thin lines
3792         if (PixelInTarget(x0, y0)) DrawPixel(x0, y0);
3793     }
3794 }
3795 
3796 // -----------------------------------------------------------------------------
3797 
PerpendicularY(int x0,int y0,int dx,int dy,int xstep,int ystep,int einit,int winit,double w,double D2)3798 void Overlay::PerpendicularY(int x0, int y0, int dx, int dy, int xstep, int ystep,
3799                              int einit, int winit, double w, double D2)
3800 {
3801     // dx <= dy
3802     int threshold = dy - 2*dx;
3803     int E_diag = -2*dy;
3804     int E_square = 2*dx;
3805     int x = x0;
3806     int y = y0;
3807     int err = -einit;
3808     int tk = dx+dy+winit;
3809 
3810     // draw left/right half of line
3811     int q = 0;
3812     while (tk <= w) {
3813         if (alphablend) {
3814             double alfa = 255 * (w - tk) / D2;
3815             if (alfa < 255) {
3816                 if (even_w != w) alfa = 128;
3817                 DrawAAPixel(x, y, 255-alfa);
3818             } else {
3819                 if (PixelInTarget(x, y)) DrawPixel(x, y);
3820             }
3821         } else {
3822             if (PixelInTarget(x, y)) DrawPixel(x, y);
3823         }
3824         if (err > threshold) {
3825             y += ystep;
3826             err += E_diag;
3827             tk += 2*dx;
3828         }
3829         err += E_square;
3830         x += xstep;
3831         tk += 2*dy;
3832         q++;
3833     }
3834 
3835     y = y0;
3836     x = x0;
3837     err = einit;
3838     tk = dx+dy-winit;
3839 
3840     // draw other half of line
3841     int p = 0;
3842     while (tk <= even_w) {
3843         if (p > 0) {
3844             if (alphablend) {
3845                 double alfa = 255 * (w - tk) / D2;
3846                 if (alfa < 255) {
3847                     if (even_w != w) alfa = 128;
3848                     DrawAAPixel(x, y, 255-alfa);
3849                 } else {
3850                     if (PixelInTarget(x, y)) DrawPixel(x, y);
3851                 }
3852             } else {
3853                 if (PixelInTarget(x, y)) DrawPixel(x, y);
3854             }
3855         }
3856         if (err >= threshold) {
3857             y -= ystep;
3858             err += E_diag;
3859             tk += 2*dx;
3860         }
3861         err += E_square;
3862         x -= xstep;
3863         tk += 2*dy;
3864         p++;
3865     }
3866 
3867     if (q == 0 && p < 2) {
3868         // needed for very thin lines
3869         if (PixelInTarget(x0, y0)) DrawPixel(x0, y0);
3870     }
3871 }
3872 
3873 // -----------------------------------------------------------------------------
3874 
DrawThickLine(int x0,int y0,int x1,int y1)3875 void Overlay::DrawThickLine(int x0, int y0, int x1, int y1)
3876 {
3877     // based on code from http://kt8216.unixcab.org/murphy/index.html
3878 
3879     // following code fixes alignment problems when linewidth is an even number
3880     if (x0 > x1) {
3881         // swap starting and end points so we always draw lines from left to right
3882         int tempx = x0; x0 = x1; x1 = tempx;
3883         int tempy = y0; y0 = y1; y1 = tempy;
3884     } else if (x0 == x1 && y0 > y1) {
3885         // swap y coords so vertical lines are always drawn from top to bottom
3886         int tempy = y0; y0 = y1; y1 = tempy;
3887     }
3888 
3889     int dx = x1 - x0;
3890     int dy = y1 - y0;
3891     int xstep = 1;
3892     int ystep = 1;
3893     int pxstep = 0;
3894     int pystep = 0;
3895 
3896     if (dx < 0) { dx = -dx; xstep = -1; }
3897     if (dy < 0) { dy = -dy; ystep = -1; }
3898 
3899     if (dx == 0 && dy == 0) {
3900         if (PixelInTarget(x0, y0)) DrawPixel(x0, y0);
3901         return;
3902     }
3903 
3904     if (dx == 0) xstep = 0;
3905     if (dy == 0) ystep = 0;
3906 
3907     switch (xstep + ystep*4) {
3908         case -1 + -1*4 : pystep = -1; pxstep =  1; break;   // -5
3909         case -1 +  0*4 : pystep = -1; pxstep =  0; break;   // -1
3910         case -1 +  1*4 : pystep =  1; pxstep =  1; break;   //  3
3911         case  0 + -1*4 : pystep =  0; pxstep = -1; break;   // -4
3912         case  0 +  0*4 : pystep =  0; pxstep =  0; break;   //  0
3913         case  0 +  1*4 : pystep =  0; pxstep =  1; break;   //  4
3914         case  1 + -1*4 : pystep = -1; pxstep = -1; break;   // -3
3915         case  1 +  0*4 : pystep = -1; pxstep =  0; break;   //  1
3916         case  1 +  1*4 : pystep =  1; pxstep = -1; break;   //  5
3917     }
3918 
3919     double D = sqrt(double(dx*dx + dy*dy));
3920     double D2 = 2*D;
3921     double w = (linewidth + 1) * D;
3922 
3923     // need to reduce thickness of line if linewidth is an even number
3924     // and line is vertical or horizontal
3925     if (linewidth % 2 == 0 && (dx == 0 || dy == 0)) {
3926         even_w = linewidth * D;
3927     } else {
3928         even_w = w;
3929     }
3930 
3931     // this hack is needed to improve antialiased sloped lines of width 2
3932     if (alphablend && linewidth == 2 && dx != 0 && dy != 0) {
3933         even_w = (linewidth + 1.75) * D;
3934         w = even_w;
3935     }
3936 
3937     int p_error = 0;
3938     int err = 0;
3939     int x = x0;
3940     int y = y0;
3941 
3942     if (dx > dy) {
3943         int threshold = dx - 2*dy;
3944         int E_diag = -2*dx;
3945         int E_square = 2*dy;
3946         int length = dx + 1;
3947         for (int p = 0; p < length; p++) {
3948             PerpendicularX(x, y, dx, dy, pxstep, pystep, p_error, err, w, D2);
3949             if (err >= threshold) {
3950                 y += ystep;
3951                 err += E_diag;
3952                 if (p_error >= threshold) {
3953                     p_error += E_diag;
3954                     PerpendicularX(x, y, dx, dy, pxstep, pystep, p_error+E_square, err, w, D2);
3955                 }
3956                 p_error += E_square;
3957             }
3958             err += E_square;
3959             x += xstep;
3960         }
3961     } else {
3962         int threshold = dy - 2*dx;
3963         int E_diag = -2*dy;
3964         int E_square = 2*dx;
3965         int length = dy + 1;
3966         for (int p = 0; p < length; p++) {
3967             PerpendicularY(x, y, dx, dy, pxstep, pystep, p_error, err, w, D2);
3968             if (err >= threshold) {
3969                 x += xstep;
3970                 err += E_diag;
3971                 if (p_error >= threshold) {
3972                     p_error += E_diag;
3973                     PerpendicularY(x, y, dx, dy, pxstep, pystep, p_error+E_square, err, w, D2);
3974                 }
3975                 p_error += E_square;
3976             }
3977             err += E_square;
3978             y += ystep;
3979         }
3980     }
3981 }
3982 
3983 // -----------------------------------------------------------------------------
3984 
DrawAntialiasedLine(int x0,int y0,int x1,int y1)3985 void Overlay::DrawAntialiasedLine(int x0, int y0, int x1, int y1)
3986 {
3987     // based on code from http://members.chello.at/~easyfilter/bresenham.html
3988 
3989     long dx = abs(x1-x0);
3990     long dy = abs(y1-y0);
3991     long err = dx-dy;
3992     long e2, x2;
3993     double ed = dx+dy == 0 ? 1 : sqrt(double(dx*dx+dy*dy));
3994     int sx = x0 < x1 ? 1 : -1;
3995     int sy = y0 < y1 ? 1 : -1;
3996 
3997     while (true) {
3998         DrawAAPixel(x0, y0, 255*abs(err-dx+dy)/ed);
3999         e2 = err;
4000         x2 = x0;
4001         if (2*e2 >= -dx) {
4002             if (x0 == x1) break;
4003             if (e2+dy < ed) DrawAAPixel(x0, y0+sy, 255*(e2+dy)/ed);
4004             err -= dy;
4005             x0 += sx;
4006         }
4007         if (2*e2 <= dy) {
4008             if (y0 == y1) break;
4009             if (dx-e2 < ed) DrawAAPixel(x2+sx, y0, 255*(dx-e2)/ed);
4010             err += dx;
4011             y0 += sy;
4012         }
4013     }
4014 }
4015 
4016 // -----------------------------------------------------------------------------
4017 
DoLine(lua_State * L,int n,bool connected,int * nresults)4018 const char *Overlay::DoLine(lua_State *L, int n, bool connected, int *nresults)
4019 {
4020     if (pixmap == NULL) return OverlayError(no_overlay);
4021 
4022     // check if there are arguments
4023     // note: it is possible that n > 1 and arguments have nil values
4024     int valid = false;
4025     int i = 2;
4026     int type = -1;
4027 
4028     if (n > 1) {
4029         // get line start coordinate pair
4030         lua_rawgeti(L, 1, i++);
4031         int x1 = (int)lua_tonumberx(L, -1, &valid);
4032         lua_pop(L, 1);
4033         if (!valid) {
4034             if (connected) {
4035                 return OverlayError("line command has illegal start x");
4036             } else {
4037                 return OverlayError("lines command has illegal start x");
4038             }
4039         }
4040         lua_rawgeti(L, 1, i++);
4041         int y1 = (int)lua_tonumberx(L, -1, &valid);
4042         lua_pop(L, 1);
4043         if (!valid) {
4044             if (connected) {
4045                 return OverlayError("line command has illegal start y");
4046             } else {
4047                 return OverlayError("lines command has illegal start y");
4048             }
4049         }
4050         // get line end coordinate pair
4051         lua_rawgeti(L, 1, i++);
4052         int x2 = (int)lua_tonumberx(L, -1, &valid);
4053         lua_pop(L, 1);
4054         if (!valid) {
4055             if (connected) {
4056                 return OverlayError("line command has illegal end x");
4057             } else {
4058                 return OverlayError("lines command has illegal end x");
4059             }
4060         }
4061         lua_rawgeti(L, 1, i++);
4062         int y2 = (int)lua_tonumberx(L, -1, &valid);
4063         lua_pop(L, 1);
4064         if (!valid) {
4065             if (connected) {
4066                 return OverlayError("line command has illegal end y");
4067             } else {
4068                 return OverlayError("lines command has illegal end y");
4069             }
4070         }
4071 
4072         // mark target clip as changed
4073         DisableTargetClipIndex();
4074 
4075         // draw the first line
4076         RenderLine(x1, y1, x2, y2);
4077 
4078         // draw any follow on lines
4079         while (i <= n) {
4080             if (connected) {
4081                 // start point is previous line's end point
4082                 x1 = x2;
4083                 y1 = y2;
4084             } else {
4085                 // read the next start point
4086                 lua_rawgeti(L, 1, i++);
4087                 x1 = (int)lua_tonumberx(L, -1, &valid);
4088                 if (!valid) break;
4089                 lua_pop(L, 1);
4090                 lua_rawgeti(L, 1, i++);
4091                 y1 = (int)lua_tonumberx(L, -1, &valid);
4092                 if (!valid) break;
4093                 lua_pop(L, 1);
4094             }
4095             // read the next end point
4096             lua_rawgeti(L, 1, i++);
4097             x2 = (int)lua_tonumberx(L, -1, &valid);
4098             if (!valid) break;
4099             lua_pop(L, 1);
4100             lua_rawgeti(L, 1, i++);
4101             y2 = (int)lua_tonumberx(L, -1, &valid);
4102             if (!valid) break;
4103             lua_pop(L, 1);
4104 
4105             // draw the line
4106             RenderLine(x1, y1, x2, y2);
4107         }
4108 
4109         // check if loop terminated because of failed number conversion
4110         if (!valid) {
4111             // get the type of the argument
4112             type = lua_type(L, -1);
4113             lua_pop(L, 1);
4114         }
4115     } else {
4116         // no arguments supplied
4117         if (connected) {
4118             return OverlayError("line command requires at least two coordinate pairs");
4119         } else {
4120             return OverlayError("lines command requires at least two coordinate pairs");
4121         }
4122     }
4123 
4124     // check if there were errors
4125     if (!valid) {
4126         if (connected) {
4127             // check if the argument number is a multiple of 2 and the argument is nil
4128             if (!((((i - 3) & 1) == 0) && (type == LUA_TNIL))) {
4129                 switch ((i - 3) & 1) {
4130                     case 0:
4131                         return OverlayError("line command has illegal end x");
4132                         break;
4133                     case 1:
4134                         return OverlayError("line command has illegal end y");
4135                         break;
4136                 }
4137             }
4138         } else {
4139             // check if the argument number is a multiple of 4 and the argument is nil
4140             if (!((((i - 3) & 3) == 0) && (type == LUA_TNIL))) {
4141                 switch ((i - 3) & 1) {
4142                     case 0:
4143                         return OverlayError("lines command has illegal start x");
4144                         break;
4145                     case 1:
4146                         return OverlayError("lines command has illegal start y");
4147                         break;
4148                     case 2:
4149                         return OverlayError("lines command has illegal end x");
4150                         break;
4151                     case 3:
4152                         return OverlayError("lines command has illegal end y");
4153                         break;
4154                 }
4155             }
4156         }
4157     }
4158 
4159     return NULL;
4160 }
4161 
4162 // -----------------------------------------------------------------------------
4163 
DoLine(const char * args,bool connected)4164 const char *Overlay::DoLine(const char *args, bool connected)
4165 {
4166     if (pixmap == NULL) return OverlayError(no_overlay);
4167 
4168     int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
4169     args = GetCoordinatePair((char*)args, &x1, &y1);
4170     if (!args) {
4171         if (connected) {
4172             return OverlayError("line command requires at least two coordinate pairs");
4173         } else {
4174             return OverlayError("lines command requires at least two coordinate pairs");
4175         }
4176     }
4177     args = GetCoordinatePair((char*)args, &x2, &y2);
4178     if (!args) {
4179         if (connected) {
4180             return OverlayError("line command requires at least two coordinate pairs");
4181         } else {
4182             return OverlayError("lines command requires at least two coordinate pairs");
4183         }
4184     }
4185 
4186     // mark target clip as changed
4187     DisableTargetClipIndex();
4188 
4189     // draw the line
4190     RenderLine(x1, y1, x2, y2);
4191 
4192     // read any further coordinates
4193     while (*args) {
4194         if (connected) {
4195             x1 = x2;
4196             y1 = y2;
4197             args = GetCoordinatePair((char*)args, &x2, &y2);
4198             if (!args) return OverlayError("line command has illegal coordinates");
4199         } else {
4200             args = GetCoordinatePair((char*)args, &x1, &y1);
4201             if (!args) return OverlayError("lines command has illegal coordinates");
4202             args = GetCoordinatePair((char*)args, &x2, &y2);
4203             if (!args) return OverlayError("lines command has illegal coordinates");
4204         }
4205         RenderLine(x1, y1, x2, y2);
4206     }
4207     return NULL;
4208 }
4209 
4210 
4211 // -----------------------------------------------------------------------------
4212 
RenderLine(int x0,int y0,int x1,int y1)4213 void Overlay::RenderLine(int x0, int y0, int x1, int y1) {
4214     if (linewidth > 1) {
4215         DrawThickLine(x0, y0, x1, y1);
4216         return;
4217     }
4218 
4219     if (x0 == x1 && y0 == y1) {
4220         if (PixelInTarget(x0, y0)) DrawPixel(x0, y0);
4221         return;
4222     }
4223 
4224     if (alphablend) {
4225         DrawAntialiasedLine(x0, y0, x1, y1);
4226         return;
4227     }
4228 
4229     // no alpha blending so use fast copy
4230     unsigned int rgba = rgbadraw;
4231     unsigned int *lpixmap = (unsigned int*)pixmap;
4232 
4233     // draw a line of pixels from x0,y0 to x1,y1 using Bresenham's algorithm
4234     int dx = x1 - x0;
4235     int ax = abs(dx) * 2;
4236     int sx = dx < 0 ? -1 : 1;
4237 
4238     int dy = y1 - y0;
4239     int ay = abs(dy) * 2;
4240     int sy = dy < 0 ? -1 : 1;
4241 
4242     if (ax > ay) {
4243         int d = ay - (ax / 2);
4244         while (x0 != x1) {
4245             if (PixelInTarget(x0, y0)) *(lpixmap + y0*wd + x0) = rgba;
4246             if (d >= 0) {
4247                 y0 = y0 + sy;
4248                 d = d - ax;
4249             }
4250             x0 = x0 + sx;
4251             d = d + ay;
4252         }
4253     } else {
4254         int d = ax - (ay / 2);
4255         while (y0 != y1) {
4256             if (PixelInTarget(x0, y0)) *(lpixmap + y0*wd + x0) = rgba;
4257             if (d >= 0) {
4258                 x0 = x0 + sx;
4259                 d = d - ay;
4260             }
4261             y0 = y0 + sy;
4262             d = d + ax;
4263         }
4264     }
4265     if (PixelInTarget(x1, y1)) *(lpixmap + y1*wd + x1) = rgba;
4266 }
4267 
4268 // -----------------------------------------------------------------------------
4269 
DrawThickEllipse(int x0,int y0,int x1,int y1)4270 void Overlay::DrawThickEllipse(int x0, int y0, int x1, int y1)
4271 {
4272     // based on code from http://members.chello.at/~easyfilter/bresenham.html
4273 
4274     if (linewidth == 1) {
4275         if (alphablend) {
4276             DrawAntialiasedEllipse(x0, y0, x1, y1);
4277         } else {
4278             DrawEllipse(x0, y0, x1, y1);
4279         }
4280         return;
4281     }
4282 
4283     if (x1 == x0 || y1 == y0) {
4284         DrawThickLine(x0, y0, x1, y1);
4285         return;
4286     }
4287 
4288     double th = linewidth;
4289     long a0 = abs(x1-x0);
4290     long b0 = abs(y1-y0);
4291     long b1 = b0&1;
4292     double a2 = a0-2*th;
4293     double b2 = b0-2*th;
4294     double dx = 4*(a0-1)*b0*b0;
4295     double dy = 4*(b1-1)*a0*a0;
4296     double i = a0+b2;
4297     double err = b1*a0*a0;
4298     double dx2, dy2, e2, ed;
4299 
4300     if ((th-1)*(2*b0-th) > a0*a0) {
4301         b2 = sqrt(a0*(b0-a0)*i*a2)/(a0-th);
4302     }
4303     if ((th-1)*(2*a0-th) > b0*b0) {
4304         a2 = sqrt(b0*(a0-b0)*i*b2)/(b0-th);
4305         th = (a0-a2)/2;
4306     }
4307 
4308     if (b2 <= 0) th = a0;    // filled ellipse
4309 
4310     e2 = th-floor(th);
4311     th = x0+th-e2;
4312     dx2 = 4*(a2+2*e2-1)*b2*b2;
4313     dy2 = 4*(b1-1)*a2*a2;
4314     e2 = dx2*e2;
4315     y0 += (b0+1)>>1;
4316     y1 = y0-b1;
4317     a0 = 8*a0*a0;
4318     b1 = 8*b0*b0;
4319     a2 = 8*a2*a2;
4320     b2 = 8*b2*b2;
4321 
4322     do {
4323         while (true) {
4324             if (err < 0 || x0 > x1) { i = x0; break; }
4325             // do outside antialiasing
4326             i = dx < dy ? dx : dy;
4327             ed = dx > dy ? dx : dy;
4328             if (y0 == y1+1 && 2*err > dx && a0 > b1) {
4329                 ed = a0/4;
4330             } else {
4331                 ed += 2*ed*i*i/(4*ed*ed+i*i+1)+1;
4332             }
4333             i = 255*err/ed;
4334             // i can be > 255
4335             if (i <= 255) {
4336                 // extra tests avoid some pixels being drawn twice
4337                 if (x0 == x1) {
4338                     DrawAAPixel(x0, y0, i);
4339                     DrawAAPixel(x0, y1, i);
4340                 } else if (y0 == y1) {
4341                     DrawAAPixel(x0, y0, i);
4342                     DrawAAPixel(x1, y0, i);
4343                 } else {
4344                     // x0 != x1 and y0 != y1
4345                     DrawAAPixel(x0, y0, i);
4346                     DrawAAPixel(x0, y1, i);
4347                     DrawAAPixel(x1, y0, i);
4348                     DrawAAPixel(x1, y1, i);
4349                 }
4350             }
4351             if (err+dy+a0 < dx) { i = x0+1; break; }
4352             x0++;
4353             x1--;
4354             err -= dx;
4355             dx -= b1;
4356         }
4357         while (i < th && 2*i <= x0+x1) {
4358             // set pixel within line
4359             int x = x0+x1-i;
4360             // extra tests avoid some pixels being drawn twice
4361             if (x == i && y0 == y1) {
4362                 if (PixelInTarget(i, y0)) DrawPixel(i, y0);
4363             } else if (x == i) {
4364                 if (PixelInTarget(i, y0)) DrawPixel(i, y0);
4365                 if (PixelInTarget(i, y1)) DrawPixel(i, y1);
4366             } else if (y0 == y1) {
4367                 if (PixelInTarget(i, y0)) DrawPixel(i, y0);
4368                 if (PixelInTarget(x, y0)) DrawPixel(x, y0);
4369             } else {
4370                 // x != i and y0 != y1
4371                 if (PixelInTarget(i, y0)) DrawPixel(i, y0);
4372                 if (PixelInTarget(x, y0)) DrawPixel(x, y0);
4373                 if (PixelInTarget(i, y1)) DrawPixel(i, y1);
4374                 if (PixelInTarget(x, y1)) DrawPixel(x, y1);
4375             }
4376             i++;
4377         }
4378         while (e2 > 0 && x0+x1 >= 2*th) {
4379             // do inside antialiasing
4380             i = dx2 < dy2 ? dx2 : dy2;
4381             ed = dx2 > dy2 ? dx2 : dy2;
4382             if (y0 == y1+1 && 2*e2 > dx2 && a2 > b2) {
4383                 ed = a2/4;
4384             } else {
4385                 ed += 2*ed*i*i/(4*ed*ed+i*i);
4386             }
4387             i = 255-255*e2/ed;
4388             // i can be -ve
4389             if (i < 0) i = 0;
4390             int x = x0+x1-th;
4391             // extra test avoids some pixels being drawn twice
4392             if (x == th) {
4393                 DrawAAPixel(x, y0, i);
4394                 DrawAAPixel(x, y1, i);
4395             } else {
4396                 DrawAAPixel(th, y0, i);
4397                 DrawAAPixel(x,  y0, i);
4398                 DrawAAPixel(th, y1, i);
4399                 DrawAAPixel(x,  y1, i);
4400             }
4401             if (e2+dy2+a2 < dx2) break;
4402             th++;
4403             e2 -= dx2;
4404             dx2 -= b2;
4405         }
4406         e2 += dy2 += a2;
4407         y0++;
4408         y1--;
4409         err += dy += a0;
4410     } while (x0 < x1);
4411 
4412     if (y0-y1 <= b0) {
4413         if (err > dy+a0) {
4414             y0--;
4415             y1++;
4416             err -= dy -= a0;
4417         }
4418         while (y0-y1 <= b0) {
4419             i = 255*4*err/b1;
4420             DrawAAPixel(x0, y0,   i);
4421             DrawAAPixel(x1, y0++, i);
4422             DrawAAPixel(x0, y1,   i);
4423             DrawAAPixel(x1, y1--, i);
4424             err += dy += a0;
4425         }
4426     }
4427 }
4428 
4429 // -----------------------------------------------------------------------------
4430 
DrawAntialiasedEllipse(int x0,int y0,int x1,int y1)4431 void Overlay::DrawAntialiasedEllipse(int x0, int y0, int x1, int y1)
4432 {
4433     // based on code from http://members.chello.at/~easyfilter/bresenham.html
4434 
4435     long a0 = abs(x1-x0);
4436     long b0 = abs(y1-y0);
4437     long b1 = b0&1;
4438     double dx = 4*(a0-1.0)*b0*b0;
4439     double dy = 4*(b1+1.0)*a0*a0;
4440     double err = b1*a0*a0-dx+dy;
4441     double ed, i;
4442     bool f;
4443 
4444     if (a0 == 0 || b0 == 0) {
4445         DrawAntialiasedLine(x0, y0, x1, y1);
4446         return;
4447     }
4448 
4449     y0 += (b0+1)/2;
4450     y1 = y0-b1;
4451     a0 = 8*a0*a0;
4452     b1 = 8*b0*b0;
4453 
4454     while (true) {
4455         i = dx < dy ? dx : dy;
4456         ed = dx > dy ? dx : dy;
4457         if (y0 == y1+1 && err > dy && a0 > b1) {
4458             ed = 255*4.0/a0;
4459         } else {
4460             ed = 255/(ed+2*ed*i*i/(4*ed*ed+i*i));
4461         }
4462         i = ed*fabs(err+dx-dy);     // intensity depends on pixel error
4463 
4464         // extra tests avoid pixels at extremities being drawn twice
4465         if (x0 == x1) {
4466             DrawAAPixel(x0, y0, i);
4467             DrawAAPixel(x0, y1, i);
4468         } else if (y0 == y1) {
4469             DrawAAPixel(x0, y0, i);
4470             DrawAAPixel(x1, y0, i);
4471         } else {
4472             // x0 != x1 and y0 != y1
4473             DrawAAPixel(x0, y0, i);
4474             DrawAAPixel(x0, y1, i);
4475             DrawAAPixel(x1, y0, i);
4476             DrawAAPixel(x1, y1, i);
4477         }
4478 
4479         f = 2*err+dy >= 0;
4480         if (f) {
4481             if (x0 >= x1) break;
4482             i = ed*(err+dx);
4483             if (i < 255) {
4484                 DrawAAPixel(x0, y0+1, i);
4485                 DrawAAPixel(x0, y1-1, i);
4486                 DrawAAPixel(x1, y0+1, i);
4487                 DrawAAPixel(x1, y1-1, i);
4488             }
4489         }
4490         if (2*err <= dx) {
4491             i = ed*(dy-err);
4492             if (i < 255) {
4493                 DrawAAPixel(x0+1, y0, i);
4494                 DrawAAPixel(x1-1, y0, i);
4495                 DrawAAPixel(x0+1, y1, i);
4496                 DrawAAPixel(x1-1, y1, i);
4497             }
4498             y0++;
4499             y1--;
4500             err += dy += a0;
4501         }
4502         if (f) {
4503             x0++;
4504             x1--;
4505             err -= dx -= b1;
4506         }
4507     }
4508 
4509     if (--x0 == x1++) {
4510         while (y0-y1 < b0) {
4511             i = 255*4*fabs(err+dx)/b1;
4512             DrawAAPixel(x0, ++y0, i);
4513             DrawAAPixel(x1,   y0, i);
4514             DrawAAPixel(x0, --y1, i);
4515             DrawAAPixel(x1,   y1, i);
4516             err += dy += a0;
4517         }
4518     }
4519 }
4520 
4521 // -----------------------------------------------------------------------------
4522 
DrawEllipse(int x0,int y0,int x1,int y1)4523 void Overlay::DrawEllipse(int x0, int y0, int x1, int y1)
4524 {
4525     // based on code from http://members.chello.at/~easyfilter/bresenham.html
4526 
4527     long a0 = abs(x1-x0);
4528     long b0 = abs(y1-y0);
4529     long b1 = b0&1;
4530     double dx = 4*(1.0-a0)*b0*b0;
4531     double dy = 4*(b1+1.0)*a0*a0;
4532     double err = dx+dy+b1*a0*a0;
4533     double e2;
4534 
4535     y0 += (b0+1)/2;
4536     y1 = y0-b1;
4537     a0 *= 8*a0;
4538     b1 = 8*b0*b0;
4539 
4540     do {
4541         if (PixelInTarget(x1, y0)) DrawPixel(x1, y0);
4542         if (PixelInTarget(x0, y0)) DrawPixel(x0, y0);
4543         if (PixelInTarget(x0, y1)) DrawPixel(x0, y1);
4544         if (PixelInTarget(x1, y1)) DrawPixel(x1, y1);
4545         e2 = 2*err;
4546         if (e2 <= dy) {
4547             y0++;
4548             y1--;
4549             err += dy += a0;
4550         }
4551         if (e2 >= dx || 2*err > dy) {
4552             x0++;
4553             x1--;
4554             err += dx += b1;
4555         }
4556     } while (x0 <= x1);
4557 
4558     // note that next test must be <= b0
4559     while (y0-y1 <= b0) {
4560         // finish tip of ellipse
4561         if (PixelInTarget(x0-1, y0)) DrawPixel(x0-1, y0);
4562         if (PixelInTarget(x1+1, y0)) DrawPixel(x1+1, y0);
4563         if (PixelInTarget(x0-1, y1)) DrawPixel(x0-1, y1);
4564         if (PixelInTarget(x1+1, y1)) DrawPixel(x1+1, y1);
4565         y0++;
4566         y1--;
4567     }
4568 }
4569 
4570 // -----------------------------------------------------------------------------
4571 
DoEllipse(const char * args)4572 const char *Overlay::DoEllipse(const char *args)
4573 {
4574     if (pixmap == NULL) return OverlayError(no_overlay);
4575 
4576     int x, y, w, h;
4577     if (sscanf(args, " %d %d %d %d", &x, &y, &w, &h) != 4) {
4578         return OverlayError("ellipse command requires 4 arguments");
4579     }
4580 
4581     // treat non-positive w/h as inset from overlay's width/height
4582     if (w <= 0) w = wd + w;
4583     if (h <= 0) h = ht + h;
4584     if (w <= 0) return OverlayError("ellipse width must be > 0");
4585     if (h <= 0) return OverlayError("ellipse height must be > 0");
4586 
4587     // mark target clip as changed
4588     DisableTargetClipIndex();
4589 
4590     if (linewidth > 1) {
4591         DrawThickEllipse(x, y, x+w-1, y+h-1);
4592         return NULL;
4593     }
4594 
4595     if (alphablend) {
4596         DrawAntialiasedEllipse(x, y, x+w-1, y+h-1);
4597         return NULL;
4598     }
4599 
4600     // draw a non-antialiased ellipse where linewidth is 1
4601     DrawEllipse(x, y, x+w-1, y+h-1);
4602     return NULL;
4603 }
4604 
4605 // -----------------------------------------------------------------------------
4606 
FillRect(int x,int y,int w,int h)4607 void Overlay::FillRect(int x, int y, int w, int h)
4608 {
4609     // get rgba drawing color
4610     const unsigned int source = rgbadraw;
4611     // get destination location
4612     unsigned int *lp = ((unsigned int*)pixmap) + y * wd + x;
4613     // check for alphablending
4614     if (alphablend && a < 255) {
4615         // only draw if source not transparent
4616         if (a) {
4617             const unsigned int alpha = a + 1;
4618             const unsigned int invalpha = 256 - a;
4619             const unsigned int sourcearb = alpha * RBRIGHT(source & RBMASK);
4620             const unsigned int sourceag =  alpha *        (source & GMASK);
4621             unsigned int dest;
4622             if (alphablend == 1) {
4623                 // full alpha blend
4624                 for (int j = 0; j < h; j++) {
4625                     for (int i = 0; i < w; i++) {
4626                         dest = *lp;
4627                         ALPHABLENDPRE(source, sourcearb, sourceag, dest, lp, alpha, invalpha);
4628                         lp++;
4629                     }
4630                     lp += wd - w;
4631                 }
4632             } else {
4633                 // fast alpha blend (opaque destination)
4634                 for (int j = 0 ; j < h; j++) {
4635                     for (int i = 0; i < w; i++) {
4636                         dest = *lp;
4637                         ALPHABLENDPREOPAQUEDEST(sourcearb, sourceag, dest, lp, invalpha);
4638                         lp++;
4639                     }
4640                     lp += wd - w;
4641                 }
4642             }
4643         }
4644     } else {
4645         // create first row
4646         unsigned int *dest = lp;
4647         for (int i = 0; i < w; i++) {
4648             *dest++ = source;
4649         }
4650 
4651         // copy first row to remaining rows
4652         dest = lp;
4653         int wbytes = w * 4;
4654         for (int i = 1; i < h; i++) {
4655             dest += wd;
4656             memcpy(dest, lp, wbytes);
4657         }
4658     }
4659 }
4660 
4661 // -----------------------------------------------------------------------------
4662 
DoFill(lua_State * L,int n,int * nresults)4663 const char *Overlay::DoFill(lua_State *L, int n, int *nresults)
4664 {
4665     if (pixmap == NULL) return OverlayError(no_overlay);
4666 
4667     // check if there are arguments
4668     // note: it is possible that n > 1 and arguments have nil values
4669     int valid = false;
4670     int i = 2;
4671     int type = -1;
4672 
4673     if (n > 1) {
4674         // mark target clip as changed
4675         DisableTargetClipIndex();
4676 
4677         // draw each rectangle
4678         do {
4679             // get the coordinates
4680             lua_rawgeti(L, 1, i++);
4681             int x = (int)lua_tonumberx(L, -1, &valid);
4682             if (!valid) break;
4683             lua_pop(L, 1);
4684             lua_rawgeti(L, 1, i++);
4685             int y = (int)lua_tonumberx(L, -1, &valid);
4686             if (!valid) break;
4687             lua_pop(L, 1);
4688             lua_rawgeti(L, 1, i++);
4689             int w = (int)lua_tonumberx(L, -1, &valid);
4690             if (!valid) break;
4691             lua_pop(L, 1);
4692             lua_rawgeti(L, 1, i++);
4693             int h = (int)lua_tonumberx(L, -1, &valid);
4694             if (!valid) break;
4695             lua_pop(L, 1);
4696 
4697             // treat non-positive w/h as inset from overlay's width/height
4698             if (w <= 0) w = wd + w;
4699             if (h <= 0) h = ht + h;
4700             if (w <= 0) return OverlayError("fill width must be > 0");
4701             if (h <= 0) return OverlayError("fill height must be > 0");
4702 
4703             // ignore rect if completely outside target edges
4704             if (!RectOutsideTarget(x, y, w, h)) {
4705                 // clip any part of rect outside target edges
4706                 int xmax = x + w - 1;
4707                 int ymax = y + h - 1;
4708                 if (x < 0) x = 0;
4709                 if (y < 0) y = 0;
4710                 if (xmax >= wd) xmax = wd - 1;
4711                 if (ymax >= ht) ymax = ht - 1;
4712                 w = xmax - x + 1;
4713                 h = ymax - y + 1;
4714 
4715                 // fill visible rect with current RGBA values
4716                 FillRect(x, y, w, h);
4717             }
4718         } while (i <= n);
4719 
4720         // check if loop terminated because of failed number conversion
4721         if (!valid) {
4722             // get the type of the argument
4723             type = lua_type(L, -1);
4724             lua_pop(L, 1);
4725         }
4726     }
4727 
4728     // check if there were no arguments
4729     // either none supplied, or the first argument value was nil
4730     if (n == 1 || (i == 3 && type == LUA_TNIL)) {
4731         // fill entire target with current RGBA values
4732         FillRect(0, 0, wd, ht);
4733         valid = true;
4734     }
4735 
4736     // check if there were errors
4737     if (!valid) {
4738         // check if the argument number is a multiple of 4 and the argument is nil
4739         if ((((i - 3) & 3) == 0) && (type == LUA_TNIL)) {
4740             // command was valid
4741             valid = true;
4742         }
4743     }
4744 
4745     if (!valid) {
4746         // return appropriate error message
4747         switch ((i - 3) & 3) {
4748             case 0:
4749                 return OverlayError("fill command has illegal x");
4750                 break;
4751             case 1:
4752                 return OverlayError("fill command has illegal y");
4753                 break;
4754             case 2:
4755                 return OverlayError("fill command has illegal width");
4756                 break;
4757             case 3:
4758                 return OverlayError("fill command has illegal height");
4759                 break;
4760         }
4761     }
4762 
4763     return NULL;
4764 }
4765 
4766 // -----------------------------------------------------------------------------
4767 
DoFill(const char * args)4768 const char *Overlay::DoFill(const char *args)
4769 {
4770     if (pixmap == NULL) return OverlayError(no_overlay);
4771 
4772     if (*args == ' ') {
4773         int x = 0, y = 0, w = 0, h = 0;
4774         args = GetCoordinatePair((char*)args, &x, &y);
4775         if (!args) return OverlayError("fill command requires 0 or at least 4 arguments");
4776         args = GetCoordinatePair((char*)args, &w, &h);
4777         if (!args) return OverlayError("fill command requires 0 or at least 4 arguments");
4778 
4779         // treat non-positive w/h as inset from overlay's width/height
4780         if (w <= 0) w = wd + w;
4781         if (h <= 0) h = ht + h;
4782         if (w <= 0) return OverlayError("fill width must be > 0");
4783         if (h <= 0) return OverlayError("fill height must be > 0");
4784 
4785         // mark target clip as changed
4786         DisableTargetClipIndex();
4787 
4788         // ignore rect if completely outside target edges
4789         if (!RectOutsideTarget(x, y, w, h)) {
4790             // clip any part of rect outside target edges
4791             int xmax = x + w - 1;
4792             int ymax = y + h - 1;
4793             if (x < 0) x = 0;
4794             if (y < 0) y = 0;
4795             if (xmax >= wd) xmax = wd - 1;
4796             if (ymax >= ht) ymax = ht - 1;
4797             w = xmax - x + 1;
4798             h = ymax - y + 1;
4799 
4800             // fill visible rect with current RGBA values
4801             FillRect(x, y, w, h);
4802         }
4803 
4804         while (*args) {
4805             args = GetCoordinatePair((char*)args, &x, &y);
4806             if (!args) return OverlayError("fill command invalid arguments");
4807             args = GetCoordinatePair((char*)args, &w, &h);
4808             if (!args) return OverlayError("fill command invalid arguments");
4809 
4810             // treat non-positive w/h as inset from overlay's width/height
4811             if (w <= 0) w = wd + w;
4812             if (h <= 0) h = ht + h;
4813             if (w <= 0) return OverlayError("fill width must be > 0");
4814             if (h <= 0) return OverlayError("fill height must be > 0");
4815 
4816             // ignore rect if completely outside target edges
4817             if (!RectOutsideTarget(x, y, w, h)) {
4818                 // clip any part of rect outside target edges
4819                 int xmax = x + w - 1;
4820                 int ymax = y + h - 1;
4821                 if (x < 0) x = 0;
4822                 if (y < 0) y = 0;
4823                 if (xmax >= wd) xmax = wd - 1;
4824                 if (ymax >= ht) ymax = ht - 1;
4825                 w = xmax - x + 1;
4826                 h = ymax - y + 1;
4827 
4828                 // fill visible rect with current RGBA values
4829                 FillRect(x, y, w, h);
4830             }
4831         }
4832     } else {
4833         // mark target clip as changed
4834         DisableTargetClipIndex();
4835 
4836         // fill entire target with current RGBA values
4837         FillRect(0, 0, wd, ht);
4838     }
4839 
4840     return NULL;
4841 }
4842 
4843 // -----------------------------------------------------------------------------
4844 
DoCopy(const char * args)4845 const char *Overlay::DoCopy(const char *args)
4846 {
4847     if (pixmap == NULL) return OverlayError(no_overlay);
4848 
4849     int x, y, w, h;
4850     int namepos;
4851     char dummy;
4852     if (sscanf(args, " %d %d %d %d %n%c", &x, &y, &w, &h, &namepos, &dummy) != 5) {
4853         // note that %n is not included in the count
4854         return OverlayError("copy command requires 5 arguments");
4855     }
4856 
4857     // treat non-positive w/h as inset from overlay's width/height
4858     // (makes it easy to copy entire overlay via "copy 0 0 0 0 all")
4859     if (w <= 0) w = wd + w;
4860     if (h <= 0) h = ht + h;
4861     if (w <= 0) return OverlayError("copy width must be > 0");
4862     if (h <= 0) return OverlayError("copy height must be > 0");
4863 
4864     std::string name = args + namepos;
4865 
4866     // delete any existing clip data with the given name
4867     std::map<std::string,Clip*>::iterator it;
4868     it = clips.find(name);
4869     if (it != clips.end()) {
4870         delete it->second;
4871         clips.erase(it);
4872     }
4873 
4874     bool use_calloc;
4875     if (RectInsideTarget(x, y, w, h)) {
4876         // use malloc to allocate clip memory
4877         use_calloc = false;
4878     } else {
4879         // use calloc so parts outside target will be transparent
4880         use_calloc = true;
4881     }
4882 
4883     Clip *newclip = new Clip(w, h, use_calloc);
4884     if (newclip == NULL || newclip->cdata == NULL) {
4885         delete newclip;
4886         return OverlayError("not enough memory to copy pixels");
4887     }
4888 
4889     if (use_calloc) {
4890         if (RectOutsideTarget(x, y, w, h)) {
4891             // clip rect is completely outside target so no need to copy
4892             // target pixels (clip pixels are all transparent)
4893         } else {
4894             // calculate offsets in clip data and bytes per row
4895             int clipx = x >= 0 ? 0 : -x;
4896             int clipy = y >= 0 ? 0 : -y;
4897             int cliprowbytes = w * 4;
4898 
4899             // set x,y,w,h to intersection with target
4900             int xmax = x + w - 1;
4901             int ymax = y + h - 1;
4902             if (x < 0) x = 0;
4903             if (y < 0) y = 0;
4904             if (xmax >= wd) xmax = wd - 1;
4905             if (ymax >= ht) ymax = ht - 1;
4906             w = xmax - x + 1;
4907             h = ymax - y + 1;
4908 
4909             // copy intersection rect from target into corresponding area of clip data
4910             unsigned char *dest = newclip->cdata + clipy*cliprowbytes + clipx*4;
4911             int rowbytes = wd * 4;
4912             int wbytes = w * 4;
4913             unsigned char *src = pixmap + y*rowbytes + x*4;
4914             for (int i = 0; i < h; i++) {
4915                 memcpy(dest, src, wbytes);
4916                 src += rowbytes;
4917                 dest += cliprowbytes;
4918             }
4919         }
4920     } else {
4921         // given rectangle is within target so fill newclip->cdata with
4922         // pixel data from that rectangle in pixmap
4923         unsigned char *dest = newclip->cdata;
4924 
4925         if (x == 0 && y == 0 && w == wd && h == ht) {
4926             // clip and overlay are the same size so do a fast copy
4927             memcpy(dest, pixmap, w * h * 4);
4928 
4929         } else {
4930             // use memcpy to copy each row
4931             int rowbytes = wd * 4;
4932             int wbytes = w * 4;
4933             unsigned char *src = pixmap + y*rowbytes + x*4;
4934             for (int i = 0; i < h; i++) {
4935                 memcpy(dest, src, wbytes);
4936                 src += rowbytes;
4937                 dest += wbytes;
4938             }
4939         }
4940     }
4941 
4942     clips[name] = newclip;      // create named clip for later use by paste, scale, etc
4943 
4944     return NULL;
4945 }
4946 
4947 // -----------------------------------------------------------------------------
4948 
DisableTargetClipIndex()4949 void Overlay::DisableTargetClipIndex()
4950 {
4951     // if the current target is a clip then it may have been modified so disable index
4952     if (renderclip) {
4953         renderclip->RemoveIndex();
4954     }
4955 }
4956 
4957 // -----------------------------------------------------------------------------
4958 
DoOptimize(const char * args)4959 const char *Overlay::DoOptimize(const char *args)
4960 {
4961     if (pixmap == NULL) return OverlayError(no_overlay);
4962 
4963     int namepos;
4964     char dummy;
4965     if (sscanf(args, " %n%c", &namepos, &dummy) != 1) {
4966         // note that %n is not included in the count
4967         return OverlayError("optimize command requires an argument");
4968     }
4969 
4970     std::string name = args + namepos;
4971     std::map<std::string,Clip*>::iterator it;
4972     it = clips.find(name);
4973     if (it == clips.end()) {
4974         static std::string msg;
4975         msg = "unknown optimize clip (";
4976         msg += name;
4977         msg += ")";
4978         return OverlayError(msg.c_str());
4979     }
4980     Clip *clipptr = it->second;
4981 
4982     // add index to the clip
4983     clipptr->AddIndex();
4984 
4985     // return the bounding box x, y, w, h of non-transparent pixels in the clip
4986     static char result[64];
4987     sprintf(result, "%d %d %d %d", clipptr->xbb, clipptr->ybb, clipptr->wbb, clipptr->hbb);
4988     return result;
4989 }
4990 
4991 // -----------------------------------------------------------------------------
4992 
DoPaste(lua_State * L,int n,int * nresults)4993 const char *Overlay::DoPaste(lua_State *L, int n, int *nresults)
4994 {
4995     const char *result = NULL;
4996 
4997     // clip name
4998     const char *clipname = NULL;
4999     int clipi = 0;
5000 
5001     // allocate space for coordinate values
5002     if (n > 1) {
5003         int *coords = (int*)malloc((n - 1) * sizeof(int));
5004         int j = 0;
5005 
5006         // get the array of coordinates
5007         int valid = true;
5008         int i = 2;
5009         while (i <= n && valid) {
5010             // read the element at the next index
5011             lua_rawgeti(L, 1, i);
5012             // attempt to decode as a number
5013             lua_Number value = lua_tonumberx(L, -1, &valid);
5014             if (valid) {
5015                 // store the number
5016                 coords[j++] = (int)value;
5017             } else {
5018                 // was not a number so check the type
5019                 int type = lua_type(L, -1);
5020                 if (type == LUA_TSTRING) {
5021                     // first time decode as a string after that it's an error
5022                     if (clipname == NULL) {
5023                         clipname = lua_tostring(L, -1);
5024                         clipi = i;
5025                         valid = true;
5026                     }
5027                 } else {
5028                     if (type == LUA_TNIL) {
5029                         // if it's nil then stop
5030                         n = i - 1;
5031                         valid = true;
5032                     }
5033                 }
5034             }
5035             lua_pop(L, 1);
5036             i++;
5037         }
5038 
5039         // clip name must be last argument
5040         if (clipname && (clipi != n)) {
5041             valid = false;
5042         }
5043 
5044         // check if the coordinates were all numbers
5045         static std::string msg;
5046         if (valid) {
5047             // lookup the named clip
5048             std::string name = clipname;
5049             std::map<std::string,Clip*>::iterator it;
5050             it = clips.find(name);
5051             if (it == clips.end()) {
5052                 msg = "unknown paste clip (";
5053                 msg += name;
5054                 msg += ")";
5055                 result = OverlayError(msg.c_str());
5056             } else {
5057                 // mark target clip as changed
5058                 DisableTargetClipIndex();
5059 
5060                 // call the required function
5061                 Clip *clipptr = it->second;
5062                 result = DoPaste(coords, j, clipptr);
5063             }
5064         }
5065 
5066         // free argument list
5067         free(coords);
5068 
5069         if (!valid) {
5070             result = OverlayError("paste command has invalid arguments");
5071         }
5072     }
5073 
5074     return result;
5075 }
5076 
5077 // -----------------------------------------------------------------------------
5078 
DoPaste(const int * coords,int n,const Clip * clipptr)5079 const char *Overlay::DoPaste(const int *coords, int n, const Clip *clipptr)
5080 {
5081     if (pixmap == NULL) return OverlayError(no_overlay);
5082 
5083     // check that coordinates are supplied and that there are an even number
5084     if (clipptr == NULL) return OverlayError("paste command requires a clip");
5085     if (n < 2) return OverlayError("paste command requires coordinate pairs");
5086     if ((n & 1) != 0) return OverlayError("paste command has illegal coordinates");
5087 
5088     // clip dimensions and data
5089     int w, h, xoff, yoff;
5090     unsigned int *clipdata;
5091     if (alphablend) {
5092         // use non-zero alpha bounding box if alphablending
5093         w = clipptr->wbb;
5094         h = clipptr->hbb;
5095         xoff = clipptr->xbb;
5096         yoff = clipptr->ybb;
5097         clipdata = (unsigned int*)clipptr->cdatabb;
5098     } else {
5099         // use entire clip if not
5100         w = clipptr->cwd;
5101         h = clipptr->cht;
5102         xoff = 0;
5103         yoff = 0;
5104         clipdata = (unsigned int*)clipptr->cdata;
5105     }
5106 
5107     // paste at each coordinate pair
5108     const int ow = w;
5109     const int oh = h;
5110     int ci = 0;
5111     do {
5112         // add the bounding box offset to the coordinates
5113         int x = coords[ci++] + xoff;
5114         int y = coords[ci++] + yoff;
5115 
5116         // set original width and height since these can be changed below if clipping required
5117         w = ow;
5118         h = oh;
5119 
5120         // discard if location is completely outside target
5121         if (!RectOutsideTarget(x, y, w, h)) {
5122             // check for transformation
5123             if (identity) {
5124                 // no transformation, check for clip and target the same size without alpha blending
5125                 if (!alphablend && x == 0 && y == 0 && w == wd && h == ht) {
5126                     // fast paste with single memcpy call
5127                     memcpy(pixmap, clipptr->cdata, w * h * 4);
5128                 } else {
5129                     // get the clip data
5130                     unsigned int *ldata = clipdata;
5131                     int cliprowpixels = clipptr->cwd;
5132                     int rowoffset = yoff;
5133 
5134                     // check for clipping
5135                     int xmax = x + w - 1;
5136                     int ymax = y + h - 1;
5137                     if (x < 0) {
5138                         // skip pixels off left edge
5139                         ldata += -x;
5140                         x = 0;
5141                     }
5142                     if (y < 0) {
5143                         // skip pixels off top edge
5144                         ldata += -y * cliprowpixels;
5145                         rowoffset += -y;
5146                         y = 0;
5147                     }
5148                     if (xmax >= wd) xmax = wd - 1;
5149                     if (ymax >= ht) ymax = ht - 1;
5150                     w = xmax - x + 1;
5151                     h = ymax - y + 1;
5152 
5153                     // get the paste target data
5154                     int targetrowpixels = wd;
5155                     unsigned int *lp = (unsigned int*)pixmap;
5156                     lp += y * targetrowpixels + x;
5157                     unsigned int source, dest, pa, alpha, invalpha;
5158 
5159                     // check for alpha blending
5160                     if (alphablend) {
5161                         // alpha blending
5162                         const rowtype *rowindex = clipptr->rowindex;
5163                         if (!rowindex) {
5164                             // clip only has mixed alpha rows
5165                             for (int j = 0; j < h; j++) {
5166                                 // row contains pixels with different alpha values
5167                                 if (alphablend == 1) {
5168                                     // full alpha blend
5169                                     for (int i = 0; i < w ; i++) {
5170                                         source = *ldata;
5171                                         pa = ALPHA2BYTE(source);
5172                                         if (pa < 255) {
5173                                             // source pixel is not opaque
5174                                             if (pa) {
5175                                                 // source pixel is translucent so blend with destination pixel
5176                                                 alpha = pa + 1;
5177                                                 invalpha = 256 - pa;
5178                                                 dest = *lp;
5179                                                 ALPHABLEND(source, dest, lp, alpha, invalpha);
5180                                             }
5181                                         } else {
5182                                             // pixel is opaque so copy it
5183                                             *lp = source;
5184                                         }
5185                                         lp++;
5186                                         ldata++;
5187                                     }
5188                                 } else {
5189                                     // fast alpha blend (opaque destination)
5190                                     for (int i = 0; i < w; i++) {
5191                                         source = *ldata++;
5192                                         pa = ALPHA2BYTE(source);
5193                                         alpha = pa + 1;
5194                                         invalpha = 256 - pa;
5195                                         dest = *lp;
5196                                         ALPHABLENDOPAQUEDEST(source, dest, lp, alpha, invalpha);
5197                                         lp++;
5198                                     }
5199                                 }
5200                                 // next clip and target row
5201                                 lp += targetrowpixels - w;
5202                                 ldata += cliprowpixels - w;
5203                             }
5204                         } else {
5205                             for (int j = rowoffset; j < h + rowoffset; j++) {
5206                                 rowtype rowflag = rowindex[j];
5207                                 switch (rowflag) {
5208                                 case alpha0:
5209                                     // if all pixels are transparent then skip the row
5210                                     ldata += w;
5211                                     lp += w;
5212                                     break;
5213                                 case opaque:
5214                                     // if all pixels are opaque then use memcpy
5215                                     memcpy(lp, ldata, w << 2);
5216                                     ldata += w;
5217                                     lp += w;
5218                                     break;
5219                                 case both:
5220                                     // row contains mix of opaque and transparent pixels
5221                                     for (int i = 0; i < w; i++) {
5222                                         source = *ldata++;
5223                                         // copy pixel if not transparent
5224                                         if (source & AMASK) {
5225                                             *lp = source;
5226                                         }
5227                                         lp++;
5228                                     }
5229                                     break;
5230                                 case mixed:
5231                                     // row contains pixels with different alpha values
5232                                     if (alphablend == 1) {
5233                                         // full alpha blend
5234                                         for (int i = 0; i < w; i++) {
5235                                             source = *ldata;
5236                                             pa = ALPHA2BYTE(source);
5237                                             if (pa < 255) {
5238                                                 // source pixel is not opaque
5239                                                 if (pa) {
5240                                                     // source pixel is translucent so blend with destination pixel
5241                                                     alpha = pa + 1;
5242                                                     invalpha = 256 - pa;
5243                                                     dest = *lp;
5244                                                     ALPHABLEND(source, dest, lp, alpha, invalpha);
5245                                                 }
5246                                             } else {
5247                                                 // pixel is opaque so copy it
5248                                                 *lp = source;
5249                                             }
5250                                             lp++;
5251                                             ldata++;
5252                                         }
5253                                     } else {
5254                                         // fast alpha blend (destination is opaque)
5255                                         for (int i = 0; i < w; i++) {
5256                                             source = *ldata++;
5257                                             pa = ALPHA2BYTE(source);
5258                                             alpha = pa + 1;
5259                                             invalpha = 256 - pa;
5260                                             dest = *lp;
5261                                             ALPHABLENDOPAQUEDEST(source, dest, lp, alpha, invalpha);
5262                                             lp++;
5263                                         }
5264                                     }
5265                                     break;
5266                                 }
5267                                 // next clip and target row
5268                                 lp += targetrowpixels - w;
5269                                 ldata += cliprowpixels - w;
5270                             }
5271                         }
5272                     } else {
5273                         // no alpha blending
5274                         for (int j = 0; j < h; j++) {
5275                             // copy each row with memcpy
5276                             memcpy(lp, ldata, w << 2);
5277                             lp += targetrowpixels;
5278                             ldata += cliprowpixels;
5279                         }
5280                     }
5281                 }
5282             } else {
5283                 // do an affine transformation
5284                 unsigned int *data = (unsigned int*)clipptr->cdata;
5285                 w = clipptr->cwd;
5286                 h = clipptr->cht;
5287                 x -= xoff;
5288                 y -= yoff;
5289                 const int x0 = x - (x * axx + y * axy);
5290                 const int y0 = y - (x * ayx + y * ayy);
5291 
5292                 // check for alpha blend
5293                 if (alphablend) {
5294                     // save RGBA values
5295                     unsigned int savergba = rgbadraw;
5296                     unsigned char savea = a;
5297 
5298                     for (int j = 0; j < h; j++) {
5299                         for (int i = 0; i < w; i++) {
5300                             rgbadraw = *data++;
5301                             a = ALPHA2BYTE(rgbadraw);
5302                             int newx = x0 + x * axx + y * axy;
5303                             int newy = y0 + x * ayx + y * ayy;
5304                             if (PixelInTarget(newx, newy)) DrawPixel(newx, newy);
5305                             x++;
5306                         }
5307                         y++;
5308                         x -= w;
5309                     }
5310 
5311                     // restore saved RGBA values
5312                     rgbadraw = savergba;
5313                     a = savea;
5314                 } else {
5315                     // no alpha blend
5316                     unsigned int *ldata = (unsigned int*)data;
5317                     unsigned int *lp = (unsigned int*)pixmap;
5318                     for (int j = 0; j < h; j++) {
5319                         for (int i = 0; i < w; i++) {
5320                             int newx = x0 + x * axx + y * axy;
5321                             int newy = y0 + x * ayx + y * ayy;
5322                             if (PixelInTarget(newx, newy)) *(lp + newy * wd + newx) = *ldata;
5323                             ldata++;
5324                             x++;
5325                         }
5326                         y++;
5327                         x -= w;
5328                     }
5329                 }
5330             }
5331         }
5332     }
5333     while (ci < n - 1);
5334 
5335     return NULL;
5336 }
5337 
5338 // -----------------------------------------------------------------------------
5339 
DoPaste(const char * args)5340 const char *Overlay::DoPaste(const char *args)
5341 {
5342     if (pixmap == NULL) return OverlayError(no_overlay);
5343 
5344     int x, y;
5345 
5346     // find out the length of the argument string
5347     int arglen = strlen(args);
5348     if (arglen == 0) {
5349         return OverlayError("paste command requires at least 3 arguments");
5350     }
5351 
5352     // make a copy of the arguments so we can change them
5353     char *buffer = (char*)malloc(arglen + 1);   // add 1 for the terminating nul
5354     if (buffer == NULL) return OverlayError("not enough memory for paste");
5355     char *copy = buffer;
5356     strcpy(copy, args);
5357 
5358     // find the last argument which should be the clip name
5359     char *lastarg = copy + arglen - 1;
5360 
5361     // skip trailing whitespace
5362     while (lastarg >= copy && *lastarg == ' ') {
5363         lastarg--;
5364     }
5365 
5366     // skip until whitespace
5367     while (lastarg >= copy && *lastarg != ' ') {
5368         lastarg--;
5369     }
5370 
5371     // check if clip name was found
5372     if (lastarg < copy) {
5373         free(buffer);
5374         return OverlayError("paste command requires at least 3 arguments");
5375     }
5376 
5377     // null terminate the arguments before the clip name
5378     *lastarg++ = 0;
5379 
5380     // lookup the named clip
5381     std::string name = lastarg;
5382     std::map<std::string,Clip*>::iterator it;
5383     it = clips.find(name);
5384     if (it == clips.end()) {
5385         static std::string msg;
5386         msg = "unknown paste clip (";
5387         msg += name;
5388         msg += ")";
5389         free(buffer);
5390         return OverlayError(msg.c_str());
5391     }
5392     Clip *clipptr = it->second;
5393 
5394     // read the first coordinate pair
5395     copy = (char*)GetCoordinatePair(copy, &x, &y);
5396     if (!copy) {
5397         free(buffer);
5398         return OverlayError("paste command requires a least one coordinate pair");
5399     }
5400 
5401     // mark target clip as changed
5402     DisableTargetClipIndex();
5403 
5404     // clip dimensions and data
5405     int w, h, xoff, yoff;
5406     unsigned int *clipdata;
5407     if (alphablend) {
5408         // use non-zero alpha bounding box if alphablending
5409         w = clipptr->wbb;
5410         h = clipptr->hbb;
5411         xoff = clipptr->xbb;
5412         yoff = clipptr->ybb;
5413         clipdata = (unsigned int*)clipptr->cdatabb;
5414     } else {
5415         // use entire clip if not
5416         w = clipptr->cwd;
5417         h = clipptr->cht;
5418         xoff = 0;
5419         yoff = 0;
5420         clipdata = (unsigned int*)clipptr->cdata;
5421     }
5422 
5423     // paste at each coordinate pair
5424     const int ow = w;
5425     const int oh = h;
5426     do {
5427         // add the bounding box offset to the coordinates
5428         x += xoff;
5429         y += yoff;
5430 
5431         // set original width and height since these can be changed below if clipping required
5432         w = ow;
5433         h = oh;
5434 
5435         if (!RectOutsideTarget(x, y, w, h)) {
5436             // check for transformation
5437             if (identity) {
5438                 // no transformation, check for clip and target the same size without alpha blending
5439                 if (!alphablend && x == 0 && y == 0 && w == wd && h == ht) {
5440                     // fast paste with single memcpy call
5441                     memcpy(pixmap, clipptr->cdata, w * h * 4);
5442                 } else {
5443                     // get the clip data
5444                     unsigned int *ldata = clipdata;
5445                     int cliprowpixels = clipptr->cwd;
5446                     int rowoffset = yoff;
5447 
5448                     // check for clipping
5449                     int xmax = x + w - 1;
5450                     int ymax = y + h - 1;
5451                     if (x < 0) {
5452                         // skip pixels off left edge
5453                         ldata += -x;
5454                         x = 0;
5455                     }
5456                     if (y < 0) {
5457                         // skip pixels off top edge
5458                         ldata += -y * cliprowpixels;
5459                         rowoffset += -y;
5460                         y = 0;
5461                     }
5462                     if (xmax >= wd) xmax = wd - 1;
5463                     if (ymax >= ht) ymax = ht - 1;
5464                     w = xmax - x + 1;
5465                     h = ymax - y + 1;
5466 
5467                     // get the paste target data
5468                     int targetrowpixels = wd;
5469                     unsigned int *lp = (unsigned int*)pixmap;
5470                     lp += y * targetrowpixels + x;
5471                     unsigned int source, dest, pa, alpha, invalpha;
5472 
5473                     // check for alpha blending
5474                     if (alphablend) {
5475                         // alpha blending
5476                         const rowtype *rowindex = clipptr->rowindex;
5477                         if (!rowindex) {
5478                             // clip only has mixed alpha rows
5479                             for (int j = 0; j < h; j++) {
5480                                 // row contains pixels with different alpha values
5481                                 if (alphablend == 1) {
5482                                     // full alpha blend
5483                                     for (int i = 0; i < w; i++) {
5484                                         // get the source pixel
5485                                         source = *ldata;
5486                                         pa = ALPHA2BYTE(source);
5487                                         if (pa < 255) {
5488                                             // source pixel is not opaque
5489                                             if (pa) {
5490                                                 // source pixel is translucent so blend with destination pixel
5491                                                 alpha = pa + 1;
5492                                                 invalpha = 256 - pa;
5493                                                 dest = *lp;
5494                                                 ALPHABLEND(source, dest, lp, alpha, invalpha);
5495                                             }
5496                                         } else {
5497                                             // pixel is opaque so copy it
5498                                             *lp = source;
5499                                         }
5500                                         lp++;
5501                                         ldata++;
5502                                     }
5503                                 } else {
5504                                     // fast alpha blend (opaque destination)
5505                                     for (int i = 0; i < w; i++) {
5506                                         source = *ldata++;
5507                                         pa = ALPHA2BYTE(source);
5508                                         alpha = pa + 1;
5509                                         invalpha = 256 - pa;
5510                                         dest = *lp;
5511                                         ALPHABLENDOPAQUEDEST(source, dest, lp, alpha, invalpha);
5512                                         lp++;
5513                                     }
5514                                 }
5515                                 // next clip and target row
5516                                 lp += targetrowpixels - w;
5517                                 ldata += cliprowpixels - w;
5518                             }
5519                         } else {
5520                             for (int j = rowoffset; j < h + rowoffset; j++) {
5521                                 rowtype rowflag = rowindex[j];
5522                                 switch (rowflag) {
5523                                 case alpha0:
5524                                     // if all pixels are transparent then skip the row
5525                                     ldata += w;
5526                                     lp += w;
5527                                     break;
5528                                 case opaque:
5529                                     // if all pixels are opaque then use memcpy
5530                                     memcpy(lp, ldata, w << 2);
5531                                     ldata += w;
5532                                     lp += w;
5533                                     break;
5534                                 case both:
5535                                     // row contains mix of opaque and transparent pixels
5536                                     for (int i = 0; i < w; i++) {
5537                                         source = *ldata++;
5538                                         // copy pixel if not transparent
5539                                         if (source & AMASK) {
5540                                             *lp = source;
5541                                         }
5542                                         lp++;
5543                                     }
5544                                     break;
5545                                 case mixed:
5546                                     // row contains pixels with different alpha values
5547                                     if (alphablend == 1) {
5548                                         // full alpha blend
5549                                         for (int i = 0; i < w; i++) {
5550                                             // get the source pixel
5551                                             source = *ldata;
5552                                             pa = ALPHA2BYTE(source);
5553                                             if (pa < 255) {
5554                                                 // source pixel is not opaque
5555                                                 if (pa) {
5556                                                     // source pixel is translucent so blend with destination pixel
5557                                                     alpha = pa + 1;
5558                                                     invalpha = 256 - pa;
5559                                                     dest = *lp;
5560                                                     ALPHABLEND(source, dest, lp, alpha, invalpha);
5561                                                 }
5562                                             } else {
5563                                                 // pixel is opaque so copy it
5564                                                 *lp = source;
5565                                             }
5566                                             lp++;
5567                                             ldata++;
5568                                         }
5569                                     } else {
5570                                         // fast alpha blend (destination is opaque)
5571                                         for (int i = 0; i < w; i++) {
5572                                             source = *ldata++;
5573                                             pa = ALPHA2BYTE(source);
5574                                             alpha = pa + 1;
5575                                             invalpha = 256 - pa;
5576                                             dest = *lp;
5577                                             ALPHABLENDOPAQUEDEST(source, dest, lp, alpha, invalpha);
5578                                             lp++;
5579                                         }
5580                                     }
5581                                     break;
5582                                 }
5583                                 // next clip and target row
5584                                 lp += targetrowpixels - w;
5585                                 ldata += cliprowpixels - w;
5586                             }
5587                         }
5588                     } else {
5589                         // no alpha blending
5590                         for (int j = 0; j < h; j++) {
5591                             // copy each row with memcpy
5592                             memcpy(lp, ldata, w << 2);
5593                             lp += targetrowpixels;
5594                             ldata += cliprowpixels;
5595                         }
5596                     }
5597                 }
5598             } else {
5599                 // do an affine transformation
5600                 unsigned int *data = (unsigned int*)clipptr->cdata;
5601                 w = clipptr->cwd;
5602                 h = clipptr->cht;
5603                 x -= xoff;
5604                 y -= yoff;
5605                 const int x0 = x - (x * axx + y * axy);
5606                 const int y0 = y - (x * ayx + y * ayy);
5607 
5608                 // check for alpha blend
5609                 if (alphablend) {
5610                     // save RGBA values
5611                     unsigned int savergba = rgbadraw;
5612                     unsigned char savea = a;
5613 
5614                     for (int j = 0; j < h; j++) {
5615                         for (int i = 0; i < w; i++) {
5616                             rgbadraw = *data++;
5617                             a = ALPHA2BYTE(rgbadraw);
5618                             int newx = x0 + x * axx + y * axy;
5619                             int newy = y0 + x * ayx + y * ayy;
5620                             if (PixelInTarget(newx, newy)) DrawPixel(newx, newy);
5621                             x++;
5622                         }
5623                         y++;
5624                         x -= w;
5625                     }
5626 
5627                     // restore saved RGBA values
5628                     rgbadraw = savergba;
5629                     a = savea;
5630                 } else {
5631                     // no alpha blend
5632                     unsigned int *ldata = (unsigned int*)data;
5633                     unsigned int *lp = (unsigned int*)pixmap;
5634                     for (int j = 0; j < h; j++) {
5635                         for (int i = 0; i < w; i++) {
5636                             int newx = x0 + x * axx + y * axy;
5637                             int newy = y0 + x * ayx + y * ayy;
5638                             if (PixelInTarget(newx, newy)) *(lp + newy * wd + newx) = *ldata;
5639                             ldata++;
5640                             x++;
5641                         }
5642                         y++;
5643                         x -= w;
5644                     }
5645                 }
5646             }
5647         }
5648     }
5649     while ((copy = (char*)GetCoordinatePair(copy, &x, &y)) != 0);
5650 
5651     // free the buffer
5652     free(buffer);
5653 
5654     return NULL;
5655 }
5656 
5657 // assumes alpha blend, identity transformation and opaque destination pixels
Draw3DCell(int x,int y,const Clip * clipptr)5658 void Overlay::Draw3DCell(int x, int y, const Clip *clipptr)
5659 {
5660     // check that a clip is supplied
5661     if (clipptr == NULL) return;
5662 
5663     // add bounding box to drawing location
5664     y += clipptr->ybb;
5665     x += clipptr->xbb;
5666 
5667     // get bounding box width and height
5668     int h = clipptr->hbb;
5669     int w = clipptr->wbb;
5670 
5671     // discard if location is completely outside target
5672     if (RectOutsideTarget(x, y, w, h)) return;
5673 
5674     // get the clip data
5675     unsigned int *ldata = (unsigned int*)clipptr->cdatabb;
5676     const int cliprowpixels = clipptr->cwd;
5677     int rowoffset = clipptr->ybb;
5678 
5679     // check for clipping
5680     int xmax = x + w - 1;
5681     int ymax = y + h - 1;
5682     if (x < 0) {
5683         // skip pixels off left edge
5684         ldata += -x;
5685         x = 0;
5686     }
5687     if (y < 0) {
5688         // skip pixels off top edge
5689         ldata += -y * cliprowpixels;
5690         rowoffset -= y;
5691         y = 0;
5692     }
5693     if (xmax >= wd) xmax = wd - 1;
5694     if (ymax >= ht) ymax = ht - 1;
5695     w = xmax - x + 1;
5696     h = ymax - y + 1;
5697 
5698     // get the paste target data
5699     const int targetrowpixels = wd;
5700     unsigned int *lp = ((unsigned int*)pixmap) + y * targetrowpixels + x;
5701     unsigned int source, dest, pa, alpha, invalpha;
5702 
5703     // check if the clip has a row index
5704     const rowtype *rowindex = clipptr->rowindex;
5705     if (!rowindex) {
5706         // clip only has mixed alpha rows
5707         for (int j = 0; j < h; j++) {
5708             // row contains pixels with different alpha values
5709             for (int i = 0; i < w; i++) {
5710                 source = *ldata++;
5711                 pa = ALPHA2BYTE(source);
5712                 alpha = pa + 1;
5713                 invalpha = 256 - pa;
5714                 dest = *lp;
5715                 ALPHABLENDOPAQUEDEST(source, dest, lp, alpha, invalpha);
5716                 lp++;
5717             }
5718             // next clip and target row
5719             lp += targetrowpixels - w;
5720             ldata += cliprowpixels - w;
5721         }
5722     } else {
5723         for (int j = rowoffset; j < h + rowoffset; j++) {
5724             rowtype rowflag = rowindex[j];
5725             switch (rowflag) {
5726             case alpha0:
5727                 // if all pixels are transparent then skip the row
5728                 ldata += w;
5729                 lp += w;
5730                 break;
5731             case opaque:
5732                 // if all pixels are opaque then use memcpy
5733                 memcpy(lp, ldata, w << 2);
5734                 ldata += w;
5735                 lp += w;
5736                 break;
5737             case both:
5738                 // row contains mix of opaque and transparent pixels
5739                 for (int i = 0; i < w; i++) {
5740                     source = *ldata++;
5741                     // copy pixel if not transparent
5742                     if (source & AMASK) {
5743                         *lp = source;
5744                     }
5745                     lp++;
5746                 }
5747                 break;
5748             case mixed:
5749                 // row contains pixels with different alpha values
5750                 for (int i = 0; i < w; i++) {
5751                     source = *ldata++;
5752                     pa = ALPHA2BYTE(source);
5753                     alpha = pa + 1;
5754                     invalpha = 256 - pa;
5755                     dest = *lp;
5756                     ALPHABLENDOPAQUEDEST(source, dest, lp, alpha, invalpha);
5757                     lp++;
5758                 }
5759                 break;
5760             }
5761             // next clip and target row
5762             lp += targetrowpixels - w;
5763             ldata += cliprowpixels - w;
5764         }
5765     }
5766 }
5767 
5768 // -----------------------------------------------------------------------------
5769 
DoScale(const char * args)5770 const char *Overlay::DoScale(const char *args)
5771 {
5772     if (pixmap == NULL) return OverlayError(no_overlay);
5773 
5774     wxImageResizeQuality quality;
5775     if (strncmp(args, " best ", 6) == 0) {
5776         quality = wxIMAGE_QUALITY_HIGH;
5777         args += 6;
5778     } else if (strncmp(args, " fast ", 6) == 0) {
5779         quality = wxIMAGE_QUALITY_NORMAL;
5780         args += 6;
5781     } else {
5782         return OverlayError("scale quality must be best or fast");
5783     }
5784 
5785     int x, y, w, h;
5786     int namepos;
5787     char dummy;
5788     if (sscanf(args, "%d %d %d %d %n%c", &x, &y, &w, &h, &namepos, &dummy) != 5) {
5789         // note that %n is not included in the count
5790         return OverlayError("scale command requires 5 arguments");
5791     }
5792 
5793     // treat non-positive w/h as inset from target's width/height
5794     if (w <= 0) w = wd + w;
5795     if (h <= 0) h = ht + h;
5796     if (w <= 0) return OverlayError("scale width must be > 0");
5797     if (h <= 0) return OverlayError("scale height must be > 0");
5798 
5799     std::string name = args + namepos;
5800     std::map<std::string,Clip*>::iterator it;
5801     it = clips.find(name);
5802     if (it == clips.end()) {
5803         static std::string msg;
5804         msg = "unknown scale clip (";
5805         msg += name;
5806         msg += ")";
5807         return OverlayError(msg.c_str());
5808     }
5809 
5810     // do nothing if scaled rect is completely outside target
5811     if (RectOutsideTarget(x, y, w, h)) return NULL;
5812 
5813     Clip *clipptr = it->second;
5814     int clipw = clipptr->cwd;
5815     int cliph = clipptr->cht;
5816 
5817     if (w > clipw && w % clipw == 0 &&
5818         h > cliph && h % cliph == 0 && quality == wxIMAGE_QUALITY_NORMAL) {
5819         // no need to create a wxImage to expand pixels by integer multiples
5820         DisableTargetClipIndex();
5821         int xscale = w / clipw;
5822         int yscale = h / cliph;
5823         unsigned int *data = (unsigned int*)clipptr->cdata;
5824 
5825         // save current RGBA values
5826         unsigned int savergba = rgbadraw;
5827         unsigned char savea = a;
5828 
5829         if (RectInsideTarget(x, y, w, h)) {
5830             for (int j = 0; j < cliph; j++) {
5831                 for (int i = 0; i < clipw; i++) {
5832                     rgbadraw = *data++;
5833                     a = ALPHA2BYTE(rgbadraw);
5834                     FillRect(x, y, xscale, yscale);
5835                     x += xscale;
5836                 }
5837                 y += yscale;
5838                 x -= clipw * xscale;
5839             }
5840         } else {
5841             for (int j = 0; j < cliph; j++) {
5842                 for (int i = 0; i < clipw; i++) {
5843                     rgbadraw = *data++;
5844                     a = ALPHA2BYTE(rgbadraw);
5845                     if (RectOutsideTarget(x, y, xscale, yscale)) {
5846                         // expanded pixel is outside target
5847                     } else {
5848                         for (int row = 0; row < yscale; row++) {
5849                             for (int col = 0; col < xscale; col++) {
5850                                 if (PixelInTarget(x+col, y+row)) DrawPixel(x+col, y+row);
5851                             }
5852                         }
5853                     }
5854                     x += xscale;
5855                 }
5856                 y += yscale;
5857                 x -= clipw * xscale;
5858             }
5859         }
5860 
5861         // restore saved RGBA values
5862         rgbadraw = savergba;
5863         a = savea;
5864 
5865         return NULL;
5866     }
5867 
5868     // get the clip's RGB and alpha data so we can create a wxImage
5869     unsigned char *rgbdata = (unsigned char*) malloc(clipw * cliph * 3);
5870     if (rgbdata== NULL) {
5871         return OverlayError("not enough memory to scale rgb data");
5872     }
5873     unsigned char *alphadata = (unsigned char*) malloc(clipw * cliph);
5874     if (alphadata == NULL) {
5875         free(rgbdata);
5876         return OverlayError("not enough memory to scale alpha data");
5877     }
5878 
5879     unsigned char *p = clipptr->cdata;
5880     int rgbpos = 0;
5881     int alphapos = 0;
5882     for (int j = 0; j < cliph; j++) {
5883         for (int i = 0; i < clipw; i++) {
5884             rgbdata[rgbpos++] = *p++;
5885             rgbdata[rgbpos++] = *p++;
5886             rgbdata[rgbpos++] = *p++;
5887             alphadata[alphapos++] = *p++;
5888         }
5889     }
5890 
5891     // create wxImage with the given clip's size and using its RGB and alpha data;
5892     // static_data flag is false so wxImage dtor will free rgbdata and alphadata
5893     wxImage image(clipw, cliph, rgbdata, alphadata, false);
5894 
5895     // scale the wxImage to the requested width and height
5896     image.Rescale(w, h, quality);
5897 
5898     // mark target clip as changed
5899     DisableTargetClipIndex();
5900 
5901     // save current RGBA values
5902     unsigned int savergba = rgbadraw;
5903     unsigned char savea = a;
5904 
5905     // copy the pixels from the scaled wxImage into the current target
5906     unsigned char *rdata = image.GetData();
5907     unsigned char *adata = image.GetAlpha();
5908     rgbpos = 0;
5909     alphapos = 0;
5910     for (int j = 0; j < h; j++) {
5911         for (int i = 0; i < w; i++) {
5912             if (PixelInTarget(x, y)) {
5913                 rgbadraw =  BYTE2RED(rdata[rgbpos++]);
5914                 rgbadraw |= BYTE2GREEN(rdata[rgbpos++]);
5915                 rgbadraw |= BYTE2BLUE(rdata[rgbpos++]);
5916                 rgbadraw |= BYTE2ALPHA(adata[alphapos++]);
5917                 a = ALPHA2BYTE(rgbadraw);
5918                 DrawPixel(x, y);
5919             } else {
5920                 rgbpos += 3;
5921                 alphapos++;
5922             }
5923             x++;
5924         }
5925         y++;
5926         x -= w;
5927     }
5928 
5929     // restore saved RGBA values
5930     rgbadraw = savergba;
5931     a = savea;
5932 
5933     return NULL;
5934 }
5935 
5936 // -----------------------------------------------------------------------------
5937 
DoTarget(const char * args)5938 const char *Overlay::DoTarget(const char *args)
5939 {
5940     if (pixmap == NULL) return OverlayError(no_overlay);
5941 
5942     int namepos;
5943     char dummy;
5944     int numargs = sscanf(args, " %n%c", &namepos, &dummy);
5945     if (numargs != 1) {
5946         if (*args == 0 || *args == ' ') {
5947             numargs = 0;
5948         } else {
5949             return OverlayError("target command requires 0 or 1 arguments");
5950         }
5951     }
5952 
5953     // previous target name
5954     static std::string result;
5955     result = targetname;
5956 
5957     // no arguments means overlay is the target
5958     if (numargs == 0) {
5959         SetRenderTarget(ovpixmap, ovwd, ovht, NULL);
5960         targetname = "";
5961     } else {
5962         // one argument means clip is the target
5963         std::string name = args + namepos;
5964         std::map<std::string,Clip*>::iterator it;
5965         it = clips.find(name);
5966         if (it == clips.end()) {
5967             static std::string msg;
5968             msg = "unknown target name (";
5969             msg += name;
5970             msg += ")";
5971             return OverlayError(msg.c_str());
5972         } else {
5973             // set clip as the target
5974             Clip *clipptr = it->second;
5975             SetRenderTarget(clipptr->cdata, clipptr->cwd, clipptr->cht, clipptr);
5976             targetname = name;
5977         }
5978     }
5979 
5980     // return previous target
5981     return result.c_str();
5982 }
5983 
5984 // -----------------------------------------------------------------------------
5985 
DoDelete(const char * args)5986 const char *Overlay::DoDelete(const char *args)
5987 {
5988     if (pixmap == NULL) return OverlayError(no_overlay);
5989 
5990     // check for optional clip name
5991     int namepos;
5992     char dummy;
5993     int numargs = sscanf(args, " %n%c", &namepos, &dummy);
5994     if (numargs != 1) {
5995         if (*args == 0 || *args == ' ') {
5996             numargs = 0;
5997         } else {
5998             return OverlayError("delete command requires 0 or 1 arguments");
5999         }
6000     }
6001 
6002     // was optional clip name specified
6003     if (numargs == 0) {
6004         // no so delete overlay
6005         DeleteOverlay();
6006     } else {
6007         // yes so look up clip by name
6008         std::string name = args + namepos;
6009         std::map<std::string,Clip*>::iterator it;
6010         it = clips.find(name);
6011         if (it == clips.end()) {
6012             static std::string msg;
6013             msg = "unknown delete clip (";
6014             msg += name;
6015             msg += ")";
6016             return OverlayError(msg.c_str());
6017         }
6018         // check if the clip is the current render target
6019         if (name == targetname) {
6020             return OverlayError("delete clip is current render target");
6021         } else {
6022             // delete the clip
6023             delete it->second;
6024             clips.erase(it);
6025         }
6026     }
6027 
6028     return NULL;
6029 }
6030 
6031 // -----------------------------------------------------------------------------
6032 
DoLoad(const char * args)6033 const char *Overlay::DoLoad(const char *args)
6034 {
6035     if (pixmap == NULL) return OverlayError(no_overlay);
6036 
6037     int x, y;
6038     int filepos;
6039     char dummy;
6040     if (sscanf(args, " %d %d %n%c", &x, &y, &filepos, &dummy) != 3) {
6041         // note that %n is not included in the count
6042         return OverlayError("load command requires 3 arguments");
6043     }
6044 
6045     wxString filepath = wxString(args + filepos, wxConvLocal);
6046     if (!wxFileExists(filepath)) {
6047         return OverlayError("given file does not exist");
6048     }
6049 
6050     wxImage image;
6051     if (!image.LoadFile(filepath)) {
6052         return OverlayError("failed to load image from given file");
6053     }
6054 
6055     int imgwd = image.GetWidth();
6056     int imght = image.GetHeight();
6057     if (RectOutsideTarget(x, y, imgwd, imght)) {
6058         // do nothing if image rect is completely outside target,
6059         // but we still return the image dimensions so users can do things
6060         // like center the image within the target
6061     } else {
6062         // mark target clip as changed
6063         DisableTargetClipIndex();
6064 
6065         // use alpha data if it exists otherwise try looking for mask
6066         unsigned char *alphadata = NULL;
6067         if (image.HasAlpha()) {
6068             alphadata = image.GetAlpha();
6069         }
6070         unsigned char maskr = 0;
6071         unsigned char maskg = 0;
6072         unsigned char maskb = 0;
6073         bool hasmask = false;
6074         if (alphadata == NULL) {
6075             hasmask = image.GetOrFindMaskColour(&maskr, &maskg, &maskb);
6076         }
6077 
6078         // save current RGBA values
6079         unsigned int savergba = rgbadraw;
6080         unsigned char saver = r;
6081         unsigned char saveg = g;
6082         unsigned char saveb = b;
6083         unsigned char savea = a;
6084 
6085         unsigned char *rgbdata = image.GetData();
6086         int rgbpos = 0;
6087         int alphapos = 0;
6088         for (int j = 0; j < imght; j++) {
6089             for (int i = 0; i < imgwd; i++) {
6090                 r = rgbdata[rgbpos++];
6091                 g = rgbdata[rgbpos++];
6092                 b = rgbdata[rgbpos++];
6093                 if (alphadata) {
6094                     a = alphadata[alphapos++];
6095                 } else if (hasmask && r == maskr && g == maskg && b == maskb) {
6096                     // transparent pixel
6097                     a = 0;
6098                 } else {
6099                     a = 255;
6100                 }
6101                 rgbadraw = BYTE2RED(r) | BYTE2GREEN(g) | BYTE2BLUE(b) | BYTE2ALPHA(a);
6102                 if (PixelInTarget(x, y)) DrawPixel(x, y);
6103                 x++;
6104             }
6105             y++;
6106             x -= imgwd;
6107         }
6108 
6109         // restore saved RGBA values
6110         rgbadraw = savergba;
6111         r = saver;
6112         g = saveg;
6113         b = saveb;
6114         a = savea;
6115     }
6116 
6117     // return image dimensions
6118     static char result[32];
6119     sprintf(result, "%d %d", imgwd, imght);
6120     return result;
6121 }
6122 
6123 // -----------------------------------------------------------------------------
6124 
DoSave(const char * args)6125 const char *Overlay::DoSave(const char *args)
6126 {
6127     if (pixmap == NULL) return OverlayError(no_overlay);
6128 
6129     int x, y, w, h;
6130     int filepos;
6131     char dummy;
6132     if (sscanf(args, " %d %d %d %d %n%c", &x, &y, &w, &h, &filepos, &dummy) != 5) {
6133         // note that %n is not included in the count
6134         return OverlayError("save command requires 5 arguments");
6135     }
6136 
6137     // treat non-positive w/h as inset from overlay's width/height
6138     // (makes it easy to save entire overlay via "save 0 0 0 0 foo.png")
6139     if (w <= 0) w = wd + w;
6140     if (h <= 0) h = ht + h;
6141     if (w <= 0) return OverlayError("save width must be > 0");
6142     if (h <= 0) return OverlayError("save height must be > 0");
6143 
6144     if (x < 0 || x+w > wd || y < 0 || y+h > ht) {
6145         return OverlayError("save rectangle must be within overlay");
6146     }
6147 
6148     wxString filepath = wxString(args + filepos, wxConvLocal);
6149     wxString ext = filepath.AfterLast('.');
6150     if (!ext.IsSameAs(wxT("png"),false)) {
6151         return OverlayError("save file must have a .png extension");
6152     }
6153 
6154     unsigned char *rgbdata = (unsigned char*) malloc(w * h * 3);
6155     if (rgbdata== NULL) {
6156         return OverlayError("not enough memory to save RGB data");
6157     }
6158     unsigned char *alphadata = (unsigned char*) malloc(w * h);
6159     if (alphadata == NULL) {
6160         free(rgbdata);
6161         return OverlayError("not enough memory to save alpha data");
6162     }
6163 
6164     int rgbpos = 0;
6165     int alphapos = 0;
6166     int rowbytes = wd * 4;
6167     for (int j=y; j<y+h; j++) {
6168         for (int i=x; i<x+w; i++) {
6169             // get pixel at i,j
6170             unsigned char *p = pixmap + j*rowbytes + i*4;
6171             rgbdata[rgbpos++] = p[0];
6172             rgbdata[rgbpos++] = p[1];
6173             rgbdata[rgbpos++] = p[2];
6174             alphadata[alphapos++] = p[3];
6175         }
6176     }
6177 
6178     // create image of requested size using the given RGB and alpha data;
6179     // static_data flag is false so wxImage dtor will free rgbdata and alphadata
6180     wxImage image(w, h, rgbdata, alphadata, false);
6181 
6182     if (!image.SaveFile(filepath)) {
6183         return OverlayError("failed to save image in given file");
6184     }
6185 
6186     return NULL;
6187 }
6188 
6189 // -----------------------------------------------------------------------------
6190 
SaveOverlay(const wxString & pngpath)6191 void Overlay::SaveOverlay(const wxString &pngpath)
6192 {
6193     if (ovpixmap == NULL) {
6194         Warning(_("There is no overlay data to save!"));
6195         return;
6196     }
6197 
6198     unsigned char *rgbdata = (unsigned char*) malloc(ovwd * ovht * 3);
6199     if (rgbdata== NULL) {
6200         Warning(_("Not enough memory to copy RGB data."));
6201         return;
6202     }
6203     unsigned char *alphadata = (unsigned char*) malloc(ovwd * ovht);
6204     if (alphadata == NULL) {
6205         free(rgbdata);
6206         Warning(_("Not enough memory to copy alpha data."));
6207         return;
6208     }
6209 
6210     unsigned char *p = ovpixmap;
6211     int rgbpos = 0;
6212     int alphapos = 0;
6213     for (int j=0; j<ht; j++) {
6214         for (int i=0; i<wd; i++) {
6215             rgbdata[rgbpos++] = p[0];
6216             rgbdata[rgbpos++] = p[1];
6217             rgbdata[rgbpos++] = p[2];
6218             alphadata[alphapos++] = p[3];
6219             p += 4;
6220         }
6221     }
6222 
6223     // create image using the given RGB and alpha data;
6224     // static_data flag is false so wxImage dtor will free rgbdata and alphadata
6225     wxImage image(wd, ht, rgbdata, alphadata, false);
6226 
6227     if (!image.SaveFile(pngpath)) {
6228         Warning(_("Failed to save overlay in given file."));
6229     }
6230 }
6231 
6232 // -----------------------------------------------------------------------------
6233 
DoFlood(const char * args)6234 const char *Overlay::DoFlood(const char *args)
6235 {
6236     if (pixmap == NULL) return OverlayError(no_overlay);
6237 
6238     int x, y;
6239     if (sscanf(args, " %d %d", &x, &y) != 2) {
6240         return OverlayError("flood command requires 2 arguments");
6241     }
6242 
6243     // // check if x,y is outside pixmap
6244     if (!PixelInTarget(x, y)) return NULL;
6245 
6246     unsigned int *lp = (unsigned int*)pixmap;
6247     unsigned int oldpxl = *(lp + y * wd + x);
6248 
6249     // do nothing if color of given pixel matches current RGBA values
6250     if (oldpxl == rgbadraw) return NULL;
6251 
6252     // mark target clip as changed
6253     DisableTargetClipIndex();
6254 
6255     // do flood fill using fast scanline algorithm
6256     // (based on code at http://lodev.org/cgtutor/floodfill.html)
6257     bool slowdraw = alphablend && a < 255;
6258     int maxyv = ht - 1;
6259     std::vector<int> xcoord;
6260     std::vector<int> ycoord;
6261     xcoord.push_back(x);
6262     ycoord.push_back(y);
6263     while (!xcoord.empty()) {
6264         // get pixel coords from end of vectors
6265         x = xcoord.back();
6266         y = ycoord.back();
6267         xcoord.pop_back();
6268         ycoord.pop_back();
6269 
6270         bool above = false;
6271         bool below = false;
6272 
6273         unsigned int *newpxl = lp + y * wd + x;
6274         while (x >= 0 && *newpxl == oldpxl) {
6275             x--;
6276             newpxl--;
6277         }
6278         x++;
6279         newpxl++;
6280 
6281         while (x < wd && *newpxl == oldpxl) {
6282             if (slowdraw) {
6283                 // pixel is within pixmap
6284                 DrawPixel(x,y);
6285             } else {
6286                 *newpxl = rgbadraw;
6287             }
6288 
6289             if (y > 0) {
6290                 unsigned int *apxl = newpxl - wd;    // pixel at x, y-1
6291 
6292                 if (!above && *apxl == oldpxl) {
6293                     xcoord.push_back(x);
6294                     ycoord.push_back(y-1);
6295                     above = true;
6296                 } else if (above && !(*apxl == oldpxl)) {
6297                     above = false;
6298                 }
6299             }
6300 
6301             if (y < maxyv) {
6302                 unsigned int *bpxl = newpxl + wd;    // pixel at x, y+1
6303 
6304                 if (!below && *bpxl == oldpxl) {
6305                     xcoord.push_back(x);
6306                     ycoord.push_back(y+1);
6307                     below = true;
6308                 } else if (below && !(*bpxl == oldpxl)) {
6309                     below = false;
6310                 }
6311             }
6312 
6313             x++;
6314             newpxl++;
6315         }
6316     }
6317 
6318     return NULL;
6319 }
6320 
6321 // -----------------------------------------------------------------------------
6322 
DoBlend(const char * args)6323 const char *Overlay::DoBlend(const char *args)
6324 {
6325     if (pixmap == NULL) return OverlayError(no_overlay);
6326 
6327     int i;
6328     if (sscanf(args, " %d", &i) != 1) {
6329         return OverlayError("blend command requires 1 argument");
6330     }
6331 
6332     if (i < 0 || i > 2) {
6333         return OverlayError("blend value must be 0, 1 or 2");
6334     }
6335 
6336     int oldblend = alphablend;
6337     alphablend = i;
6338 
6339     // return old value
6340     static char result[2];
6341     sprintf(result, "%d", oldblend);
6342     return result;
6343 }
6344 
6345 // -----------------------------------------------------------------------------
6346 
DoFont(const char * args)6347 const char *Overlay::DoFont(const char *args)
6348 {
6349     if (pixmap == NULL) return OverlayError(no_overlay);
6350 
6351     bool samename = false;      // only change font size?
6352     const char *newname = NULL;
6353     int newsize;
6354     int namepos;
6355     char dummy;
6356     int numargs = sscanf(args, " %d %n%c", &newsize, &namepos, &dummy);
6357     if (numargs == 1) {
6358         samename = true;
6359     } else if (numargs != 2) {
6360         // note that %n is not included in the count
6361         return OverlayError("font command requires 1 or 2 arguments");
6362     }
6363 
6364     if (newsize <= 0 || newsize >= 1000) {
6365         return OverlayError("font size must be > 0 and < 1000");
6366     }
6367 
6368     #ifdef __WXMAC__
6369         // need to increase Mac font size by 25% to match text size on Win/Linux
6370         int ptsize = int(newsize * 1.25 + 0.5);
6371 
6372         // set extraht to avoid GetTextExtent bug that clips descenders
6373         extraht = 1;
6374         if (strncmp(args+namepos, "default", 7) == 0 &&
6375             (newsize == 20 || newsize == 24 || newsize == 47)) {
6376             // strange but true
6377             extraht = 2;
6378         }
6379     #else
6380         int ptsize = newsize;
6381     #endif
6382 
6383     if (samename) {
6384         // just change the current font's size
6385         currfont.SetPointSize(ptsize);
6386 
6387     } else {
6388         newname = args + namepos;
6389 
6390         // check if given font name is valid
6391         if (strcmp(newname, "default") == 0) {
6392             currfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
6393 
6394         } else if (strcmp(newname, "default-bold") == 0) {
6395             currfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
6396             currfont.SetWeight(wxFONTWEIGHT_BOLD);
6397 
6398         } else if (strcmp(newname, "default-italic") == 0) {
6399             currfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
6400             currfont.SetStyle(wxFONTSTYLE_ITALIC);
6401 
6402         } else if (strcmp(newname, "mono") == 0) {
6403             currfont = wxFont(ptsize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
6404 
6405         } else if (strcmp(newname, "mono-bold") == 0) {
6406             currfont = wxFont(ptsize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
6407 
6408         } else if (strcmp(newname, "mono-italic") == 0) {
6409             currfont = wxFont(ptsize, wxFONTFAMILY_MODERN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL);
6410 
6411         } else if (strcmp(newname, "roman") == 0) {
6412             currfont = wxFont(ptsize, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
6413 
6414         } else if (strcmp(newname, "roman-bold") == 0) {
6415             currfont = wxFont(ptsize, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
6416 
6417         } else if (strcmp(newname, "roman-italic") == 0) {
6418             currfont = wxFont(ptsize, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL);
6419 
6420         } else {
6421             return OverlayError("unknown font name");
6422         }
6423 
6424         // note that calling SetPointSize here avoids a bug in wxFont
6425         // that causes a 70pt font to end up as 8pt
6426         currfont.SetPointSize(ptsize);
6427     }
6428 
6429     int oldfontsize = fontsize;
6430     std::string oldfontname = fontname;
6431 
6432     fontsize = newsize;
6433     if (!samename) fontname = newname;
6434 
6435     // return old fontsize and fontname
6436     char ibuff[16];
6437     sprintf(ibuff, "%d", oldfontsize);
6438     static std::string result;
6439     result = ibuff;
6440     result += " ";
6441     result += oldfontname;
6442     return result.c_str();
6443 }
6444 
6445 // -----------------------------------------------------------------------------
6446 
TextOptionAlign(const char * args)6447 const char *Overlay::TextOptionAlign(const char *args)
6448 {
6449     text_alignment newalign;
6450 
6451     // check the specified alignment
6452     if (strcmp(args, "left") == 0) {
6453         newalign = left;
6454     } else if (strcmp(args, "right") == 0) {
6455         newalign = right;
6456     } else if (strcmp(args, "center") == 0) {
6457         newalign = center;
6458     } else {
6459         return OverlayError("unknown text alignment");
6460     }
6461 
6462     // get old value as string
6463     static char result[8];
6464 
6465     if (align == left) {
6466         sprintf(result, "left");
6467     } else if (align == right) {
6468         sprintf(result, "right");
6469     } else {
6470         sprintf(result, "center");
6471     }
6472 
6473     // save alignment settings
6474     align = newalign;
6475 
6476     return result;
6477 }
6478 
6479 // -----------------------------------------------------------------------------
6480 
TextOptionBackground(const char * args)6481 const char *Overlay::TextOptionBackground(const char *args)
6482 {
6483     int a1, a2, a3, a4;
6484     if (sscanf(args, " %d %d %d %d", &a1, &a2, &a3, &a4) != 4) {
6485         return OverlayError("textoption background command requires 4 arguments");
6486     }
6487 
6488     if (a1 < 0 || a1 > 255 ||
6489         a2 < 0 || a2 > 255 ||
6490         a3 < 0 || a3 > 255 ||
6491         a4 < 0 || a4 > 255) {
6492         return OverlayError("background rgba values must be from 0 to 255");
6493     }
6494 
6495     unsigned char oldr;
6496     unsigned char oldg;
6497     unsigned char oldb;
6498     unsigned char olda;
6499     GetRGBA(&oldr, &oldg, &oldb, &olda, textbgRGBA);
6500 
6501     SetRGBA(a1, a2, a3, a4, &textbgRGBA);
6502 
6503     // return old values
6504     static char result[16];
6505     sprintf(result, "%hhu %hhu %hhu %hhu", oldr, oldg, oldb, olda);
6506     return result;
6507 }
6508 
6509 // -----------------------------------------------------------------------------
6510 
DoTextOption(const char * args)6511 const char *Overlay::DoTextOption(const char *args)
6512 {
6513     if (pixmap == NULL) return OverlayError(no_overlay);
6514 
6515     if (strncmp(args, "align ", 6) == 0)       return TextOptionAlign(args+6);
6516     if (strncmp(args, "background ", 11) == 0) return TextOptionBackground(args+11);
6517 
6518     return OverlayError("unknown textoption command");
6519 }
6520 
6521 // -----------------------------------------------------------------------------
6522 
DoText(const char * args)6523 const char *Overlay::DoText(const char *args)
6524 {
6525     if (pixmap == NULL) return OverlayError(no_overlay);
6526 
6527     // we don't use sscanf to parse the args because we want to allow the
6528     // text to start with a space
6529     int namepos = 0;
6530     int textpos = 0;
6531     const char *p = args;
6532     while (*p && *p == ' ') {
6533         namepos++;
6534         p++;
6535     }
6536     if (namepos > 0 && *p) {
6537         textpos = namepos;
6538         while (*p && *p != ' ') {
6539             textpos++;
6540             p++;
6541         }
6542         if (*p) p++;        // skip past space after clip name
6543         if (*p) {
6544             textpos++;
6545         } else {
6546             textpos = 0;    // no text supplied
6547         }
6548     }
6549     if (namepos == 0 || textpos == 0) {
6550         return OverlayError("text command requires 2 arguments");
6551     }
6552 
6553     std::string name = args + namepos;
6554     name = name.substr(0, name.find(" "));
6555     // check if the clip is the current render target
6556     if (name == targetname) {
6557         return OverlayError("text clip is current render target");
6558     }
6559 
6560     // create memory drawing context
6561     wxMemoryDC dc;
6562     dc.SetFont(currfont);
6563 
6564     // get the line height and descent (leading is ignored)
6565     wxString textstr = _("M");
6566     int textwd, descent, ignored;
6567     int lineht = 0;
6568     dc.GetTextExtent(textstr, &textwd, &lineht, &descent, &ignored);
6569 
6570     #ifdef __WXMAC__
6571         // increase lineht and descent on Mac to avoid clipping descenders
6572         lineht += extraht;
6573         descent += extraht;
6574     #endif
6575 
6576     // count lines
6577     char *textarg = (char*)args + textpos;
6578     char *index = textarg;
6579     int lines = 1;
6580     while (*index) {
6581         if (*index == '\n') lines++;
6582         index++;
6583     }
6584 
6585     // allocate buffers for line width and start position
6586     int *width = (int*) malloc(lines * sizeof(int));
6587     char **line = (char**) malloc(lines * sizeof(char*));
6588 
6589     // find first line
6590     char *textlines = textarg;
6591     index = strchr(textlines, '\n');
6592 
6593     // process each line of text to size the bitmap
6594     int bitmapwd = 0;
6595     int bitmapht = 0;
6596     int i = 0;
6597     do {
6598         // save pointer to the line start
6599         line[i] = textlines;
6600         if (index) {
6601             // null terminate line
6602             *index = 0;
6603         }
6604 
6605         // get the drawn string width
6606         textstr = wxString(textlines, wxConvUTF8);
6607         dc.GetTextExtent(textstr, &textwd, &ignored, &ignored, &ignored);
6608 
6609         // save the line width
6610         width[i] = textwd;
6611 
6612         // update bitmap width
6613         if (bitmapwd < textwd) bitmapwd = textwd;
6614 
6615         // update the bitmap height
6616         bitmapht += lineht;
6617 
6618         // next line
6619         if (index) {
6620             textlines = index + 1;
6621             index = strchr(textlines, '\n');
6622         }
6623         i++;
6624     } while (i < lines);
6625 
6626     // delete any existing clip data with the given name
6627     std::map<std::string,Clip*>::iterator it;
6628     it = clips.find(name);
6629     if (it != clips.end()) {
6630         delete it->second;
6631         clips.erase(it);
6632     }
6633 
6634     // create clip data with given name and big enough to enclose text
6635     Clip *textclip = new Clip(bitmapwd, bitmapht);
6636     if (textclip == NULL || textclip->cdata == NULL) {
6637         delete textclip;
6638         free(width);
6639         free(line);
6640         return OverlayError("not enough memory for text clip");
6641     }
6642 
6643     // get background color
6644     unsigned char bgr, bgg, bgb, bga;
6645     GetRGBA(&bgr, &bgg, &bgb, &bga, textbgRGBA);
6646     wxColour textbgcol(bgr, bgg, bgb, bga);
6647     wxColour transbgcol(255, 255, 255, 255);
6648 
6649     // get text foreground color
6650     wxColour textfgcol(r, g, b, a);
6651     wxColour transfgcol(255 - a, 255 - a, 255 - a, 255);
6652 
6653     // create the bitmap
6654     wxBitmap bitmap(bitmapwd, bitmapht, 32);
6655 
6656     // select the bitmap
6657     dc.SelectObject(bitmap);
6658 
6659     // create a rectangle to fill the bitmap
6660     wxRect rect(0, 0, bitmapwd, bitmapht);
6661     dc.SetPen(*wxTRANSPARENT_PEN);
6662     wxBrush brush(textbgcol);
6663 
6664     // if blending use transparent and replace later
6665     if (bga < 255) brush.SetColour(transbgcol);
6666 
6667     // fill the bitmap
6668     dc.SetBrush(brush);
6669     dc.DrawRectangle(rect);
6670     dc.SetBrush(wxNullBrush);
6671     dc.SetPen(wxNullPen);
6672 
6673     // set text background color to transparent
6674     dc.SetBackgroundMode(wxTRANSPARENT);
6675 
6676     // set text foreground color
6677     if (bga < 255) {
6678         dc.SetTextForeground(transfgcol);
6679     } else {
6680         dc.SetTextForeground(textfgcol);
6681     }
6682 
6683     // draw each text line
6684     int xpos = 0;
6685     int textrow = 0;
6686     for (i = 0; i < lines; i++) {
6687         // check if the line is empty
6688         if (*line[i]) {
6689             textstr = wxString(line[i], wxConvUTF8);
6690 
6691             // check text alignment
6692             xpos = 0;
6693             if (align != left) {
6694                 if (align == right) {
6695                     xpos = bitmapwd - width[i];
6696                 } else {
6697                     xpos = (bitmapwd - width[i]) / 2;
6698                 }
6699             }
6700 
6701             // draw text
6702             dc.DrawText(textstr, xpos, textrow);
6703         }
6704 
6705         // next line
6706         textrow += lineht;
6707     }
6708 
6709     // deallocate buffers
6710     free(width);
6711     free(line);
6712 
6713     // deselect the bitmap
6714     dc.SelectObject(wxNullBitmap);
6715 
6716     // copy text from top left corner of offscreen image into clip data
6717     unsigned int *m = (unsigned int*)textclip->cdata;
6718     unsigned char bitmapr;
6719     const unsigned int rgbdraw = rgbadraw & RGBMASK;
6720 
6721     // get iterator over bitmap data
6722     wxAlphaPixelData data(bitmap);
6723     wxAlphaPixelData::Iterator iter(data);
6724 
6725     // check for transparent background
6726     if (bga < 255) {
6727         // transparent so look for background pixels to swap
6728         for (int y = 0; y < bitmapht; y++) {
6729             wxAlphaPixelData::Iterator rowstart = iter;
6730             for (int x = 0; x < bitmapwd; x++) {
6731                 // get pixel RGB components
6732                 bitmapr = iter.Red();
6733 
6734                 if ((BYTE2RED(bitmapr) | BYTE2GREEN(iter.Green()) | BYTE2BLUE(iter.Blue())) == RGBMASK) {
6735                     // background found so replace with transparent pixel
6736                     *m++ = 0;
6737                 } else {
6738                     // foreground found so replace with foreground color and set alpha based on grayness
6739                     *m++ = rgbdraw | BYTE2ALPHA(255 - bitmapr);
6740                 }
6741 
6742                 // pre-increment is faster
6743                 ++iter;
6744             }
6745             iter = rowstart;
6746             iter.OffsetY(data, 1);
6747         }
6748     } else {
6749         // opaque background so just copy pixels
6750         for (int y = 0; y < bitmapht; y++) {
6751             wxAlphaPixelData::Iterator rowstart = iter;
6752             for (int x = 0; x < bitmapwd; x++) {
6753                 *m++ = BYTE2RED(iter.Red()) | BYTE2GREEN(iter.Green()) | BYTE2BLUE(iter.Blue()) | AMASK;
6754 
6755                 // pre-increment is faster
6756                 ++iter;
6757             }
6758             iter = rowstart;
6759             iter.OffsetY(data, 1);
6760         }
6761     }
6762 
6763     // create named clip for later use by paste, scale, etc
6764     clips[name] = textclip;
6765 
6766     // return text info
6767     static char result[48];
6768     sprintf(result, "%d %d %d", bitmapwd, bitmapht, descent);
6769     return result;
6770 }
6771 
6772 // -----------------------------------------------------------------------------
6773 
6774 #ifdef ENABLE_SOUND
SoundPlay(const char * args,bool loop)6775 const char *Overlay::SoundPlay(const char *args, bool loop)
6776 {
6777     // check for engine
6778     if (engine) {
6779         if (*args == 0) {
6780             if (loop) {
6781                 return OverlayError("sound loop requires an argument");
6782             } else {
6783                 return OverlayError("sound play requires an argument");
6784             }
6785         }
6786 
6787         // check for the optional volume argument
6788         float v = 1;
6789         const char *name = args;
6790 
6791         // skip name
6792         char *scan = (char*)args;
6793         while (*scan && *scan != ' ') {
6794             scan++;
6795         }
6796 
6797         // check if there is a volume argument
6798         if (*scan) {
6799             if (sscanf(scan, " %f", &v) == 1) {
6800                 if (v < 0.0 || v > 1.0) {
6801                     if (loop) {
6802                         return OverlayError("sound loop volume must be in the range 0 to 1");
6803                     } else {
6804                         return OverlayError("sound play volume must be in the range 0 to 1");
6805                     }
6806                 }
6807             }
6808 
6809             // null terminate name
6810             *scan = 0;
6811         }
6812 
6813         // lookup the sound source
6814         ISoundSource *source = engine->getSoundSource(name, false);
6815         if (!source) {
6816             // create and preload the sound source
6817             source = engine->addSoundSourceFromFile(name, ESM_AUTO_DETECT, true);
6818             if (!source) {
6819                 // don't throw error just return error message
6820                 return "could not find sound";
6821             }
6822         }
6823 
6824         // check if the sound exists
6825         ISound *sound = NULL;
6826         std::map<std::string,ISound*>::iterator it;
6827         it = sounds.find(name);
6828         if (it != sounds.end()) {
6829             // sound exists so drop it
6830             sound = it->second;
6831             if (!sound->isFinished()) {
6832                 sound->stop();
6833             }
6834             sound->drop();
6835             sounds.erase(it);
6836         }
6837 
6838         // prepare to play the sound (but pause it so we can adjust volume first)
6839         sound = engine->play2D(source, loop, true);
6840         if (!sound) {
6841             // don't throw error just return error message
6842             return "could not play sound";
6843         }
6844 
6845         // set the volume and then play
6846         sound->setVolume(v);
6847         sound->setIsPaused(false);
6848 
6849         // cache the sound
6850         sounds[name] = sound;
6851     }
6852 
6853     return NULL;
6854 }
6855 #endif
6856 
6857 // -----------------------------------------------------------------------------
6858 
6859 #ifdef ENABLE_SOUND
SoundStop(const char * args)6860 const char *Overlay::SoundStop(const char *args)
6861 {
6862     // check for engine
6863     if (engine) {
6864         // check for argument
6865         if (*args == 0) {
6866             // stop all sounds
6867             engine->stopAllSounds();
6868         } else {
6869             // skip whitespace
6870             while (*args == ' ') {
6871                 args++;
6872             }
6873 
6874             // stop named sound
6875             ISoundSource *source = engine->getSoundSource(args, false);
6876             if (source) {
6877                 // find the sound
6878                 std::map<std::string,ISound*>::iterator it;
6879                 it = sounds.find(args);
6880                 if (it != sounds.end()) {
6881                    // stop the sound
6882                    ISound *sound = it->second;
6883                    if (!sound->isFinished()) {
6884                        sound->stop();
6885                    }
6886                 }
6887             }
6888         }
6889     }
6890 
6891     return NULL;
6892 }
6893 #endif
6894 
6895 // -----------------------------------------------------------------------------
6896 
6897 #ifdef ENABLE_SOUND
SoundState(const char * args)6898 const char *Overlay::SoundState(const char *args)
6899 {
6900     bool playing = false;
6901     bool paused = false;
6902 
6903     // check for engine
6904     if (engine) {
6905         // check for argument
6906         if (*args == 0) {
6907             // see if any sounds are playing
6908             for (int i = 0; i < engine->getSoundSourceCount(); i++) {
6909                 if (engine->isCurrentlyPlaying(engine->getSoundSource(i))) {
6910                     playing = true;
6911                 }
6912             }
6913         } else {
6914             // skip whitespace
6915             while (*args == ' ') {
6916                 args++;
6917             }
6918 
6919             // see if named sound is playing
6920             ISoundSource *source = engine->getSoundSource(args, false);
6921             if (!source) {
6922                 return "unknown";
6923             } else {
6924                 // find the sound
6925                 std::map<std::string,ISound*>::iterator it;
6926                 it = sounds.find(args);
6927                 if (it != sounds.end()) {
6928                     ISound *sound = it->second;
6929                     if (sound->getIsPaused()) {
6930                         paused = true;
6931                     }
6932                     if (engine->isCurrentlyPlaying(source)) {
6933                         playing = true;
6934                     }
6935                 }
6936             }
6937         }
6938     }
6939 
6940     // return status as string
6941     if (paused && playing) {
6942         return "paused";
6943     } else {
6944         if (playing) {
6945             return "playing";
6946         } else {
6947             return "stopped";
6948         }
6949     }
6950 }
6951 #endif
6952 
6953 // -----------------------------------------------------------------------------
6954 
6955 #ifdef ENABLE_SOUND
SoundVolume(const char * args)6956 const char *Overlay::SoundVolume(const char *args)
6957 {
6958     // check for engine
6959     if (engine) {
6960         float v = 1;
6961         const char *name = args;
6962 
6963         // skip name
6964         char *scan = (char*)args;
6965         while (*scan && *scan != ' ') {
6966             scan++;
6967         }
6968 
6969         // check if there is a volume argument
6970         if (*scan) {
6971             if (sscanf(scan, " %f", &v) == 1) {
6972                 if (v < 0.0 || v > 1.0) {
6973                     return OverlayError("sound volume must be in the range 0 to 1");
6974                 }
6975             } else {
6976                 return OverlayError("sound volume command requires two arguments");
6977             }
6978 
6979             // null terminate name
6980             *scan = 0;
6981         }
6982 
6983         // lookup the sound
6984         ISoundSource *source = engine->getSoundSource(name, false);
6985         if (source) {
6986             // set the default volume for the source
6987             source->setDefaultVolume(v);
6988 
6989             // check if the sound is playing
6990             std::map<std::string,ISound*>::iterator it;
6991             it = sounds.find(name);
6992             if (it != sounds.end()) {
6993                // set the sound volume
6994                ISound *sound = it->second;
6995                if (!sound->isFinished()) {
6996                    sound->setVolume(v);
6997                }
6998             }
6999         }
7000     }
7001 
7002     return NULL;
7003 }
7004 #endif
7005 
7006 // -----------------------------------------------------------------------------
7007 
7008 #ifdef ENABLE_SOUND
SoundPause(const char * args)7009 const char *Overlay::SoundPause(const char *args)
7010 {
7011     // check for engine
7012     if (engine) {
7013         // check for argument
7014         if (*args == 0) {
7015             // pause all sounds
7016             engine->setAllSoundsPaused();
7017         } else {
7018             // skip whitespace
7019             while (*args == ' ') {
7020                 args++;
7021             }
7022 
7023             // pause named sound
7024             ISoundSource *source = engine->getSoundSource(args, false);
7025             if (source) {
7026                 // find the sound
7027                 std::map<std::string,ISound*>::iterator it;
7028                 it = sounds.find(args);
7029                 if (it != sounds.end()) {
7030                    // pause the sound
7031                    ISound *sound = it->second;
7032                    if (!sound->isFinished()) {
7033                        sound->setIsPaused();
7034                    }
7035                 }
7036             }
7037         }
7038     }
7039 
7040     return NULL;
7041 }
7042 #endif
7043 
7044 // -----------------------------------------------------------------------------
7045 
7046 #ifdef ENABLE_SOUND
SoundResume(const char * args)7047 const char *Overlay::SoundResume(const char *args)
7048 {
7049     // check for engine
7050     if (engine) {
7051         // check for argument
7052         if (*args == 0) {
7053             // resume all paused sounds
7054             engine->setAllSoundsPaused(false);
7055         } else {
7056             // skip whitespace
7057             while (*args == ' ') {
7058                 args++;
7059             }
7060 
7061             // resume named sound
7062             ISoundSource *source = engine->getSoundSource(args, false);
7063             if (source) {
7064                 // find the sound
7065                 std::map<std::string,ISound*>::iterator it;
7066                 it = sounds.find(args);
7067                 if (it != sounds.end()) {
7068                    // resume the sound
7069                    ISound *sound = it->second;
7070                    if (!sound->isFinished()) {
7071                        sound->setIsPaused(false);
7072                    }
7073                 }
7074             }
7075         }
7076     }
7077 
7078     return NULL;
7079 }
7080 #endif
7081 
7082 // -----------------------------------------------------------------------------
7083 
DoSound(const char * args)7084 const char *Overlay::DoSound(const char *args)
7085 {
7086     if (pixmap == NULL) return OverlayError(no_overlay);
7087 
7088     #ifdef ENABLE_SOUND
7089     // check for sound engine query
7090     if (!*args) {
7091         if (engine) {
7092             // sound engine enabled
7093             return "2";
7094         } else {
7095             // sound engine failed to start
7096             return "1";
7097         }
7098     }
7099 
7100     // skip whitespace
7101     while (*args == ' ') {
7102         args++;
7103     }
7104 
7105     // check which sound command is specified
7106     if (strncmp(args, "play ", 5) == 0)    return SoundPlay(args+5, false);
7107     if (strncmp(args, "loop ", 5) == 0)    return SoundPlay(args+5, true);
7108     if (strncmp(args, "stop", 4) == 0)     return SoundStop(args+4);
7109     if (strncmp(args, "state", 5) == 0)    return SoundState(args+5);
7110     if (strncmp(args, "volume ", 7) == 0)  return SoundVolume(args+7);
7111     if (strncmp(args, "pause", 5) == 0)    return SoundPause(args+5);
7112     if (strncmp(args, "resume", 6) == 0)   return SoundResume(args+6);
7113 
7114     return OverlayError("unknown sound command");
7115     #else
7116     // if sound support not enabled then just return
7117     return "0";
7118     #endif
7119 }
7120 
7121 // -----------------------------------------------------------------------------
7122 
DoTransform(const char * args)7123 const char *Overlay::DoTransform(const char *args)
7124 {
7125     if (pixmap == NULL) return OverlayError(no_overlay);
7126 
7127     int a1, a2, a3, a4;
7128     if (sscanf(args, " %d %d %d %d", &a1, &a2, &a3, &a4) != 4) {
7129         return OverlayError("transform command requires 4 arguments");
7130     }
7131 
7132     if (a1 < -1 || a1 > 1 ||
7133         a2 < -1 || a2 > 1 ||
7134         a3 < -1 || a3 > 1 ||
7135         a4 < -1 || a4 > 1) {
7136         return OverlayError("transform values must be 0, 1 or -1");
7137     }
7138 
7139     int oldaxx = axx;
7140     int oldaxy = axy;
7141     int oldayx = ayx;
7142     int oldayy = ayy;
7143 
7144     axx = a1;
7145     axy = a2;
7146     ayx = a3;
7147     ayy = a4;
7148     identity = (axx == 1) && (axy == 0) && (ayx == 0) && (ayy == 1);
7149 
7150     // return old values
7151     static char result[16];
7152     sprintf(result, "%d %d %d %d", oldaxx, oldaxy, oldayx, oldayy);
7153     return result;
7154 }
7155 
7156 // -----------------------------------------------------------------------------
7157 
OnlyDrawOverlay()7158 bool Overlay::OnlyDrawOverlay()
7159 {
7160     // only use the overlay
7161     if (ovpixmap == NULL) return false;
7162 
7163     if (only_draw_overlay) {
7164         // this flag must only be used for one refresh so reset it immediately
7165         only_draw_overlay = false;
7166         return showoverlay && !(numlayers > 1 && tilelayers);
7167     } else {
7168         return false;
7169     }
7170 }
7171 
7172 // -----------------------------------------------------------------------------
7173 
DoUpdate()7174 const char *Overlay::DoUpdate()
7175 {
7176     if (pixmap == NULL) return OverlayError(no_overlay);
7177 
7178     #ifdef ENABLE_SOUND
7179     // update sound engine (in case threading not supported)
7180     if (engine) {
7181         engine->update();
7182     }
7183     #endif
7184 
7185     if (mainptr->IsIconized()) return NULL;
7186 
7187     only_draw_overlay = true;
7188     viewptr->Refresh(false);
7189     viewptr->Update();
7190     // DrawView in wxrender.cpp will call OnlyDrawOverlay (see above)
7191 
7192     #ifdef __WXGTK__
7193         // needed on Linux to see update immediately
7194         insideYield = true;
7195         wxGetApp().Yield(true);
7196         insideYield = false;
7197     #endif
7198 
7199     return NULL;
7200 }
7201 
7202 // -----------------------------------------------------------------------------
7203 
OverlayError(const char * msg)7204 const char *Overlay::OverlayError(const char *msg)
7205 {
7206     static std::string err;
7207     err = "ERR:";
7208     err += msg;
7209     return err.c_str();
7210 }
7211 
7212 // -----------------------------------------------------------------------------
7213 
ReadLuaBoolean(lua_State * L,const int n,int i,bool * value,const char * name)7214 const char *Overlay::ReadLuaBoolean(lua_State *L, const int n, int i, bool *value, const char *name) {
7215     static std::string err;
7216     if (i > n)  {
7217         err = "missing argument: ";
7218         err += name;
7219         return OverlayError(err.c_str());
7220     }
7221     lua_rawgeti(L, 1, i);
7222     int type = lua_type(L, -1);
7223     if (type != LUA_TBOOLEAN) {
7224         lua_pop(L, 1);
7225         err = "argument is not a boolean: ";
7226         err += name;
7227         return OverlayError(err.c_str());
7228     }
7229     *value = (bool)lua_toboolean(L, -1);
7230     lua_pop(L, 1);
7231     return NULL;
7232 }
7233 
7234 // -----------------------------------------------------------------------------
7235 
ReadLuaNumber(lua_State * L,const int n,int i,double * value,const char * name)7236 const char *Overlay::ReadLuaNumber(lua_State *L, const int n, int i, double *value, const char *name) {
7237     static std::string err;
7238     if (i > n)  {
7239         err = "missing argument: ";
7240         err += name;
7241         return OverlayError(err.c_str());
7242     }
7243     lua_rawgeti(L, 1, i);
7244     int type = lua_type(L, -1);
7245     if (type != LUA_TNUMBER) {
7246         lua_pop(L, 1);
7247         err = "argument is not a number: ";
7248         err += name;
7249         return OverlayError(err.c_str());
7250     }
7251     *value = (double)lua_tonumber(L, -1);
7252     lua_pop(L, 1);
7253     return NULL;
7254 }
7255 
7256 // -----------------------------------------------------------------------------
7257 
ReadLuaInteger(lua_State * L,const int n,int i,int * value,const char * name)7258 const char *Overlay::ReadLuaInteger(lua_State *L, const int n, int i, int *value, const char *name) {
7259     static std::string err;
7260     if (i > n)  {
7261         err = "missing argument: ";
7262         err += name;
7263         return OverlayError(err.c_str());
7264     }
7265     lua_rawgeti(L, 1, i);
7266     int type = lua_type(L, -1);
7267     if (type != LUA_TNUMBER) {
7268         lua_pop(L, 1);
7269         err = "argument is not a number: ";
7270         err += name;
7271         return OverlayError(err.c_str());
7272     }
7273     *value = (int)lua_tonumber(L, -1);
7274     lua_pop(L, 1);
7275     return NULL;
7276 }
7277 
7278 // -----------------------------------------------------------------------------
7279 
ReadLuaString(lua_State * L,const int n,int i,const char ** value,const char * name)7280 const char *Overlay::ReadLuaString(lua_State *L, const int n, int i, const char **value, const char *name) {
7281     static std::string err;
7282     if (i > n)  {
7283         err = "missing argument: ";
7284         err += name;
7285         return OverlayError(err.c_str());
7286     }
7287     lua_rawgeti(L, 1, i);
7288     int type = lua_type(L, -1);
7289     if (type != LUA_TSTRING) {
7290         lua_pop(L, 1);
7291         err = "argument is not a string: ";
7292         err += name;
7293         return OverlayError(err.c_str());
7294     }
7295     *value = lua_tostring(L, -1);
7296     lua_pop(L, 1);
7297     return NULL;
7298 }
7299 
7300 // -----------------------------------------------------------------------------
7301 // Arguments:
7302 // type         string      "cube", "sphere", "point"
7303 
Do3DSetCellType(lua_State * L,const int n,int * nresults)7304 const char *Overlay::Do3DSetCellType(lua_State *L, const int n, int *nresults) {
7305     const char *error = NULL;
7306 
7307     // get cell type
7308     const char *type = NULL;
7309 
7310     int idx = 2;
7311     if ((error = ReadLuaString(L, n, idx++, &type, "type")) != NULL) return error;
7312     if (strcmp(type, "cube") == 0) {
7313         celltype = cube;
7314     } else if (strcmp(type, "sphere") == 0) {
7315         celltype = sphere;
7316     } else if (strcmp(type, "point") == 0) {
7317         celltype = point;
7318     } else {
7319         return OverlayError("illegal cell type");
7320     }
7321 
7322     return error;
7323 }
7324 
7325 // -----------------------------------------------------------------------------
7326 // Arguments:
7327 // depthshading boolean
7328 // depthlayers  integer
7329 // mindepth     integer
7330 // maxdepth     integer
7331 
Do3DSetDepthShading(lua_State * L,const int n,int * nresults)7332 const char *Overlay::Do3DSetDepthShading(lua_State *L, const int n, int *nresults) {
7333     const char *error = NULL;
7334 
7335     // get depth shading flag
7336     int idx = 2;
7337     if ((error = ReadLuaBoolean(L, n, idx++, &depthshading, "depthshading")) != NULL) return error;
7338     if ((error = ReadLuaInteger(L, n, idx++, &depthlayers, "depthlayers")) != NULL) return error;
7339     if ((error = ReadLuaInteger(L, n, idx++, &mindepth, "mindepth")) != NULL) return error;
7340     if ((error = ReadLuaInteger(L, n, idx++, &maxdepth, "maxdepth")) != NULL) return error;
7341 
7342     return error;
7343 }
7344 
7345 // -----------------------------------------------------------------------------
7346 // Arguments:
7347 // xixo         number
7348 // xiyo         number
7349 // xizo         number
7350 // yixo         number
7351 // yiyo         number
7352 // yizo         number
7353 // zixo         number
7354 // ziyo         number
7355 // zizo         number
7356 
Do3DSetTransform(lua_State * L,const int n,int * nresults)7357 const char *Overlay::Do3DSetTransform(lua_State *L, const int n, int *nresults) {
7358     const char *error = NULL;
7359     const double digits = 100000000.0;   // for rounding values from Lua
7360 
7361     // get transformation matrix
7362     int idx = 2;
7363     if ((error = ReadLuaNumber(L, n, idx++, &xixo, "xixo")) != NULL) return error;
7364     if ((error = ReadLuaNumber(L, n, idx++, &xiyo, "xiyo")) != NULL) return error;
7365     if ((error = ReadLuaNumber(L, n, idx++, &xizo, "xizo")) != NULL) return error;
7366     if ((error = ReadLuaNumber(L, n, idx++, &yixo, "yixo")) != NULL) return error;
7367     if ((error = ReadLuaNumber(L, n, idx++, &yiyo, "yiyo")) != NULL) return error;
7368     if ((error = ReadLuaNumber(L, n, idx++, &yizo, "yizo")) != NULL) return error;
7369     if ((error = ReadLuaNumber(L, n, idx++, &zixo, "zixo")) != NULL) return error;
7370     if ((error = ReadLuaNumber(L, n, idx++, &ziyo, "ziyo")) != NULL) return error;
7371     if ((error = ReadLuaNumber(L, n, idx++, &zizo, "zizo")) != NULL) return error;
7372 
7373     // round values to prevent rendering glitches
7374     xixo = round(digits * xixo) / digits;
7375     xiyo = round(digits * xiyo) / digits;
7376     xizo = round(digits * xizo) / digits;
7377 
7378     yixo = round(digits * yixo) / digits;
7379     yiyo = round(digits * yiyo) / digits;
7380     yizo = round(digits * yizo) / digits;
7381 
7382     zixo = round(digits * zixo) / digits;
7383     ziyo = round(digits * ziyo) / digits;
7384     zizo = round(digits * zizo) / digits;
7385 
7386     return error;
7387 }
7388 
7389 // -----------------------------------------------------------------------------
7390 
GetClip(const char * clipname)7391 const Clip *Overlay::GetClip(const char *clipname) {
7392     Clip *result = NULL;
7393 
7394     // lookup the named clip
7395     std::string name = clipname;
7396     std::map<std::string,Clip*>::iterator it;
7397     it = clips.find(name);
7398     if (it != clips.end()) {
7399         result = it->second;
7400     }
7401 
7402     return result;
7403 }
7404 
7405 // -----------------------------------------------------------------------------
7406 
Update3DClips(const bool editing)7407 const char *Overlay::Update3DClips(const bool editing) {
7408     char clipname[20];
7409     const Clip *current;
7410     int numclips = maxdepth - mindepth + 1;
7411 
7412     // clear current clips
7413     clipmanager.Clear();
7414 
7415     // get history clips
7416     if (showhistory > 0) {
7417         if (fadehistory) {
7418             // history fading clips
7419             for (int i = 1; i <= showhistory; i++) {
7420                 sprintf(clipname, "h%d", i);
7421                 if ((current = GetClip(clipname)) == NULL) return OverlayError("missing history fade clip");
7422                 clipmanager.AddHistoryClip(current);
7423             }
7424         } else {
7425             // history clip
7426             if ((current = GetClip("h")) == NULL) return OverlayError("missing history clip");
7427             clipmanager.SetHistoryClip(current);
7428         }
7429     }
7430 
7431     // check algo
7432     if (ruletype == bb || ruletype == bbw) {
7433         // busyboxes
7434         if (depthshading && celltype != point) {
7435             // get depth shading clips
7436             for (int i = 0; i < numclips; i++) {
7437                 sprintf(clipname, "E%d", i + mindepth);
7438                 if ((current = GetClip(clipname)) == NULL) return OverlayError("missing even depth clip");
7439                 clipmanager.AddEvenClip(current);
7440                 sprintf(clipname, "O%d", i + mindepth);
7441                 if ((current = GetClip(clipname)) == NULL) return OverlayError("missing odd depth clip");
7442                 clipmanager.AddOddClip(current);
7443             }
7444         } else {
7445             // get standard clips
7446             if ((current = GetClip("E")) == NULL) return OverlayError("missing even clip");
7447             clipmanager.SetEvenClip(current);
7448             if ((current = GetClip("O")) == NULL) return OverlayError("missing odd clip");
7449             clipmanager.SetOddClip(current);
7450         }
7451     } else {
7452         // standard algos
7453         if (depthshading && celltype != point) {
7454             // get depth shading clips
7455             for (int i = 0; i < numclips; i++) {
7456                 sprintf(clipname, "L%d", i + mindepth);
7457                 if ((current = GetClip(clipname)) == NULL) return OverlayError("missing live depth clip");
7458                 clipmanager.AddLiveClip(current);
7459             }
7460         } else {
7461             // get standard clips
7462             if ((current = GetClip("L")) == NULL) return OverlayError("missing live clip");
7463             clipmanager.SetLiveClip(current);
7464         }
7465     }
7466 
7467     // check for select
7468     if (select3d.GetNumKeys() > 0) {
7469         if ((current = GetClip("s")) == NULL) return OverlayError("missing select clip");
7470         clipmanager.SetSelectClip(current);
7471     }
7472 
7473     // check for paste
7474     if (paste3d.GetNumKeys() > 0) {
7475         if ((current = GetClip("p")) == NULL) return OverlayError("missing paste clip");
7476         clipmanager.SetPasteClip(current);
7477     }
7478 
7479     // check for editing
7480     if (editing) {
7481         // check for active
7482         if (active3d.GetNumKeys() > 0) {
7483             if ((current = GetClip("a")) == NULL) return OverlayError("missing active clip");
7484             clipmanager.SetActiveClip(current);
7485         }
7486         // check for live not active clips
7487         if (ruletype == bb || ruletype == bbw) {
7488             if ((current = GetClip("EN")) == NULL) return OverlayError("missing even live not active clip");
7489             clipmanager.SetEvenLiveNotActiveClip(current);
7490             if ((current = GetClip("ON")) == NULL) return OverlayError("missing odd live not active clip");
7491             clipmanager.SetOddLiveNotActiveClip(current);
7492         } else {
7493             if ((current = GetClip("LN")) == NULL) return OverlayError("missing live not active clip");
7494             clipmanager.SetLiveNotActiveClip(current);
7495         }
7496         // select not active clip
7497         if ((current = GetClip("sN")) == NULL) return OverlayError("missing select not active clip");
7498         clipmanager.SetSelectNotActiveClip(current);
7499         // history not active clip
7500         if (showhistory > 0) {
7501             if ((current = GetClip("hN")) == NULL) return OverlayError("missing history not active clip");
7502             clipmanager.SetHistoryNotActiveClip(current);
7503         }
7504     }
7505 
7506     return NULL;
7507 }
7508 
7509 // -----------------------------------------------------------------------------
7510 // Arguments:
7511 // fromx        integer
7512 // tox          integer
7513 // stepx        integer
7514 // fromy        integer
7515 // toy          integer
7516 // stepy        integer
7517 // fromx        integer
7518 // tox          integer
7519 // stepx        integer
7520 // cellsize     integer
7521 // editing      boolean
7522 // toolbarht    integer
7523 
Do3DDisplayCells(lua_State * L,const int n,int * nresults)7524 const char *Overlay::Do3DDisplayCells(lua_State *L, const int n, int *nresults) {
7525     const char *error = NULL;
7526 
7527     const int N = gridsize;
7528     if (N == 0) return OverlayError("grid size not set");
7529 
7530     // check div table exists
7531     if (modN == NULL) if (!CreateDivTable()) return OverlayError("could not allocate div table");
7532 
7533     // whether editing flag
7534     bool editing = false;
7535 
7536     int idx = 2;
7537     if ((error = ReadLuaInteger(L, n, idx++, &fromx, "fromx")) != NULL) return error;
7538     if ((error = ReadLuaInteger(L, n, idx++, &tox, "tox")) != NULL) return error;
7539     if ((error = ReadLuaInteger(L, n, idx++, &stepx, "stepx")) != NULL) return error;
7540     if ((error = ReadLuaInteger(L, n, idx++, &fromy, "fromy")) != NULL) return error;
7541     if ((error = ReadLuaInteger(L, n, idx++, &toy, "toy")) != NULL) return error;
7542     if ((error = ReadLuaInteger(L, n, idx++, &stepy, "stepy")) != NULL) return error;
7543     if ((error = ReadLuaInteger(L, n, idx++, &fromz, "fromz")) != NULL) return error;
7544     if ((error = ReadLuaInteger(L, n, idx++, &toz, "toz")) != NULL) return error;
7545     if ((error = ReadLuaInteger(L, n, idx++, &stepz, "stepz")) != NULL) return error;
7546     if ((error = ReadLuaInteger(L, n, idx++, &cellsize, "cellsize")) != NULL) return error;
7547     if ((error = ReadLuaBoolean(L, n, idx++, &editing, "editing")) != NULL) return error;
7548     if ((error = ReadLuaInteger(L, n, idx++, &toolbarht, "toolbarht")) != NULL) return error;
7549 
7550     // ensure required clips are present
7551     if ((error = Update3DClips(editing)) != NULL) return error;
7552 
7553     // compute midcell
7554     midcell = (cellsize / 2) - ((gridsize + 1 - (gridsize % 2)) * cellsize / 2);
7555 
7556     // set flag if overlays (select, paste, active or history) need to be drawn
7557     const int numselectkeys = select3d.GetNumKeys();
7558     const int numpastekeys = paste3d.GetNumKeys();
7559     const int numactivekeys = active3d.GetNumKeys();
7560     const bool drawover = editing || numselectkeys > 0 || numpastekeys > 0 || numactivekeys > 0 || showhistory > 0;
7561 
7562     // compute midpoint of overlay
7563     const int midx = ovwd / 2;
7564     const int midy = ovht / 2 + toolbarht / 2;
7565 
7566     // compute loop increments
7567     const int stepi = gridsize * stepy;
7568     const int stepj = gridsize * stepz;
7569 
7570     // check for history display
7571     if (showhistory > 0) UpdateBoundingBoxFromHistory();
7572 
7573     // adjust for loop
7574     tox += stepx;
7575     toy += stepy;
7576     toz += stepz;
7577 
7578     // enable blending
7579     alphablend = true;
7580 
7581     // mark target clip as changed
7582     DisableTargetClipIndex();
7583 
7584     // check rule family
7585     if (ruletype == bb || ruletype == bbw) {
7586         if (drawover) {
7587             Display3DBusyBoxesEditing(midx, midy, stepi, stepj, editing);
7588         } else {
7589             Display3DBusyBoxes(midx, midy, stepi, stepj);
7590         }
7591     } else {
7592         if (drawover) {
7593             Display3DNormalEditing(midx, midy, stepi, stepj, editing);
7594         } else {
7595             Display3DNormal(midx, midy, stepi, stepj);
7596         }
7597     }
7598 
7599     // disable blending
7600     alphablend = false;
7601 
7602     return error;
7603 }
7604 
7605 // -----------------------------------------------------------------------------
7606 
Display3DNormal(const int midx,const int midy,const int stepi,const int stepj)7607 void Overlay::Display3DNormal(const int midx, const int midy, const int stepi, const int stepj) {
7608     // iterate over the grid in the order specified
7609     const unsigned char *grid3values = grid3d.GetValues();
7610 
7611     // get midpoint
7612     int mx = 0;
7613     int my = 0;
7614     int x, y, z;
7615     int drawx, drawy;
7616 
7617     // check for depth shading
7618     int j = gridsize * fromz;
7619     if (depthshading && celltype != point) {
7620         const double zdepth = gridsize * cellsize * 0.5;
7621         const double zdepth2 = zdepth + zdepth;
7622 
7623         // get depth shading clips
7624         int numclips;
7625         const Clip **liveclips = clipmanager.GetLiveClips(&numclips);
7626         const Clip *liveclip = *liveclips;
7627         // get midpoint of clip
7628         int livew = liveclip->cwd >> 1;
7629         mx = midx - livew;
7630         my = midy - livew;
7631 
7632         // iterate over cells back to front
7633         for (z = fromz; z != toz; z += stepz) {
7634             if (zaxis[z]) {
7635                 int i = gridsize * (fromy + j);
7636                 for (y = fromy; y != toy; y += stepy) {
7637                     if (yaxis[y]) {
7638                         for (x = fromx; x != tox; x += stepx) {
7639                             if (grid3values[i + x]) {
7640                                 // use orthographic projection
7641                                 int xc = x * cellsize + midcell;
7642                                 int yc = y * cellsize + midcell;
7643                                 int zc = z * cellsize + midcell;
7644                                 double zval = xc * zixo + yc * ziyo + zc * zizo;
7645                                 int layer = depthlayers * (zval + zdepth) / zdepth2 - mindepth;
7646                                 drawx = mx + xc * xixo + yc * xiyo + zc * xizo;
7647                                 drawy = my + xc * yixo + yc * yiyo + zc * yizo;
7648                                 Draw3DCell(drawx, drawy, liveclips[layer]);
7649                             }
7650                         }
7651                     }
7652                     i += stepi;
7653                 }
7654             }
7655             j += stepj;
7656         }
7657     } else {
7658         // flat shading
7659         int livew = 0;
7660         const Clip *liveclip = clipmanager.GetLiveClip(&livew);
7661         // get midpoint of clip
7662         livew >>= 1;
7663         mx = midx - livew;
7664         my = midy - livew;
7665 
7666         // check if drawing points and point is opaque
7667         if (celltype == point && liveclip->cdata[3] == 255) {
7668             // set the drawing color to the pixel in the clip
7669             unsigned int rgba = *(unsigned int*)liveclip->cdata;
7670             // iterate over cells back to front
7671             unsigned int *lpixmap = (unsigned int*)pixmap;
7672             for (z = fromz; z != toz; z += stepz) {
7673                 if (zaxis[z]) {
7674                     int i = gridsize * (fromy + j);
7675                     for (y = fromy; y != toy; y += stepy) {
7676                         if (yaxis[y]) {
7677                             for (x = fromx; x != tox; x += stepx) {
7678                                 if (grid3values[i + x]) {
7679                                     // use orthographic projection
7680                                     int xc = x * cellsize + midcell;
7681                                     int yc = y * cellsize + midcell;
7682                                     int zc = z * cellsize + midcell;
7683                                     drawx = mx + xc * xixo + yc * xiyo + zc * xizo;
7684                                     drawy = my + xc * yixo + yc * yiyo + zc * yizo;
7685                                     if (PixelInTarget(drawx, drawy)) *(lpixmap + drawy*wd + drawx) = rgba;
7686                                 }
7687                             }
7688                         }
7689                         i += stepi;
7690                     }
7691                 }
7692                 j += stepj;
7693             }
7694         } else {
7695             for (z = fromz; z != toz; z += stepz) {
7696                 if (zaxis[z]) {
7697                     int i = gridsize * (fromy + j);
7698                     for (y = fromy; y != toy; y += stepy) {
7699                         if (yaxis[y]) {
7700                             for (x = fromx; x != tox; x += stepx) {
7701                                 if (grid3values[i + x]) {
7702                                     // use orthographic projection
7703                                     int xc = x * cellsize + midcell;
7704                                     int yc = y * cellsize + midcell;
7705                                     int zc = z * cellsize + midcell;
7706                                     drawx = mx + xc * xixo + yc * xiyo + zc * xizo;
7707                                     drawy = my + xc * yixo + yc * yiyo + zc * yizo;
7708                                     Draw3DCell(drawx, drawy, liveclip);
7709                                 }
7710                             }
7711                         }
7712                         i += stepi;
7713                     }
7714                 }
7715                 j += stepj;
7716             }
7717         }
7718     }
7719 }
7720 
7721 // -----------------------------------------------------------------------------
7722 
Display3DNormalEditing(const int midx,const int midy,const int stepi,const int stepj,const bool editing)7723 void Overlay::Display3DNormalEditing(const int midx, const int midy, const int stepi, const int stepj, const bool editing) {
7724     // iterate over the grid in the order specified
7725     const unsigned char *grid3values = grid3d.GetValues();
7726     int j = gridsize * fromz;
7727     int x, y, z;
7728     const Clip **liveclips = NULL;
7729     const Clip *liveclip = NULL;
7730     int livew = 0;
7731     const Clip **historyclips = NULL;
7732     const Clip *historyclip = NULL;
7733     int historyw = 0;
7734     double zd = 0;
7735     double zd2 = 0;
7736     const bool usedepth = (depthshading && celltype != point);
7737 
7738     // get required clips and compute midpoints
7739     // history cells
7740     if (showhistory > 0) {
7741         int numclips;
7742         if (fadehistory) {
7743             historyclips = clipmanager.GetHistoryClips(&numclips);
7744             historyclip = *historyclips;
7745             historyw = historyclip->cwd;
7746         } else {
7747             historyclip = clipmanager.GetHistoryClip(&historyw);
7748         }
7749     }
7750     // live cells
7751     if (usedepth) {
7752         int numclips;
7753         liveclips = clipmanager.GetLiveClips(&numclips);
7754         liveclip = *liveclips;
7755         livew = liveclip->cwd;
7756         zd = gridsize * cellsize * 0.5;
7757         zd2 = zd + zd;
7758     } else {
7759         liveclip = clipmanager.GetLiveClip(&livew);
7760     }
7761     // select paste, active, etc.
7762     int selectw = 0;
7763     const Clip *selectclip = clipmanager.GetSelectClip(&selectw);
7764     int pastew = 0;
7765     const Clip *pasteclip = clipmanager.GetPasteClip(&pastew);
7766     int activew = 0;
7767     const Clip *activeclip = clipmanager.GetActiveClip(&activew);
7768     int livenotw = 0;
7769     const Clip *livenotclip = clipmanager.GetLiveNotActiveClip(&livenotw);
7770     int selectnotw = 0;
7771     const Clip *selectnotclip = clipmanager.GetSelectNotActiveClip(&selectnotw);
7772     int historynotw = 0;
7773     const Clip *historynotclip = clipmanager.GetHistoryNotActiveClip(&historynotw);
7774     historyw >>= 1;
7775     livew >>= 1;
7776     selectw >>= 1;
7777     pastew >>= 1;
7778     activew >>= 1;
7779     livenotw >>= 1;
7780     selectnotw >>= 1;
7781     historynotw >>= 1;
7782     const double zdepth = zd;
7783     const double zdepth2 = zd2;
7784 
7785     // lookup the select, paste, active and history grids
7786     const unsigned char *select3values = select3d.GetValues();
7787     const unsigned char *paste3values = paste3d.GetValues();
7788     const unsigned char *active3values = active3d.GetValues();
7789     const unsigned char *history3values = history3d.GetValues();
7790 
7791     unsigned char gv = 0;
7792     unsigned char sv = 0;
7793     unsigned char pv = 0;
7794     unsigned char av = 0;
7795     unsigned char hv = 0;
7796     int ix;
7797     int drawx, drawy;
7798 
7799     // draw cells with select/paste/active/history
7800     for (z = fromz; z != toz; z += stepz) {
7801         int i = gridsize * (fromy + j);
7802         for (y = fromy; y != toy; y += stepy) {
7803             for (x = fromx; x != tox; x += stepx) {
7804                 ix = i + x;
7805                 gv = grid3values[ix];
7806                 sv = select3values[ix];
7807                 pv = paste3values[ix];
7808                 av = active3values[ix];
7809                 hv = history3values[ix];
7810                 if (gv || sv || pv || av || hv) {
7811                     // use orthographic projection
7812                     int xc = x * cellsize + midcell;
7813                     int yc = y * cellsize + midcell;
7814                     int zc = z * cellsize + midcell;
7815                     if (usedepth) {
7816                         double zval = xc * zixo + yc * ziyo + zc * zizo;
7817                         int layer = depthlayers * (zval + zdepth) / zdepth2 - mindepth;
7818                         liveclip = liveclips[layer];
7819                     }
7820                     drawx = midx + xc * xixo + yc * xiyo + zc * xizo;
7821                     drawy = midy + xc * yixo + yc * yiyo + zc * yizo;
7822                     // check for editing
7823                     if (editing) {
7824                         // draw the cell
7825                         if (av) {
7826                             // cell is within active plane
7827                             if (gv) {
7828                                 // draw live cell
7829                                 Draw3DCell(drawx - livew, drawy - livew, liveclip);
7830                             }
7831                             // draw active plane cell
7832                             Draw3DCell(drawx - activew, drawy - activew, activeclip);
7833                             if (sv) {
7834                                 // draw selected cell
7835                                 Draw3DCell(drawx - selectw, drawy - selectw, selectclip);
7836                             }
7837                             // check for history
7838                             if (hv) {
7839                                 // draw history cell
7840                                 if (fadehistory) {
7841                                     Draw3DCell(drawx - historyw, drawy - historyw, historyclips[showhistory - hv]);
7842                                 } else {
7843                                     Draw3DCell(drawx - historyw, drawy - historyw, historyclip);
7844                                 }
7845                             }
7846                         } else {
7847                             // cell is outside of active plan
7848                             if (gv) {
7849                                 // draw live cell as a point
7850                                 Draw3DCell(drawx - livenotw, drawy - livenotw, livenotclip);
7851                             }
7852                             if (sv) {
7853                                 // draw selected cell as a point
7854                                 Draw3DCell(drawx - selectnotw, drawy - selectnotw, selectnotclip);
7855                             }
7856                             if (hv) {
7857                                 // draw history cell as a point
7858                                 Draw3DCell(drawx - historynotw, drawy - historynotw, historynotclip);
7859                             }
7860                         }
7861                     } else {
7862                         // active plane is not displayed
7863                         if (gv) {
7864                             // draw live cell
7865                             Draw3DCell(drawx - livew, drawy - livew, liveclip);
7866                         }
7867                         if (sv) {
7868                             // draw selected cell
7869                             Draw3DCell(drawx - selectw, drawy - selectw, selectclip);
7870                         }
7871                         // check for history
7872                         if (hv) {
7873                             // draw history cell
7874                             if (fadehistory) {
7875                                 Draw3DCell(drawx - historyw, drawy - historyw, historyclips[showhistory - hv]);
7876                             } else {
7877                                 Draw3DCell(drawx - historyw, drawy - historyw, historyclip);
7878                             }
7879                         }
7880                     }
7881                     // check for paste
7882                     if (pv) {
7883                         // draw live cell
7884                         Draw3DCell(drawx - livew, drawy - livew, liveclip);
7885                         // draw paste cell
7886                         Draw3DCell(drawx - pastew, drawy - pastew, pasteclip);
7887                     }
7888                 }
7889             }
7890             i += stepi;
7891         }
7892         j += stepj;
7893     }
7894 }
7895 
7896 // -----------------------------------------------------------------------------
7897 
Display3DBusyBoxes(const int midx,const int midy,const int stepi,const int stepj)7898 void Overlay::Display3DBusyBoxes(const int midx, const int midy, const int stepi, const int stepj) {
7899     // iterate over the grid in the order specified
7900     const unsigned char *grid3values = grid3d.GetValues();
7901     int x, y, z;
7902 
7903     // lookup busyboxes clips
7904     const Clip **eclips = NULL;
7905     const Clip **oclips = NULL;
7906     const Clip *eclip = NULL;
7907     const Clip *oclip = NULL;
7908     int evenw = 0;
7909     int oddw = 0;
7910     double zd = 0;
7911     double zd2 = 0;
7912     const bool usedepth = (depthshading && celltype != point);
7913 
7914     // check for depth shading
7915     if (usedepth) {
7916         // get the depth shading clip lists
7917         int numevenclips, numoddclips;
7918         eclips = clipmanager.GetEvenClips(&numevenclips);
7919         oclips = clipmanager.GetOddClips(&numoddclips);
7920         // get width of first clip in each list
7921         eclip = *eclips;
7922         evenw = eclip->cwd;
7923         oclip = *oclips;
7924         oddw = oclip->cwd;
7925         zd = gridsize * cellsize * 0.5;
7926         zd2 = zd + zd;
7927     } else {
7928         // get the odd and even clips
7929         eclip = clipmanager.GetEvenClip(&evenw);
7930         oclip = clipmanager.GetOddClip(&oddw);
7931     }
7932     // get midpoint of clips
7933     evenw >>= 1;
7934     oddw >>= 1;
7935     const double zdepth = zd;
7936     const double zdepth2 = zd2;
7937     int drawx, drawy;
7938     int j = gridsize * fromz;
7939 
7940     // check if drawing points and points are opaque
7941     if (celltype == point && eclip->cdata[3] == 255 && oclip->cdata[3] == 255) {
7942         // set the even drawing color to the pixel in the clip
7943         unsigned int evenrgba = *(unsigned int*)eclip->cdata;
7944         // set the odd drawing color to the pixel in the clip
7945         unsigned int oddrgba = *(unsigned int*)oclip->cdata;
7946         unsigned int *lpixmap = (unsigned int*)pixmap;
7947         // iterate over cells back to front
7948         for (z = fromz; z != toz; z += stepz) {
7949             if (zaxis[z]) {
7950                 int i = gridsize * (fromy + j);
7951                 for (y = fromy; y != toy; y += stepy) {
7952                     if (yaxis[y]) {
7953                         int evencell = ((fromx + y + z) & 1) == 0;
7954                         for (x = fromx; x != tox; x += stepx) {
7955                             if (grid3values[i + x]) {
7956                                 // use orthographic projection
7957                                 int xc = x * cellsize + midcell;
7958                                 int yc = y * cellsize + midcell;
7959                                 int zc = z * cellsize + midcell;
7960                                 drawx = midx + xc * xixo + yc * xiyo + zc * xizo;
7961                                 drawy = midy + xc * yixo + yc * yiyo + zc * yizo;
7962                                 if (evencell) {
7963                                     if (PixelInTarget(drawx, drawy)) *(lpixmap + drawy*wd + drawx) = evenrgba;
7964                                 } else {
7965                                     if (PixelInTarget(drawx, drawy)) *(lpixmap + drawy*wd + drawx) = oddrgba;
7966                                 }
7967                             }
7968                             evencell = !evencell;
7969                         }
7970                     }
7971                     i += stepi;
7972                 }
7973             }
7974             j += stepj;
7975         }
7976     } else {
7977         // iterate over cells back to front
7978         for (z = fromz; z != toz; z += stepz) {
7979             if (zaxis[z]) {
7980                 int i = gridsize * (fromy + j);
7981                 for (y = fromy; y != toy; y += stepy) {
7982                     if (yaxis[y]) {
7983                         int evencell = ((fromx + y + z) & 1) == 0;
7984                         for (x = fromx; x != tox; x += stepx) {
7985                             if (grid3values[i + x]) {
7986                                 // use orthographic projection
7987                                 int xc = x * cellsize + midcell;
7988                                 int yc = y * cellsize + midcell;
7989                                 int zc = z * cellsize + midcell;
7990                                 drawx = midx + xc * xixo + yc * xiyo + zc * xizo;
7991                                 drawy = midy + xc * yixo + yc * yiyo + zc * yizo;
7992                                 if (usedepth) {
7993                                     double zval = xc * zixo + yc * ziyo + zc * zizo;
7994                                     int layer = depthlayers * (zval + zdepth) / zdepth2;
7995                                     if (evencell) {
7996                                         Draw3DCell(drawx - evenw, drawy - evenw, eclips[layer - mindepth]);
7997                                     } else {
7998                                         Draw3DCell(drawx - oddw, drawy - oddw, oclips[layer - mindepth]);
7999                                     }
8000                                 } else {
8001                                     if (evencell) {
8002                                         Draw3DCell(drawx - evenw, drawy - evenw, eclip);
8003                                     } else {
8004                                         Draw3DCell(drawx - oddw, drawy - oddw, oclip);
8005                                     }
8006                                 }
8007                             }
8008                             evencell = !evencell;
8009                         }
8010                     }
8011                     i += stepi;
8012                 }
8013             }
8014             j += stepj;
8015         }
8016     }
8017 }
8018 
8019 // -----------------------------------------------------------------------------
8020 
Display3DBusyBoxesEditing(const int midx,const int midy,const int stepi,const int stepj,const bool editing)8021 void Overlay::Display3DBusyBoxesEditing(const int midx, const int midy, const int stepi, const int stepj, const bool editing) {
8022     // iterate over the grid in the order specified
8023     const unsigned char *grid3values = grid3d.GetValues();
8024     int x, y, z;
8025 
8026     // lookup busyboxes clips
8027     const Clip **evenclips = NULL;
8028     const Clip **oddclips = NULL;
8029     const Clip **historyclips = NULL;
8030     const Clip *evenclip = NULL;
8031     const Clip *oddclip = NULL;
8032     const Clip *historyclip = NULL;
8033     int evenw = 0;
8034     int oddw = 0;
8035     int historyw = 0;
8036     double zd = 0;
8037     double zd2 = 0;
8038     const bool usedepth = (depthshading && celltype != point);
8039 
8040     // get history clip
8041     if (showhistory > 0) {
8042         int numclips;
8043         if (fadehistory) {
8044             historyclips = clipmanager.GetHistoryClips(&numclips);
8045             historyclip = *historyclips;
8046             historyw = historyclip->cwd;
8047         } else {
8048             historyclip = clipmanager.GetHistoryClip(&historyw);
8049         }
8050     }
8051     // check for depth shading
8052     if (usedepth) {
8053         // get the depth shading clip lists
8054         int numevenclips, numoddclips;
8055         evenclips = clipmanager.GetEvenClips(&numevenclips);
8056         oddclips = clipmanager.GetOddClips(&numoddclips);
8057         // get width of first clip in each list
8058         evenclip = *evenclips;
8059         evenw = evenclip->cwd;
8060         oddclip = *oddclips;
8061         oddw = oddclip->cwd;
8062         zd = gridsize * cellsize * 0.5;
8063         zd2 = zd + zd;
8064     } else {
8065         // get the odd and even clips
8066         evenclip = clipmanager.GetEvenClip(&evenw);
8067         oddclip = clipmanager.GetOddClip(&oddw);
8068     }
8069     int selectw = 0;
8070     const Clip *selectclip = clipmanager.GetSelectClip(&selectw);
8071     int pastew = 0;
8072     const Clip *pasteclip = clipmanager.GetPasteClip(&pastew);
8073     int activew = 0;
8074     const Clip *activeclip = clipmanager.GetActiveClip(&activew);
8075     int evenlivenotw = 0;
8076     const Clip *evenlivenotclip = clipmanager.GetEvenLiveNotActiveClip(&evenlivenotw);
8077     int oddlivenotw = 0;
8078     const Clip *oddlivenotclip = clipmanager.GetOddLiveNotActiveClip(&oddlivenotw);
8079     int selectnotw = 0;
8080     const Clip *selectnotclip = clipmanager.GetSelectNotActiveClip(&selectnotw);
8081     int historynotw = 0;
8082     const Clip *historynotclip = clipmanager.GetHistoryNotActiveClip(&historynotw);
8083     evenw >>= 1;
8084     oddw >>= 1;
8085     selectw >>= 1;
8086     pastew >>= 1;
8087     activew >>= 1;
8088     evenlivenotw >>= 1;
8089     oddlivenotw >>= 1;
8090     selectnotw >>= 1;
8091     historyw >>= 1;
8092     historynotw >>= 1;
8093     const double zdepth = zd;
8094     const double zdepth2 = zd2;
8095 
8096     // lookup the select, paste, active and history grids
8097     const unsigned char *select3values = select3d.GetValues();
8098     const unsigned char *paste3values = paste3d.GetValues();
8099     const unsigned char *active3values = active3d.GetValues();
8100     const unsigned char *history3values = history3d.GetValues();
8101 
8102     unsigned char gv = 0;
8103     unsigned char sv = 0;
8104     unsigned char pv = 0;
8105     unsigned char av = 0;
8106     unsigned char hv = 0;
8107     int ix;
8108     int drawx, drawy;
8109     const Clip *liveclip = NULL;
8110     int livew = evenw;  // assume odd and even clips are the same size
8111 
8112     // iterate over cells back to front
8113     int j = gridsize * fromz;
8114     for (z = fromz; z != toz; z += stepz) {
8115         int i = gridsize * (fromy + j);
8116         for (y = fromy; y != toy; y += stepy) {
8117             int evencell = ((fromx + y + z) & 1) == 0;
8118             for (x = fromx; x != tox; x += stepx) {
8119                 ix = i + x;
8120                 gv = grid3values[ix];
8121                 sv = select3values[ix];
8122                 pv = paste3values[ix];
8123                 av = active3values[ix];
8124                 hv = history3values[ix];
8125                 if (gv || sv || pv || av || hv) {
8126                     // use orthographic projection
8127                     int xc = x * cellsize + midcell;
8128                     int yc = y * cellsize + midcell;
8129                     int zc = z * cellsize + midcell;
8130                     if (usedepth) {
8131                         double zval = xc * zixo + yc * ziyo + zc * zizo;
8132                         int layer = depthlayers * (zval + zdepth) / zdepth2 - mindepth;
8133                         if (evencell) {
8134                             liveclip = evenclips[layer];
8135                         } else {
8136                             liveclip = oddclips[layer];
8137                         }
8138                     } else {
8139                         if (evencell) {
8140                             liveclip = evenclip;
8141                         } else {
8142                             liveclip = oddclip;
8143                         }
8144                     }
8145                     drawx = midx + xc * xixo + yc * xiyo + zc * xizo;
8146                     drawy = midy + xc * yixo + yc * yiyo + zc * yizo;
8147                     // check for editing
8148                     if (editing) {
8149                         // draw the cell
8150                         if (av) {
8151                             // cell is within active plane
8152                             if (gv) {
8153                                 // draw live cell
8154                                 Draw3DCell(drawx - livew, drawy - livew, liveclip);
8155                             }
8156                             // draw active plane cell
8157                             Draw3DCell(drawx - activew, drawy - activew, activeclip);
8158                             if (sv) {
8159                                 // draw selected cell
8160                                 Draw3DCell(drawx - selectw, drawy - selectw, selectclip);
8161                             }
8162                             // check for history
8163                             if (hv) {
8164                                 // draw history cell
8165                                 if (fadehistory) {
8166                                     Draw3DCell(drawx - historyw, drawy - historyw, historyclips[showhistory - hv]);
8167                                 } else {
8168                                     Draw3DCell(drawx - historyw, drawy - historyw, historyclip);
8169                                 }
8170                             }
8171                         } else {
8172                             // cell is outside of active plan
8173                             if (gv) {
8174                                 // draw live cell as a point
8175                                 if (evencell) {
8176                                     Draw3DCell(drawx - evenlivenotw, drawy - evenlivenotw, evenlivenotclip);
8177                                 } else {
8178                                     Draw3DCell(drawx - oddlivenotw, drawy - oddlivenotw, oddlivenotclip);
8179                                 }
8180                             }
8181                             if (sv) {
8182                                 // draw selected cell as a point
8183                                 Draw3DCell(drawx - selectnotw, drawy - selectnotw, selectnotclip);
8184                             }
8185                             if (hv) {
8186                                 // draw history cell as a point
8187                                 Draw3DCell(drawx - historynotw, drawy - historynotw, historynotclip);
8188                             }
8189                         }
8190                     } else {
8191                         // active plane is not displayed
8192                         if (gv) {
8193                             // draw live cell
8194                             Draw3DCell(drawx - livew, drawy - livew, liveclip);
8195                         }
8196                         if (sv) {
8197                             // draw selected cell
8198                             Draw3DCell(drawx - selectw, drawy - selectw, selectclip);
8199                         }
8200                         // check for history
8201                         if (hv) {
8202                             // draw history cell
8203                             if (fadehistory) {
8204                                 Draw3DCell(drawx - historyw, drawy - historyw, historyclips[showhistory - hv]);
8205                             } else {
8206                                 Draw3DCell(drawx - historyw, drawy - historyw, historyclip);
8207                             }
8208                         }
8209                     }
8210                     // check for paste
8211                     if (pv) {
8212                         // draw live cell
8213                         Draw3DCell(drawx - livew, drawy - livew, liveclip);
8214                         // draw paste cell
8215                         Draw3DCell(drawx - pastew, drawy - pastew, pasteclip);
8216                     }
8217                 }
8218                 evencell = !evencell;
8219             }
8220             i += stepi;
8221         }
8222         j += stepj;
8223     }
8224 }
8225 
8226 // -----------------------------------------------------------------------------
8227 // Arguments:
8228 // step         integer      >= 1
8229 
Do3DSetStepSize(lua_State * L,const int n,int * nresults)8230 const char *Overlay::Do3DSetStepSize(lua_State *L, const int n, int *nresults) {
8231     const char *error = NULL;
8232 
8233     // get step size
8234     int idx = 2;
8235     int N;
8236     if ((error = ReadLuaInteger(L, n, idx++, &N, "step")) != NULL) return error;
8237     if (N < 1) return OverlayError("step must be at least 1");
8238 
8239     // set the step size
8240     stepsize = N;
8241 
8242     return error;
8243 }
8244 
8245 // -----------------------------------------------------------------------------
8246 // Arguments:
8247 // size         integer      >= 1
8248 
Do3DSetGridSize(lua_State * L,const int n,int * nresults)8249 const char *Overlay::Do3DSetGridSize(lua_State *L, const int n, int *nresults) {
8250     const char *error = NULL;
8251 
8252     // get grid size
8253     int idx = 2;
8254     int N;
8255     if ((error = ReadLuaInteger(L, n, idx++, &N, "size")) != NULL) return error;
8256     if (N < 1 || N > 256) return OverlayError("size must be from 1 to 256");
8257 
8258     // set the grid size
8259     gridsize = N;
8260     const int NNN = N * N * N;
8261 
8262     // create the div table
8263     if (!CreateDivTable()) return OverlayError("could not allocate div table");
8264 
8265     // create the axis flags
8266     if (!CreateAxisFlags()) return OverlayError("could not allocate axis flags");
8267 
8268     // resize tables
8269     if (!grid3d.SetSize(NNN)) return OverlayError("could not allocate grid3d");
8270     if (!count1.SetSize(NNN)) return OverlayError("could not allocate count1");
8271     if (!count2.SetSize(NNN)) return OverlayError("could not allocate count2");
8272     if (!next3d.SetSize(NNN)) return OverlayError("could not allocate next3d");
8273     if (!paste3d.SetSize(NNN)) return OverlayError("could not allocate paste3d");
8274     if (!select3d.SetSize(NNN)) return OverlayError("could not allocate select3d");
8275     if (!active3d.SetSize(NNN)) return OverlayError("could not allocate active3d");
8276     if (!history3d.SetSize(NNN)) return OverlayError("could not allocate history3d");
8277 
8278     return error;
8279 }
8280 
8281 // -----------------------------------------------------------------------------
8282 // Arguments:
8283 // type         string      "", "F", "C", "E", "H", "BB" or "BBW"
8284 // survivals    table       boolean
8285 // births       table       boolean
8286 
Do3DSetRule(lua_State * L,const int n,int * nresults)8287 const char *Overlay::Do3DSetRule(lua_State *L, const int n, int *nresults) {
8288     const char *error = NULL;
8289 
8290     // read rule type
8291     int idx = 2;
8292     const char *rulestring;
8293     if ((error = ReadLuaString(L, n, idx++, &rulestring, "type")) != NULL) return error;
8294     if (strcmp(rulestring, "") == 0) {
8295         ruletype = moore;
8296     } else if (strcmp(rulestring, "F") == 0) {
8297         ruletype = face;
8298     } else if (strcmp(rulestring, "C") == 0) {
8299         ruletype = corner;
8300     } else if (strcmp(rulestring, "E") == 0) {
8301         ruletype = edge;
8302     } else if (strcmp(rulestring, "H") == 0) {
8303         ruletype = hexahedral;
8304     } else if (strcmp(rulestring, "BB") == 0) {
8305         ruletype = bb;
8306     } else if (strcmp(rulestring, "BBW") == 0) {
8307         ruletype = bbw;
8308     } else {
8309         return OverlayError("type argument is invalid");
8310     }
8311 
8312     // don't need survivals and births for BusyBoxes rules
8313     if (ruletype != bb && ruletype != bbw) {
8314         // initialize survivals and births
8315         for (int i = 0; i < 27; i++) {
8316             survivals[i] = false;
8317             births[i]    = false;
8318         }
8319         bool valid = true;
8320 
8321         // read survivals list
8322         if (idx > n) return OverlayError("missing survivals argument");
8323         lua_rawgeti(L, 1, idx);
8324         int type = lua_type(L, -1);
8325         if (type != LUA_TTABLE) {
8326             lua_pop(L, 1);
8327             return OverlayError("survivals argument is not a table");
8328         }
8329         lua_pushvalue(L, -1);
8330         lua_pushnil(L);
8331         while (lua_next(L, -2)) {
8332             lua_pushvalue(L, -2);
8333             int k = lua_tointeger(L, -1);
8334             if (k < 0 || k >= 27) {
8335                 valid = false;
8336                 break;
8337             }
8338             lua_pop(L, 2);
8339             survivals[k] = true;
8340         }
8341         lua_pop(L, 1);
8342         if (!valid) return OverlayError("survivals element is out of range");
8343 
8344         // read births list
8345         idx++;
8346         if (idx > n) return OverlayError("missing births argument");
8347         lua_rawgeti(L, 1, idx);
8348         type = lua_type(L, -1);
8349         if (type != LUA_TTABLE) {
8350             lua_pop(L, 1);
8351             return OverlayError("births argument is not a table");
8352         }
8353         lua_pushvalue(L, -1);
8354         lua_pushnil(L);
8355         while (lua_next(L, -2)) {
8356             lua_pushvalue(L, -2);
8357             int k = lua_tointeger(L, -1);
8358             if (k < 0 || k >= 27) {
8359                 valid = false;
8360                 break;
8361             }
8362             lua_pop(L, 2);
8363             births[k] = true;
8364         }
8365         lua_pop(L, 1);
8366         if (!valid) return OverlayError("births element is out of range");
8367     }
8368 
8369     return error;
8370 }
8371 
8372 // -----------------------------------------------------------------------------
8373 
FreeDivTable()8374 void Overlay::FreeDivTable() {
8375     if (modN) {
8376         free(modN);
8377         modN = NULL;
8378     }
8379     if (modNN) {
8380         free(modNN);
8381         modNN = NULL;
8382     }
8383     if (xyz) {
8384         free(xyz);
8385         xyz = NULL;
8386     }
8387 }
8388 
8389 // -----------------------------------------------------------------------------
8390 
CreateDivTable()8391 bool Overlay::CreateDivTable() {
8392     if (gridsize == 0) return false;
8393 
8394     const int N = gridsize;
8395     const int NN = N * N;
8396     const int NNN = NN * N;
8397 
8398     // free existing table
8399     FreeDivTable();
8400 
8401     // allocate new table
8402     modN     = (int*)malloc(NNN * sizeof(*modN));
8403     modNN    = (int*)malloc(NNN * sizeof(*modNN));
8404     xyz      = (unsigned int*)malloc(NNN * sizeof(*xyz));
8405 
8406     // check allocation succeeded
8407     if (modN == NULL || modNN == NULL || xyz == NULL) {
8408         FreeDivTable();
8409         return false;
8410     }
8411 
8412     // populate table
8413     for (int i = 0; i < NNN; i++) {
8414         modN[i]  = i % N;
8415         modNN[i] = i % NN;
8416     }
8417     for (int i = 0; i < NNN; i++) {
8418         xyz[i] = (modN[i] << 16) | (modN[i / N] << 8) | i / NN;
8419     }
8420 
8421     return true;
8422 }
8423 
8424 // -----------------------------------------------------------------------------
8425 
FreeAxisFlags()8426 void Overlay::FreeAxisFlags() {
8427     if (xaxis) {
8428         free(xaxis);
8429         xaxis = NULL;
8430     }
8431     if (yaxis) {
8432         free(yaxis);
8433         yaxis = NULL;
8434     }
8435     if (zaxis) {
8436         free(zaxis);
8437         zaxis = NULL;
8438     }
8439 }
8440 
8441 // -----------------------------------------------------------------------------
8442 
CreateAxisFlags()8443 bool Overlay::CreateAxisFlags() {
8444     if (gridsize == 0) return false;
8445 
8446     const int N = gridsize;
8447 
8448     // free existing flags
8449     FreeAxisFlags();
8450 
8451     // allocate flags
8452     xaxis = (char*)malloc(N * sizeof(*xaxis));
8453     yaxis = (char*)malloc(N * sizeof(*yaxis));
8454     zaxis = (char*)malloc(N * sizeof(*zaxis));
8455 
8456     // check allocation succeeded
8457     if (xaxis == NULL || yaxis == NULL || zaxis == NULL) {
8458         FreeAxisFlags();
8459         return false;
8460     }
8461 
8462     return true;
8463 }
8464 
8465 // -----------------------------------------------------------------------------
8466 
ClearAxisFlags()8467 void Overlay::ClearAxisFlags() {
8468     if (gridsize == 0) return;
8469 
8470     const int N = gridsize;
8471 
8472     // clear the axis flags
8473     memset(xaxis, 0, N * sizeof(*xaxis));
8474     memset(yaxis, 0, N * sizeof(*yaxis));
8475     memset(zaxis, 0, N * sizeof(*zaxis));
8476 }
8477 
8478 // -----------------------------------------------------------------------------
8479 
UpdateBoundingBox()8480 void Overlay::UpdateBoundingBox() {
8481     if (gridsize == 0) return;
8482 
8483     const int Nm1 = gridsize - 1;
8484 
8485     // guaranteed at least one live cell
8486     minx = 0;
8487     while (!xaxis[minx]) minx++;
8488     miny = 0;
8489     while (!yaxis[miny]) miny++;
8490     minz = 0;
8491     while (!zaxis[minz]) minz++;
8492     maxx = Nm1;
8493     while (!xaxis[maxx]) maxx--;
8494     maxy = Nm1;
8495     while (!yaxis[maxy]) maxy--;
8496     maxz = Nm1;
8497     while (!zaxis[maxz]) maxz--;
8498 
8499     if (minx == 0 || miny == 0 || minz == 0 || maxx == Nm1 || maxy == Nm1 || maxz == Nm1) {
8500         liveedge = true;
8501     }
8502 }
8503 
8504 // -----------------------------------------------------------------------------
8505 
CreateResultsFromC1(lua_State * L,const bool laststep)8506 int Overlay::CreateResultsFromC1(lua_State *L, const bool laststep) {
8507     // create results for BusyBoxes
8508     if (laststep) {
8509         lua_newtable(L);
8510     }
8511     next3d.Clear();
8512 
8513     // clear axes
8514     ClearAxisFlags();
8515 
8516     int numkeys;
8517     const int *count1keys = count1.GetKeys(&numkeys);
8518     const unsigned char *count1values = count1.GetValues();
8519 
8520     if (laststep) {
8521         // on the last step update the return grid
8522         for (int i = 0; i < numkeys; i++) {
8523             const int k = count1keys[i];
8524             if (count1values[k]) {
8525                 // create a live cell in next grid
8526                 lua_pushnumber(L, 1);
8527                 lua_rawseti(L, -2, k);
8528                 next3d.SetTo1(k);
8529                 const unsigned int loc = xyz[k];
8530                 xaxis[loc >> 16] = 1;
8531                 yaxis[(loc >> 8) & 0xff] = 1;
8532                 zaxis[loc & 0xff] = 1;
8533             }
8534         }
8535     } else {
8536         for (int i = 0; i < numkeys; i++) {
8537             const int k = count1keys[i];
8538             if (count1values[k]) {
8539                 // create a live cell in next grid
8540                 next3d.SetTo1(k);
8541                 const unsigned int loc = xyz[k];
8542                 xaxis[loc >> 16] = 1;
8543                 yaxis[(loc >> 8) & 0xff] = 1;
8544                 zaxis[loc & 0xff] = 1;
8545             }
8546         }
8547     }
8548     grid3d.Copy(next3d);
8549     UpdateBoundingBox();
8550 
8551     // return the population
8552     return next3d.GetNumKeys();
8553 }
8554 
8555 // -----------------------------------------------------------------------------
8556 
CreateResultsFromC1G3(lua_State * L,const bool laststep)8557 int Overlay::CreateResultsFromC1G3(lua_State *L, const bool laststep) {
8558     // create results for Moore
8559     if (laststep) {
8560         lua_newtable(L);
8561     }
8562     next3d.Clear();
8563     int numkeys;
8564     const int *count1keys = count1.GetKeys(&numkeys);
8565     const unsigned char *count1values = count1.GetValues();
8566     const unsigned char *grid3dvalues = grid3d.GetValues();
8567 
8568     // clear axes
8569     ClearAxisFlags();
8570 
8571     if (laststep) {
8572         // on the last step update the return grid
8573         for (int i = 0; i < numkeys; i++) {
8574             const int k = count1keys[i];
8575             const unsigned char v = count1values[k];
8576             const unsigned char src = grid3dvalues[k];
8577             if ((src && survivals[v - 1]) || (births[v] && !src)) {
8578                 // create a live cell in next grid
8579                 lua_pushnumber(L, 1);
8580                 lua_rawseti(L, -2, k);
8581                 next3d.SetTo1(k);
8582                 const unsigned int loc = xyz[k];
8583                 xaxis[loc >> 16] = 1;
8584                 yaxis[(loc >> 8) & 0xff] = 1;
8585                 zaxis[loc & 0xff] = 1;
8586             }
8587         }
8588     } else {
8589         for (int i = 0; i < numkeys; i++) {
8590             const int k = count1keys[i];
8591             const unsigned char v = count1values[k];
8592             const unsigned char src = grid3dvalues[k];
8593             if ((src && survivals[v - 1]) || (births[v] && !src)) {
8594                 // create a live cell in next grid
8595                 next3d.SetTo1(k);
8596                 const unsigned int loc = xyz[k];
8597                 xaxis[loc >> 16] = 1;
8598                 yaxis[(loc >> 8) & 0xff] = 1;
8599                 zaxis[loc & 0xff] = 1;
8600             }
8601         }
8602     }
8603     grid3d.Copy(next3d);
8604     UpdateBoundingBox();
8605 
8606     // return the population
8607     return next3d.GetNumKeys();
8608 }
8609 
8610 // -----------------------------------------------------------------------------
8611 
CreateResultsFromC1C2(lua_State * L,const bool laststep)8612 int Overlay::CreateResultsFromC1C2(lua_State *L, const bool laststep) {
8613     // create results for Face, Corner, Edge or Hexahedral
8614     if (laststep) {
8615         lua_newtable(L);
8616     }
8617     next3d.Clear();
8618 
8619     // clear axes
8620     ClearAxisFlags();
8621 
8622     // use count1 and survivals to put live cells in grid
8623     int numkeys;
8624     const int *count1keys = count1.GetKeys(&numkeys);
8625     const unsigned char *count1values = count1.GetValues();
8626 
8627     if (laststep) {
8628         // on the last step update the return grid
8629         for (int i = 0; i < numkeys; i++) {
8630             const int k = count1keys[i];
8631             const unsigned char v = count1values[k];
8632             if (survivals[v]) {
8633                 // create a live cell in next grid
8634                 lua_pushnumber(L, 1);
8635                 lua_rawseti(L, -2, k);
8636                 next3d.SetValue(k, 1);
8637                 const unsigned int loc = xyz[k];
8638                 xaxis[loc >> 16] = 1;
8639                 yaxis[(loc >> 8) & 0xff] = 1;
8640                 zaxis[loc & 0xff] = 1;
8641             }
8642         }
8643     } else {
8644         for (int i = 0; i < numkeys; i++) {
8645             const int k = count1keys[i];
8646             const unsigned char v = count1values[k];
8647             if (survivals[v]) {
8648                 // create a live cell in next grid
8649                 next3d.SetValue(k, 1);
8650                 const unsigned int loc = xyz[k];
8651                 xaxis[loc >> 16] = 1;
8652                 yaxis[(loc >> 8) & 0xff] = 1;
8653                 zaxis[loc & 0xff] = 1;
8654             }
8655         }
8656     }
8657 
8658     // use count2 and births to put live cells in grid
8659     const int *count2keys = count2.GetKeys(&numkeys);
8660     const unsigned char *count2values = count2.GetValues();
8661 
8662     if (laststep) {
8663         for (int i = 0; i < numkeys; i++) {
8664             const int k = count2keys[i];
8665             const unsigned char v = count2values[k];
8666             if (births[v]) {
8667                 // create a live cell in next grid
8668                 lua_pushnumber(L, 1);
8669                 lua_rawseti(L, -2, k);
8670                 next3d.SetValue(k, 1);
8671                 const unsigned int loc = xyz[k];
8672                 xaxis[loc >> 16] = 1;
8673                 yaxis[(loc >> 8) & 0xff] = 1;
8674                 zaxis[loc & 0xff] = 1;
8675             }
8676         }
8677     } else {
8678         for (int i = 0; i < numkeys; i++) {
8679             const int k = count2keys[i];
8680             const unsigned char v = count2values[k];
8681             if (births[v]) {
8682                 // create a live cell in next grid
8683                 next3d.SetValue(k, 1);
8684                 const unsigned int loc = xyz[k];
8685                 xaxis[loc >> 16] = 1;
8686                 yaxis[(loc >> 8) & 0xff] = 1;
8687                 zaxis[loc & 0xff] = 1;
8688             }
8689         }
8690     }
8691     grid3d.Copy(next3d);
8692     UpdateBoundingBox();
8693 
8694     // return the population
8695     return next3d.GetNumKeys();
8696 }
8697 
8698 // -----------------------------------------------------------------------------
8699 
PopulateAxis()8700 void Overlay::PopulateAxis() {
8701     if (gridsize == 0) return;
8702 
8703     int numkeys;
8704     const int *grid3dkeys = grid3d.GetKeys(&numkeys);
8705     for (int i = 0; i < numkeys; i++) {
8706         int k = grid3dkeys[i];
8707         const unsigned int loc = xyz[k];
8708         xaxis[loc >> 16] = 1;
8709         yaxis[(loc >> 8) & 0xff] = 1;
8710         zaxis[loc & 0xff] = 1;
8711     }
8712     UpdateBoundingBox();
8713 }
8714 
8715 // -----------------------------------------------------------------------------
8716 
PopulateGrid(lua_State * L,const int n,int idx,Table & destgrid)8717 const char *Overlay::PopulateGrid(lua_State *L, const int n, int idx, Table &destgrid) {
8718     const int N = gridsize;
8719     const unsigned int NNN = N * N * N;
8720 
8721     // fill grid
8722     destgrid.Clear();
8723     if (idx > n) return OverlayError("missing grid argument");
8724     lua_rawgeti(L, 1, idx);
8725     int type = lua_type(L, -1);
8726     if (type != LUA_TTABLE) {
8727         lua_pop(L, 1);
8728         return OverlayError("grid argument is not a table");
8729     }
8730     lua_pushvalue(L, -1);
8731     lua_pushnil(L);
8732     bool valid = true;
8733     while (lua_next(L, -2)) {
8734         lua_pushvalue(L, -2);
8735         int k = lua_tointeger(L, -1);
8736         lua_pop(L, 2);
8737         // check that the cell coordinates are within the grid
8738         if ((unsigned int)k >= NNN) {
8739             valid = false;
8740             break;
8741         }
8742         destgrid.SetTo1(k);
8743     }
8744     lua_pop(L, 1);
8745     if (!valid) return OverlayError("pattern is larger than the grid");
8746 
8747     return NULL;
8748 }
8749 
8750 // -----------------------------------------------------------------------------
8751 // Arguments:
8752 // grid             table      integer
8753 // clearhistory     boolean
8754 
Do3DSetPattern(lua_State * L,const int n,int * nresults)8755 const char *Overlay::Do3DSetPattern(lua_State *L, const int n, int *nresults) {
8756     const char *error = NULL;
8757     if (gridsize == 0) return OverlayError("grid size not set");
8758 
8759     // populate the grid from the supplied pattern
8760     int idx = 2;
8761     if ((error = PopulateGrid(L, n, idx++, grid3d)) != NULL) return error;
8762     PopulateAxis();
8763 
8764     // read the clear history flag
8765     bool clearhistory = false;
8766     if ((error = ReadLuaBoolean(L, n, idx++, &clearhistory, "clearhistory")) != NULL) return error;
8767 
8768     // clear history if requested
8769     if (clearhistory) {
8770         history3d.Clear();
8771     }
8772 
8773     return NULL;
8774 }
8775 
8776 // -----------------------------------------------------------------------------
8777 // Arguments:
8778 // selected         table       integer
8779 // pastepatt        table       integer
8780 // active           table       integer
8781 
Do3DSetSelectPasteActive(lua_State * L,const int n,int * nresults)8782 const char *Overlay::Do3DSetSelectPasteActive(lua_State *L, const int n, int *nresults) {
8783     const char *error = NULL;
8784     if (gridsize == 0) return OverlayError("grid size not set");
8785 
8786     int idx = 2;
8787     if ((error = PopulateGrid(L, n, idx++, select3d)) != NULL) return error;
8788     if ((error = PopulateGrid(L, n, idx++, paste3d)) != NULL) return error;
8789     if ((error = PopulateGrid(L, n, idx++, active3d)) != NULL) return error;
8790 
8791     return error;
8792 }
8793 
8794 // -----------------------------------------------------------------------------
8795 
UpdateHistoryFromLive()8796 void Overlay::UpdateHistoryFromLive() {
8797     int numkeys = 0;
8798     int numhistkeys = 0;
8799     const int *grid3dkeys = grid3d.GetKeys(&numkeys);
8800     const int *history3keys = history3d.GetKeys(&numhistkeys);
8801 
8802     if (fadehistory) {
8803         // reduce history on all cells by 1
8804         for (int i = 0; i < numhistkeys; i++) {
8805             int k = history3keys[i];
8806             history3d.DecrementTo1(k);
8807         }
8808     }
8809 
8810     // set history to maximum longevity for any live cells
8811     for (int i = 0; i < numkeys; i++) {
8812         int k = grid3dkeys[i];
8813         history3d.SetValue(k, showhistory);
8814     }
8815 }
8816 
8817 // -----------------------------------------------------------------------------
8818 
UpdateBoundingBoxFromHistory()8819 void Overlay::UpdateBoundingBoxFromHistory() {
8820     const int N = gridsize;
8821     int numhistkeys = 0;
8822     const int *history3keys = history3d.GetKeys(&numhistkeys);
8823 
8824     // do nothing if no history cells
8825     if (numhistkeys == 0) return;
8826 
8827     // set history min, max defaults
8828     int hminx = N;
8829     int hmaxx = -1;
8830     int hminy = N;
8831     int hmaxy = -1;
8832     int hminz = N;
8833     int hmaxz = -1;
8834     // compute bounding box for history cells
8835     for (int i = 0; i < numhistkeys; i++) {
8836         const int k = history3keys[i];
8837         const unsigned int loc = xyz[k];
8838         const int x = loc >> 16;
8839         const int y = (loc >> 8) & 0xff;
8840         const int z = loc & 0xff;
8841         if (x < hminx) hminx = x;
8842         if (x > hmaxx) hmaxx = x;
8843         if (y < hminy) hminy = y;
8844         if (y > hmaxy) hmaxy = y;
8845         if (z < hminz) hminz = z;
8846         if (z > hmaxz) hmaxz = z;
8847     }
8848 
8849     // adjust drawing range to include history bounding box
8850     if (stepx < 0) {
8851         if (hminx < tox) tox = hminx;
8852         if (hmaxx > fromx) fromx = hmaxx;
8853     } else {
8854         if (hminx < fromx) fromx = hminx;
8855         if (hmaxx > tox) tox = hmaxx;
8856     }
8857     if (stepy < 0) {
8858         if (hminy < toy) toy = hminy;
8859         if (hmaxy > fromy) fromy = hmaxy;
8860     } else {
8861         if (hminy < fromy) fromy = hminy;
8862         if (hmaxy > toy) toy = hmaxy;
8863     }
8864     if (stepz < 0) {
8865         if (hminz < toz) toz = hminz;
8866         if (hmaxz > fromz) fromz = hmaxz;
8867     } else {
8868         if (hminz < fromz) fromz = hminz;
8869         if (hmaxz > toz) toz = hmaxz;
8870     }
8871 }
8872 
8873 // -----------------------------------------------------------------------------
8874 // Arguments:
8875 // showhistory      integer     0..255
8876 // fadehistory      boolean
8877 
Do3DSetCellHistory(lua_State * L,const int n,int * nresults)8878 const char *Overlay::Do3DSetCellHistory(lua_State *L, const int n, int *nresults) {
8879     const char *error = NULL;
8880 
8881     // read the show history longevity
8882     int idx = 2;
8883     int oldshow = showhistory;
8884     int value = 0;
8885     if ((error = ReadLuaInteger(L, n, idx++, &value, "showhistory")) != NULL) return error;
8886     if (value < 0 || value > 255) return OverlayError("showhistory must be from 0 to 255");
8887     showhistory = value;
8888 
8889     // read the fade history flag
8890     if ((error = ReadLuaBoolean(L, n, idx++, &fadehistory, "fadehistory")) != NULL) return error;
8891 
8892     // clear history if mode changed (but not if only fading changed)
8893     if (oldshow != showhistory) {
8894         history3d.Clear();
8895     }
8896 
8897     return NULL;
8898 }
8899 
8900 // -----------------------------------------------------------------------------
8901 // Arguments:
8902 // gencount         integer
8903 // liveedge         boolean     (if algo is not BusyBoxes)
8904 
Do3DNextGen(lua_State * L,const int n,int * nresults)8905 const char *Overlay::Do3DNextGen(lua_State *L, const int n, int *nresults) {
8906     const char *error = NULL;
8907 
8908     // get the grid size
8909     if (gridsize == 0) return OverlayError("grid size not set");
8910 
8911     // read gencount
8912     int idx = 2;
8913     int gencount = 0;
8914     if ((error = ReadLuaInteger(L, n, idx++, &gencount, "gencount")) != NULL) return error;
8915 
8916     // for non-BusyBoxes algos get liveedge flag
8917     liveedge = false;
8918     if (!(ruletype == bb || ruletype == bbw)) {
8919         if ((error = ReadLuaBoolean(L, n, idx++, &liveedge, "liveedge")) != NULL) return error;
8920     }
8921 
8922     // check div table exists
8923     if (modN == NULL) if (!CreateDivTable()) return OverlayError("could not allocate div table");
8924 
8925     // process each step
8926     int lastgen = gencount - (gencount % stepsize) + stepsize;
8927     int newpop = 0;
8928     bool laststep = false;
8929 
8930     while (gencount < lastgen) {
8931         // set the laststep flag
8932         if (gencount == lastgen - 1) laststep = true;
8933 
8934         // clear the intermediate counts
8935         count1.ClearKeys();
8936         if (!(ruletype == bb || ruletype == bbw)) {
8937             count2.ClearKeys();
8938         }
8939 
8940         // call the appropriate algorithm
8941         switch (ruletype) {
8942             case moore:
8943                 Do3DNextGenMoore();
8944                 newpop = CreateResultsFromC1G3(L, laststep);
8945                 break;
8946             case face:
8947                 Do3DNextGenFace();
8948                 newpop = CreateResultsFromC1C2(L, laststep);
8949                 break;
8950             case corner:
8951                 Do3DNextGenCorner();
8952                 newpop = CreateResultsFromC1C2(L, laststep);
8953                 break;
8954             case edge:
8955                 Do3DNextGenEdge();
8956                 newpop = CreateResultsFromC1C2(L, laststep);
8957                 break;
8958             case hexahedral:
8959                 Do3DNextGenHexahedral();
8960                 newpop = CreateResultsFromC1C2(L, laststep);
8961                 break;
8962             case bb:
8963                 if ((gridsize & 1) == 1) return OverlayError("grid size must be even for BusyBoxes");
8964                 Do3DNextGenBB(true, gencount);
8965                 newpop = CreateResultsFromC1(L, laststep);
8966                 break;
8967             case bbw:
8968                 if ((gridsize & 1) == 1) return OverlayError("grid size must be even for BusyBoxes");
8969                 Do3DNextGenBB(false, gencount);
8970                 newpop = CreateResultsFromC1(L, laststep);
8971                 break;
8972             default:
8973                 return OverlayError("illegal rule specified");
8974         }
8975 
8976         // update history if required
8977         if (showhistory > 0) UpdateHistoryFromLive();
8978 
8979         // next step
8980         gencount++;
8981 
8982         // exit if population is zero
8983         if (newpop == 0) break;
8984     }
8985 
8986     // return the population
8987     lua_pushinteger(L, newpop);
8988 
8989     // return the gencount
8990     lua_pushinteger(L, gencount);
8991 
8992     // return the grid bounding box
8993     lua_pushinteger(L, minx);
8994     lua_pushinteger(L, maxx);
8995     lua_pushinteger(L, miny);
8996     lua_pushinteger(L, maxy);
8997     lua_pushinteger(L, minz);
8998     lua_pushinteger(L, maxz);
8999 
9000     *nresults = 9;  // table, popcount, gencount, minx, maxx, miny, maxy, minz, maxz
9001 
9002     return NULL;
9003 }
9004 
9005 // -----------------------------------------------------------------------------
9006 
Do3DNextGenBB(const bool mirror,const int gencount)9007 void Overlay::Do3DNextGenBB(const bool mirror, const int gencount) {
9008     // the algorithm used below is a slightly modified (and corrected!)
9009     // version of the kernel code in Ready's Salt 3D example
9010     // (see Patterns/CellularAutomata/Salt/salt3D_circular330.vti);
9011     // it uses a rule based on 28 cells in a 7x7 neighborhood for each live cell
9012 
9013     // swap site locations
9014     static const int swap1[] = { 1,  1};
9015     static const int swap2[] = {-1,  1};
9016     static const int swap3[] = {-1, -1};
9017     static const int swap4[] = { 1, -1};
9018 
9019     // activator locations
9020     static const int act5[]  = { 2, -1};
9021     static const int act6[]  = { 2,  1};
9022     static const int act7[]  = { 1,  2};
9023     static const int act8[]  = {-1,  2};
9024     static const int act9[]  = {-2,  1};
9025     static const int act10[] = {-2, -1};
9026     static const int act11[] = {-1, -2};
9027     static const int act12[] = { 1, -2};
9028 
9029     // inhibitor locations
9030     static const int inhib13[] = {-2, -3};
9031     static const int inhib14[] = { 0, -3};
9032     static const int inhib15[] = { 2, -3};
9033     static const int inhib16[] = {-3, -2};
9034     static const int inhib17[] = { 3, -2};
9035     static const int inhib18[] = { 0, -1};
9036     static const int inhib19[] = {-3,  0};
9037     static const int inhib20[] = {-1,  0};
9038     static const int inhib21[] = { 1,  0};
9039     static const int inhib22[] = { 3,  0};
9040     static const int inhib23[] = { 0,  1};
9041     static const int inhib24[] = {-3,  2};
9042     static const int inhib25[] = { 3,  2};
9043     static const int inhib26[] = {-2,  3};
9044     static const int inhib27[] = { 0,  3};
9045     static const int inhib28[] = { 2,  3};
9046 
9047     static const int *coords[] = {
9048         // 1 to 4 are the coordinates for the 4 potential swap sites:
9049         swap1, swap2, swap3, swap4,
9050         // 5 to 12 are activators:
9051         act5, act6, act7, act8, act9, act10, act11, act12,
9052         // 13 to 28 are inhibitors:
9053         inhib13, inhib14, inhib15, inhib16, inhib17, inhib18,
9054         inhib19, inhib20, inhib21, inhib22, inhib23, inhib24,
9055         inhib25, inhib26, inhib27, inhib28
9056     };
9057 
9058     // numbers are indices into the coords array
9059     static const int actidx1[] = {4,  7};
9060     static const int actidx2[] = {6,  9};
9061     static const int actidx3[] = {8, 11};
9062     static const int actidx4[] = {5, 10};
9063     static const int *activators[] = {actidx1, actidx2, actidx3, actidx4};
9064 
9065     static const int inhibidx1[] = {17, 24, 21, 26, 19, 27, 6,  9,  8, 11,  5, 10};
9066     static const int inhibidx2[] = {17, 23, 18, 26, 20, 25, 4,  7,  8, 11,  5, 10};
9067     static const int inhibidx3[] = {15, 22, 13, 18, 12, 20, 4,  7,  6,  9,  5, 10};
9068     static const int inhibidx4[] = {19, 14, 13, 21, 16, 22, 4,  7,  6,  9,  8, 11};
9069     static const int *inhibitors[] = {inhibidx1, inhibidx2, inhibidx3, inhibidx4};
9070 
9071     int numkeys;
9072     const int phase = gencount % 6;
9073     const int N = gridsize;
9074     const int NN = N * N;
9075     const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9076     const unsigned char *grid3dvalues = grid3d.GetValues();
9077 
9078     // apply rule
9079     unsigned char val[28];
9080     for (int i = 0; i < numkeys; i++) {
9081         const int k = grid3dkeys[i];
9082         const unsigned int loc = xyz[k];
9083         int x = loc >> 16;
9084         int y = (loc >> 8) & 0xff;
9085         int z = loc & 0xff;
9086         if (((x + y + z) & 1) == (phase & 1)) {
9087             // this live cell has the right parity so get values for its 28 neighbors
9088             int sx, sy, sz;
9089             int j = 0;
9090             const int *coordsj;
9091             if (phase == 0 || phase == 3) {
9092                 const int Nz = N * z;
9093                 // use XY plane
9094                 x += N;
9095                 y += N;
9096                 while (j < 28) {
9097                     // compute the next two values
9098                     coordsj = coords[j];
9099                     sx = modN[x + coordsj[0]];
9100                     sy = modN[y + coordsj[1]];
9101                     val[j++] = grid3dvalues[sx + N * (sy + Nz)];
9102                     coordsj = coords[j];
9103                     sx = modN[x + coordsj[0]];
9104                     sy = modN[y + coordsj[1]];
9105                     val[j++] = grid3dvalues[sx + N * (sy + Nz)];
9106                 }
9107             } else {
9108                 if (phase == 1 || phase == 4) {
9109                     // use YZ plane
9110                     y += N;
9111                     z += N;
9112                     while (j < 28) {
9113                         // compute the next two values
9114                         coordsj = coords[j];
9115                         sy = modN[y + coordsj[0]];
9116                         sz = modN[z + coordsj[1]];
9117                         val[j++] = grid3dvalues[x + N * (sy + N * sz)];
9118                         coordsj = coords[j];
9119                         sy = modN[y + coordsj[0]];
9120                         sz = modN[z + coordsj[1]];
9121                         val[j++] = grid3dvalues[x + N * (sy + N * sz)];
9122                     }
9123                 } else {
9124                     // phase == 2 or 5 so use XZ plane
9125                     x += N;
9126                     z += N;
9127                     const int Ny = N * y;
9128                     while (j < 28) {
9129                         // compute the next two values
9130                         coordsj = coords[j];
9131                         sx = modN[x + coordsj[0]];
9132                         sz = modN[z + coordsj[1]];
9133                         val[j++] = grid3dvalues[sx + Ny + NN * sz];
9134                         coordsj = coords[j];
9135                         sx = modN[x + coordsj[0]];
9136                         sz = modN[z + coordsj[1]];
9137                         val[j++] = grid3dvalues[sx + Ny + NN * sz];
9138                     }
9139                 }
9140             }
9141 
9142             // find the potential swaps
9143             int numswaps = 0;
9144             int swapi = 0;
9145             for (j = 0; j <= 3; j++) {
9146                 const int *activatorsj = activators[j];
9147                 const int *inhibitorsj = inhibitors[j];
9148                 // if either activator is a live cell then the swap is possible,
9149                 // but if any inhibitor is a live cell then the swap is forbidden
9150                 if ((val[activatorsj[0]] || val[activatorsj[1]])
9151                     && ! (val[inhibitorsj[0]] || val[inhibitorsj[1]] ||
9152                              val[inhibitorsj[2]] || val[inhibitorsj[3]] ||
9153                              val[inhibitorsj[4]] || val[inhibitorsj[5]] ||
9154                              val[inhibitorsj[6]] || val[inhibitorsj[7]] ||
9155                              val[inhibitorsj[8]] || val[inhibitorsj[9]] ||
9156                              val[inhibitorsj[10]] || val[inhibitorsj[11]])) {
9157                     numswaps++;
9158                     if (numswaps > 1) break;
9159                     swapi = j;   // index of swap location in coords array (0..3)
9160                 }
9161             }
9162 
9163             // if only one swap, and only to an empty cell, then do it
9164             if (numswaps == 1 && ! val[swapi]) {
9165                 // calculate the swap position
9166                 int newx, newy, newz;
9167                 if (phase == 0 || phase == 3) {
9168                     // use XY plane
9169                     newx = x - N + coords[swapi][0];
9170                     newy = y - N + coords[swapi][1];
9171                     newz = z;
9172                 } else {
9173                     if (phase == 1 || phase == 4) {
9174                         // use YZ plane
9175                         newx = x;
9176                         newy = y - N + coords[swapi][0];
9177                         newz = z - N + coords[swapi][1];
9178                     } else {
9179                         // phase == 2 or 5 so use XZ plane
9180                         newx = x - N + coords[swapi][0];
9181                         newy = y;
9182                         newz = z - N + coords[swapi][1];
9183                     }
9184                 }
9185                 // if using mirror mode then don't wrap
9186                 if (mirror &&
9187                     (newx < 0 || newx >= N ||
9188                      newy < 0 || newy >= N ||
9189                      newz < 0 || newz >= N)) {
9190                     // swap position is outside grid so don't do it
9191                     count1.SetTo1(k);
9192                 } else {
9193                     // do the swap, wrapping if necessary
9194                     newx = modN[newx + N];
9195                     newy = modN[newy + N];
9196                     newz = modN[newz + N];
9197                     count1.SetTo1(newx + N * (newy + N * newz));
9198                 }
9199             } else {
9200                 // don't swap this live cell
9201                 count1.SetTo1(k);
9202             }
9203         } else {
9204             // live cell with wrong parity
9205             count1.SetTo1(k);
9206         }
9207     }
9208 }
9209 
9210 // -----------------------------------------------------------------------------
9211 
Do3DNextGenFace()9212 void Overlay::Do3DNextGenFace() {
9213     int numkeys;
9214     const int N = gridsize;
9215     const int NN = N * N;
9216 
9217     // check whether to use wrap
9218     if (liveedge) {
9219         // use wrap version
9220         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9221         const unsigned char *grid3dvalues = grid3d.GetValues();
9222         for (int i = 0; i < numkeys; i++) {
9223             const int k = grid3dkeys[i];
9224             const unsigned int loc = xyz[k];
9225             const int x = loc >> 16;
9226             const int y = (loc >> 8) & 0xff;
9227             const int z = loc & 0xff;
9228             count1.SetValue(k, 0);
9229             const int Ny = N * y;
9230             const int NNz = NN * z;
9231             const int NypNNz = Ny + NNz;
9232             const int xpNNz = x + NNz;
9233             const int xpNy = x + Ny;
9234 
9235             // calculate the positions of the 6 cells next to each face of this cell
9236             const int xp1 = modN[x + 1] + NypNNz;
9237             const int xm1 = modN[x - 1 + N] + NypNNz;
9238             const int yp1 = N * modN[y + 1] + xpNNz;
9239             const int ym1 = N * modN[y - 1 + N] + xpNNz;
9240             const int zp1 = NN * modN[z + 1] + xpNy;
9241             const int zm1 = NN * modN[z - 1 + N] + xpNy;
9242 
9243             if (grid3dvalues[xp1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xp1, 1); }
9244             if (grid3dvalues[xm1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xm1, 1); }
9245             if (grid3dvalues[yp1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(yp1, 1); }
9246             if (grid3dvalues[ym1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ym1, 1); }
9247             if (grid3dvalues[zp1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(zp1, 1); }
9248             if (grid3dvalues[zm1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(zm1, 1); }
9249         }
9250     } else {
9251         // use no wrap version
9252         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9253         const unsigned char *grid3dvalues = grid3d.GetValues();
9254         for (int i = 0; i < numkeys; i++) {
9255             const int k = grid3dkeys[i];
9256             count1.SetValue(k, 0);
9257 
9258             // calculate the positions of the 6 cells next to each face of this cell
9259             const int xp1 = k + 1;
9260             const int xm1 = k - 1;
9261             const int yp1 = k + N;
9262             const int ym1 = k - N;
9263             const int zp1 = k + NN;
9264             const int zm1 = k - NN;
9265 
9266             if (grid3dvalues[xp1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xp1, 1); }
9267             if (grid3dvalues[xm1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xm1, 1); }
9268             if (grid3dvalues[yp1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(yp1, 1); }
9269             if (grid3dvalues[ym1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ym1, 1); }
9270             if (grid3dvalues[zp1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(zp1, 1); }
9271             if (grid3dvalues[zm1]) { count1.AddToValue(k, 1); } else { count2.AddToValue(zm1, 1); }
9272         }
9273     }
9274 }
9275 
9276 // -----------------------------------------------------------------------------
9277 
Do3DNextGenCorner()9278 void Overlay::Do3DNextGenCorner() {
9279     int numkeys;
9280     const int N = gridsize;
9281     const int NN = N * N;
9282 
9283     // check whether to use wrap
9284     if (liveedge) {
9285         // use wrap version
9286         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9287         const unsigned char *grid3dvalues = grid3d.GetValues();
9288         for (int i = 0; i < numkeys; i++) {
9289             const int k = grid3dkeys[i];
9290             const unsigned int loc = xyz[k];
9291             const int x = loc >> 16;
9292             const int y = (loc >> 8) & 0xff;
9293             const int z = loc & 0xff;
9294             count1.SetValue(k, 0);
9295 
9296             const int xp1 = modN[x + 1];
9297             const int xm1 = modN[x - 1 + N];
9298             const int yp1 = N * modN[y + 1];
9299             const int ym1 = N * modN[y - 1 + N];
9300             const int zp1 = NN * modN[z + 1];
9301             const int zm1 = NN * modN[z - 1 + N];
9302 
9303             // calculate the positions of the 8 cells cells touching each corner of this cell
9304             const int ppp = xp1 + yp1 + zp1;
9305             const int mmm = xm1 + ym1 + zm1;
9306             const int ppm = xp1 + yp1 + zm1;
9307             const int mmp = xm1 + ym1 + zp1;
9308             const int mpp = xm1 + yp1 + zp1;
9309             const int pmm = xp1 + ym1 + zm1;
9310             const int pmp = xp1 + ym1 + zp1;
9311             const int mpm = xm1 + yp1 + zm1;
9312 
9313             if (grid3dvalues[ppp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ppp, 1); }
9314             if (grid3dvalues[mmm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mmm, 1); }
9315             if (grid3dvalues[ppm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ppm, 1); }
9316             if (grid3dvalues[mmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mmp, 1); }
9317             if (grid3dvalues[mpp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpp, 1); }
9318             if (grid3dvalues[pmm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmm, 1); }
9319             if (grid3dvalues[pmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmp, 1); }
9320             if (grid3dvalues[mpm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpm, 1); }
9321         }
9322     } else {
9323         // use no wrap version
9324         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9325         const unsigned char *grid3dvalues = grid3d.GetValues();
9326         for (int i = 0; i < numkeys; i++) {
9327             const int k = grid3dkeys[i];
9328             count1.SetValue(k, 0);
9329 
9330             // calculate the positions of the 8 cells cells touching each corner of this cell
9331             const int ppp = k + 1 + N + NN;
9332             const int mmm = k - 1 - N - NN;
9333             const int ppm = k + 1 + N - NN;
9334             const int mmp = k - 1 - N + NN;
9335             const int mpp = k - 1 + N + NN;
9336             const int pmm = k + 1 - N - NN;
9337             const int pmp = k + 1 - N + NN;
9338             const int mpm = k - 1 + N - NN;
9339 
9340             if (grid3dvalues[ppp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ppp, 1); }
9341             if (grid3dvalues[mmm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mmm, 1); }
9342             if (grid3dvalues[ppm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ppm, 1); }
9343             if (grid3dvalues[mmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mmp, 1); }
9344             if (grid3dvalues[mpp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpp, 1); }
9345             if (grid3dvalues[pmm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmm, 1); }
9346             if (grid3dvalues[pmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmp, 1); }
9347             if (grid3dvalues[mpm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpm, 1); }
9348         }
9349     }
9350 }
9351 
9352 // -----------------------------------------------------------------------------
9353 
Do3DNextGenEdge()9354 void Overlay::Do3DNextGenEdge() {
9355     int numkeys;
9356     const int N = gridsize;
9357     const int NN = N * N;
9358 
9359     // check whether to use wrap
9360     if (liveedge) {
9361         // use wrap version
9362         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9363         const unsigned char *grid3dvalues = grid3d.GetValues();
9364         for (int i = 0; i < numkeys; i++) {
9365             const int k = grid3dkeys[i];
9366             const unsigned int loc = xyz[k];
9367             const int x = loc >> 16;
9368             const int y = (loc >> 8) & 0xff;
9369             const int z = loc & 0xff;
9370             count1.SetValue(k, 0);
9371 
9372             const int xp1 = modN[x + 1];
9373             const int xm1 = modN[x - 1 + N];
9374             const int yp1 = N * modN[y + 1];
9375             const int ym1 = N * modN[y - 1 + N];
9376             const int zp1 = NN * modN[z + 1];
9377             const int zm1 = NN * modN[z - 1 + N];
9378             const int Ny = N * y;
9379             const int NNz = NN * z;
9380 
9381             // calculate the positions of the 12 cells next to each edge of this cell
9382             const int xpp = x + yp1 + zp1;
9383             const int xmm = x + ym1 + zm1;
9384             const int xpm = x + yp1 + zm1;
9385             const int xmp = x + ym1 + zp1;
9386 
9387             const int pyp = xp1 + Ny + zp1;
9388             const int mym = xm1 + Ny + zm1;
9389             const int pym = xp1 + Ny + zm1;
9390             const int myp = xm1 + Ny + zp1;
9391 
9392             const int ppz = xp1 + yp1 + NNz;
9393             const int mmz = xm1 + ym1 + NNz;
9394             const int pmz = xp1 + ym1 + NNz;
9395             const int mpz = xm1 + yp1 + NNz;
9396 
9397             if (grid3dvalues[xpp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpp, 1); }
9398             if (grid3dvalues[xmm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmm, 1); }
9399             if (grid3dvalues[xpm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpm, 1); }
9400             if (grid3dvalues[xmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmp, 1); }
9401 
9402             if (grid3dvalues[pyp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pyp, 1); }
9403             if (grid3dvalues[mym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mym, 1); }
9404             if (grid3dvalues[pym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pym, 1); }
9405             if (grid3dvalues[myp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(myp, 1); }
9406 
9407             if (grid3dvalues[ppz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ppz, 1); }
9408             if (grid3dvalues[mmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mmz, 1); }
9409             if (grid3dvalues[pmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmz, 1); }
9410             if (grid3dvalues[mpz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpz, 1); }
9411         }
9412     } else {
9413         // use no wrap version
9414         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9415         const unsigned char *grid3dvalues = grid3d.GetValues();
9416         for (int i = 0; i < numkeys; i++) {
9417             const int k = grid3dkeys[i];
9418             count1.SetValue(k, 0);
9419 
9420             // calculate the positions of the 12 cells next to each edge of this cell
9421             const int xpp = k + N + NN;
9422             const int xmm = k - N - NN;
9423             const int xpm = k + N - NN;
9424             const int xmp = k - N + NN;
9425 
9426             const int pyp = k + 1 + NN;
9427             const int mym = k - 1 - NN;
9428             const int pym = k + 1 - NN;
9429             const int myp = k - 1 + NN;
9430 
9431             const int ppz = k + 1 + N;
9432             const int mmz = k - 1 - N;
9433             const int pmz = k + 1 - N;
9434             const int mpz = k - 1 + N;
9435 
9436             if (grid3dvalues[xpp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpp, 1); }
9437             if (grid3dvalues[xmm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmm, 1); }
9438             if (grid3dvalues[xpm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpm, 1); }
9439             if (grid3dvalues[xmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmp, 1); }
9440 
9441             if (grid3dvalues[pyp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pyp, 1); }
9442             if (grid3dvalues[mym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mym, 1); }
9443             if (grid3dvalues[pym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pym, 1); }
9444             if (grid3dvalues[myp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(myp, 1); }
9445 
9446             if (grid3dvalues[ppz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(ppz, 1); }
9447             if (grid3dvalues[mmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mmz, 1); }
9448             if (grid3dvalues[pmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmz, 1); }
9449             if (grid3dvalues[mpz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpz, 1); }
9450         }
9451     }
9452 }
9453 
9454 // -----------------------------------------------------------------------------
9455 
Do3DNextGenHexahedral()9456 void Overlay::Do3DNextGenHexahedral() {
9457     int numkeys;
9458     const int N = gridsize;
9459     const int NN = N * N;
9460 
9461     // check whether to use wrap
9462     if (liveedge) {
9463         // use wrap version
9464         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9465         const unsigned char *grid3dvalues = grid3d.GetValues();
9466         for (int i = 0; i < numkeys; i++) {
9467             const int k = grid3dkeys[i];
9468             const unsigned int loc = xyz[k];
9469             const int x = loc >> 16;
9470             const int y = (loc >> 8) & 0xff;
9471             const int z = loc & 0xff;
9472             count1.SetValue(k, 0);
9473 
9474             const int xp1 = modN[x + 1];
9475             const int xm1 = modN[x - 1 + N];
9476             const int yp1 = N * modN[y + 1];
9477             const int ym1 = N * modN[y - 1 + N];
9478             const int zp1 = NN * modN[z + 1];
9479             const int zm1 = NN * modN[z - 1 + N];
9480             const int Ny = N * y;
9481             const int NNz = NN * z;
9482 
9483             // calculate the positions of the 12 neighboring cells (using the top offsets given
9484             // on page 872 in http://www.complex-systems.com/pdf/01-5-1.pdf)
9485             const int xym = x + Ny + zm1;
9486             const int xyp = x + Ny + zp1;
9487             const int xpm = x + yp1 + zm1;
9488             const int xpz = x + yp1 + NNz;
9489             const int xmp = x + ym1 + zp1;
9490             const int xmz = x + ym1 + NNz;
9491             const int pym = xp1 + Ny + zm1;
9492             const int pyz = xp1 + Ny + NNz;
9493             const int myp = xm1 + Ny + zp1;
9494             const int myz = xm1 + Ny + NNz;
9495             const int pmz = xp1 + ym1 + NNz;
9496             const int mpz = xm1 + yp1 + NNz;
9497 
9498             if (grid3dvalues[xym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xym, 1); }
9499             if (grid3dvalues[xyp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xyp, 1); }
9500             if (grid3dvalues[xpm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpm, 1); }
9501             if (grid3dvalues[xpz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpz, 1); }
9502 
9503             if (grid3dvalues[xmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmp, 1); }
9504             if (grid3dvalues[xmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmz, 1); }
9505             if (grid3dvalues[pym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pym, 1); }
9506             if (grid3dvalues[pyz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pyz, 1); }
9507 
9508             if (grid3dvalues[myp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(myp, 1); }
9509             if (grid3dvalues[myz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(myz, 1); }
9510             if (grid3dvalues[pmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmz, 1); }
9511             if (grid3dvalues[mpz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpz, 1); }
9512         }
9513     } else {
9514         // use no wrap version
9515         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9516         const unsigned char *grid3dvalues = grid3d.GetValues();
9517         for (int i = 0; i < numkeys; i++) {
9518             const int k = grid3dkeys[i];
9519             count1.SetValue(k, 0);
9520 
9521             // calculate the positions of the 12 neighboring cells (using the top offsets given
9522             // on page 872 in http://www.complex-systems.com/pdf/01-5-1.pdf)
9523             const int xym = k - NN;
9524             const int xyp = k + NN;
9525             const int xpm = k + N - NN;
9526             const int xpz = k + N;
9527             const int xmp = k - N + NN;
9528             const int xmz = k - N;
9529             const int pym = k + 1 - NN;
9530             const int pyz = k + 1;
9531             const int myp = k - 1 + NN;
9532             const int myz = k - 1;
9533             const int pmz = k + 1 - N;
9534             const int mpz = k - 1 + N;
9535 
9536             if (grid3dvalues[xym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xym, 1); }
9537             if (grid3dvalues[xyp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xyp, 1); }
9538             if (grid3dvalues[xpm]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpm, 1); }
9539             if (grid3dvalues[xpz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xpz, 1); }
9540 
9541             if (grid3dvalues[xmp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmp, 1); }
9542             if (grid3dvalues[xmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(xmz, 1); }
9543             if (grid3dvalues[pym]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pym, 1); }
9544             if (grid3dvalues[pyz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pyz, 1); }
9545 
9546             if (grid3dvalues[myp]) { count1.AddToValue(k, 1); } else { count2.AddToValue(myp, 1); }
9547             if (grid3dvalues[myz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(myz, 1); }
9548             if (grid3dvalues[pmz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(pmz, 1); }
9549             if (grid3dvalues[mpz]) { count1.AddToValue(k, 1); } else { count2.AddToValue(mpz, 1); }
9550         }
9551     }
9552 }
9553 
9554 // -----------------------------------------------------------------------------
9555 
Do3DNextGenMoore()9556 void Overlay::Do3DNextGenMoore() {
9557     int numkeys;
9558     const int *count1keys = NULL;
9559     const unsigned char *count1values = NULL;
9560     const int N = gridsize;
9561     const int NN = N * N;
9562     const int NNN = NN * N;
9563 
9564     // check whether to use wrap
9565     if (liveedge) {
9566         // use wrap version
9567         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9568         const int NNmN = NN - N;
9569         for (int i = 0; i < numkeys; i++) {
9570             const int k = grid3dkeys[i];
9571             const int y = modNN[k];
9572             count1.AddToValue(k, 1);
9573             count1.AddToValue(k + (y >= NNmN ? -NNmN: N), 1);
9574             count1.AddToValue(k + (y < N ? NNmN : -N), 1);
9575         }
9576 
9577         // get keys and values in count1
9578         count1keys = count1.GetKeys(&numkeys);
9579         count1values = count1.GetValues();
9580         const int Nm1 = N - 1;
9581         for (int i = 0; i < numkeys; i++) {
9582             const int k = count1keys[i];
9583             const unsigned char v = count1values[k];
9584             const int x = modN[k];
9585             count2.AddToValue(k, v);
9586             count2.AddToValue(k + (x == Nm1 ? -Nm1 : 1), v);
9587             count2.AddToValue(k + (x == 0 ? Nm1 : -1), v);
9588         }
9589 
9590         // get keys and values in count2
9591         const int *count2keys = count2.GetKeys(&numkeys);
9592         const unsigned char *count2values = count2.GetValues();
9593         const int NNNmNN = NNN - NN;
9594         count1.ClearKeys();
9595         for (int i = 0; i < numkeys; i++) {
9596             const int k = count2keys[i];
9597             const unsigned char v = count2values[k];
9598             count1.AddToValue(k, v);
9599             count1.AddToValue(k >= NNNmNN ? k - NNNmNN : k + NN, v);
9600             count1.AddToValue(k < NN ? k + NNNmNN : k - NN, v);
9601         }
9602     } else {
9603         // use nowrap version
9604         const int *grid3dkeys = grid3d.GetKeys(&numkeys);
9605         for (int i = 0; i < numkeys; i++) {
9606             const int k = grid3dkeys[i];
9607             count1.AddToValue(k, 1);
9608             count1.AddToValue(k + N, 1);
9609             count1.AddToValue(k - N, 1);
9610         }
9611 
9612         // get keys and values in count1
9613         count1keys = count1.GetKeys(&numkeys);
9614         count1values = count1.GetValues();
9615         for (int i = 0; i < numkeys; i++) {
9616             const int k = count1keys[i];
9617             const unsigned char v = count1values[k];
9618             count2.AddToValue(k, v);
9619             count2.AddToValue(k + 1, v);
9620             count2.AddToValue(k - 1, v);
9621         }
9622 
9623         // get keys and values in count2
9624         const int *count2keys = count2.GetKeys(&numkeys);
9625         const unsigned char *count2values = count2.GetValues();
9626         count1.ClearKeys();
9627         for (int i = 0; i < numkeys; i++) {
9628             const int k = count2keys[i];
9629             const unsigned char v = count2values[k];
9630             count1.AddToValue(k, v);
9631             count1.AddToValue(k + NN, v);
9632             count1.AddToValue(k - NN, v);
9633         }
9634     }
9635 }
9636 
9637 // -----------------------------------------------------------------------------
9638 
DoOverlayCommand(const char * cmd)9639 const char *Overlay::DoOverlayCommand(const char *cmd)
9640 {
9641     // determine which command to run
9642     if (strncmp(cmd, "set ", 4) == 0)          return DoSetPixel(cmd+4);
9643     if (strncmp(cmd, "get ", 4) == 0)          return DoGetPixel(cmd+4);
9644     if (strcmp(cmd,  "xy") == 0)               return DoGetXY();
9645     if (strncmp(cmd, "paste", 5) == 0)         return DoPaste(cmd+5);
9646     if (strncmp(cmd, "rgba", 4) == 0)          return DoSetRGBA(cmd+4);
9647     if (strncmp(cmd, "blend", 5) == 0)         return DoBlend(cmd+5);
9648     if (strncmp(cmd, "fill", 4) == 0)          return DoFill(cmd+4);
9649     if (strncmp(cmd, "copy", 4) == 0)          return DoCopy(cmd+4);
9650     if (strncmp(cmd, "optimize", 8) == 0)      return DoOptimize(cmd+8);
9651     if (strncmp(cmd, "lineoption ", 11) == 0)  return DoLineOption(cmd+11);
9652     if (strncmp(cmd, "lines", 5) == 0)         return DoLine(cmd+5, false);
9653     if (strncmp(cmd, "line", 4) == 0)          return DoLine(cmd+4, true);
9654     if (strncmp(cmd, "ellipse", 7) == 0)       return DoEllipse(cmd+7);
9655     if (strncmp(cmd, "flood", 5) == 0)         return DoFlood(cmd+5);
9656     if (strncmp(cmd, "textoption ", 11) == 0)  return DoTextOption(cmd+11);
9657     if (strncmp(cmd, "text", 4) == 0)          return DoText(cmd+4);
9658     if (strncmp(cmd, "font", 4) == 0)          return DoFont(cmd+4);
9659     if (strncmp(cmd, "transform", 9) == 0)     return DoTransform(cmd+9);
9660     if (strncmp(cmd, "position", 8) == 0)      return DoPosition(cmd+8);
9661     if (strncmp(cmd, "load", 4) == 0)          return DoLoad(cmd+4);
9662     if (strncmp(cmd, "save", 4) == 0)          return DoSave(cmd+4);
9663     if (strncmp(cmd, "scale", 5) == 0)         return DoScale(cmd+5);
9664     if (strncmp(cmd, "cursor", 6) == 0)        return DoCursor(cmd+6);
9665     if (strcmp(cmd,  "update") == 0)           return DoUpdate();
9666     if (strncmp(cmd, "create", 6) == 0)        return DoCreate(cmd+6);
9667     if (strncmp(cmd, "resize", 6) == 0)        return DoResize(cmd+6);
9668     if (strncmp(cmd, "cellview ", 9) == 0)     return DoCellView(cmd+9);
9669     if (strncmp(cmd, "celloption ", 11) == 0)  return DoCellOption(cmd+11);
9670     if (strncmp(cmd, "camera ", 7) == 0)       return DoCamera(cmd+7);
9671     if (strncmp(cmd, "theme ", 6) == 0)        return DoTheme(cmd+6);
9672     if (strncmp(cmd, "target", 6) == 0)        return DoTarget(cmd+6);
9673     if (strncmp(cmd, "replace ", 8) == 0)      return DoReplace(cmd+8);
9674     if (strncmp(cmd, "sound", 5) == 0)         return DoSound(cmd+5);
9675     if (strcmp(cmd,  "updatecells") == 0)      return DoUpdateCells();
9676     if (strcmp(cmd,  "drawcells") == 0)        return DoDrawCells();
9677     if (strncmp(cmd, "delete", 6) == 0)        return DoDelete(cmd+6);
9678     return OverlayError("unknown command");
9679 }
9680 
9681 // -----------------------------------------------------------------------------
9682 
DoOverlayTable(const char * cmd,lua_State * L,int n,int * nresults)9683 const char *Overlay::DoOverlayTable(const char *cmd, lua_State *L, int n, int *nresults)
9684 {
9685     // determine which command to run
9686     if ((strcmp(cmd, "set")) == 0)               return DoSetPixel(L, n, nresults);
9687     if ((strcmp(cmd, "get")) == 0)               return DoGet(L, n, nresults);
9688     if ((strcmp(cmd, "paste")) == 0)             return DoPaste(L, n, nresults);
9689     if ((strcmp(cmd, "rgba")) == 0)              return DoSetRGBA(cmd, L, n, nresults);
9690     if ((strcmp(cmd, "line")) == 0)              return DoLine(L, n, true, nresults);
9691     if ((strcmp(cmd, "lines")) == 0)             return DoLine(L, n, false, nresults);
9692     if ((strcmp(cmd, "fill")) == 0)              return DoFill(L, n, nresults);
9693 
9694     // customized commands to speed up 3D.lua
9695     if ((strcmp(cmd, "nextgen3d")) == 0)         return Do3DNextGen(L, n, nresults);
9696     if ((strcmp(cmd, "setrule3d")) == 0)         return Do3DSetRule(L, n, nresults);
9697     if ((strcmp(cmd, "setsize3d")) == 0)         return Do3DSetGridSize(L, n, nresults);
9698     if ((strcmp(cmd, "setstep3d")) == 0)         return Do3DSetStepSize(L, n, nresults);
9699     if ((strcmp(cmd, "settrans3d")) == 0)        return Do3DSetTransform(L, n, nresults);
9700     if ((strcmp(cmd, "displaycells3d")) == 0)    return Do3DDisplayCells(L, n, nresults);
9701     if ((strcmp(cmd, "setcelltype3d")) == 0)     return Do3DSetCellType(L, n, nresults);
9702     if ((strcmp(cmd, "setdepthshading3d")) == 0) return Do3DSetDepthShading(L, n, nresults);
9703     if ((strcmp(cmd, "setpattern3d")) == 0)      return Do3DSetPattern(L, n, nresults);
9704     if ((strcmp(cmd, "setselpasact3d")) == 0)    return Do3DSetSelectPasteActive(L, n, nresults);
9705     if ((strcmp(cmd, "sethistory3d")) == 0)      return Do3DSetCellHistory(L, n, nresults);
9706 
9707     return OverlayError("unknown command");
9708 }
9709