add color curve filter

This commit is contained in:
ourfor 2023-07-07 22:40:28 +08:00
parent 09fd0de63d
commit cc762df338
6 changed files with 194 additions and 0 deletions

View File

@ -14,6 +14,7 @@ import { Stage } from './Stage';
import { Context } from './Context';
import { Shape } from './Shape';
import { Layer } from './Layer';
import { ColorCurveType } from './filters/ColorCurve';
export type Filter = (this: Node, imageData: ImageData) => void;
@ -2576,6 +2577,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
blue: GetSet<number, this>;
brightness: GetSet<number, this>;
contrast: GetSet<number, this>;
colorCurve: GetSet<ColorCurveType, this>;
blurRadius: GetSet<number, this>;
luminance: GetSet<number, this>;
green: GetSet<number, this>;

View File

@ -25,6 +25,7 @@ import { Wedge } from './shapes/Wedge';
import { Blur } from './filters/Blur';
import { Brighten } from './filters/Brighten';
import { Contrast } from './filters/Contrast';
import { ColorCurve } from './filters/ColorCurve';
import { Emboss } from './filters/Emboss';
import { Enhance } from './filters/Enhance';
import { Grayscale } from './filters/Grayscale';
@ -69,6 +70,7 @@ export const Konva = Core.Util._assign(Core, {
Blur,
Brighten,
Contrast,
ColorCurve,
Emboss,
Enhance,
Grayscale,

87
src/filters/ColorCurve.ts Normal file
View File

@ -0,0 +1,87 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
export interface ColorCurveType {
red?: number|number[]
green?: number|number[]
blue?: number|number[]
}
export const RGBA = {
R: 0,
G: 1,
B: 2,
A: 3,
size: 4
}
export const DEFAULT_COLOR_CURVE = {
red: 1,
green: 1,
blue: 1
}
function applyToChannel(imageData: ImageData, channel: number, value?: number|number[]) {
if (!value) return
const data = imageData.data
const length = data.length
if (typeof value === "number") {
if (value < 0) return
for (let i = 0; i < length; i += RGBA.size) {
const idx = i + channel
data[idx] = data[idx] * value
if (data[idx] > 255) data[idx] %= 256
}
} else {
if (value.length !== 256) return
for (let i = 0; i < length; i += RGBA.size) {
const idx = i + channel
data[idx] = value[data[idx] % 256]
}
}
}
/**
* ColorCurve Filter.
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* @author ourfor
* @example
* node.cache();
* node.filters([Konva.Filters.ColorCurve]);
* // number: color -> color * 0.8
* node.colorCurve({red: 0.8});
* // number[]: color -> map[color]
* const blue = []
* for (let i=0; i < 256; i++) blue.push(i <= 128 ? (-1/64) * i * i + 3 * i : (1/64)*i*i - 5*i + 512)
* node.colorCurve({blue})
*/
export const ColorCurve: Filter = function (imageData: ImageData) {
const curve = this.colorCurve()
if (!curve) return
applyToChannel(imageData, RGBA.R, curve.red)
applyToChannel(imageData, RGBA.G, curve.green)
applyToChannel(imageData, RGBA.B, curve.blue)
}
/**
* get/set filter color curve.
* Use with {@link Konva.Filters.ColorCurve} filter.
* @name Konva.Node#colorCurve
* @method
* @param {ColorCurveType} colorCurve an object contains redgreen and blue
* @param {Number|Array<Integer>} colorCurve.red a number from 0 to 255 or an array of 256 color values, applied to the red channel
* @param {Number|Array<Integer>} colorCurve.green a number from 0 to 255 or an array of 256 color values, applied to the green channel
* @param {Number|Array<Integer>} colorCurve.blue a number from 0 to 255 or an array of 256 color values, applied to the blue channel
* @returns {ColorCurveType}
*/
Factory.addGetterSetter(
Node,
'colorCurve',
DEFAULT_COLOR_CURVE,
null,
Factory.afterSetFilter
);

View File

@ -10,6 +10,7 @@
import { Blur } from './filters/Blur';
import { Brighten } from './filters/Brighten';
import { Contrast } from './filters/Contrast';
import { ColorCurve } from './filters/ColorCurve'
import { Emboss } from './filters/Emboss';
import { Enhance } from './filters/Enhance';
import { Grayscale } from './filters/Grayscale';
@ -159,6 +160,7 @@ declare namespace Konva {
Blur: typeof Blur;
Brighten: typeof Brighten;
Contrast: typeof Contrast;
ColorCurve: typeof ColorCurve;
Emboss: typeof Emboss;
Enhance: typeof Enhance;
Grayscale: typeof Grayscale;

View File

@ -39,6 +39,7 @@
import './manual/Posterize-test.ts';
import './manual/Sepia-test.ts';
import './manual/Contrast-test.ts';
import './manual/ColorCurve-test.ts';
import './manual/Emboss-test.ts';
import './manual/Solarize-test.ts';
import './manual/Kaleidoscope-test.ts';

View File

@ -0,0 +1,100 @@
import { assert } from 'chai';
import { addStage, Konva, loadImage } from '../unit/test-utils';
describe('Filter ColorCurve', function () {
// ======================================================
it('basic', function (done) {
var stage = addStage();
loadImage('darth-vader.jpg', (imageObj) => {
var layer = new Konva.Layer();
var darth = new Konva.Image({
x: 10,
y: 10,
image: imageObj,
draggable: true,
});
layer.add(darth);
stage.add(layer);
darth.cache();
darth.filters([Konva.Filters.ColorCurve]);
darth.colorCurve({red: 0.8});
layer.draw();
assert.equal(darth.colorCurve().red, 0.8);
done();
});
});
// ======================================================
it('tween', function (done) {
var stage = addStage();
loadImage('darth-vader.jpg', (imageObj) => {
var layer = new Konva.Layer();
var darth = new Konva.Image({
x: 10,
y: 10,
image: imageObj,
draggable: true,
});
layer.add(darth);
stage.add(layer);
darth.cache();
darth.filters([Konva.Filters.ColorCurve]);
darth.colorCurve({red: 0.8});
layer.draw();
var tween = new Konva.Tween({
node: darth,
duration: 2.0,
contrast: 0,
easing: Konva.Easings.EaseInOut,
});
darth.on('mouseover', function () {
tween.play();
});
darth.on('mouseout', function () {
tween.reverse();
});
done();
});
});
// ======================================================
it('crop', function (done) {
var stage = addStage();
loadImage('darth-vader.jpg', (imageObj) => {
var layer = new Konva.Layer();
var darth = new Konva.Image({
x: 10,
y: 10,
image: imageObj,
crop: { x: 128, y: 48, width: 256, height: 128 },
draggable: true,
});
layer.add(darth);
stage.add(layer);
darth.cache();
darth.filters([Konva.Filters.ColorCurve]);
darth.colorCurve({red: 0.8});
layer.draw();
assert.equal(darth.colorCurve().red, 0.8);
done();
});
});
});