2 * Copyright 2011, Blender Foundation.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "COM_DoubleEdgeMaskOperation.h"
25 #include "DNA_node_types.h"
27 // this part has been copied from the double edge mask
28 // Contributor(s): Peter Larabell.
29 static void do_adjacentKeepBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) {
31 unsigned int isz=0; // inner edge size
32 unsigned int osz=0; // outer edge size
33 unsigned int gsz=0; // gradient fill area size
34 /* Test the four corners */
35 /* upper left corner */
37 // test if inner mask is filled
39 // test if pixel underneath, or to the right, are empty in the inner mask,
40 // but filled in the outer mask
41 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x+1] && lomask[x+1])) {
42 isz++; // increment inner edge size
43 lres[x]=4; // flag pixel as inner edge
46 res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
49 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
50 osz++; // increment outer edge size
51 lres[x] = 3; // flag pixel as outer edge
53 /* upper right corner */
55 // test if inner mask is filled
57 // test if pixel underneath, or to the left, are empty in the inner mask,
58 // but filled in the outer mask
59 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x-1] && lomask[x-1])) {
60 isz++; // increment inner edge size
61 lres[x]=4; // flag pixel as inner edge
64 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
67 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
68 osz++; // increment outer edge size
69 lres[x]=3; // flag pixel as outer edge
71 /* lower left corner */
73 // test if inner mask is filled
75 // test if pixel above, or to the right, are empty in the inner mask,
76 // but filled in the outer mask
77 if ((!limask[x+rw] && lomask[x+rw]) || (!limask[x+1] && lomask[x+1])) {
78 isz++; // increment inner edge size
79 lres[x]=4; // flag pixel as inner edge
82 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
85 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
86 osz++; // increment outer edge size
87 lres[x]=3; // flag pixel as outer edge
89 /* lower right corner */
91 // test if inner mask is filled
93 // test if pixel above, or to the left, are empty in the inner mask,
94 // but filled in the outer mask
95 if ((!limask[x+rw] && lomask[x+rw]) || (!limask[x-1] && lomask[x-1])) {
96 isz++; // increment inner edge size
97 lres[x]=4; // flag pixel as inner edge
100 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
103 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
104 osz++; // increment outer edge size
105 lres[x]=3; // flag pixel as outer edge
108 /* Test the TOP row of pixels in buffer, except corners */
109 for (x= t-1; x>=(t-rw)+2; x--) {
110 // test if inner mask is filled
112 // test if pixel to the right, or to the left, are empty in the inner mask,
113 // but filled in the outer mask
114 if ((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
115 isz++; // increment inner edge size
116 lres[x]=4; // flag pixel as inner edge
119 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
122 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
123 osz++; // increment outer edge size
124 lres[x]=3; // flag pixel as outer edge
128 /* Test the BOTTOM row of pixels in buffer, except corners */
129 for (x= rw-2; x; x--) {
130 // test if inner mask is filled
132 // test if pixel to the right, or to the left, are empty in the inner mask,
133 // but filled in the outer mask
134 if ((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
135 isz++; // increment inner edge size
136 lres[x]=4; // flag pixel as inner edge
139 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
142 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
143 osz++; // increment outer edge size
144 lres[x]=3; // flag pixel as outer edge
147 /* Test the LEFT edge of pixels in buffer, except corners */
148 for (x= t-(rw<<1)+1; x>=rw; x-=rw) {
149 // test if inner mask is filled
151 // test if pixel underneath, or above, are empty in the inner mask,
152 // but filled in the outer mask
153 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
154 isz++; // increment inner edge size
155 lres[x]=4; // flag pixel as inner edge
158 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
161 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
162 osz++; // increment outer edge size
163 lres[x]=3; // flag pixel as outer edge
167 /* Test the RIGHT edge of pixels in buffer, except corners */
168 for (x= t-rw; x>rw; x-=rw) {
169 // test if inner mask is filled
171 // test if pixel underneath, or above, are empty in the inner mask,
172 // but filled in the outer mask
173 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
174 isz++; // increment inner edge size
175 lres[x]=4; // flag pixel as inner edge
178 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
181 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
182 osz++; // increment outer edge size
183 lres[x]=3; // flag pixel as outer edge
187 rsize[0]=isz; // fill in our return sizes for edges + fill
192 static void do_adjacentBleedBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) {
194 unsigned int isz=0; // inner edge size
195 unsigned int osz=0; // outer edge size
196 unsigned int gsz=0; // gradient fill area size
197 /* Test the four corners */
198 /* upper left corner */
200 // test if inner mask is filled
202 // test if pixel underneath, or to the right, are empty in the inner mask,
203 // but filled in the outer mask
204 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x+1] && lomask[x+1])) {
205 isz++; // increment inner edge size
206 lres[x]=4; // flag pixel as inner edge
209 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
212 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
213 if (!lomask[x-rw] || !lomask[x+1]) { // test if outer mask is empty underneath or to the right
214 osz++; // increment outer edge size
215 lres[x]=3; // flag pixel as outer edge
218 gsz++; // increment the gradient pixel count
219 lres[x]=2; // flag pixel as gradient
222 /* upper right corner */
224 // test if inner mask is filled
226 // test if pixel underneath, or to the left, are empty in the inner mask,
227 // but filled in the outer mask
228 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x-1] && lomask[x-1])) {
229 isz++; // increment inner edge size
230 lres[x]=4; // flag pixel as inner edge
233 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
236 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
237 if (!lomask[x-rw] || !lomask[x-1]) { // test if outer mask is empty underneath or to the left
238 osz++; // increment outer edge size
239 lres[x]=3; // flag pixel as outer edge
242 gsz++; // increment the gradient pixel count
243 lres[x]=2; // flag pixel as gradient
246 /* lower left corner */
248 // test if inner mask is filled
250 // test if pixel above, or to the right, are empty in the inner mask,
251 // but filled in the outer mask
252 if ((!limask[x+rw] && lomask[x+rw]) || (!limask[x+1] && lomask[x+1])) {
253 isz++; // increment inner edge size
254 lres[x]=4; // flag pixel as inner edge
257 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
260 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
261 if (!lomask[x+rw] || !lomask[x+1]) { // test if outer mask is empty above or to the right
262 osz++; // increment outer edge size
263 lres[x]=3; // flag pixel as outer edge
266 gsz++; // increment the gradient pixel count
267 lres[x]=2; // flag pixel as gradient
270 /* lower right corner */
272 // test if inner mask is filled
274 // test if pixel above, or to the left, are empty in the inner mask,
275 // but filled in the outer mask
276 if ((!limask[x+rw] && lomask[x+rw]) || (!limask[x-1] && lomask[x-1])) {
277 isz++; // increment inner edge size
278 lres[x]=4; // flag pixel as inner edge
281 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
284 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
285 if (!lomask[x+rw] || !lomask[x-1]) { // test if outer mask is empty above or to the left
286 osz++; // increment outer edge size
287 lres[x]=3; // flag pixel as outer edge
290 gsz++; // increment the gradient pixel count
291 lres[x]=2; // flag pixel as gradient
294 /* Test the TOP row of pixels in buffer, except corners */
295 for (x= t-1; x>=(t-rw)+2; x--) {
296 // test if inner mask is filled
298 // test if pixel to the left, or to the right, are empty in the inner mask,
299 // but filled in the outer mask
300 if ((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
301 isz++; // increment inner edge size
302 lres[x]=4; // flag pixel as inner edge
305 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
308 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
309 if (!lomask[x-1] || !lomask[x+1]) { // test if outer mask is empty to the left or to the right
310 osz++; // increment outer edge size
311 lres[x]=3; // flag pixel as outer edge
314 gsz++; // increment the gradient pixel count
315 lres[x]=2; // flag pixel as gradient
320 /* Test the BOTTOM row of pixels in buffer, except corners */
321 for (x= rw-2; x; x--) {
322 // test if inner mask is filled
324 // test if pixel to the left, or to the right, are empty in the inner mask,
325 // but filled in the outer mask
326 if ((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
327 isz++; // increment inner edge size
328 lres[x]=4; // flag pixel as inner edge
331 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
334 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
335 if (!lomask[x-1] || !lomask[x+1]) { // test if outer mask is empty to the left or to the right
336 osz++; // increment outer edge size
337 lres[x]=3; // flag pixel as outer edge
340 gsz++; // increment the gradient pixel count
341 lres[x]=2; // flag pixel as gradient
345 /* Test the LEFT edge of pixels in buffer, except corners */
346 for (x= t-(rw<<1)+1; x>=rw; x-=rw) {
347 // test if inner mask is filled
349 // test if pixel underneath, or above, are empty in the inner mask,
350 // but filled in the outer mask
351 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
352 isz++; // increment inner edge size
353 lres[x]=4; // flag pixel as inner edge
356 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
359 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
360 if (!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
361 osz++; // increment outer edge size
362 lres[x]=3; // flag pixel as outer edge
365 gsz++; // increment the gradient pixel count
366 lres[x]=2; // flag pixel as gradient
371 /* Test the RIGHT edge of pixels in buffer, except corners */
372 for (x= t-rw; x>rw; x-=rw) {
373 // test if inner mask is filled
375 // test if pixel underneath, or above, are empty in the inner mask,
376 // but filled in the outer mask
377 if ((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
378 isz++; // increment inner edge size
379 lres[x]=4; // flag pixel as inner edge
382 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
385 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
386 if (!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
387 osz++; // increment outer edge size
388 lres[x]=3; // flag pixel as outer edge
391 gsz++; // increment the gradient pixel count
392 lres[x]=2; // flag pixel as gradient
397 rsize[0]=isz; // fill in our return sizes for edges + fill
402 static void do_allKeepBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) {
404 unsigned int isz=0; // inner edge size
405 unsigned int osz=0; // outer edge size
406 unsigned int gsz=0; // gradient fill area size
407 /* Test the four corners */
408 /* upper left corner */
410 // test if inner mask is filled
412 // test if the inner mask is empty underneath or to the right
413 if (!limask[x-rw] || !limask[x+1]) {
414 isz++; // increment inner edge size
415 lres[x]=4; // flag pixel as inner edge
418 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
421 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
422 osz++; // increment outer edge size
423 lres[x]=3; // flag pixel as outer edge
425 /* upper right corner */
427 // test if inner mask is filled
429 // test if the inner mask is empty underneath or to the left
430 if (!limask[x-rw] || !limask[x-1]) {
431 isz++; // increment inner edge size
432 lres[x]=4; // flag pixel as inner edge
435 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
438 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
439 osz++; // increment outer edge size
440 lres[x]=3; // flag pixel as outer edge
442 /* lower left corner */
444 // test if inner mask is filled
446 // test if inner mask is empty above or to the right
447 if (!limask[x+rw] || !limask[x+1]) {
448 isz++; // increment inner edge size
449 lres[x]=4; // flag pixel as inner edge
452 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
455 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
456 osz++; // increment outer edge size
457 lres[x]=3; // flag pixel as outer edge
459 /* lower right corner */
461 // test if inner mask is filled
463 // test if inner mask is empty above or to the left
464 if (!limask[x+rw] || !limask[x-1]) {
465 isz++; // increment inner edge size
466 lres[x]=4; // flag pixel as inner edge
469 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
472 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
473 osz++; // increment outer edge size
474 lres[x]=3; // flag pixel as outer edge
477 /* Test the TOP row of pixels in buffer, except corners */
478 for (x= t-1; x>=(t-rw)+2; x--) {
479 // test if inner mask is filled
481 // test if inner mask is empty to the left or to the right
482 if (!limask[x-1] || !limask[x+1]) {
483 isz++; // increment inner edge size
484 lres[x]=4; // flag pixel as inner edge
487 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
490 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
491 osz++; // increment outer edge size
492 lres[x]=3; // flag pixel as outer edge
496 /* Test the BOTTOM row of pixels in buffer, except corners */
497 for (x= rw-2; x; x--) {
498 // test if inner mask is filled
500 // test if inner mask is empty to the left or to the right
501 if (!limask[x-1] || !limask[x+1]) {
502 isz++; // increment inner edge size
503 lres[x]=4; // flag pixel as inner edge
506 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
509 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
510 osz++; // increment outer edge size
511 lres[x]=3; // flag pixel as outer edge
514 /* Test the LEFT edge of pixels in buffer, except corners */
515 for (x= t-(rw<<1)+1; x>=rw; x-=rw) {
516 // test if inner mask is filled
518 // test if inner mask is empty underneath or above
519 if (!limask[x-rw] || !limask[x+rw]) {
520 isz++; // increment inner edge size
521 lres[x]=4; // flag pixel as inner edge
524 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
527 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
528 osz++; // increment outer edge size
529 lres[x]=3; // flag pixel as outer edge
533 /* Test the RIGHT edge of pixels in buffer, except corners */
534 for (x= t-rw; x>rw; x-=rw) {
535 // test if inner mask is filled
537 // test if inner mask is empty underneath or above
538 if (!limask[x-rw] || !limask[x+rw]) {
539 isz++; // increment inner edge size
540 lres[x]=4; // flag pixel as inner edge
543 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
546 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
547 osz++; // increment outer edge size
548 lres[x]=3; // flag pixel as outer edge
552 rsize[0]=isz; // fill in our return sizes for edges + fill
557 static void do_allBleedBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) {
559 unsigned int isz=0; // inner edge size
560 unsigned int osz=0; // outer edge size
561 unsigned int gsz=0; // gradient fill area size
562 /* Test the four corners */
563 /* upper left corner */
565 // test if inner mask is filled
567 // test if the inner mask is empty underneath or to the right
568 if (!limask[x-rw] || !limask[x+1]) {
569 isz++; // increment inner edge size
570 lres[x]=4; // flag pixel as inner edge
573 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
576 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
577 if (!lomask[x-rw] || !lomask[x+1]) { // test if outer mask is empty underneath or to the right
578 osz++; // increment outer edge size
579 lres[x]=3; // flag pixel as outer edge
582 gsz++; // increment the gradient pixel count
583 lres[x]=2; // flag pixel as gradient
586 /* upper right corner */
588 // test if inner mask is filled
590 // test if the inner mask is empty underneath or to the left
591 if (!limask[x-rw] || !limask[x-1]) {
592 isz++; // increment inner edge size
593 lres[x]=4; // flag pixel as inner edge
596 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
599 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
600 if (!lomask[x-rw] || !lomask[x-1]) { // test if outer mask is empty above or to the left
601 osz++; // increment outer edge size
602 lres[x]=3; // flag pixel as outer edge
605 gsz++; // increment the gradient pixel count
606 lres[x]=2; // flag pixel as gradient
609 /* lower left corner */
611 // test if inner mask is filled
613 // test if inner mask is empty above or to the right
614 if (!limask[x+rw] || !limask[x+1]) {
615 isz++; // increment inner edge size
616 lres[x]=4; // flag pixel as inner edge
619 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
622 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
623 if (!lomask[x+rw] || !lomask[x+1]) { // test if outer mask is empty underneath or to the right
624 osz++; // increment outer edge size
625 lres[x]=3; // flag pixel as outer edge
628 gsz++; // increment the gradient pixel count
629 lres[x]=2; // flag pixel as gradient
632 /* lower right corner */
634 // test if inner mask is filled
636 // test if inner mask is empty above or to the left
637 if (!limask[x+rw] || !limask[x-1]) {
638 isz++; // increment inner edge size
639 lres[x]=4; // flag pixel as inner edge
642 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
645 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
646 if (!lomask[x+rw] || !lomask[x-1]) { // test if outer mask is empty underneath or to the left
647 osz++; // increment outer edge size
648 lres[x]=3; // flag pixel as outer edge
651 gsz++; // increment the gradient pixel count
652 lres[x]=2; // flag pixel as gradient
655 /* Test the TOP row of pixels in buffer, except corners */
656 for (x= t-1; x>=(t-rw)+2; x--) {
657 // test if inner mask is filled
659 // test if inner mask is empty to the left or to the right
660 if (!limask[x-1] || !limask[x+1]) {
661 isz++; // increment inner edge size
662 lres[x]=4; // flag pixel as inner edge
665 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
668 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
669 if (!lomask[x-1] || !lomask[x+1]) { // test if outer mask is empty to the left or to the right
670 osz++; // increment outer edge size
671 lres[x]=3; // flag pixel as outer edge
674 gsz++; // increment the gradient pixel count
675 lres[x]=2; // flag pixel as gradient
680 /* Test the BOTTOM row of pixels in buffer, except corners */
681 for (x= rw-2; x; x--) {
682 // test if inner mask is filled
684 // test if inner mask is empty to the left or to the right
685 if (!limask[x-1] || !limask[x+1]) {
686 isz++; // increment inner edge size
687 lres[x]=4; // flag pixel as inner edge
690 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
693 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
694 if (!lomask[x-1] || !lomask[x+1]) { // test if outer mask is empty to the left or to the right
695 osz++; // increment outer edge size
696 lres[x]=3; // flag pixel as outer edge
699 gsz++; // increment the gradient pixel count
700 lres[x]=2; // flag pixel as gradient
704 /* Test the LEFT edge of pixels in buffer, except corners */
705 for (x= t-(rw<<1)+1; x>=rw; x-=rw) {
706 // test if inner mask is filled
708 // test if inner mask is empty underneath or above
709 if (!limask[x-rw] || !limask[x+rw]) {
710 isz++; // increment inner edge size
711 lres[x]=4; // flag pixel as inner edge
714 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
717 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
718 if (!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
719 osz++; // increment outer edge size
720 lres[x]=3; // flag pixel as outer edge
723 gsz++; // increment the gradient pixel count
724 lres[x]=2; // flag pixel as gradient
729 /* Test the RIGHT edge of pixels in buffer, except corners */
730 for (x= t-rw; x>rw; x-=rw) {
731 // test if inner mask is filled
733 // test if inner mask is empty underneath or above
734 if (!limask[x-rw] || !limask[x+rw]) {
735 isz++; // increment inner edge size
736 lres[x]=4; // flag pixel as inner edge
739 res[x]=1.0f; // pixel is just part of inner mask, and it's not an edge
742 else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
743 if (!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
744 osz++; // increment outer edge size
745 lres[x]=3; // flag pixel as outer edge
748 gsz++; // increment the gradient pixel count
749 lres[x]=2; // flag pixel as gradient
754 rsize[0]=isz; // fill in our return sizes for edges + fill
759 static void do_allEdgeDetection(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, unsigned int in_isz, unsigned int in_osz, unsigned int in_gsz) {
760 int x; // x = pixel loop counter
761 int a; // a = pixel loop counter
762 int dx; // dx = delta x
763 int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop
764 int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop
765 int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop
766 int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop
767 /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
768 for (x= (t-rw)+1, dx=x-(rw-2); dx>rw; x-=rw,dx-=rw) {
775 if (!limask[a]) { // if the inner mask is empty
776 if (lomask[a]) { // if the outer mask is full
778 Next we test all 4 directions around the current pixel: next/prev/up/down
779 The test ensures that the outer mask is empty and that the inner mask
780 is also empty. If both conditions are true for any one of the 4 adjacent pixels
781 then the current pixel is counted as being a true outer edge pixel.
783 if ((!lomask[pix_nextCol] && !limask[pix_nextCol]) ||
784 (!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
785 (!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
786 (!lomask[pix_prevRow] && !limask[pix_prevRow]))
788 in_osz++; // increment the outer boundary pixel count
789 lres[a]=3; // flag pixel as part of outer edge
791 else { // it's not a boundary pixel, but it is a gradient pixel
792 in_gsz++; // increment the gradient pixel count
793 lres[a]=2; // flag pixel as gradient
799 if (!limask[pix_nextCol] || !limask[pix_prevCol] || !limask[pix_nextRow] || !limask[pix_prevRow]) {
800 in_isz++; // increment the inner boundary pixel count
801 lres[a]=4; // flag pixel as part of inner edge
804 res[a]=1.0f; // pixel is part of inner mask, but not at an edge
815 rsize[0]=in_isz; // fill in our return sizes for edges + fill
820 static void do_adjacentEdgeDetection(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, unsigned int in_isz, unsigned int in_osz, unsigned int in_gsz) {
821 int x; // x = pixel loop counter
822 int a; // a = pixel loop counter
823 int dx; // dx = delta x
824 int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop
825 int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop
826 int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop
827 int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop
828 /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
829 for (x= (t-rw)+1, dx=x-(rw-2); dx>rw; x-=rw,dx-=rw) {
836 if (!limask[a]) { // if the inner mask is empty
837 if (lomask[a]) { // if the outer mask is full
839 Next we test all 4 directions around the current pixel: next/prev/up/down
840 The test ensures that the outer mask is empty and that the inner mask
841 is also empty. If both conditions are true for any one of the 4 adjacent pixels
842 then the current pixel is counted as being a true outer edge pixel.
844 if ((!lomask[pix_nextCol] && !limask[pix_nextCol]) ||
845 (!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
846 (!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
847 (!lomask[pix_prevRow] && !limask[pix_prevRow]))
849 in_osz++; // increment the outer boundary pixel count
850 lres[a]=3; // flag pixel as part of outer edge
852 else { // it's not a boundary pixel, but it is a gradient pixel
853 in_gsz++; // increment the gradient pixel count
854 lres[a]=2; // flag pixel as gradient
860 if ((!limask[pix_nextCol] && lomask[pix_nextCol]) ||
861 (!limask[pix_prevCol] && lomask[pix_prevCol]) ||
862 (!limask[pix_nextRow] && lomask[pix_nextRow]) ||
863 (!limask[pix_prevRow] && lomask[pix_prevRow]))
865 in_isz++; // increment the inner boundary pixel count
866 lres[a]=4; // flag pixel as part of inner edge
869 res[a]=1.0f; // pixel is part of inner mask, but not at an edge
873 pix_prevRow--; // advance all four "surrounding" pixel pointers
880 rsize[0]=in_isz; // fill in our return sizes for edges + fill
885 static void do_createEdgeLocationBuffer(unsigned int t, unsigned int rw, unsigned int *lres, float *res, unsigned short *gbuf, unsigned int *innerEdgeOffset, unsigned int *outerEdgeOffset, unsigned int isz, unsigned int gsz) {
886 int x; // x = pixel loop counter
887 int a; // a = temporary pixel index buffer loop counter
888 unsigned int ud; // ud = unscaled edge distance
889 unsigned int dmin; // dmin = minimun edge distance
891 unsigned int rsl; // long used for finding fast 1.0/sqrt
892 unsigned int gradientFillOffset;
893 unsigned int innerAccum=0; // for looping inner edge pixel indexes, represents current position from offset
894 unsigned int outerAccum=0; // for looping outer edge pixel indexes, represents current position from offset
895 unsigned int gradientAccum=0; // for looping gradient pixel indexes, represents current position from offset
897 Here we compute the size of buffer needed to hold (row,col) coordinates
898 for each pixel previously determined to be either gradient, inner edge,
901 Allocation is done by requesting 4 bytes "sizeof(int)" per pixel, even
902 though gbuf[] is declared as unsigned short* (2 bytes) because we don't
903 store the pixel indexes, we only store x,y location of pixel in buffer.
905 This does make the assumption that x and y can fit in 16 unsigned bits
906 so if Blender starts doing renders greater than 65536 in either direction
907 this will need to allocate gbuf[] as unsigned int* and allocate 8 bytes
910 In general, the buffer on-screen:
912 Example: 9 by 9 pixel block
914 . = pixel non-white in both outer and inner mask
915 o = pixel white in outer, but not inner mask, adjacent to "." pixel
916 g = pixel white in outer, but not inner mask, not adjacent to "." pixel
917 i = pixel white in inner mask, adjacent to "g" or "." pixel
918 F = pixel white in inner mask, only adjacent to other pixels white in the inner mask
921 ......... <----- pixel #80
929 pixel #00 -----> .........
931 gsz = 18 (18 "g" pixels above)
932 isz = 4 (4 "i" pixels above)
933 osz = 18 (18 "o" pixels above)
936 The memory in gbuf[] after filling will look like this:
938 gradientFillOffset (0 pixels) innerEdgeOffset (18 pixels) outerEdgeOffset (22 pixels)
941 |X Y X Y X Y X Y > <X Y X Y > <X Y X Y X Y > <X Y X Y | <- (x,y)
942 +--------------------------------> <----------------> <------------------------> <----------------+
943 |0 2 4 6 8 10 12 14 > ... <68 70 72 74 > ... <80 82 84 86 88 90 > ... <152 154 156 158 | <- bytes
944 +--------------------------------> <----------------> <------------------------> <----------------+
945 |g0 g0 g1 g1 g2 g2 g3 g3 > <g17 g17 i0 i0 > <i2 i2 i3 i3 o0 o0 > <o16 o16 o17 o17 | <- pixel
949 +---------- gradientAccum (18) ---------+ +--- innerAccum (22) ---+ +--- outerAccum (40) ---+
952 Ultimately we do need the pixel's memory buffer index to set the output
953 pixel color, but it's faster to reconstruct the memory buffer location
954 each iteration of the final gradient calculation than it is to deconstruct
955 a memory location into x,y pairs each round.
959 gradientFillOffset=0; // since there are likely "more" of these, put it first. :)
960 *innerEdgeOffset=gradientFillOffset+gsz; // set start of inner edge indexes
961 *outerEdgeOffset=(*innerEdgeOffset)+isz; // set start of outer edge indexes
962 /* set the accumulators to correct positions */ // set up some accumulator variables for loops
963 gradientAccum = gradientFillOffset; // each accumulator variable starts at its respective
964 innerAccum = *innerEdgeOffset; // section's offset so when we start filling, each
965 outerAccum = *outerEdgeOffset; // section fills up it's allocated space in gbuf
966 //uses dmin=row, rsl=col
967 for (x=0,dmin=0; x<t; x+=rw,dmin++) {
968 for (rsl=0; rsl<rw; rsl++) {
970 if (lres[a]==2) { // it is a gradient pixel flagged by 2
971 ud=gradientAccum<<1; // double the index to reach correct unsigned short location
972 gbuf[ud]=dmin; // insert pixel's row into gradient pixel location buffer
973 gbuf[ud+1]=rsl; // insert pixel's column into gradient pixel location buffer
974 gradientAccum++; // increment gradient index buffer pointer
976 else if (lres[a]==3) { // it is an outer edge pixel flagged by 3
977 ud=outerAccum<<1; // double the index to reach correct unsigned short location
978 gbuf[ud]=dmin; // insert pixel's row into outer edge pixel location buffer
979 gbuf[ud+1]=rsl; // insert pixel's column into outer edge pixel location buffer
980 outerAccum++; // increment outer edge index buffer pointer
981 res[a]=0.0f; // set output pixel intensity now since it won't change later
983 else if (lres[a]==4) { // it is an inner edge pixel flagged by 4
984 ud=innerAccum<<1; // double int index to reach correct unsigned short location
985 gbuf[ud]=dmin; // insert pixel's row into inner edge pixel location buffer
986 gbuf[ud+1]=rsl; // insert pixel's column into inner edge pixel location buffer
987 innerAccum++; // increment inner edge index buffer pointer
988 res[a]=1.0f; // set output pixel intensity now since it won't change later
995 static void do_fillGradientBuffer(unsigned int rw, float *res, unsigned short *gbuf, unsigned int isz, unsigned int osz, unsigned int gsz, unsigned int innerEdgeOffset, unsigned int outerEdgeOffset) {
996 int x; // x = pixel loop counter
997 int a; // a = temporary pixel index buffer loop counter
998 int fsz; // size of the frame
999 unsigned int rsl; // long used for finding fast 1.0/sqrt
1000 float rsf; // float used for finding fast 1.0/sqrt
1001 const float rsopf = 1.5f; // constant float used for finding fast 1.0/sqrt
1003 unsigned int gradientFillOffset;
1005 unsigned int ud; // ud = unscaled edge distance
1006 unsigned int dmin; // dmin = minimun edge distance
1007 float odist; // odist = current outer edge distance
1008 float idist; // idist = current inner edge distance
1009 int dx; // dx = X-delta (used for distance proportion calculation)
1010 int dy; // dy = Y-delta (used for distance proportion calculation)
1013 The general algorithm used to color each gradient pixel is:
1015 1.) Loop through all gradient pixels.
1016 A.) For each gradient pixel:
1017 a.) Loop though all outside edge pixels, looking for closest one
1018 to the gradient pixel we are in.
1019 b.) Loop through all inside edge pixels, looking for closest one
1020 to the gradient pixel we are in.
1021 c.) Find proportion of distance from gradient pixel to inside edge
1022 pixel compared to sum of distance to inside edge and distance to
1026 . = blank (black) pixels, not covered by inner mask or outer mask
1027 + = desired gradient pixels, covered only by outer mask
1028 * = white full mask pixels, covered by at least inner mask
1030 ...............................
1031 ...............+++++++++++.....
1032 ...+O++++++..++++++++++++++....
1033 ..+++\++++++++++++++++++++.....
1034 .+++++G+++++++++*******+++.....
1035 .+++++|+++++++*********+++.....
1036 .++***I****************+++.....
1037 .++*******************+++......
1038 .+++*****************+++.......
1039 ..+++***************+++........
1040 ....+++**********+++...........
1041 ......++++++++++++.............
1042 ...............................
1044 O = outside edge pixel
1048 I = inside edge pixel
1051 *note that IO does not need to be a straight line, in fact
1052 many cases can arise where straight lines do not work
1056 d.) Pixel color is assigned as |GO| / ( |GI| + |GO| )
1058 The implementation does not compute distance, but the reciprocal of the
1059 distance. This is done to avoid having to compute a square root, as a
1060 reciprocal square root can be computed faster. Therefore, the code computes
1061 pixel color as |GI| / (|GI| + |GO|). Since these are reciprocals, GI serves the
1062 purpose of GO for the proportion calculation.
1064 For the purposes of the minimun distance comparisons, we only check
1065 the sums-of-squares against eachother, since they are in the same
1066 mathematical sort-order as if we did go ahead and take square roots
1068 Loop through all gradient pixels.
1071 for (x= gsz-1; x>=0; x--) {
1072 gradientFillOffset=x<<1;
1073 t=gbuf[gradientFillOffset]; // calculate column of pixel indexed by gbuf[x]
1074 fsz=gbuf[gradientFillOffset+1]; // calculate row of pixel indexed by gbuf[x]
1075 dmin=0xffffffff; // reset min distance to edge pixel
1076 for (a=outerEdgeOffset+osz-1; a>=outerEdgeOffset; a--) { // loop through all outer edge buffer pixels
1078 dy=t-gbuf[ud]; // set dx to gradient pixel column - outer edge pixel row
1079 dx=fsz-gbuf[ud+1]; // set dy to gradient pixel row - outer edge pixel column
1080 ud=dx*dx+dy*dy; // compute sum of squares
1081 if (ud<dmin) { // if our new sum of squares is less than the current minimum
1082 dmin=ud; // set a new minimum equal to the new lower value
1085 odist=(float)(dmin); // cast outer min to a float
1087 rsl=*(unsigned int*)&odist; // use some peculiar properties of the way bits are stored
1088 rsl=0x5f3759df-(rsl>>1); // in floats vs. unsigned ints to compute an approximate
1089 odist=*(float*)&rsl; // reciprocal square root
1090 odist=odist*(rsopf-(rsf*odist*odist)); // -- ** this line can be iterated for more accuracy ** --
1091 dmin=0xffffffff; // reset min distance to edge pixel
1092 for (a= innerEdgeOffset+isz-1; a>=innerEdgeOffset; a--) { // loop through all inside edge pixels
1094 dy=t-gbuf[ud]; // compute delta in Y from gradient pixel to inside edge pixel
1095 dx=fsz-gbuf[ud+1]; // compute delta in X from gradient pixel to inside edge pixel
1096 ud=dx*dx+dy*dy; // compute sum of squares
1097 if (ud<dmin) { // if our new sum of squares is less than the current minimum we've found
1098 dmin=ud; // set a new minimum equal to the new lower value
1101 idist=(float)(dmin); // cast inner min to a float
1103 rsl=*(unsigned int*)&idist; //
1104 rsl=0x5f3759df-(rsl>>1); // see notes above
1105 idist=*(float*)&rsl; //
1106 idist=idist*(rsopf-(rsf*idist*idist)); //
1108 Note once again that since we are using reciprocals of distance values our
1109 proportion is already the correct intensity, and does not need to be
1110 subracted from 1.0 like it would have if we used real distances.
1114 Here we reconstruct the pixel's memory location in the CompBuf by
1115 Pixel Index = Pixel Column + ( Pixel Row * Row Width )
1117 res[gbuf[gradientFillOffset+1]+(gbuf[gradientFillOffset]*rw)]=(idist/(idist+odist)); //set intensity
1124 void DoubleEdgeMaskOperation::doDoubleEdgeMask(float* imask, float *omask, float *res) {
1126 unsigned int *lres; // lres = unsigned int pointer to output pixel buffer (for bit operations)
1127 unsigned int *limask; // limask = unsigned int pointer to inner mask (for bit operations)
1128 unsigned int *lomask; // lomask = unsigned int pointer to outer mask (for bit operations)
1130 int rw; // rw = pixel row width
1131 int t; // t = total number of pixels in buffer - 1 (used for loop starts)
1132 int fsz; // size of the frame
1134 unsigned int isz=0; // size (in pixels) of inside edge pixel index buffer
1135 unsigned int osz=0; // size (in pixels) of outside edge pixel index buffer
1136 unsigned int gsz=0; // size (in pixels) of gradient pixel index buffer
1137 unsigned int rsize[3]; // size storage to pass to helper functions
1138 unsigned int innerEdgeOffset=0; // offset into final buffer where inner edge pixel indexes start
1139 unsigned int outerEdgeOffset=0; // offset into final buffer where outer edge pixel indexes start
1141 unsigned short *gbuf; // gradient/inner/outer pixel location index buffer
1143 if (true) { // if both input sockets have some data coming in...
1145 t=(this->getWidth()*this->getHeight())-1; // determine size of the frame
1147 lres= (unsigned int*)res; // unsigned int pointer to output buffer (for bit level ops)
1148 limask=(unsigned int*)imask; // unsigned int pointer to input mask (for bit level ops)
1149 lomask=(unsigned int*)omask; // unsigned int pointer to output mask (for bit level ops)
1150 rw= this->getWidth(); // width of a row of pixels
1154 The whole buffer is broken up into 4 parts. The four CORNERS, the FIRST and LAST rows, the
1155 LEFT and RIGHT edges (excluding the corner pixels), and all OTHER rows.
1156 This allows for quick computation of outer edge pixels where
1157 a screen edge pixel is marked to be gradient.
1159 The pixel type (gradient vs inner-edge vs outer-edge) tests change
1160 depending on the user selected "Inner Edge Mode" and the user selected
1161 "Buffer Edge Mode" on the node's GUI. There are 4 sets of basically the
1164 1.) Inner Edge -> Adjacent Only
1165 Buffer Edge -> Keep Inside
1167 2.) Inner Edge -> Adjacent Only
1168 Buffer Edge -> Bleed Out
1170 3.) Inner Edge -> All
1171 Buffer Edge -> Keep Inside
1173 4.) Inner Edge -> All
1174 Buffer Edge -> Bleed Out
1176 Each version has slightly different criteria for detecting an edge pixel.
1178 if (this->adjecentOnly) { // if "adjacent only" inner edge mode is turned on
1179 if (this->keepInside) { // if "keep inside" buffer edge mode is turned on
1180 do_adjacentKeepBorders(t,rw,limask,lomask,lres,res,rsize);
1182 else { // "bleed out" buffer edge mode is turned on
1183 do_adjacentBleedBorders(t,rw,limask,lomask,lres,res,rsize);
1185 isz=rsize[0]; // set up inner edge, outer edge, and gradient buffer sizes after border pass
1188 // detect edges in all non-border pixels in the buffer
1189 do_adjacentEdgeDetection(t,rw,limask,lomask,lres,res,rsize,isz,osz,gsz);
1191 else { // "all" inner edge mode is turned on
1192 if (this->keepInside) { // if "keep inside" buffer edge mode is turned on
1193 do_allKeepBorders(t,rw,limask,lomask,lres,res,rsize);
1195 else { // "bleed out" buffer edge mode is turned on
1196 do_allBleedBorders(t,rw,limask,lomask,lres,res,rsize);
1198 isz=rsize[0]; // set up inner edge, outer edge, and gradient buffer sizes after border pass
1201 // detect edges in all non-border pixels in the buffer
1202 do_allEdgeDetection(t,rw,limask,lomask,lres,res,rsize,isz,osz,gsz);
1205 isz=rsize[0]; // set edge and gradient buffer sizes once again...
1206 osz=rsize[1]; // the sizes in rsize[] may have been modified
1207 gsz=rsize[2]; // by the do_*EdgeDetection() function.
1209 fsz=gsz+isz+osz; // calculate size of pixel index buffer needed
1210 gbuf= new unsigned short[fsz*2]; // allocate edge/gradient pixel index buffer
1212 do_createEdgeLocationBuffer(t,rw,lres,res,gbuf,&innerEdgeOffset,&outerEdgeOffset,isz,gsz);
1213 do_fillGradientBuffer(rw,res,gbuf,isz,osz,gsz,innerEdgeOffset,outerEdgeOffset);
1215 delete gbuf; // free the gradient index buffer
1219 DoubleEdgeMaskOperation::DoubleEdgeMaskOperation(): NodeOperation() {
1220 this->addInputSocket(COM_DT_VALUE);
1221 this->addInputSocket(COM_DT_VALUE);
1222 this->addOutputSocket(COM_DT_VALUE);
1223 this->inputInnerMask = NULL;
1224 this->inputOuterMask = NULL;
1225 this->adjecentOnly = false;
1226 this->keepInside = false;
1227 this->setComplex(true);
1230 bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) {
1231 if (this->cachedInstance == NULL) {
1233 newInput.xmax = this->getWidth();
1235 newInput.ymax = this->getHeight();
1237 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
1244 void DoubleEdgeMaskOperation::initExecution() {
1245 this->inputInnerMask = this->getInputSocketReader(0);
1246 this->inputOuterMask = this->getInputSocketReader(1);
1248 this->cachedInstance = NULL;
1251 void* DoubleEdgeMaskOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers) {
1252 if (this->cachedInstance) return this->cachedInstance;
1254 BLI_mutex_lock(getMutex());
1255 if (this->cachedInstance == NULL) {
1256 MemoryBuffer* innerMask = (MemoryBuffer*)inputInnerMask->initializeTileData(rect, memoryBuffers);
1257 MemoryBuffer* outerMask= (MemoryBuffer*)inputOuterMask->initializeTileData(rect, memoryBuffers);
1258 float* data = new float[this->getWidth()*this->getHeight()];
1259 float* imask = innerMask->convertToValueBuffer();
1260 float* omask = outerMask->convertToValueBuffer();
1261 doDoubleEdgeMask(imask, omask, data);
1264 this->cachedInstance = data;
1266 BLI_mutex_unlock(getMutex());
1267 return this->cachedInstance;
1269 void DoubleEdgeMaskOperation::executePixel(float* color, int x, int y, MemoryBuffer *inputBuffers[], void* data) {
1270 float* buffer = (float*) data;
1271 int index = (y*this->getWidth() + x);
1272 color[0] = buffer[index];
1273 color[1] = buffer[index+1];
1274 color[2] = buffer[index+2];
1275 color[3] = buffer[index+3];
1278 void DoubleEdgeMaskOperation::deinitExecution() {
1279 this->inputInnerMask = NULL;
1280 this->inputOuterMask = NULL;
1282 if (this->cachedInstance) {
1283 delete cachedInstance;
1284 this->cachedInstance = NULL;