Blender  V2.93
COM_DoubleEdgeMaskOperation.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2011, Blender Foundation.
17  */
18 
19 #include <cstdlib>
20 
21 #include "BLI_math.h"
23 #include "DNA_node_types.h"
24 #include "MEM_guardedalloc.h"
25 
26 namespace blender::compositor {
27 
28 // this part has been copied from the double edge mask
29 static void do_adjacentKeepBorders(unsigned int t,
30  unsigned int rw,
31  const unsigned int *limask,
32  const unsigned int *lomask,
33  unsigned int *lres,
34  float *res,
35  unsigned int *rsize)
36 {
37  int x;
38  unsigned int isz = 0; // inner edge size
39  unsigned int osz = 0; // outer edge size
40  unsigned int gsz = 0; // gradient fill area size
41  /* Test the four corners */
42  /* upper left corner */
43  x = t - rw + 1;
44  // test if inner mask is filled
45  if (limask[x]) {
46  // test if pixel underneath, or to the right, are empty in the inner mask,
47  // but filled in the outer mask
48  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) {
49  isz++; // increment inner edge size
50  lres[x] = 4; // flag pixel as inner edge
51  }
52  else {
53  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
54  }
55  }
56  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
57  osz++; // increment outer edge size
58  lres[x] = 3; // flag pixel as outer edge
59  }
60  /* upper right corner */
61  x = t;
62  // test if inner mask is filled
63  if (limask[x]) {
64  // test if pixel underneath, or to the left, are empty in the inner mask,
65  // but filled in the outer mask
66  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) {
67  isz++; // increment inner edge size
68  lres[x] = 4; // flag pixel as inner edge
69  }
70  else {
71  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
72  }
73  }
74  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
75  osz++; // increment outer edge size
76  lres[x] = 3; // flag pixel as outer edge
77  }
78  /* lower left corner */
79  x = 0;
80  // test if inner mask is filled
81  if (limask[x]) {
82  // test if pixel above, or to the right, are empty in the inner mask,
83  // but filled in the outer mask
84  if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) {
85  isz++; // increment inner edge size
86  lres[x] = 4; // flag pixel as inner edge
87  }
88  else {
89  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
90  }
91  }
92  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
93  osz++; // increment outer edge size
94  lres[x] = 3; // flag pixel as outer edge
95  }
96  /* lower right corner */
97  x = rw - 1;
98  // test if inner mask is filled
99  if (limask[x]) {
100  // test if pixel above, or to the left, are empty in the inner mask,
101  // but filled in the outer mask
102  if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) {
103  isz++; // increment inner edge size
104  lres[x] = 4; // flag pixel as inner edge
105  }
106  else {
107  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
108  }
109  }
110  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
111  osz++; // increment outer edge size
112  lres[x] = 3; // flag pixel as outer edge
113  }
114 
115  /* Test the TOP row of pixels in buffer, except corners */
116  for (x = t - 1; x >= (t - rw) + 2; x--) {
117  // test if inner mask is filled
118  if (limask[x]) {
119  // test if pixel to the right, or to the left, are empty in the inner mask,
120  // but filled in the outer mask
121  if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
122  isz++; // increment inner edge size
123  lres[x] = 4; // flag pixel as inner edge
124  }
125  else {
126  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
127  }
128  }
129  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
130  osz++; // increment outer edge size
131  lres[x] = 3; // flag pixel as outer edge
132  }
133  }
134 
135  /* Test the BOTTOM row of pixels in buffer, except corners */
136  for (x = rw - 2; x; x--) {
137  // test if inner mask is filled
138  if (limask[x]) {
139  // test if pixel to the right, or to the left, are empty in the inner mask,
140  // but filled in the outer mask
141  if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
142  isz++; // increment inner edge size
143  lres[x] = 4; // flag pixel as inner edge
144  }
145  else {
146  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
147  }
148  }
149  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
150  osz++; // increment outer edge size
151  lres[x] = 3; // flag pixel as outer edge
152  }
153  }
154  /* Test the LEFT edge of pixels in buffer, except corners */
155  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
156  // test if inner mask is filled
157  if (limask[x]) {
158  // test if pixel underneath, or above, are empty in the inner mask,
159  // but filled in the outer mask
160  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
161  isz++; // increment inner edge size
162  lres[x] = 4; // flag pixel as inner edge
163  }
164  else {
165  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
166  }
167  }
168  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
169  osz++; // increment outer edge size
170  lres[x] = 3; // flag pixel as outer edge
171  }
172  }
173 
174  /* Test the RIGHT edge of pixels in buffer, except corners */
175  for (x = t - rw; x > rw; x -= rw) {
176  // test if inner mask is filled
177  if (limask[x]) {
178  // test if pixel underneath, or above, are empty in the inner mask,
179  // but filled in the outer mask
180  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
181  isz++; // increment inner edge size
182  lres[x] = 4; // flag pixel as inner edge
183  }
184  else {
185  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
186  }
187  }
188  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
189  osz++; // increment outer edge size
190  lres[x] = 3; // flag pixel as outer edge
191  }
192  }
193 
194  rsize[0] = isz; // fill in our return sizes for edges + fill
195  rsize[1] = osz;
196  rsize[2] = gsz;
197 }
198 
199 static void do_adjacentBleedBorders(unsigned int t,
200  unsigned int rw,
201  const unsigned int *limask,
202  const unsigned int *lomask,
203  unsigned int *lres,
204  float *res,
205  unsigned int *rsize)
206 {
207  int x;
208  unsigned int isz = 0; // inner edge size
209  unsigned int osz = 0; // outer edge size
210  unsigned int gsz = 0; // gradient fill area size
211  /* Test the four corners */
212  /* upper left corner */
213  x = t - rw + 1;
214  // test if inner mask is filled
215  if (limask[x]) {
216  // test if pixel underneath, or to the right, are empty in the inner mask,
217  // but filled in the outer mask
218  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) {
219  isz++; // increment inner edge size
220  lres[x] = 4; // flag pixel as inner edge
221  }
222  else {
223  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
224  }
225  }
226  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
227  if (!lomask[x - rw] ||
228  !lomask[x + 1]) { // test if outer mask is empty underneath or to the right
229  osz++; // increment outer edge size
230  lres[x] = 3; // flag pixel as outer edge
231  }
232  else {
233  gsz++; // increment the gradient pixel count
234  lres[x] = 2; // flag pixel as gradient
235  }
236  }
237  /* upper right corner */
238  x = t;
239  // test if inner mask is filled
240  if (limask[x]) {
241  // test if pixel underneath, or to the left, are empty in the inner mask,
242  // but filled in the outer mask
243  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) {
244  isz++; // increment inner edge size
245  lres[x] = 4; // flag pixel as inner edge
246  }
247  else {
248  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
249  }
250  }
251  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
252  if (!lomask[x - rw] ||
253  !lomask[x - 1]) { // test if outer mask is empty underneath or to the left
254  osz++; // increment outer edge size
255  lres[x] = 3; // flag pixel as outer edge
256  }
257  else {
258  gsz++; // increment the gradient pixel count
259  lres[x] = 2; // flag pixel as gradient
260  }
261  }
262  /* lower left corner */
263  x = 0;
264  // test if inner mask is filled
265  if (limask[x]) {
266  // test if pixel above, or to the right, are empty in the inner mask,
267  // but filled in the outer mask
268  if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) {
269  isz++; // increment inner edge size
270  lres[x] = 4; // flag pixel as inner edge
271  }
272  else {
273  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
274  }
275  }
276  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
277  if (!lomask[x + rw] || !lomask[x + 1]) { // test if outer mask is empty above or to the right
278  osz++; // increment outer edge size
279  lres[x] = 3; // flag pixel as outer edge
280  }
281  else {
282  gsz++; // increment the gradient pixel count
283  lres[x] = 2; // flag pixel as gradient
284  }
285  }
286  /* lower right corner */
287  x = rw - 1;
288  // test if inner mask is filled
289  if (limask[x]) {
290  // test if pixel above, or to the left, are empty in the inner mask,
291  // but filled in the outer mask
292  if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) {
293  isz++; // increment inner edge size
294  lres[x] = 4; // flag pixel as inner edge
295  }
296  else {
297  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
298  }
299  }
300  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
301  if (!lomask[x + rw] || !lomask[x - 1]) { // test if outer mask is empty above or to the left
302  osz++; // increment outer edge size
303  lres[x] = 3; // flag pixel as outer edge
304  }
305  else {
306  gsz++; // increment the gradient pixel count
307  lres[x] = 2; // flag pixel as gradient
308  }
309  }
310  /* Test the TOP row of pixels in buffer, except corners */
311  for (x = t - 1; x >= (t - rw) + 2; x--) {
312  // test if inner mask is filled
313  if (limask[x]) {
314  // test if pixel to the left, or to the right, are empty in the inner mask,
315  // but filled in the outer mask
316  if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
317  isz++; // increment inner edge size
318  lres[x] = 4; // flag pixel as inner edge
319  }
320  else {
321  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
322  }
323  }
324  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
325  if (!lomask[x - 1] ||
326  !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
327  osz++; // increment outer edge size
328  lres[x] = 3; // flag pixel as outer edge
329  }
330  else {
331  gsz++; // increment the gradient pixel count
332  lres[x] = 2; // flag pixel as gradient
333  }
334  }
335  }
336 
337  /* Test the BOTTOM row of pixels in buffer, except corners */
338  for (x = rw - 2; x; x--) {
339  // test if inner mask is filled
340  if (limask[x]) {
341  // test if pixel to the left, or to the right, are empty in the inner mask,
342  // but filled in the outer mask
343  if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
344  isz++; // increment inner edge size
345  lres[x] = 4; // flag pixel as inner edge
346  }
347  else {
348  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
349  }
350  }
351  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
352  if (!lomask[x - 1] ||
353  !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
354  osz++; // increment outer edge size
355  lres[x] = 3; // flag pixel as outer edge
356  }
357  else {
358  gsz++; // increment the gradient pixel count
359  lres[x] = 2; // flag pixel as gradient
360  }
361  }
362  }
363  /* Test the LEFT edge of pixels in buffer, except corners */
364  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
365  // test if inner mask is filled
366  if (limask[x]) {
367  // test if pixel underneath, or above, are empty in the inner mask,
368  // but filled in the outer mask
369  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
370  isz++; // increment inner edge size
371  lres[x] = 4; // flag pixel as inner edge
372  }
373  else {
374  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
375  }
376  }
377  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
378  if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
379  osz++; // increment outer edge size
380  lres[x] = 3; // flag pixel as outer edge
381  }
382  else {
383  gsz++; // increment the gradient pixel count
384  lres[x] = 2; // flag pixel as gradient
385  }
386  }
387  }
388 
389  /* Test the RIGHT edge of pixels in buffer, except corners */
390  for (x = t - rw; x > rw; x -= rw) {
391  // test if inner mask is filled
392  if (limask[x]) {
393  // test if pixel underneath, or above, are empty in the inner mask,
394  // but filled in the outer mask
395  if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
396  isz++; // increment inner edge size
397  lres[x] = 4; // flag pixel as inner edge
398  }
399  else {
400  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
401  }
402  }
403  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
404  if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
405  osz++; // increment outer edge size
406  lres[x] = 3; // flag pixel as outer edge
407  }
408  else {
409  gsz++; // increment the gradient pixel count
410  lres[x] = 2; // flag pixel as gradient
411  }
412  }
413  }
414 
415  rsize[0] = isz; // fill in our return sizes for edges + fill
416  rsize[1] = osz;
417  rsize[2] = gsz;
418 }
419 
420 static void do_allKeepBorders(unsigned int t,
421  unsigned int rw,
422  const unsigned int *limask,
423  const unsigned int *lomask,
424  unsigned int *lres,
425  float *res,
426  unsigned int *rsize)
427 {
428  int x;
429  unsigned int isz = 0; // inner edge size
430  unsigned int osz = 0; // outer edge size
431  unsigned int gsz = 0; // gradient fill area size
432  /* Test the four corners */
433  /* upper left corner */
434  x = t - rw + 1;
435  // test if inner mask is filled
436  if (limask[x]) {
437  // test if the inner mask is empty underneath or to the right
438  if (!limask[x - rw] || !limask[x + 1]) {
439  isz++; // increment inner edge size
440  lres[x] = 4; // flag pixel as inner edge
441  }
442  else {
443  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
444  }
445  }
446  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
447  osz++; // increment outer edge size
448  lres[x] = 3; // flag pixel as outer edge
449  }
450  /* upper right corner */
451  x = t;
452  // test if inner mask is filled
453  if (limask[x]) {
454  // test if the inner mask is empty underneath or to the left
455  if (!limask[x - rw] || !limask[x - 1]) {
456  isz++; // increment inner edge size
457  lres[x] = 4; // flag pixel as inner edge
458  }
459  else {
460  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
461  }
462  }
463  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
464  osz++; // increment outer edge size
465  lres[x] = 3; // flag pixel as outer edge
466  }
467  /* lower left corner */
468  x = 0;
469  // test if inner mask is filled
470  if (limask[x]) {
471  // test if inner mask is empty above or to the right
472  if (!limask[x + rw] || !limask[x + 1]) {
473  isz++; // increment inner edge size
474  lres[x] = 4; // flag pixel as inner edge
475  }
476  else {
477  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
478  }
479  }
480  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
481  osz++; // increment outer edge size
482  lres[x] = 3; // flag pixel as outer edge
483  }
484  /* lower right corner */
485  x = rw - 1;
486  // test if inner mask is filled
487  if (limask[x]) {
488  // test if inner mask is empty above or to the left
489  if (!limask[x + rw] || !limask[x - 1]) {
490  isz++; // increment inner edge size
491  lres[x] = 4; // flag pixel as inner edge
492  }
493  else {
494  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
495  }
496  }
497  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
498  osz++; // increment outer edge size
499  lres[x] = 3; // flag pixel as outer edge
500  }
501 
502  /* Test the TOP row of pixels in buffer, except corners */
503  for (x = t - 1; x >= (t - rw) + 2; x--) {
504  // test if inner mask is filled
505  if (limask[x]) {
506  // test if inner mask is empty to the left or to the right
507  if (!limask[x - 1] || !limask[x + 1]) {
508  isz++; // increment inner edge size
509  lres[x] = 4; // flag pixel as inner edge
510  }
511  else {
512  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
513  }
514  }
515  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
516  osz++; // increment outer edge size
517  lres[x] = 3; // flag pixel as outer edge
518  }
519  }
520 
521  /* Test the BOTTOM row of pixels in buffer, except corners */
522  for (x = rw - 2; x; x--) {
523  // test if inner mask is filled
524  if (limask[x]) {
525  // test if inner mask is empty to the left or to the right
526  if (!limask[x - 1] || !limask[x + 1]) {
527  isz++; // increment inner edge size
528  lres[x] = 4; // flag pixel as inner edge
529  }
530  else {
531  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
532  }
533  }
534  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
535  osz++; // increment outer edge size
536  lres[x] = 3; // flag pixel as outer edge
537  }
538  }
539  /* Test the LEFT edge of pixels in buffer, except corners */
540  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
541  // test if inner mask is filled
542  if (limask[x]) {
543  // test if inner mask is empty underneath or above
544  if (!limask[x - rw] || !limask[x + rw]) {
545  isz++; // increment inner edge size
546  lres[x] = 4; // flag pixel as inner edge
547  }
548  else {
549  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
550  }
551  }
552  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
553  osz++; // increment outer edge size
554  lres[x] = 3; // flag pixel as outer edge
555  }
556  }
557 
558  /* Test the RIGHT edge of pixels in buffer, except corners */
559  for (x = t - rw; x > rw; x -= rw) {
560  // test if inner mask is filled
561  if (limask[x]) {
562  // test if inner mask is empty underneath or above
563  if (!limask[x - rw] || !limask[x + rw]) {
564  isz++; // increment inner edge size
565  lres[x] = 4; // flag pixel as inner edge
566  }
567  else {
568  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
569  }
570  }
571  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
572  osz++; // increment outer edge size
573  lres[x] = 3; // flag pixel as outer edge
574  }
575  }
576 
577  rsize[0] = isz; // fill in our return sizes for edges + fill
578  rsize[1] = osz;
579  rsize[2] = gsz;
580 }
581 
582 static void do_allBleedBorders(unsigned int t,
583  unsigned int rw,
584  const unsigned int *limask,
585  const unsigned int *lomask,
586  unsigned int *lres,
587  float *res,
588  unsigned int *rsize)
589 {
590  int x;
591  unsigned int isz = 0; // inner edge size
592  unsigned int osz = 0; // outer edge size
593  unsigned int gsz = 0; // gradient fill area size
594  /* Test the four corners */
595  /* upper left corner */
596  x = t - rw + 1;
597  // test if inner mask is filled
598  if (limask[x]) {
599  // test if the inner mask is empty underneath or to the right
600  if (!limask[x - rw] || !limask[x + 1]) {
601  isz++; // increment inner edge size
602  lres[x] = 4; // flag pixel as inner edge
603  }
604  else {
605  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
606  }
607  }
608  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
609  if (!lomask[x - rw] ||
610  !lomask[x + 1]) { // test if outer mask is empty underneath or to the right
611  osz++; // increment outer edge size
612  lres[x] = 3; // flag pixel as outer edge
613  }
614  else {
615  gsz++; // increment the gradient pixel count
616  lres[x] = 2; // flag pixel as gradient
617  }
618  }
619  /* upper right corner */
620  x = t;
621  // test if inner mask is filled
622  if (limask[x]) {
623  // test if the inner mask is empty underneath or to the left
624  if (!limask[x - rw] || !limask[x - 1]) {
625  isz++; // increment inner edge size
626  lres[x] = 4; // flag pixel as inner edge
627  }
628  else {
629  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
630  }
631  }
632  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
633  if (!lomask[x - rw] || !lomask[x - 1]) { // test if outer mask is empty above or to the left
634  osz++; // increment outer edge size
635  lres[x] = 3; // flag pixel as outer edge
636  }
637  else {
638  gsz++; // increment the gradient pixel count
639  lres[x] = 2; // flag pixel as gradient
640  }
641  }
642  /* lower left corner */
643  x = 0;
644  // test if inner mask is filled
645  if (limask[x]) {
646  // test if inner mask is empty above or to the right
647  if (!limask[x + rw] || !limask[x + 1]) {
648  isz++; // increment inner edge size
649  lres[x] = 4; // flag pixel as inner edge
650  }
651  else {
652  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
653  }
654  }
655  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
656  if (!lomask[x + rw] ||
657  !lomask[x + 1]) { // test if outer mask is empty underneath or to the right
658  osz++; // increment outer edge size
659  lres[x] = 3; // flag pixel as outer edge
660  }
661  else {
662  gsz++; // increment the gradient pixel count
663  lres[x] = 2; // flag pixel as gradient
664  }
665  }
666  /* lower right corner */
667  x = rw - 1;
668  // test if inner mask is filled
669  if (limask[x]) {
670  // test if inner mask is empty above or to the left
671  if (!limask[x + rw] || !limask[x - 1]) {
672  isz++; // increment inner edge size
673  lres[x] = 4; // flag pixel as inner edge
674  }
675  else {
676  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
677  }
678  }
679  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
680  if (!lomask[x + rw] ||
681  !lomask[x - 1]) { // test if outer mask is empty underneath or to the left
682  osz++; // increment outer edge size
683  lres[x] = 3; // flag pixel as outer edge
684  }
685  else {
686  gsz++; // increment the gradient pixel count
687  lres[x] = 2; // flag pixel as gradient
688  }
689  }
690  /* Test the TOP row of pixels in buffer, except corners */
691  for (x = t - 1; x >= (t - rw) + 2; x--) {
692  // test if inner mask is filled
693  if (limask[x]) {
694  // test if inner mask is empty to the left or to the right
695  if (!limask[x - 1] || !limask[x + 1]) {
696  isz++; // increment inner edge size
697  lres[x] = 4; // flag pixel as inner edge
698  }
699  else {
700  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
701  }
702  }
703  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
704  if (!lomask[x - 1] ||
705  !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
706  osz++; // increment outer edge size
707  lres[x] = 3; // flag pixel as outer edge
708  }
709  else {
710  gsz++; // increment the gradient pixel count
711  lres[x] = 2; // flag pixel as gradient
712  }
713  }
714  }
715 
716  /* Test the BOTTOM row of pixels in buffer, except corners */
717  for (x = rw - 2; x; x--) {
718  // test if inner mask is filled
719  if (limask[x]) {
720  // test if inner mask is empty to the left or to the right
721  if (!limask[x - 1] || !limask[x + 1]) {
722  isz++; // increment inner edge size
723  lres[x] = 4; // flag pixel as inner edge
724  }
725  else {
726  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
727  }
728  }
729  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
730  if (!lomask[x - 1] ||
731  !lomask[x + 1]) { // test if outer mask is empty to the left or to the right
732  osz++; // increment outer edge size
733  lres[x] = 3; // flag pixel as outer edge
734  }
735  else {
736  gsz++; // increment the gradient pixel count
737  lres[x] = 2; // flag pixel as gradient
738  }
739  }
740  }
741  /* Test the LEFT edge of pixels in buffer, except corners */
742  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
743  // test if inner mask is filled
744  if (limask[x]) {
745  // test if inner mask is empty underneath or above
746  if (!limask[x - rw] || !limask[x + rw]) {
747  isz++; // increment inner edge size
748  lres[x] = 4; // flag pixel as inner edge
749  }
750  else {
751  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
752  }
753  }
754  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
755  if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
756  osz++; // increment outer edge size
757  lres[x] = 3; // flag pixel as outer edge
758  }
759  else {
760  gsz++; // increment the gradient pixel count
761  lres[x] = 2; // flag pixel as gradient
762  }
763  }
764  }
765 
766  /* Test the RIGHT edge of pixels in buffer, except corners */
767  for (x = t - rw; x > rw; x -= rw) {
768  // test if inner mask is filled
769  if (limask[x]) {
770  // test if inner mask is empty underneath or above
771  if (!limask[x - rw] || !limask[x + rw]) {
772  isz++; // increment inner edge size
773  lres[x] = 4; // flag pixel as inner edge
774  }
775  else {
776  res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge
777  }
778  }
779  else if (lomask[x]) { // inner mask was empty, test if outer mask is filled
780  if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above
781  osz++; // increment outer edge size
782  lres[x] = 3; // flag pixel as outer edge
783  }
784  else {
785  gsz++; // increment the gradient pixel count
786  lres[x] = 2; // flag pixel as gradient
787  }
788  }
789  }
790 
791  rsize[0] = isz; // fill in our return sizes for edges + fill
792  rsize[1] = osz;
793  rsize[2] = gsz;
794 }
795 
796 static void do_allEdgeDetection(unsigned int t,
797  unsigned int rw,
798  const unsigned int *limask,
799  const unsigned int *lomask,
800  unsigned int *lres,
801  float *res,
802  unsigned int *rsize,
803  unsigned int in_isz,
804  unsigned int in_osz,
805  unsigned int in_gsz)
806 {
807  int x; // x = pixel loop counter
808  int a; // a = pixel loop counter
809  int dx; // dx = delta x
810  int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop
811  int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop
812  int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop
813  int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop
814  /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
815  for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) {
816  a = x - 2;
817  pix_prevRow = a + rw;
818  pix_nextRow = a - rw;
819  pix_prevCol = a + 1;
820  pix_nextCol = a - 1;
821  while (a > dx - 2) {
822  if (!limask[a]) { // if the inner mask is empty
823  if (lomask[a]) { // if the outer mask is full
824  /*
825  * Next we test all 4 directions around the current pixel: next/prev/up/down
826  * The test ensures that the outer mask is empty and that the inner mask
827  * is also empty. If both conditions are true for any one of the 4 adjacent pixels
828  * then the current pixel is counted as being a true outer edge pixel.
829  */
830  if ((!lomask[pix_nextCol] && !limask[pix_nextCol]) ||
831  (!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
832  (!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
833  (!lomask[pix_prevRow] && !limask[pix_prevRow])) {
834  in_osz++; // increment the outer boundary pixel count
835  lres[a] = 3; // flag pixel as part of outer edge
836  }
837  else { // it's not a boundary pixel, but it is a gradient pixel
838  in_gsz++; // increment the gradient pixel count
839  lres[a] = 2; // flag pixel as gradient
840  }
841  }
842  }
843  else {
844  if (!limask[pix_nextCol] || !limask[pix_prevCol] || !limask[pix_nextRow] ||
845  !limask[pix_prevRow]) {
846  in_isz++; // increment the inner boundary pixel count
847  lres[a] = 4; // flag pixel as part of inner edge
848  }
849  else {
850  res[a] = 1.0f; // pixel is part of inner mask, but not at an edge
851  }
852  }
853  a--;
854  pix_prevRow--;
855  pix_nextRow--;
856  pix_prevCol--;
857  pix_nextCol--;
858  }
859  }
860 
861  rsize[0] = in_isz; // fill in our return sizes for edges + fill
862  rsize[1] = in_osz;
863  rsize[2] = in_gsz;
864 }
865 
866 static void do_adjacentEdgeDetection(unsigned int t,
867  unsigned int rw,
868  const unsigned int *limask,
869  const unsigned int *lomask,
870  unsigned int *lres,
871  float *res,
872  unsigned int *rsize,
873  unsigned int in_isz,
874  unsigned int in_osz,
875  unsigned int in_gsz)
876 {
877  int x; // x = pixel loop counter
878  int a; // a = pixel loop counter
879  int dx; // dx = delta x
880  int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop
881  int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop
882  int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop
883  int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop
884  /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
885  for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) {
886  a = x - 2;
887  pix_prevRow = a + rw;
888  pix_nextRow = a - rw;
889  pix_prevCol = a + 1;
890  pix_nextCol = a - 1;
891  while (a > dx - 2) {
892  if (!limask[a]) { // if the inner mask is empty
893  if (lomask[a]) { // if the outer mask is full
894  /*
895  * Next we test all 4 directions around the current pixel: next/prev/up/down
896  * The test ensures that the outer mask is empty and that the inner mask
897  * is also empty. If both conditions are true for any one of the 4 adjacent pixels
898  * then the current pixel is counted as being a true outer edge pixel.
899  */
900  if ((!lomask[pix_nextCol] && !limask[pix_nextCol]) ||
901  (!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
902  (!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
903  (!lomask[pix_prevRow] && !limask[pix_prevRow])) {
904  in_osz++; // increment the outer boundary pixel count
905  lres[a] = 3; // flag pixel as part of outer edge
906  }
907  else { // it's not a boundary pixel, but it is a gradient pixel
908  in_gsz++; // increment the gradient pixel count
909  lres[a] = 2; // flag pixel as gradient
910  }
911  }
912  }
913  else {
914  if ((!limask[pix_nextCol] && lomask[pix_nextCol]) ||
915  (!limask[pix_prevCol] && lomask[pix_prevCol]) ||
916  (!limask[pix_nextRow] && lomask[pix_nextRow]) ||
917  (!limask[pix_prevRow] && lomask[pix_prevRow])) {
918  in_isz++; // increment the inner boundary pixel count
919  lres[a] = 4; // flag pixel as part of inner edge
920  }
921  else {
922  res[a] = 1.0f; // pixel is part of inner mask, but not at an edge
923  }
924  }
925  a--;
926  pix_prevRow--; // advance all four "surrounding" pixel pointers
927  pix_nextRow--;
928  pix_prevCol--;
929  pix_nextCol--;
930  }
931  }
932 
933  rsize[0] = in_isz; // fill in our return sizes for edges + fill
934  rsize[1] = in_osz;
935  rsize[2] = in_gsz;
936 }
937 
938 static void do_createEdgeLocationBuffer(unsigned int t,
939  unsigned int rw,
940  const unsigned int *lres,
941  float *res,
942  unsigned short *gbuf,
943  unsigned int *innerEdgeOffset,
944  unsigned int *outerEdgeOffset,
945  unsigned int isz,
946  unsigned int gsz)
947 {
948  int x; // x = pixel loop counter
949  int a; // a = temporary pixel index buffer loop counter
950  unsigned int ud; // ud = unscaled edge distance
951  unsigned int dmin; // dmin = minimum edge distance
952 
953  unsigned int rsl; // long used for finding fast 1.0/sqrt
954  unsigned int gradientFillOffset;
955 
956  /* For looping inner edge pixel indexes, represents current position from offset. */
957  unsigned int innerAccum = 0;
958  /* For looping outer edge pixel indexes, represents current position from offset. */
959  unsigned int outerAccum = 0;
960  /* For looping gradient pixel indexes, represents current position from offset. */
961  unsigned int gradientAccum = 0;
962 
963  /* */
964  /* clang-format off */
965  /*
966  * Here we compute the size of buffer needed to hold (row,col) coordinates
967  * for each pixel previously determined to be either gradient, inner edge,
968  * or outer edge.
969  *
970  * Allocation is done by requesting 4 bytes "sizeof(int)" per pixel, even
971  * though gbuf[] is declared as (unsigned short *) (2 bytes) because we don't
972  * store the pixel indexes, we only store x,y location of pixel in buffer.
973  *
974  * This does make the assumption that x and y can fit in 16 unsigned bits
975  * so if Blender starts doing renders greater than 65536 in either direction
976  * this will need to allocate gbuf[] as unsigned int *and allocate 8 bytes
977  * per flagged pixel.
978  *
979  * In general, the buffer on-screen:
980  *
981  * Example: 9 by 9 pixel block
982  *
983  * . = pixel non-white in both outer and inner mask
984  * o = pixel white in outer, but not inner mask, adjacent to "." pixel
985  * g = pixel white in outer, but not inner mask, not adjacent to "." pixel
986  * i = pixel white in inner mask, adjacent to "g" or "." pixel
987  * F = pixel white in inner mask, only adjacent to other pixels white in the inner mask
988  *
989  *
990  * ......... <----- pixel #80
991  * ..oooo...
992  * .oggggo..
993  * .oggiggo.
994  * .ogiFigo.
995  * .oggiggo.
996  * .oggggo..
997  * ..oooo...
998  * pixel #00 -----> .........
999  *
1000  * gsz = 18 (18 "g" pixels above)
1001  * isz = 4 (4 "i" pixels above)
1002  * osz = 18 (18 "o" pixels above)
1003  *
1004  *
1005  * The memory in gbuf[] after filling will look like this:
1006  *
1007  * gradientFillOffset (0 pixels) innerEdgeOffset (18 pixels) outerEdgeOffset (22 pixels)
1008  * / / /
1009  * / / /
1010  * |X Y X Y X Y X Y > <X Y X Y > <X Y X Y X Y > <X Y X Y | <- (x,y)
1011  * +--------------------------------> <----------------> <------------------------> <----------------+
1012  * |0 2 4 6 8 10 12 14 > ... <68 70 72 74 > ... <80 82 84 86 88 90 > ... <152 154 156 158 | <- bytes
1013  * +--------------------------------> <----------------> <------------------------> <----------------+
1014  * |g0 g0 g1 g1 g2 g2 g3 g3 > <g17 g17 i0 i0 > <i2 i2 i3 i3 o0 o0 > <o16 o16 o17 o17 | <- pixel
1015  * / / /
1016  * / / /
1017  * / / /
1018  * +---------- gradientAccum (18) ---------+ +--- innerAccum (22) ---+ +--- outerAccum (40) ---+
1019  *
1020  *
1021  * Ultimately we do need the pixel's memory buffer index to set the output
1022  * pixel color, but it's faster to reconstruct the memory buffer location
1023  * each iteration of the final gradient calculation than it is to deconstruct
1024  * a memory location into x,y pairs each round.
1025  */
1026  /* clang-format on */
1027 
1028  gradientFillOffset = 0; // since there are likely "more" of these, put it first. :)
1029  *innerEdgeOffset = gradientFillOffset + gsz; // set start of inner edge indexes
1030  *outerEdgeOffset = (*innerEdgeOffset) + isz; // set start of outer edge indexes
1031  /* set the accumulators to correct positions */ // set up some accumulator variables for loops
1032  gradientAccum = gradientFillOffset; // each accumulator variable starts at its respective
1033  innerAccum = *innerEdgeOffset; // section's offset so when we start filling, each
1034  outerAccum = *outerEdgeOffset; // section fills up its allocated space in gbuf
1035  // uses dmin=row, rsl=col
1036  for (x = 0, dmin = 0; x < t; x += rw, dmin++) {
1037  for (rsl = 0; rsl < rw; rsl++) {
1038  a = x + rsl;
1039  if (lres[a] == 2) { // it is a gradient pixel flagged by 2
1040  ud = gradientAccum << 1; // double the index to reach correct unsigned short location
1041  gbuf[ud] = dmin; // insert pixel's row into gradient pixel location buffer
1042  gbuf[ud + 1] = rsl; // insert pixel's column into gradient pixel location buffer
1043  gradientAccum++; // increment gradient index buffer pointer
1044  }
1045  else if (lres[a] == 3) { // it is an outer edge pixel flagged by 3
1046  ud = outerAccum << 1; // double the index to reach correct unsigned short location
1047  gbuf[ud] = dmin; // insert pixel's row into outer edge pixel location buffer
1048  gbuf[ud + 1] = rsl; // insert pixel's column into outer edge pixel location buffer
1049  outerAccum++; // increment outer edge index buffer pointer
1050  res[a] = 0.0f; // set output pixel intensity now since it won't change later
1051  }
1052  else if (lres[a] == 4) { // it is an inner edge pixel flagged by 4
1053  ud = innerAccum << 1; // double int index to reach correct unsigned short location
1054  gbuf[ud] = dmin; // insert pixel's row into inner edge pixel location buffer
1055  gbuf[ud + 1] = rsl; // insert pixel's column into inner edge pixel location buffer
1056  innerAccum++; // increment inner edge index buffer pointer
1057  res[a] = 1.0f; // set output pixel intensity now since it won't change later
1058  }
1059  }
1060  }
1061 }
1062 
1063 static void do_fillGradientBuffer(unsigned int rw,
1064  float *res,
1065  const unsigned short *gbuf,
1066  unsigned int isz,
1067  unsigned int osz,
1068  unsigned int gsz,
1069  unsigned int innerEdgeOffset,
1070  unsigned int outerEdgeOffset)
1071 {
1072  int x; // x = pixel loop counter
1073  int a; // a = temporary pixel index buffer loop counter
1074  int fsz; // size of the frame
1075  unsigned int rsl; // long used for finding fast 1.0/sqrt
1076  float rsf; // float used for finding fast 1.0/sqrt
1077  const float rsopf = 1.5f; // constant float used for finding fast 1.0/sqrt
1078 
1079  unsigned int gradientFillOffset;
1080  unsigned int t;
1081  unsigned int ud; // ud = unscaled edge distance
1082  unsigned int dmin; // dmin = minimum edge distance
1083  float odist; // odist = current outer edge distance
1084  float idist; // idist = current inner edge distance
1085  int dx; // dx = X-delta (used for distance proportion calculation)
1086  int dy; // dy = Y-delta (used for distance proportion calculation)
1087 
1088  /*
1089  * The general algorithm used to color each gradient pixel is:
1090  *
1091  * 1.) Loop through all gradient pixels.
1092  * A.) For each gradient pixel:
1093  * a.) Loop through all outside edge pixels, looking for closest one
1094  * to the gradient pixel we are in.
1095  * b.) Loop through all inside edge pixels, looking for closest one
1096  * to the gradient pixel we are in.
1097  * c.) Find proportion of distance from gradient pixel to inside edge
1098  * pixel compared to sum of distance to inside edge and distance to
1099  * outside edge.
1100  *
1101  * In an image where:
1102  * . = blank (black) pixels, not covered by inner mask or outer mask
1103  * + = desired gradient pixels, covered only by outer mask
1104  * * = white full mask pixels, covered by at least inner mask
1105  *
1106  * ...............................
1107  * ...............+++++++++++.....
1108  * ...+O++++++..++++++++++++++....
1109  * ..+++\++++++++++++++++++++.....
1110  * .+++++G+++++++++*******+++.....
1111  * .+++++|+++++++*********+++.....
1112  * .++***I****************+++.....
1113  * .++*******************+++......
1114  * .+++*****************+++.......
1115  * ..+++***************+++........
1116  * ....+++**********+++...........
1117  * ......++++++++++++.............
1118  * ...............................
1119  *
1120  * O = outside edge pixel
1121  * \
1122  * G = gradient pixel
1123  * |
1124  * I = inside edge pixel
1125  *
1126  * __
1127  * *note that IO does not need to be a straight line, in fact
1128  * many cases can arise where straight lines do not work
1129  * correctly.
1130  *
1131  * __ __ __
1132  * d.) Pixel color is assigned as |GO| / ( |GI| + |GO| )
1133  *
1134  * The implementation does not compute distance, but the reciprocal of the
1135  * distance. This is done to avoid having to compute a square root, as a
1136  * reciprocal square root can be computed faster. Therefore, the code computes
1137  * pixel color as |GI| / (|GI| + |GO|). Since these are reciprocals, GI serves the
1138  * purpose of GO for the proportion calculation.
1139  *
1140  * For the purposes of the minimum distance comparisons, we only check
1141  * the sums-of-squares against each other, since they are in the same
1142  * mathematical sort-order as if we did go ahead and take square roots
1143  *
1144  * Loop through all gradient pixels.
1145  */
1146 
1147  for (x = gsz - 1; x >= 0; x--) {
1148  gradientFillOffset = x << 1;
1149  t = gbuf[gradientFillOffset]; // calculate column of pixel indexed by gbuf[x]
1150  fsz = gbuf[gradientFillOffset + 1]; // calculate row of pixel indexed by gbuf[x]
1151  dmin = 0xffffffff; // reset min distance to edge pixel
1152  for (a = outerEdgeOffset + osz - 1; a >= outerEdgeOffset;
1153  a--) { // loop through all outer edge buffer pixels
1154  ud = a << 1;
1155  dy = t - gbuf[ud]; // set dx to gradient pixel column - outer edge pixel row
1156  dx = fsz - gbuf[ud + 1]; // set dy to gradient pixel row - outer edge pixel column
1157  ud = dx * dx + dy * dy; // compute sum of squares
1158  if (ud < dmin) { // if our new sum of squares is less than the current minimum
1159  dmin = ud; // set a new minimum equal to the new lower value
1160  }
1161  }
1162  odist = (float)(dmin); // cast outer min to a float
1163  rsf = odist * 0.5f; //
1164  rsl = *(unsigned int *)&odist; // use some peculiar properties of the way bits are stored
1165  rsl = 0x5f3759df - (rsl >> 1); // in floats vs. unsigned ints to compute an approximate
1166  odist = *(float *)&rsl; // reciprocal square root
1167  odist = odist * (rsopf - (rsf * odist *
1168  odist)); // -- ** this line can be iterated for more accuracy ** --
1169  dmin = 0xffffffff; // reset min distance to edge pixel
1170  for (a = innerEdgeOffset + isz - 1; a >= innerEdgeOffset;
1171  a--) { // loop through all inside edge pixels
1172  ud = a << 1;
1173  dy = t - gbuf[ud]; // compute delta in Y from gradient pixel to inside edge pixel
1174  dx = fsz - gbuf[ud + 1]; // compute delta in X from gradient pixel to inside edge pixel
1175  ud = dx * dx + dy * dy; // compute sum of squares
1176  if (ud < dmin) { // if our new sum of squares is less than the current minimum we've found
1177  dmin = ud; // set a new minimum equal to the new lower value
1178  }
1179  }
1180  idist = (float)(dmin); // cast inner min to a float
1181  rsf = idist * 0.5f; //
1182  rsl = *(unsigned int *)&idist; //
1183  rsl = 0x5f3759df - (rsl >> 1); // see notes above
1184  idist = *(float *)&rsl; //
1185  idist = idist * (rsopf - (rsf * idist * idist)); //
1186  /*
1187  * Note once again that since we are using reciprocals of distance values our
1188  * proportion is already the correct intensity, and does not need to be
1189  * subtracted from 1.0 like it would have if we used real distances.
1190  */
1191 
1192  /*
1193  * Here we reconstruct the pixel's memory location in the CompBuf by
1194  * Pixel Index = Pixel Column + ( Pixel Row * Row Width )
1195  */
1196  res[gbuf[gradientFillOffset + 1] + (gbuf[gradientFillOffset] * rw)] =
1197  (idist / (idist + odist)); // set intensity
1198  }
1199 }
1200 
1201 // end of copy
1202 
1203 void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res)
1204 {
1205  unsigned int *lres; // lres = unsigned int pointer to output pixel buffer (for bit operations)
1206  unsigned int *limask; // limask = unsigned int pointer to inner mask (for bit operations)
1207  unsigned int *lomask; // lomask = unsigned int pointer to outer mask (for bit operations)
1208 
1209  int rw; // rw = pixel row width
1210  int t; // t = total number of pixels in buffer - 1 (used for loop starts)
1211  int fsz; // size of the frame
1212 
1213  unsigned int isz = 0; // size (in pixels) of inside edge pixel index buffer
1214  unsigned int osz = 0; // size (in pixels) of outside edge pixel index buffer
1215  unsigned int gsz = 0; // size (in pixels) of gradient pixel index buffer
1216  unsigned int rsize[3]; // size storage to pass to helper functions
1217  unsigned int innerEdgeOffset =
1218  0; // offset into final buffer where inner edge pixel indexes start
1219  unsigned int outerEdgeOffset =
1220  0; // offset into final buffer where outer edge pixel indexes start
1221 
1222  unsigned short *gbuf; // gradient/inner/outer pixel location index buffer
1223 
1224  if (true) { // if both input sockets have some data coming in...
1225 
1226  rw = this->getWidth(); // width of a row of pixels
1227  t = (rw * this->getHeight()) - 1; // determine size of the frame
1228  memset(res,
1229  0,
1230  sizeof(float) * (t + 1)); // clear output buffer (not all pixels will be written later)
1231 
1232  lres = (unsigned int *)res; // unsigned int pointer to output buffer (for bit level ops)
1233  limask = (unsigned int *)imask; // unsigned int pointer to input mask (for bit level ops)
1234  lomask = (unsigned int *)omask; // unsigned int pointer to output mask (for bit level ops)
1235 
1236  /*
1237  * The whole buffer is broken up into 4 parts. The four CORNERS, the FIRST and LAST rows, the
1238  * LEFT and RIGHT edges (excluding the corner pixels), and all OTHER rows.
1239  * This allows for quick computation of outer edge pixels where
1240  * a screen edge pixel is marked to be gradient.
1241  *
1242  * The pixel type (gradient vs inner-edge vs outer-edge) tests change
1243  * depending on the user selected "Inner Edge Mode" and the user selected
1244  * "Buffer Edge Mode" on the node's GUI. There are 4 sets of basically the
1245  * same algorithm:
1246  *
1247  * 1.) Inner Edge -> Adjacent Only
1248  * Buffer Edge -> Keep Inside
1249  *
1250  * 2.) Inner Edge -> Adjacent Only
1251  * Buffer Edge -> Bleed Out
1252  *
1253  * 3.) Inner Edge -> All
1254  * Buffer Edge -> Keep Inside
1255  *
1256  * 4.) Inner Edge -> All
1257  * Buffer Edge -> Bleed Out
1258  *
1259  * Each version has slightly different criteria for detecting an edge pixel.
1260  */
1261  if (this->m_adjacentOnly) { // if "adjacent only" inner edge mode is turned on
1262  if (this->m_keepInside) { // if "keep inside" buffer edge mode is turned on
1263  do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize);
1264  }
1265  else { // "bleed out" buffer edge mode is turned on
1266  do_adjacentBleedBorders(t, rw, limask, lomask, lres, res, rsize);
1267  }
1268  // set up inner edge, outer edge, and gradient buffer sizes after border pass
1269  isz = rsize[0];
1270  osz = rsize[1];
1271  gsz = rsize[2];
1272  // detect edges in all non-border pixels in the buffer
1273  do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz);
1274  }
1275  else { // "all" inner edge mode is turned on
1276  if (this->m_keepInside) { // if "keep inside" buffer edge mode is turned on
1277  do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize);
1278  }
1279  else { // "bleed out" buffer edge mode is turned on
1280  do_allBleedBorders(t, rw, limask, lomask, lres, res, rsize);
1281  }
1282  // set up inner edge, outer edge, and gradient buffer sizes after border pass
1283  isz = rsize[0];
1284  osz = rsize[1];
1285  gsz = rsize[2];
1286  // detect edges in all non-border pixels in the buffer
1287  do_allEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz);
1288  }
1289 
1290  // set edge and gradient buffer sizes once again...
1291  // the sizes in rsize[] may have been modified
1292  // by the do_*EdgeDetection() function.
1293  isz = rsize[0];
1294  osz = rsize[1];
1295  gsz = rsize[2];
1296 
1297  // calculate size of pixel index buffer needed
1298  fsz = gsz + isz + osz;
1299  // allocate edge/gradient pixel index buffer
1300  gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM");
1301 
1303  t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz);
1304  do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset);
1305 
1306  // free the gradient index buffer
1307  MEM_freeN(gbuf);
1308  }
1309 }
1310 
1312 {
1316  this->m_inputInnerMask = nullptr;
1317  this->m_inputOuterMask = nullptr;
1318  this->m_adjacentOnly = false;
1319  this->m_keepInside = false;
1320  this->flags.complex = true;
1321 }
1322 
1324  ReadBufferOperation *readOperation,
1325  rcti *output)
1326 {
1327  if (this->m_cachedInstance == nullptr) {
1328  rcti newInput;
1329  newInput.xmax = this->getWidth();
1330  newInput.xmin = 0;
1331  newInput.ymax = this->getHeight();
1332  newInput.ymin = 0;
1333  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
1334  }
1335 
1336  return false;
1337 }
1338 
1340 {
1341  this->m_inputInnerMask = this->getInputSocketReader(0);
1342  this->m_inputOuterMask = this->getInputSocketReader(1);
1343  initMutex();
1344  this->m_cachedInstance = nullptr;
1345 }
1346 
1348 {
1349  if (this->m_cachedInstance) {
1350  return this->m_cachedInstance;
1351  }
1352 
1353  lockMutex();
1354  if (this->m_cachedInstance == nullptr) {
1355  MemoryBuffer *innerMask = (MemoryBuffer *)this->m_inputInnerMask->initializeTileData(rect);
1356  MemoryBuffer *outerMask = (MemoryBuffer *)this->m_inputOuterMask->initializeTileData(rect);
1357  float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(),
1358  __func__);
1359  float *imask = innerMask->getBuffer();
1360  float *omask = outerMask->getBuffer();
1361  doDoubleEdgeMask(imask, omask, data);
1362  this->m_cachedInstance = data;
1363  }
1364  unlockMutex();
1365  return this->m_cachedInstance;
1366 }
1367 void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data)
1368 {
1369  float *buffer = (float *)data;
1370  int index = (y * this->getWidth() + x);
1371  output[0] = buffer[index];
1372 }
1373 
1375 {
1376  this->m_inputInnerMask = nullptr;
1377  this->m_inputOuterMask = nullptr;
1378  deinitMutex();
1379  if (this->m_cachedInstance) {
1380  MEM_freeN(this->m_cachedInstance);
1381  this->m_cachedInstance = nullptr;
1382  }
1383 }
1384 
1385 } // namespace blender::compositor
typedef float(TangentPoint)[2]
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
Read Guarded memory(de)allocation.
#define output
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
void doDoubleEdgeMask(float *imask, float *omask, float *res)
void executePixel(float output[4], int x, int y, void *data) override
a MemoryBuffer contains access to the data of a chunk
float * getBuffer()
get the data of this MemoryBuffer
virtual void * initializeTileData(rcti *)
void addInputSocket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void addOutputSocket(DataType datatype)
SocketReader * getInputSocketReader(unsigned int inputSocketindex)
virtual bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static unsigned a[3]
Definition: RandGen.cpp:92
static void do_fillGradientBuffer(unsigned int rw, float *res, const unsigned short *gbuf, unsigned int isz, unsigned int osz, unsigned int gsz, unsigned int innerEdgeOffset, unsigned int outerEdgeOffset)
static void do_adjacentKeepBorders(unsigned int t, unsigned int rw, const unsigned int *limask, const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize)
static void do_allEdgeDetection(unsigned int t, unsigned int rw, const unsigned int *limask, const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, unsigned int in_isz, unsigned int in_osz, unsigned int in_gsz)
static void do_allBleedBorders(unsigned int t, unsigned int rw, const unsigned int *limask, const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize)
static void do_createEdgeLocationBuffer(unsigned int t, unsigned int rw, const unsigned int *lres, float *res, unsigned short *gbuf, unsigned int *innerEdgeOffset, unsigned int *outerEdgeOffset, unsigned int isz, unsigned int gsz)
static void do_adjacentEdgeDetection(unsigned int t, unsigned int rw, const unsigned int *limask, const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, unsigned int in_isz, unsigned int in_osz, unsigned int in_gsz)
static void do_allKeepBorders(unsigned int t, unsigned int rw, const unsigned int *limask, const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize)
static void do_adjacentBleedBorders(unsigned int t, unsigned int rw, const unsigned int *limask, const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize)
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79