run method

  1. @override
Vector4? run(
  1. Float32List v,
  2. ShaderBindings bindings,
  3. FragmentContext c
)
override

Returns linear RGBA for attachment zero. Null discards the fragment.

Implementation

@override
Vector4? run(Float32List v, ShaderBindings bindings, FragmentContext c) {
  final source = bindings.textures['source_texture'];
  if (source == null) return Vector4(0.0, 0.0, 0.0, 1.0);
  final params = bindings.vec4(
    'FxaaInfo',
    'params',
    Vector4(0.0, 0.0, 0.125, 0.75),
  );

  final middle = source.sample(v[0], v[1]);
  final mid = _weight(middle);

  // Colours kept rather than only their weights: `Sharpen` below needs the
  // neighbourhood, and these are the same four taps either way.
  final northRgb = source.sample(v[0], v[1] - params.y);
  final southRgb = source.sample(v[0], v[1] + params.y);
  final westRgb = source.sample(v[0] - params.x, v[1]);
  final eastRgb = source.sample(v[0] + params.x, v[1]);
  final north = _weight(northRgb);
  final south = _weight(southRgb);
  final west = _weight(westRgb);
  final east = _weight(eastRgb);
  final sharpening = bindings.vec4('FxaaInfo', 'sharpen', Vector4.zero());
  final sharpen = sharpening.x;
  final robust = sharpening.y > 0.5;

  final lowest = math.min(
    mid,
    math.min(math.min(north, south), math.min(west, east)),
  );
  final highest = math.max(
    mid,
    math.max(math.max(north, south), math.max(west, east)),
  );
  final contrast = highest - lowest;
  if (contrast < math.max(0.0312, highest * params.z)) {
    return _sharpen(
      middle,
      northRgb,
      southRgb,
      westRgb,
      eastRgb,
      sharpen,
      robust: robust,
    );
  }

  // FXAA 3.11 Quality, step for step; see the GLSL.
  double weightAt(double x, double y) => _weight(source.sample(x, y));
  final northWest = weightAt(v[0] - params.x, v[1] - params.y);
  final southEast = weightAt(v[0] + params.x, v[1] + params.y);
  final northEast = weightAt(v[0] + params.x, v[1] - params.y);
  final southWest = weightAt(v[0] - params.x, v[1] + params.y);

  final edgeHorizontal =
      (northWest + southWest - 2.0 * west).abs() +
      2.0 * (north + south - 2.0 * mid).abs() +
      (northEast + southEast - 2.0 * east).abs();
  final edgeVertical =
      (northWest + northEast - 2.0 * north).abs() +
      2.0 * (west + east - 2.0 * mid).abs() +
      (southWest + southEast - 2.0 * south).abs();
  final horizontalSpan = edgeHorizontal >= edgeVertical;

  final lowPass =
      (2.0 * (north + south + west + east) +
          northWest +
          northEast +
          southWest +
          southEast) /
      12.0;
  final subpixC = ((lowPass - mid).abs() / contrast).clamp(0.0, 1.0);
  final subpixF = (3.0 - 2.0 * subpixC) * subpixC * subpixC;
  final subpixH = subpixF * subpixF * params.w;

  final lumaN = horizontalSpan ? north : west;
  final lumaS = horizontalSpan ? south : east;
  final gradientN = lumaN - mid;
  final gradientS = lumaS - mid;
  final pairN = gradientN.abs() >= gradientS.abs();
  final gradient = math.max(gradientN.abs(), gradientS.abs());
  final texelAcross = horizontalSpan ? params.y : params.x;
  final lengthSign = pairN ? -texelAcross : texelAcross;
  final pairAverage = 0.5 * (pairN ? lumaN + mid : lumaS + mid);

  // The edge search, along x for a horizontal span and y for a vertical.
  final alongX = horizontalSpan ? params.x : 0.0;
  final alongY = horizontalSpan ? 0.0 : params.y;
  final startX = v[0] + (horizontalSpan ? 0.0 : lengthSign * 0.5);
  final startY = v[1] + (horizontalSpan ? lengthSign * 0.5 : 0.0);
  final gradientScaled = gradient * 0.25;
  var posNX = startX - alongX;
  var posNY = startY - alongY;
  var posPX = startX + alongX;
  var posPY = startY + alongY;
  var endN = weightAt(posNX, posNY) - pairAverage;
  var endP = weightAt(posPX, posPY) - pairAverage;
  var doneN = endN.abs() >= gradientScaled;
  var doneP = endP.abs() >= gradientScaled;
  for (var i = 1; i < _searchSteps.length; i++) {
    if (doneN && doneP) break;
    final stride = _searchSteps[i];
    if (!doneN) {
      posNX -= alongX * stride;
      posNY -= alongY * stride;
      endN = weightAt(posNX, posNY) - pairAverage;
      doneN = endN.abs() >= gradientScaled;
    }
    if (!doneP) {
      posPX += alongX * stride;
      posPY += alongY * stride;
      endP = weightAt(posPX, posPY) - pairAverage;
      doneP = endP.abs() >= gradientScaled;
    }
  }

  final distanceN = horizontalSpan ? v[0] - posNX : v[1] - posNY;
  final distanceP = horizontalSpan ? posPX - v[0] : posPY - v[1];
  final middleBelow = mid - pairAverage < 0.0;
  final nearerN = distanceN < distanceP;
  final goodSpan = nearerN
      ? (endN < 0.0) != middleBelow
      : (endP < 0.0) != middleBelow;
  final nearest = math.min(distanceN, distanceP);
  final pixelOffset = 0.5 - nearest / (distanceN + distanceP);
  final offset = math.max(goodSpan ? pixelOffset : 0.0, subpixH);

  final out = horizontalSpan
      ? source.sample(v[0], v[1] + offset * lengthSign)
      : source.sample(v[0] + offset * lengthSign, v[1]);
  return _sharpen(
    out,
    northRgb,
    southRgb,
    westRgb,
    eastRgb,
    sharpen,
    robust: robust,
  );
}