Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implement normalized gradient
  • Loading branch information
ferhatb committed Oct 13, 2020
commit 876275374333e53bf9344dfe806d45df43461a95
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/recording_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/render_vertices.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/scene.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart
Expand Down
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ part 'engine/html/recording_canvas.dart';
part 'engine/html/render_vertices.dart';
part 'engine/html/scene.dart';
part 'engine/html/scene_builder.dart';
part 'engine/html/shaders/normalized_gradient.dart';
part 'engine/html/shaders/shader.dart';
part 'engine/html/shaders/shader_builder.dart';
part 'engine/html/surface.dart';
Expand Down
120 changes: 120 additions & 0 deletions lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.10
part of engine;

/// Converts colors and stops to typed array of bias, scale and threshold to use
/// in shaders.
///
/// A color is generated by taking a t value [0..1] and computing
/// t * scale + bias.
///
/// Example: For stops 0.0 t1, t2, 1.0 and colors c0, c1, c2, c3
/// Given t1<t<t2 outputColor = t * scale + bias.
/// = c1 + (t - t1)/(t2 - t1) * (c2 - c1)
/// = t * (c2 - c1)/(t2 - t1) + c1 - t1/(t2 - t1) * (c2 - c1)
/// scale = (c2 - c1) / (t2 - t1)
/// bias = c1 - t1 / (t2 - t1) * (c2 - c1)
class NormalizedGradient {
NormalizedGradient._(this.thresholdCount, this._thresholds, this._scale,
this._bias);

final Float32List _thresholds;
final Float32List _bias;
final Float32List _scale;
final int thresholdCount;

factory NormalizedGradient(List<ui.Color> colors, {List<double>? stops}) {
// If colorStops is not provided, then only two stops, at 0.0 and 1.0,
// are implied (and colors must therefore only have two entries).
assert(stops != null || colors.length == 2);
stops ??= const <double>[0.0, 1.0];
final int colorCount = colors.length;
int normalizedCount = colorCount;
bool addFirst = stops[0] != 0.0;
bool addLast = stops.last != 1.0;
if (addFirst) {
normalizedCount++;
}
if (addLast) {
normalizedCount++;
}
final Float32List bias = Float32List(normalizedCount * 4);
final Float32List scale = Float32List(normalizedCount * 4);
final Float32List thresholds = Float32List(4 * ((normalizedCount - 1)~/4 + 1));
int targetIndex = 0;
int thresholdIndex = 0;
if (addFirst) {
ui.Color c = colors[0];
bias[targetIndex++] = c.red / 255.0;
bias[targetIndex++] = c.green / 255.0;
bias[targetIndex++] = c.blue / 255.0;
bias[targetIndex++] = c.alpha / 255.0;
thresholds[thresholdIndex++] = 0.0;
}
for (ui.Color c in colors) {
bias[targetIndex++] = c.red / 255.0;
bias[targetIndex++] = c.green / 255.0;
bias[targetIndex++] = c.blue / 255.0;
bias[targetIndex++] = c.alpha / 255.0;
}
for (double stop in stops) {
thresholds[thresholdIndex++] = stop;
}
if (addLast) {
ui.Color c = colors.last;
bias[targetIndex++] = c.red / 255.0;
bias[targetIndex++] = c.green / 255.0;
bias[targetIndex++] = c.blue / 255.0;
bias[targetIndex++] = c.alpha / 255.0;
thresholds[thresholdIndex++] = 1.0;
}
// Now that we have bias for each color stop, we can compute scale based
// on delta between colors.
int lastColorIndex = 4 * (normalizedCount - 1);
for (int i = 0; i < lastColorIndex; i++) {
int thresholdIndex = i >> 2;
scale[i] = (bias[i + 4] - bias[i]) /
(thresholds[thresholdIndex + 1] - thresholds[thresholdIndex]);
}
scale[lastColorIndex] = 0.0;
scale[lastColorIndex + 1] = 0.0;
scale[lastColorIndex + 2] = 0.0;
scale[lastColorIndex + 3] = 0.0;
// Compute bias = colorAtStop - stopValue * (scale).
for (int i = 0; i < normalizedCount; i++) {
double t = thresholds[i];
int colorIndex = i * 4;
bias[colorIndex] -= t * scale[colorIndex];
bias[colorIndex + 1] -= t * scale[colorIndex + 1];
bias[colorIndex + 2] -= t * scale[colorIndex + 2];
bias[colorIndex + 3] -= t * scale[colorIndex + 3];
}
return NormalizedGradient._(normalizedCount, thresholds, scale, bias);
}

/// Sets uniforms for threshold, bias and scale for program.
void setupUniforms(_GlContext gl, _GlProgram glProgram) {
for (int i = 0; i < thresholdCount; i++) {
Object? biasId = gl.getUniformLocation(glProgram.program, 'bias_$i');
gl.setUniform4f(biasId, _bias[i * 4], _bias[i * 4 + 1], _bias[i * 4 + 2], _bias[i * 4 + 3]);
Object? scaleId = gl.getUniformLocation(glProgram.program, 'scale_$i');
gl.setUniform4f(scaleId, _scale[i * 4], _scale[i * 4 + 1], _scale[i * 4 + 2], _scale[i * 4 + 3]);
}
for (int i = 0; i < _thresholds.length; i += 4) {
Object? thresId = gl.getUniformLocation(glProgram.program, 'threshold_${i ~/ 4}');
gl.setUniform4f(thresId, _thresholds[i], _thresholds[i + 1], _thresholds[i + 2], _thresholds[i + 3]);
}
}

/// Returns bias component at index.
double biasAt(int index) => _bias[index];

/// Returns scale component at index.
double scaleAt(int index) => _scale[index];

/// Returns threshold at index.
double thresholdAt(int index) => _thresholds[index];
}
125 changes: 125 additions & 0 deletions lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.6
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui hide window;

