Created
January 14, 2019 17:44
-
-
Save long1eu/5187b17f039fa3dae484640448771009 to your computer and use it in GitHub Desktop.
ColorMatrix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math' as math; | |
import 'package:collection/collection.dart'; | |
/// 4x5 matrix for transforming the color and alpha components of a Bitmap. | |
/// The matrix can be passed as single array, and is treated as follows: | |
/// | |
/// <pre> | |
/// [ a, b, c, d, e, | |
/// f, g, h, i, j, | |
/// k, l, m, n, o, | |
/// p, q, r, s, t ]</pre> | |
/// | |
/// <p> | |
/// When applied to a color <code>[R, G, B, A]</code>, the resulting color | |
/// is computed as: | |
/// </p> | |
/// | |
/// <pre> | |
/// R’ = a*R + b*G + c*B + d*A + e; | |
/// G’ = f*R + g*G + h*B + i*A + j; | |
/// B’ = k*R + l*G + m*B + n*A + o; | |
/// A’ = p*R + q*G + r*B + s*A + t;</pre> | |
/// | |
/// <p> | |
/// That resulting color <code>[R’, G’, B’, A’]</code> | |
/// then has each channel clamped to the <code>0</code> to <code>255</code> | |
/// range. | |
/// </p> | |
/// | |
/// <p> | |
/// The sample ColorMatrix below inverts incoming colors by scaling each | |
/// channel by <code>-1</code>, and then shifting the result up by | |
/// <code>255</code> to remain in the standard color space. | |
/// </p> | |
/// | |
/// <pre> | |
/// [ -1, 0, 0, 0, 255, | |
/// 0, -1, 0, 0, 255, | |
/// 0, 0, -1, 0, 255, | |
/// 0, 0, 0, 1, 0 ]</pre> | |
class ColorMatrix { | |
ColorMatrix([List<double> src]) : _list = src != null ? src : List<double>(20); | |
final List<double> _list; | |
List<double> get data => _list.toList(growable: false); | |
/// Set this colormatrix to identity: | |
/// <pre> | |
/// [ 1 0 0 0 0 - red vector | |
/// 0 1 0 0 0 - green vector | |
/// 0 0 1 0 0 - blue vector | |
/// 0 0 0 1 0 ] - alpha vector | |
/// </pre> | |
void reset() { | |
for (int i = 0; i < _list.length; i++) { | |
_list[i] = 0; | |
} | |
_list[0] = _list[6] = _list[12] = _list[18] = 1; | |
} | |
/// Assign the array of doubles into this matrix, copying all of its values. | |
void set(List<double> src) { | |
for (int i = 0; i < _list.length; i++) { | |
_list[i] = src[i]; | |
} | |
} | |
/// Set this colormatrix to scale by the specified values. | |
void setScale(double rScale, double gScale, double bScale, double aScale) { | |
final List<double> a = _list; | |
for (int i = 19; i > 0; --i) { | |
a[i] = 0; | |
} | |
a[0] = rScale; | |
a[6] = gScale; | |
a[12] = bScale; | |
a[18] = aScale; | |
} | |
/// Set the rotation on a color axis by the specified values. | |
/// <p> | |
/// <code>axis=0</code> correspond to a rotation around the RED color | |
/// <code>axis=1</code> correspond to a rotation around the GREEN color | |
/// <code>axis=2</code> correspond to a rotation around the BLUE color | |
/// </p> | |
void setRotate(int axis, double degrees) { | |
reset(); | |
final double radians = degrees * math.pi / 180; | |
final double cosine = math.cos(radians); | |
final double sine = math.sin(radians); | |
switch (axis) { | |
// Rotation around the red color | |
case 0: | |
_list[6] = _list[12] = cosine; | |
_list[7] = sine; | |
_list[11] = -sine; | |
break; | |
// Rotation around the green color | |
case 1: | |
_list[0] = _list[12] = cosine; | |
_list[2] = -sine; | |
_list[10] = sine; | |
break; | |
// Rotation around the blue color | |
case 2: | |
_list[0] = _list[6] = cosine; | |
_list[1] = sine; | |
_list[5] = -sine; | |
break; | |
default: | |
throw StateError(''); | |
} | |
} | |
/// Set this colormatrix to the concatenation of the two specified | |
/// colormatrices, such that the resulting colormatrix has the same effect | |
/// as applying matB and then applying matA. | |
/// <p> | |
/// It is legal for either matA or matB to be the same colormatrix as this. | |
/// </p> | |
void setConcat(ColorMatrix matA, ColorMatrix matB) { | |
List<double> tmp; | |
if (matA == this || matB == this) { | |
tmp = List<double>(20); | |
} else { | |
tmp = _list; | |
} | |
final List<double> a = matA._list; | |
final List<double> b = matB._list; | |
int index = 0; | |
for (int j = 0; j < 20; j += 5) { | |
for (int i = 0; i < 4; i++) { | |
tmp[index++] = a[j + 0] * b[i + 0] + a[j + 1] * b[i + 5] + a[j + 2] * b[i + 10] + a[j + 3] * b[i + 15]; | |
} | |
tmp[index++] = a[j + 0] * b[4] + a[j + 1] * b[9] + a[j + 2] * b[14] + a[j + 3] * b[19] + a[j + 4]; | |
} | |
if (tmp != _list) { | |
set(tmp); | |
} | |
} | |
/// Concat this colormatrix with the specified prematrix. | |
/// <p> | |
/// This is logically the same as calling setConcat(this, prematrix); | |
/// </p> | |
void preConcat(ColorMatrix prematrix) { | |
setConcat(this, prematrix); | |
} | |
/// Concat this colormatrix with the specified postmatrix. | |
/// <p> | |
/// This is logically the same as calling setConcat(postmatrix, this); | |
/// </p> | |
void postConcat(ColorMatrix postmatrix) { | |
setConcat(postmatrix, this); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
/// Set the matrix to affect the saturation of colors. | |
/// @param sat A value of 0 maps the color to gray-scale. 1 is identity. | |
void setSaturation(double sat) { | |
reset(); | |
final List<double> m = _list; | |
final double invSat = 1 - sat; | |
final double R = 0.213 * invSat; | |
final double G = 0.715 * invSat; | |
final double B = 0.072 * invSat; | |
m[0] = R + sat; | |
m[1] = G; | |
m[2] = B; | |
m[5] = R; | |
m[6] = G + sat; | |
m[7] = B; | |
m[10] = R; | |
m[11] = G; | |
m[12] = B + sat; | |
} | |
/// Set the matrix to convert RGB to YUV | |
void setRGB2YUV() { | |
reset(); | |
final List<double> m = _list; | |
// these coefficients match those in libjpeg | |
m[0] = 0.299; | |
m[1] = 0.587; | |
m[2] = 0.114; | |
m[5] = -0.16874; | |
m[6] = -0.33126; | |
m[7] = 0.5; | |
m[10] = 0.5; | |
m[11] = -0.41869; | |
m[12] = -0.08131; | |
} | |
/// Set the matrix to convert from YUV to RGB | |
void setYUV2RGB() { | |
reset(); | |
final List<double> m = _list; | |
// these coefficients match those in libjpeg | |
m[2] = 1.402; | |
m[5] = 1; | |
m[6] = -0.34414; | |
m[7] = -0.71414; | |
m[10] = 1; | |
m[11] = 1.772; | |
m[12] = 0; | |
} | |
@override | |
bool operator ==(Object other) => | |
identical(this, other) || | |
other is ColorMatrix && | |
runtimeType == other.runtimeType && | |
const ListEquality<double>().equals(_list, other._list); | |
@override | |
int get hashCode => const ListEquality<double>().hash(_list); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment