mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 06:31:15 +08:00
✨ add color curve filter
This commit is contained in:
parent
09fd0de63d
commit
cc762df338
@ -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>;
|
||||
|
@ -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
87
src/filters/ColorCurve.ts
Normal 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 red、green 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
|
||||
);
|
2
src/index-types.d.ts
vendored
2
src/index-types.d.ts
vendored
@ -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;
|
||||
|
@ -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';
|
||||
|
100
test/manual/ColorCurve-test.ts
Normal file
100
test/manual/ColorCurve-test.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user