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, °, &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