void main() {
internalBootstrapBrowserTest(() => testMain);
}

void testMain() {
group('Shader Normalized Gradient', () {
test('3 stop at start', () {
NormalizedGradient gradient = NormalizedGradient(<ui.Color>[
ui.Color(0xFF000000), ui.Color(0xFFFF7f3f)
], stops: <double>[0.0, 0.5]);
int res = _computeColorAt(gradient, 0.0);
assert(res == 0xFF000000);
res = _computeColorAt(gradient, 0.25);
assert(res == 0xFF7f3f1f);
res = _computeColorAt(gradient, 0.5);
assert(res == 0xFFFF7f3f);
res = _computeColorAt(gradient, 0.7);
assert(res == 0xFFFF7f3f);
res = _computeColorAt(gradient, 1.0);
assert(res == 0xFFFF7f3f);
});

test('3 stop at end', () {
NormalizedGradient gradient = NormalizedGradient(<ui.Color>[
ui.Color(0xFF000000), ui.Color(0xFFFF7f3f)
], stops: <double>[0.5, 1.0]);
int res = _computeColorAt(gradient, 0.0);
assert(res == 0xFF000000);
res = _computeColorAt(gradient, 0.25);
assert(res == 0xFF000000);
res = _computeColorAt(gradient, 0.5);
assert(res == 0xFF000000);
res = _computeColorAt(gradient, 0.75);
assert(res == 0xFF7f3f1f);
res = _computeColorAt(gradient, 1.0);
assert(res == 0xFFFF7f3f);
});

test('4 stop', () {
NormalizedGradient gradient = NormalizedGradient(<ui.Color>[
ui.Color(0xFF000000), ui.Color(0xFFFF7f3f)
], stops: <double>[0.25, 0.5]);
int res = _computeColorAt(gradient, 0.0);
assert(res == 0xFF000000);
res = _computeColorAt(gradient, 0.25);
assert(res == 0xFF000000);
res = _computeColorAt(gradient, 0.4);
assert(res == 0xFF994c25);
res = _computeColorAt(gradient, 0.5);
assert(res == 0xFFFF7f3f);
res = _computeColorAt(gradient, 0.75);
assert(res == 0xFFFF7f3f);
res = _computeColorAt(gradient, 1.0);
assert(res == 0xFFFF7f3f);
});

test('5 stop', () {
NormalizedGradient gradient = NormalizedGradient(<ui.Color>[
ui.Color(0x10000000), ui.Color(0x20FF0000),
ui.Color(0x4000FF00), ui.Color(0x800000FF),
ui.Color(0xFFFFFFFF)
], stops: <double>[0.0, 0.1, 0.2, 0.5, 1.0]);
int res = _computeColorAt(gradient, 0.0);
assert(res == 0x10000000);
res = _computeColorAt(gradient, 0.05);
assert(res == 0x187f0000);
res = _computeColorAt(gradient, 0.1);
assert(res == 0x20ff0000);
res = _computeColorAt(gradient, 0.15);
assert(res == 0x307f7f00);
res = _computeColorAt(gradient, 0.2);
assert(res == 0x4000ff00);
res = _computeColorAt(gradient, 0.4);
assert(res == 0x6a0054a9);
res = _computeColorAt(gradient, 0.5);
assert(res == 0x800000fe);
res = _computeColorAt(gradient, 0.9);
assert(res == 0xe5ccccff);
res = _computeColorAt(gradient, 1.0);
assert(res == 0xffffffff);
});

test('2 stops at ends', () {
NormalizedGradient gradient = NormalizedGradient(<ui.Color>[
ui.Color(0x00000000), ui.Color(0xFFFFFFFF)
]);
int res = _computeColorAt(gradient, 0.0);
assert(res == 0);
res = _computeColorAt(gradient, 1.0);
assert(res == 0xFFFFFFFF);
res = _computeColorAt(gradient, 0.5);
assert(res == 0x7f7f7f7f);
});
});
}

int _computeColorAt(NormalizedGradient gradient, double t) {
int i = 0;
while (t > gradient.thresholdAt(i + 1)) {
++i;
}
double r = t * gradient.scaleAt(i * 4) + gradient.biasAt(i * 4);
double g = t * gradient.scaleAt(i * 4 + 1) + gradient.biasAt(i * 4 + 1);
double b = t * gradient.scaleAt(i * 4 + 2) + gradient.biasAt(i * 4 + 2);
double a = t * gradient.scaleAt(i * 4 + 3) + gradient.biasAt(i * 4 + 3);
int val = 0;
val |= (a * 0xFF).toInt() & 0xFF;
val<<=8;
val |= (r * 0xFF).toInt() & 0xFF;
val<<=8;
val |= (g * 0xFF).toInt() & 0xFF;
val<<=8;
val |= (b * 0xFF).toInt() & 0xFF;
return val;
}