Blender  V2.93
COM_SMAAOperation.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2017, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor: IRIE Shinsuke
19  */
20 
21 #include "COM_SMAAOperation.h"
22 #include "BLI_math.h"
23 #include "COM_SMAAAreaTexture.h"
24 
25 extern "C" {
26 #include "IMB_colormanagement.h"
27 }
28 
29 namespace blender::compositor {
30 
31 /*
32  * An implementation of Enhanced Subpixel Morphological Antialiasing (SMAA)
33  *
34  * The algorithm was proposed by:
35  * Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, Diego Gutierrez
36  *
37  * http://www.iryoku.com/smaa/
38  *
39  * This file is based on smaa-cpp:
40  *
41  * https://github.com/iRi-E/smaa-cpp
42  *
43  * Currently only SMAA 1x mode is provided, so the operation will be done
44  * with no spatial multisampling nor temporal supersampling.
45  *
46  * Note: This program assumes the screen coordinates are DirectX style, so
47  * the vertical direction is upside-down. "top" and "bottom" actually mean
48  * bottom and top, respectively.
49  */
50 
51 /*-----------------------------------------------------------------------------*/
52 /* Non-Configurable Defines */
53 
54 #define SMAA_AREATEX_SIZE 80
55 #define SMAA_AREATEX_MAX_DISTANCE 20
56 #define SMAA_AREATEX_MAX_DISTANCE_DIAG 20
57 #define SMAA_MAX_SEARCH_STEPS 362 /* 362 - 1 = 19^2 */
58 #define SMAA_MAX_SEARCH_STEPS_DIAG 19
59 
60 /*-----------------------------------------------------------------------------*/
61 /* Internal Functions to Sample Pixel Color from Image */
62 
63 static inline void sample(SocketReader *reader, int x, int y, float color[4])
64 {
65  if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) {
66  color[0] = color[1] = color[2] = color[3] = 0.0;
67  return;
68  }
69 
70  reader->read(color, x, y, nullptr);
71 }
72 
74  SocketReader *reader, int x, int y, float yoffset, float color[4])
75 {
76  float iy = floorf(yoffset);
77  float fy = yoffset - iy;
78  y += (int)iy;
79 
80  float color00[4], color01[4];
81 
82  sample(reader, x + 0, y + 0, color00);
83  sample(reader, x + 0, y + 1, color01);
84 
85  color[0] = interpf(color01[0], color00[0], fy);
86  color[1] = interpf(color01[1], color00[1], fy);
87  color[2] = interpf(color01[2], color00[2], fy);
88  color[3] = interpf(color01[3], color00[3], fy);
89 }
90 
92  SocketReader *reader, int x, int y, float xoffset, float color[4])
93 {
94  float ix = floorf(xoffset);
95  float fx = xoffset - ix;
96  x += (int)ix;
97 
98  float color00[4], color10[4];
99 
100  sample(reader, x + 0, y + 0, color00);
101  sample(reader, x + 1, y + 0, color10);
102 
103  color[0] = interpf(color10[0], color00[0], fx);
104  color[1] = interpf(color10[1], color00[1], fx);
105  color[2] = interpf(color10[2], color00[2], fx);
106  color[3] = interpf(color10[3], color00[3], fx);
107 }
108 
109 /*-----------------------------------------------------------------------------*/
110 /* Internal Functions to Sample Blending Weights from AreaTex */
111 
112 static inline const float *areatex_sample_internal(const float *areatex, int x, int y)
113 {
114  return &areatex[(CLAMPIS(x, 0, SMAA_AREATEX_SIZE - 1) +
116  2];
117 }
118 
123 static void area(int d1, int d2, int e1, int e2, float weights[2])
124 {
125  /* The areas texture is compressed quadratically: */
126  float x = (float)(SMAA_AREATEX_MAX_DISTANCE * e1) + sqrtf((float)d1);
127  float y = (float)(SMAA_AREATEX_MAX_DISTANCE * e2) + sqrtf((float)d2);
128 
129  float ix = floorf(x), iy = floorf(y);
130  float fx = x - ix, fy = y - iy;
131  int X = (int)ix, Y = (int)iy;
132 
133  const float *weights00 = areatex_sample_internal(areatex, X + 0, Y + 0);
134  const float *weights10 = areatex_sample_internal(areatex, X + 1, Y + 0);
135  const float *weights01 = areatex_sample_internal(areatex, X + 0, Y + 1);
136  const float *weights11 = areatex_sample_internal(areatex, X + 1, Y + 1);
137 
138  weights[0] = interpf(
139  interpf(weights11[0], weights01[0], fx), interpf(weights10[0], weights00[0], fx), fy);
140  weights[1] = interpf(
141  interpf(weights11[1], weights01[1], fx), interpf(weights10[1], weights00[1], fx), fy);
142 }
143 
148 static void area_diag(int d1, int d2, int e1, int e2, float weights[2])
149 {
150  int x = SMAA_AREATEX_MAX_DISTANCE_DIAG * e1 + d1;
151  int y = SMAA_AREATEX_MAX_DISTANCE_DIAG * e2 + d2;
152 
153  const float *w = areatex_sample_internal(areatex_diag, x, y);
154  copy_v2_v2(weights, w);
155 }
156 
157 /*-----------------------------------------------------------------------------*/
158 /* Edge Detection (First Pass) */
159 /*-----------------------------------------------------------------------------*/
160 
162 {
163  this->addInputSocket(DataType::Color); /* image */
164  this->addInputSocket(DataType::Value); /* depth, material ID, etc. */
166  this->flags.complex = true;
167  this->m_imageReader = nullptr;
168  this->m_valueReader = nullptr;
169  this->m_threshold = 0.1f;
170  this->m_contrast_limit = 2.0f;
171 }
172 
174 {
175  this->m_imageReader = this->getInputSocketReader(0);
176  this->m_valueReader = this->getInputSocketReader(1);
177 }
178 
180 {
181  this->m_imageReader = nullptr;
182  this->m_valueReader = nullptr;
183 }
184 
186 {
187  /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 0.5 */
188  m_threshold = scalenorm(0, 0.5, threshold);
189 }
190 
192 {
193  /* UI values are between 0 and 1 for simplicity but algorithm expects values between 1 and 10 */
194  m_contrast_limit = scalenorm(1, 10, factor);
195 }
196 
198  rcti *input, ReadBufferOperation *readOperation, rcti *output)
199 {
200  rcti newInput;
201  newInput.xmax = input->xmax + 1;
202  newInput.xmin = input->xmin - 2;
203  newInput.ymax = input->ymax + 1;
204  newInput.ymin = input->ymin - 2;
205 
206  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
207 }
208 
209 void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/)
210 {
211  float color[4];
212 
213  /* Calculate luma deltas: */
214  sample(m_imageReader, x, y, color);
215  float L = IMB_colormanagement_get_luminance(color);
216  sample(m_imageReader, x - 1, y, color);
217  float Lleft = IMB_colormanagement_get_luminance(color);
218  sample(m_imageReader, x, y - 1, color);
219  float Ltop = IMB_colormanagement_get_luminance(color);
220  float Dleft = fabsf(L - Lleft);
221  float Dtop = fabsf(L - Ltop);
222 
223  /* We do the usual threshold: */
224  output[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f;
225  output[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f;
226  output[2] = 0.0f;
227  output[3] = 1.0f;
228 
229  /* Then discard if there is no edge: */
230  if (is_zero_v2(output)) {
231  return;
232  }
233 
234  /* Calculate right and bottom deltas: */
235  sample(m_imageReader, x + 1, y, color);
236  float Lright = IMB_colormanagement_get_luminance(color);
237  sample(m_imageReader, x, y + 1, color);
238  float Lbottom = IMB_colormanagement_get_luminance(color);
239  float Dright = fabsf(L - Lright);
240  float Dbottom = fabsf(L - Lbottom);
241 
242  /* Calculate the maximum delta in the direct neighborhood: */
243  float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom));
244 
245  /* Calculate luma used for both left and top edges: */
246  sample(m_imageReader, x - 1, y - 1, color);
247  float Llefttop = IMB_colormanagement_get_luminance(color);
248 
249  /* Left edge */
250  if (output[0] != 0.0f) {
251  /* Calculate deltas around the left pixel: */
252  sample(m_imageReader, x - 2, y, color);
253  float Lleftleft = IMB_colormanagement_get_luminance(color);
254  sample(m_imageReader, x - 1, y + 1, color);
255  float Lleftbottom = IMB_colormanagement_get_luminance(color);
256  float Dleftleft = fabsf(Lleft - Lleftleft);
257  float Dlefttop = fabsf(Lleft - Llefttop);
258  float Dleftbottom = fabsf(Lleft - Lleftbottom);
259 
260  /* Calculate the final maximum delta: */
261  maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom)));
262 
263  /* Local contrast adaptation: */
264  if (maxDelta > m_contrast_limit * Dleft) {
265  output[0] = 0.0f;
266  }
267  }
268 
269  /* Top edge */
270  if (output[1] != 0.0f) {
271  /* Calculate top-top delta: */
272  sample(m_imageReader, x, y - 2, color);
273  float Ltoptop = IMB_colormanagement_get_luminance(color);
274  sample(m_imageReader, x + 1, y - 1, color);
275  float Ltopright = IMB_colormanagement_get_luminance(color);
276  float Dtoptop = fabsf(Ltop - Ltoptop);
277  float Dtopleft = fabsf(Ltop - Llefttop);
278  float Dtopright = fabsf(Ltop - Ltopright);
279 
280  /* Calculate the final maximum delta: */
281  maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright)));
282 
283  /* Local contrast adaptation: */
284  if (maxDelta > m_contrast_limit * Dtop) {
285  output[1] = 0.0f;
286  }
287  }
288 }
289 
290 /*-----------------------------------------------------------------------------*/
291 /* Blending Weight Calculation (Second Pass) */
292 /*-----------------------------------------------------------------------------*/
293 
295 {
296  this->addInputSocket(DataType::Color); /* edges */
298  this->flags.complex = true;
299  this->m_imageReader = nullptr;
300  this->m_corner_rounding = 25;
301 }
302 
304 {
305  return getInputOperation(0)->initializeTileData(rect);
306 }
307 
309 {
310  this->m_imageReader = this->getInputSocketReader(0);
311 }
312 
314 {
315  /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */
316  m_corner_rounding = static_cast<int>(scalenorm(0, 100, rounding));
317 }
318 
320  int x,
321  int y,
322  void * /*data*/)
323 {
324  float edges[4], c[4];
325 
326  zero_v4(output);
327  sample(m_imageReader, x, y, edges);
328 
329  /* Edge at north */
330  if (edges[1] > 0.0f) {
331  /* Diagonals have both north and west edges, so calculating weights for them */
332  /* in one of the boundaries is enough. */
333  calculateDiagWeights(x, y, edges, output);
334 
335  /* We give priority to diagonals, so if we find a diagonal we skip */
336  /* horizontal/vertical processing. */
337  if (!is_zero_v2(output)) {
338  return;
339  }
340 
341  /* Find the distance to the left and the right: */
342  int left = searchXLeft(x, y);
343  int right = searchXRight(x, y);
344  int d1 = x - left, d2 = right - x;
345 
346  /* Fetch the left and right crossing edges: */
347  int e1 = 0, e2 = 0;
348  sample(m_imageReader, left, y - 1, c);
349  if (c[0] > 0.0) {
350  e1 += 1;
351  }
352  sample(m_imageReader, left, y, c);
353  if (c[0] > 0.0) {
354  e1 += 2;
355  }
356  sample(m_imageReader, right + 1, y - 1, c);
357  if (c[0] > 0.0) {
358  e2 += 1;
359  }
360  sample(m_imageReader, right + 1, y, c);
361  if (c[0] > 0.0) {
362  e2 += 2;
363  }
364 
365  /* Ok, we know how this pattern looks like, now it is time for getting */
366  /* the actual area: */
367  area(d1, d2, e1, e2, output); /* R, G */
368 
369  /* Fix corners: */
370  if (m_corner_rounding) {
371  detectHorizontalCornerPattern(output, left, right, y, d1, d2);
372  }
373  }
374 
375  /* Edge at west */
376  if (edges[0] > 0.0f) {
377  /* Did we already do diagonal search for this west edge from the left neighboring pixel? */
378  if (isVerticalSearchUnneeded(x, y)) {
379  return;
380  }
381 
382  /* Find the distance to the top and the bottom: */
383  int top = searchYUp(x, y);
384  int bottom = searchYDown(x, y);
385  int d1 = y - top, d2 = bottom - y;
386 
387  /* Fetch the top and bottom crossing edges: */
388  int e1 = 0, e2 = 0;
389  sample(m_imageReader, x - 1, top, c);
390  if (c[1] > 0.0) {
391  e1 += 1;
392  }
393  sample(m_imageReader, x, top, c);
394  if (c[1] > 0.0) {
395  e1 += 2;
396  }
397  sample(m_imageReader, x - 1, bottom + 1, c);
398  if (c[1] > 0.0) {
399  e2 += 1;
400  }
401  sample(m_imageReader, x, bottom + 1, c);
402  if (c[1] > 0.0) {
403  e2 += 2;
404  }
405 
406  /* Get the area for this direction: */
407  area(d1, d2, e1, e2, output + 2); /* B, A */
408 
409  /* Fix corners: */
410  if (m_corner_rounding) {
411  detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2);
412  }
413  }
414 }
415 
417 {
418  this->m_imageReader = nullptr;
419 }
420 
422  rcti *input, ReadBufferOperation *readOperation, rcti *output)
423 {
424  rcti newInput;
425 
426  newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1);
427  newInput.xmin = input->xmin -
428  fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1);
429  newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG);
430  newInput.ymin = input->ymin -
432 
433  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
434 }
435 
436 /*-----------------------------------------------------------------------------*/
437 /* Diagonal Search Functions */
438 
442 int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found)
443 {
444  float e[4];
445  int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir;
446  *found = false;
447 
448  while (x != end) {
449  x += dir;
450  y -= dir;
451  sample(m_imageReader, x, y, e);
452  if (e[1] == 0.0f) {
453  *found = true;
454  break;
455  }
456  if (e[0] == 0.0f) {
457  *found = true;
458  return (dir < 0) ? x : x - dir;
459  }
460  }
461 
462  return x - dir;
463 }
464 
465 int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found)
466 {
467  float e[4];
468  int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir;
469  *found = false;
470 
471  while (x != end) {
472  x += dir;
473  y += dir;
474  sample(m_imageReader, x, y, e);
475  if (e[1] == 0.0f) {
476  *found = true;
477  break;
478  }
479  sample(m_imageReader, x + 1, y, e);
480  if (e[0] == 0.0f) {
481  *found = true;
482  return (dir > 0) ? x : x - dir;
483  }
484  }
485 
486  return x - dir;
487 }
488 
492 void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x,
493  int y,
494  const float edges[2],
495  float weights[2])
496 {
497  int d1, d2;
498  bool d1_found, d2_found;
499  float e[4], c[4];
500 
501  zero_v2(weights);
502 
503  if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) {
504  return;
505  }
506 
507  /* Search for the line ends: */
508  if (edges[0] > 0.0f) {
509  d1 = x - searchDiag1(x, y, -1, &d1_found);
510  }
511  else {
512  d1 = 0;
513  d1_found = true;
514  }
515  d2 = searchDiag1(x, y, 1, &d2_found) - x;
516 
517  if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */
518  int e1 = 0, e2 = 0;
519 
520  if (d1_found) {
521  /* Fetch the crossing edges: */
522  int left = x - d1, bottom = y + d1;
523 
524  sample(m_imageReader, left - 1, bottom, c);
525  if (c[1] > 0.0) {
526  e1 += 2;
527  }
528  sample(m_imageReader, left, bottom, c);
529  if (c[0] > 0.0) {
530  e1 += 1;
531  }
532  }
533 
534  if (d2_found) {
535  /* Fetch the crossing edges: */
536  int right = x + d2, top = y - d2;
537 
538  sample(m_imageReader, right + 1, top, c);
539  if (c[1] > 0.0) {
540  e2 += 2;
541  }
542  sample(m_imageReader, right + 1, top - 1, c);
543  if (c[0] > 0.0) {
544  e2 += 1;
545  }
546  }
547 
548  /* Fetch the areas for this line: */
549  area_diag(d1, d2, e1, e2, weights);
550  }
551 
552  /* Search for the line ends: */
553  d1 = x - searchDiag2(x, y, -1, &d1_found);
554  sample(m_imageReader, x + 1, y, e);
555  if (e[0] > 0.0f) {
556  d2 = searchDiag2(x, y, 1, &d2_found) - x;
557  }
558  else {
559  d2 = 0;
560  d2_found = true;
561  }
562 
563  if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */
564  int e1 = 0, e2 = 0;
565 
566  if (d1_found) {
567  /* Fetch the crossing edges: */
568  int left = x - d1, top = y - d1;
569 
570  sample(m_imageReader, left - 1, top, c);
571  if (c[1] > 0.0) {
572  e1 += 2;
573  }
574  sample(m_imageReader, left, top - 1, c);
575  if (c[0] > 0.0) {
576  e1 += 1;
577  }
578  }
579 
580  if (d2_found) {
581  /* Fetch the crossing edges: */
582  int right = x + d2, bottom = y + d2;
583 
584  sample(m_imageReader, right + 1, bottom, c);
585  if (c[1] > 0.0) {
586  e2 += 2;
587  }
588  if (c[0] > 0.0) {
589  e2 += 1;
590  }
591  }
592 
593  /* Fetch the areas for this line: */
594  float w[2];
595  area_diag(d1, d2, e1, e2, w);
596  weights[0] += w[1];
597  weights[1] += w[0];
598  }
599 }
600 
601 bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int y)
602 {
603  int d1, d2;
604  bool found;
605  float e[4];
606 
607  if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) {
608  return false;
609  }
610 
611  /* Search for the line ends: */
612  sample(m_imageReader, x - 1, y, e);
613  if (e[1] > 0.0f) {
614  d1 = x - searchDiag2(x - 1, y, -1, &found);
615  }
616  else {
617  d1 = 0;
618  }
619  d2 = searchDiag2(x - 1, y, 1, &found) - x;
620 
621  return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */
622 }
623 
624 /*-----------------------------------------------------------------------------*/
625 /* Horizontal/Vertical Search Functions */
626 
627 int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y)
628 {
629  int end = x - SMAA_MAX_SEARCH_STEPS;
630  float e[4];
631 
632  while (x > end) {
633  sample(m_imageReader, x, y, e);
634  if (e[1] == 0.0f) { /* Is the edge not activated? */
635  break;
636  }
637  if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
638  return x;
639  }
640  sample(m_imageReader, x, y - 1, e);
641  if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
642  return x;
643  }
644  x--;
645  }
646 
647  return x + 1;
648 }
649 
650 int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y)
651 {
652  int end = x + SMAA_MAX_SEARCH_STEPS;
653  float e[4];
654 
655  while (x < end) {
656  x++;
657  sample(m_imageReader, x, y, e);
658  if (e[1] == 0.0f || /* Is the edge not activated? */
659  e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
660  break;
661  }
662  sample(m_imageReader, x, y - 1, e);
663  if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
664  break;
665  }
666  }
667 
668  return x - 1;
669 }
670 
671 int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y)
672 {
673  int end = y - SMAA_MAX_SEARCH_STEPS;
674  float e[4];
675 
676  while (y > end) {
677  sample(m_imageReader, x, y, e);
678  if (e[0] == 0.0f) { /* Is the edge not activated? */
679  break;
680  }
681  if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
682  return y;
683  }
684  sample(m_imageReader, x - 1, y, e);
685  if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
686  return y;
687  }
688  y--;
689  }
690 
691  return y + 1;
692 }
693 
694 int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y)
695 {
696  int end = y + SMAA_MAX_SEARCH_STEPS;
697  float e[4];
698 
699  while (y < end) {
700  y++;
701  sample(m_imageReader, x, y, e);
702  if (e[0] == 0.0f || /* Is the edge not activated? */
703  e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
704  break;
705  }
706  sample(m_imageReader, x - 1, y, e);
707  if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
708  break;
709  }
710  }
711 
712  return y - 1;
713 }
714 
715 /*-----------------------------------------------------------------------------*/
716 /* Corner Detection Functions */
717 
718 void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern(
719  float weights[2], int left, int right, int y, int d1, int d2)
720 {
721  float factor[2] = {1.0f, 1.0f};
722  float rounding = m_corner_rounding / 100.0f;
723  float e[4];
724 
725  /* Reduce blending for pixels in the center of a line. */
726  rounding *= (d1 == d2) ? 0.5f : 1.0f;
727 
728  /* Near the left corner */
729  if (d1 <= d2) {
730  sample(m_imageReader, left, y + 1, e);
731  factor[0] -= rounding * e[0];
732  sample(m_imageReader, left, y - 2, e);
733  factor[1] -= rounding * e[0];
734  }
735  /* Near the right corner */
736  if (d1 >= d2) {
737  sample(m_imageReader, right + 1, y + 1, e);
738  factor[0] -= rounding * e[0];
739  sample(m_imageReader, right + 1, y - 2, e);
740  factor[1] -= rounding * e[0];
741  }
742 
743  weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f);
744  weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f);
745 }
746 
747 void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern(
748  float weights[2], int x, int top, int bottom, int d1, int d2)
749 {
750  float factor[2] = {1.0f, 1.0f};
751  float rounding = m_corner_rounding / 100.0f;
752  float e[4];
753 
754  /* Reduce blending for pixels in the center of a line. */
755  rounding *= (d1 == d2) ? 0.5f : 1.0f;
756 
757  /* Near the top corner */
758  if (d1 <= d2) {
759  sample(m_imageReader, x + 1, top, e);
760  factor[0] -= rounding * e[1];
761  sample(m_imageReader, x - 2, top, e);
762  factor[1] -= rounding * e[1];
763  }
764  /* Near the bottom corner */
765  if (d1 >= d2) {
766  sample(m_imageReader, x + 1, bottom + 1, e);
767  factor[0] -= rounding * e[1];
768  sample(m_imageReader, x - 2, bottom + 1, e);
769  factor[1] -= rounding * e[1];
770  }
771 
772  weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f);
773  weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f);
774 }
775 
776 /*-----------------------------------------------------------------------------*/
777 /* Neighborhood Blending (Third Pass) */
778 /*-----------------------------------------------------------------------------*/
779 
781 {
782  this->addInputSocket(DataType::Color); /* image */
783  this->addInputSocket(DataType::Color); /* blend */
785  this->flags.complex = true;
786  this->m_image1Reader = nullptr;
787  this->m_image2Reader = nullptr;
788 }
789 
791 {
792  return getInputOperation(0)->initializeTileData(rect);
793 }
794 
796 {
797  this->m_image1Reader = this->getInputSocketReader(0);
798  this->m_image2Reader = this->getInputSocketReader(1);
799 }
800 
802  int x,
803  int y,
804  void * /*data*/)
805 {
806  float w[4];
807 
808  /* Fetch the blending weights for current pixel: */
809  sample(m_image2Reader, x, y, w);
810  float left = w[2], top = w[0];
811  sample(m_image2Reader, x + 1, y, w);
812  float right = w[3];
813  sample(m_image2Reader, x, y + 1, w);
814  float bottom = w[1];
815 
816  /* Is there any blending weight with a value greater than 0.0? */
817  if (right + bottom + left + top < 1e-5f) {
818  sample(m_image1Reader, x, y, output);
819  return;
820  }
821 
822  /* Calculate the blending offsets: */
823  void (*samplefunc)(SocketReader * reader, int x, int y, float xoffset, float color[4]);
824  float offset1, offset2, weight1, weight2, color1[4], color2[4];
825 
826  if (fmaxf(right, left) > fmaxf(bottom, top)) { /* max(horizontal) > max(vertical) */
827  samplefunc = sample_bilinear_horizontal;
828  offset1 = right;
829  offset2 = -left;
830  weight1 = right / (right + left);
831  weight2 = left / (right + left);
832  }
833  else {
834  samplefunc = sample_bilinear_vertical;
835  offset1 = bottom;
836  offset2 = -top;
837  weight1 = bottom / (bottom + top);
838  weight2 = top / (bottom + top);
839  }
840 
841  /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */
842  samplefunc(m_image1Reader, x, y, offset1, color1);
843  samplefunc(m_image1Reader, x, y, offset2, color2);
844 
845  mul_v4_v4fl(output, color1, weight1);
846  madd_v4_v4fl(output, color2, weight2);
847 }
848 
850 {
851  this->m_image1Reader = nullptr;
852  this->m_image2Reader = nullptr;
853 }
854 
856  rcti *input, ReadBufferOperation *readOperation, rcti *output)
857 {
858  rcti newInput;
859 
860  newInput.xmax = input->xmax + 1;
861  newInput.xmin = input->xmin - 1;
862  newInput.ymax = input->ymax + 1;
863  newInput.ymin = input->ymin - 1;
864 
865  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
866 }
867 
868 } // namespace blender::compositor
typedef float(TangentPoint)[2]
MINLINE float interpf(float a, float b, float t)
MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void zero_v4(float r[4])
MINLINE void zero_v2(float r[2])
MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v4_v4fl(float r[4], const float a[4], float f)
#define CLAMPIS(a, b, c)
#define SMAA_AREATEX_MAX_DISTANCE
#define SMAA_AREATEX_MAX_DISTANCE_DIAG
#define SMAA_AREATEX_SIZE
#define SMAA_MAX_SEARCH_STEPS
#define SMAA_MAX_SEARCH_STEPS_DIAG
_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 right
_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 top
_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 bottom
#define X
Definition: GeomUtils.cpp:213
#define Y
Definition: GeomUtils.cpp:214
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
#define output
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
NodeOperation contains calculation logic.
virtual void * initializeTileData(rcti *)
void addInputSocket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
NodeOperation * getInputOperation(unsigned int inputSocketindex)
void read(float result[4], int x, int y, void *chunkData)
void addOutputSocket(DataType datatype)
SocketReader * getInputSocketReader(unsigned int inputSocketindex)
virtual bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
void executePixel(float output[4], int x, int y, void *data) override
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
virtual void executePixel(float output[4], int x, int y, void *data) override
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
void executePixel(float output[4], int x, int y, void *data) override
#define fmaxf(x, y)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
MINLINE float scalenorm(float a, float b, float x)
static int left
#define L
static unsigned c
Definition: RandGen.cpp:97
static void area(int d1, int d2, int e1, int e2, float weights[2])
static const float * areatex_sample_internal(const float *areatex, int x, int y)
static void sample_bilinear_horizontal(SocketReader *reader, int x, int y, float xoffset, float color[4])
static void sample(SocketReader *reader, int x, int y, float color[4])
static void sample_bilinear_vertical(SocketReader *reader, int x, int y, float yoffset, float color[4])
static void area_diag(int d1, int d2, int e1, int e2, float weights[2])
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