Skip to content

Commit

Permalink
✨ add color curve filter
Browse files Browse the repository at this point in the history
  • Loading branch information
ourfor committed Jul 9, 2023
1 parent c1ba974 commit f3b36ea
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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>;
Expand Down
2 changes: 2 additions & 0 deletions src/_FullInternals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -69,6 +70,7 @@ export const Konva = Core.Util._assign(Core, {
Blur,
Brighten,
Contrast,
ColorCurve,
Emboss,
Enhance,
Grayscale,
Expand Down
83 changes: 83 additions & 0 deletions src/filters/ColorCurve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
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
* @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. The value in color curve is a number between 0 and 255.
* Use with {@link Konva.Filters.ColorCurve} filter.
* @name Konva.Node#colorCurve
* @method
* @param {ColorCurveType} colorCurve an object contains red、green and blue, each value between 0 and 255
* @returns {ColorCurveType}
*/
Factory.addGetterSetter(
Node,
'colorCurve',
DEFAULT_COLOR_CURVE,
null,
Factory.afterSetFilter
);
2 changes: 2 additions & 0 deletions src/index-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions test/manual-tests.html
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
100 changes: 100 additions & 0 deletions test/manual/ColorCurve-test.ts
Original file line number Diff line number Diff line change
@@ -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();
});
});
});

0 comments on commit f3b36ea

Please sign in to comment.