blob: dc6de6df76348546bc6084bec0df0fb1ba222499 [file] [log] [blame]
import 'dart:math' as Math;
import 'dart:typed_data';
import '../color.dart';
import '../image.dart';
import '../internal/clamp.dart';
/**
* Adjust the color of the [src] image using various color transformations.
*
* [blacks] defines the black level of the image, as a color.
*
* [whites] defines the white level of the image, as a color.
*
* [mids] defines the mid level of hte image, as a color.
*
* [contrast] increases (> 1) / decreases (< 1) the contrast of the image by
* pushing colors away/toward neutral gray, where at 0.0 the image is entirely
* neutral gray (0 contrast), 1.0, the image is not adjusted and > 1.0 the
* image increases contrast.
*
* [saturation] increases (> 1) / decreases (< 1) the saturation of the image
* by pushing colors away/toward their grayscale value, where 0.0 is grayscale
* and 1.0 is the original image, and > 1.0 the image becomes more saturated.
*
* [brightness] is a constant scalar of the image colors. At 0 the image
* is black, 1.0 unmodified, and > 1.0 the image becomes brighter.
*
* [gamma] is an exponential scalar of the image colors. At < 1.0 the image
* becomes brighter, and > 1.0 the image becomes darker. A [gamma] of 1/2.2
* will convert the image colors to linear color space.
*
* [exposure] is an exponential scalar of the image as rgb * pow(2, exposure).
* At 0, the image is unmodified; as the exposure increases, the image
* brightens.
*
* [hue] shifts the hue component of the image colors in degrees. A [hue] of
* 0 will have no affect, and a [hue] of 45 will shift the hue of all colors
* by 45 degrees.
*
* [amount] controls how much affect this filter has on the [src] image, where
* 0.0 has no effect and 1.0 has full effect.
*/
Image adjustColor(Image src, {int blacks, int whites, int mids,
double contrast, double saturation, double brightness,
double gamma, double exposure, double hue,
double amount}) {
if (amount == 0.0) {
return src;
}
const double DEG_TO_RAD = 0.0174532925;
const double avgLumR = 0.5;
const double avgLumG = 0.5;
const double avgLumB = 0.5;
const double lumCoeffR = 0.2125;
const double lumCoeffG = 0.7154;
const double lumCoeffB = 0.0721;
double br, bg, bb;
double wr, wg, wb;
double mr, mg, mb;
if (blacks != null || whites != null || mids != null) {
br = blacks != null ? getRed(blacks) / 255.0 : 0.0;
bg = blacks != null ? getGreen(blacks) / 255.0 : 0.0;
bb = blacks != null ? getBlue(blacks) / 255.0 : 0.0;
wr = whites != null ? getRed(whites) / 255.0 : 1.0;
wg = whites != null ? getGreen(whites) / 255.0 : 1.0;
wb = whites != null ? getBlue(whites) / 255.0 : 1.0;
mr = mids != null ? getRed(mids) / 255.0 : 0.5;
mg = mids != null ? getGreen(mids) / 255.0 : 0.5;
mb = mids != null ? getBlue(mids) / 255.0 : 0.5;
mr = 1.0 / (1.0 + 2.0 * (mr - 0.5));
mg = 1.0 / (1.0 + 2.0 * (mg - 0.5));
mb = 1.0 / (1.0 + 2.0 * (mb - 0.5));
}
double invSaturation = saturation != null ? 1.0 - saturation : 0.0;
double invContrast = contrast != null ? 1.0 - contrast : 0.0;
if (exposure != null) {
exposure = Math.pow(2.0, exposure);
}
double hueR;
double hueG;
double hueB;
if (hue != null) {
hue *= DEG_TO_RAD;
double s = Math.sin(hue);
double c = Math.cos(hue);
hueR = (2.0 * c) / 3.0;
hueG = (-Math.sqrt(3.0) * s - c) / 3.0;
hueB = ((Math.sqrt(3.0) * s - c) + 1.0) / 3.0;
}
double invAmount = amount != null ? 1.0 - amount : 0.0;
Uint8List pixels = src.getBytes();
for (int i = 0, len = pixels.length; i < len; i += 4) {
double or = pixels[i] / 255.0;
double og = pixels[i + 1] / 255.0;
double ob = pixels[i + 2] / 255.0;
double r = or;
double g = og;
double b = ob;
if (br != null) {
r = Math.pow((r + br) * wr, mr);
g = Math.pow((g + bg) * wg, mg);
b = Math.pow((b + bb) * wb, mb);
}
if (brightness != null && brightness != 1.0) {
r *= brightness;
g *= brightness;
b *= brightness;
}
if (saturation != null) {
double lum = r * lumCoeffR + g * lumCoeffG + b * lumCoeffB;
r = lum * invSaturation + r * saturation;
g = lum * invSaturation + g * saturation;
b = lum * invSaturation + b * saturation;
}
if (contrast != null) {
r = avgLumR * invContrast + r * contrast;
g = avgLumG * invContrast + g * contrast;
b = avgLumB * invContrast + b * contrast;
}
if (gamma != null) {
r = Math.pow(r, gamma);
g = Math.pow(g, gamma);
b = Math.pow(b, gamma);
}
if (exposure != null) {
r = r * exposure;
g = g * exposure;
b = b * exposure;
}
if (hue != null && hue != 0.0) {
double hr = r * hueR + g * hueG + b * hueB;
double hg = r * hueB + g * hueR + b * hueG;
double hb = r * hueG + g * hueB + b * hueR;
r = hr;
g = hg;
b = hb;
}
if (amount != null) {
r = r * amount + or * invAmount;
g = g * amount + og * invAmount;
b = b * amount + ob * invAmount;
}
pixels[i] = clamp255((r * 255.0).toInt());
pixels[i + 1] = clamp255((g * 255.0).toInt());
pixels[i + 2] = clamp255((b * 255.0).toInt());
}
return src;
}