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