UNPKG

konva

Version:

HTML5 2d canvas library for interactive graphics, design editors, whiteboards, and diagrams.

120 lines (119 loc) 5.11 kB
import { Factory } from "../Factory.js"; import { Node } from "../Node.js"; import { getNumberValidator } from "../Validators.js"; /** * Emboss Filter. * @function * @memberof Konva.Filters * @param {Object} imageData * @example * node.cache(); * node.filters([Konva.Filters.Emboss]); * node.embossStrength(0.8); * node.embossWhiteLevel(0.3); * node.embossDirection('right'); * node.embossBlend(true); */ export const Emboss = function (imageData) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const data = imageData.data; const w = imageData.width; const h = imageData.height; // Inputs from Konva node const strength01 = Math.min(1, Math.max(0, (_b = (_a = this.embossStrength) === null || _a === void 0 ? void 0 : _a.call(this)) !== null && _b !== void 0 ? _b : 0.5)); // [0..1] const whiteLevel01 = Math.min(1, Math.max(0, (_d = (_c = this.embossWhiteLevel) === null || _c === void 0 ? void 0 : _c.call(this)) !== null && _d !== void 0 ? _d : 0.5)); // [0..1] // Convert string direction to degrees const directionMap = { 'top-left': 315, top: 270, 'top-right': 225, right: 180, 'bottom-right': 135, bottom: 90, 'bottom-left': 45, left: 0, }; const directionDeg = (_g = directionMap[(_f = (_e = this.embossDirection) === null || _e === void 0 ? void 0 : _e.call(this)) !== null && _f !== void 0 ? _f : 'top-left']) !== null && _g !== void 0 ? _g : 315; // degrees const blend = !!((_j = (_h = this.embossBlend) === null || _h === void 0 ? void 0 : _h.call(this)) !== null && _j !== void 0 ? _j : false); // Internal mapping: // - "strength" was 0..10; we honor your 0..1 API and scale accordingly. // - Sobel directional response is roughly in [-1020..1020] for 8-bit luminance; scale to ~±128. const strength = strength01 * 10; const bias = whiteLevel01 * 255; const dirRad = (directionDeg * Math.PI) / 180; const cx = Math.cos(dirRad); const cy = Math.sin(dirRad); const SCALE = (128 / 1020) * strength; // ≈0.1255 * strength // Precompute luminance (Rec.709) const lum = new Float32Array(w * h); for (let p = 0, i = 0; i < data.length; i += 4, p++) { lum[p] = 0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2]; } // Sobel kernels (flattened) const Gx = [-1, 0, 1, -2, 0, 2, -1, 0, 1]; const Gy = [-1, -2, -1, 0, 0, 0, 1, 2, 1]; const clamp8 = (v) => (v < 0 ? 0 : v > 255 ? 255 : v); // Extend the edge pixels so the same kernel applies at every position. for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { let sx = 0, sy = 0, k = 0; for (let dy = -1; dy <= 1; dy++) { const row = Math.max(0, Math.min(h - 1, y + dy)) * w; for (let dx = -1; dx <= 1; dx++, k++) { const sample = lum[row + Math.max(0, Math.min(w - 1, x + dx))]; sx += sample * Gx[k]; sy += sample * Gy[k]; } } const p = y * w + x; const r = cx * sx + cy * sy; // directional response const outGray = clamp8(bias + r * SCALE); // biased, scaled, clamped const o = p * 4; if (blend) { // Add the emboss "relief" around chosen bias to original RGB const delta = outGray - bias; // symmetric around whiteLevel data[o] = clamp8(data[o] + delta); data[o + 1] = clamp8(data[o + 1] + delta); data[o + 2] = clamp8(data[o + 2] + delta); } else { // Grayscale embossed output data[o] = data[o + 1] = data[o + 2] = outGray; } } } return imageData; }; Factory.addGetterSetter(Node, 'embossStrength', 0.5, getNumberValidator(), Factory.afterSetFilter); /** * get/set emboss strength. Use with {@link Konva.Filters.Emboss} filter. * @name Konva.Node#embossStrength * @method * @param {Number} level between 0 and 1. Default is 0.5 * @returns {Number} */ Factory.addGetterSetter(Node, 'embossWhiteLevel', 0.5, getNumberValidator(), Factory.afterSetFilter); /** * get/set emboss white level. Use with {@link Konva.Filters.Emboss} filter. * @name Konva.Node#embossWhiteLevel * @method * @param {Number} embossWhiteLevel between 0 and 1. Default is 0.5 * @returns {Number} */ Factory.addGetterSetter(Node, 'embossDirection', 'top-left', undefined, Factory.afterSetFilter); /** * get/set emboss direction. Use with {@link Konva.Filters.Emboss} filter. * @name Konva.Node#embossDirection * @method * @param {String} embossDirection can be top-left, top, top-right, right, bottom-right, bottom, bottom-left or left * The default is top-left * @returns {String} */ Factory.addGetterSetter(Node, 'embossBlend', false, undefined, Factory.afterSetFilter); /** * get/set emboss blend. Use with {@link Konva.Filters.Emboss} filter. * @name Konva.Node#embossBlend * @method * @param {Boolean} embossBlend * @returns {Boolean} */