mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
添加对css滤镜的支持
This commit is contained in:
parent
51bb0da805
commit
94e966d147
79
examples/demo4.html
Normal file
79
examples/demo4.html
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Nested transform tests</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#first {
|
||||||
|
background: indianred;
|
||||||
|
margin-top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#second {
|
||||||
|
border: 15px solid red;
|
||||||
|
background: darkseagreen;
|
||||||
|
-webkit-transform: rotate(7.5deg);
|
||||||
|
/* Chrome, Safari 3.1+ */
|
||||||
|
-moz-transform: rotate(7.5deg);
|
||||||
|
/* Firefox 3.5-15 */
|
||||||
|
-ms-transform: rotate(7.5deg);
|
||||||
|
/* IE 9 */
|
||||||
|
-o-transform: rotate(7.5deg);
|
||||||
|
/* Opera 10.50-12.00 */
|
||||||
|
transform: rotate(7.5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#third {
|
||||||
|
background: cadetblue;
|
||||||
|
-webkit-transform: rotate(-70.5deg);
|
||||||
|
/* Chrome, Safari 3.1+ */
|
||||||
|
-moz-transform: rotate(-70.5deg);
|
||||||
|
/* Firefox 3.5-15 */
|
||||||
|
-ms-transform: rotate(-70.5deg);
|
||||||
|
/* IE 9 */
|
||||||
|
-o-transform: rotate(-70.5deg);
|
||||||
|
/* Opera 10.50-12.00 */
|
||||||
|
transform: rotate(-70.5deg);
|
||||||
|
/* Firefox 16+, IE 10+, Opera 12.10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
#fourth {
|
||||||
|
background: #bc8f8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.test {
|
||||||
|
width: 100px;
|
||||||
|
-webkit-filter: saturate(1600%);
|
||||||
|
/* Chrome, Safari, Opera */
|
||||||
|
filter: saturate(1600%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="fourth"><img class="test" src="../tests/assets/image.jpg" crossOrigin="anonymous" alt=""></div>
|
||||||
|
<div id="fourth2"><img src="../tests/assets/image.jpg" crossOrigin="anonymous" alt=""></div>
|
||||||
|
<script type="text/javascript" src="../dist/html2canvas.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
html2canvas(document.body, { allowTaint: true, scale: 1 }).then(function (canvas) {
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -75,6 +75,7 @@ import {counterIncrement} from './property-descriptors/counter-increment';
|
|||||||
import {counterReset} from './property-descriptors/counter-reset';
|
import {counterReset} from './property-descriptors/counter-reset';
|
||||||
import {quotes} from './property-descriptors/quotes';
|
import {quotes} from './property-descriptors/quotes';
|
||||||
import {boxShadow} from './property-descriptors/box-shadow';
|
import {boxShadow} from './property-descriptors/box-shadow';
|
||||||
|
import {filter} from './property-descriptors/filter';
|
||||||
|
|
||||||
export class CSSParsedDeclaration {
|
export class CSSParsedDeclaration {
|
||||||
backgroundClip: ReturnType<typeof backgroundClip.parse>;
|
backgroundClip: ReturnType<typeof backgroundClip.parse>;
|
||||||
@ -103,6 +104,7 @@ export class CSSParsedDeclaration {
|
|||||||
boxShadow: ReturnType<typeof boxShadow.parse>;
|
boxShadow: ReturnType<typeof boxShadow.parse>;
|
||||||
color: Color;
|
color: Color;
|
||||||
display: ReturnType<typeof display.parse>;
|
display: ReturnType<typeof display.parse>;
|
||||||
|
filter: ReturnType<typeof filter.parse>;
|
||||||
float: ReturnType<typeof float.parse>;
|
float: ReturnType<typeof float.parse>;
|
||||||
fontFamily: ReturnType<typeof fontFamily.parse>;
|
fontFamily: ReturnType<typeof fontFamily.parse>;
|
||||||
fontSize: LengthPercentage;
|
fontSize: LengthPercentage;
|
||||||
@ -168,6 +170,7 @@ export class CSSParsedDeclaration {
|
|||||||
this.boxShadow = parse(boxShadow, declaration.boxShadow);
|
this.boxShadow = parse(boxShadow, declaration.boxShadow);
|
||||||
this.color = parse(color, declaration.color);
|
this.color = parse(color, declaration.color);
|
||||||
this.display = parse(display, declaration.display);
|
this.display = parse(display, declaration.display);
|
||||||
|
this.filter = parse(filter, declaration.filter);
|
||||||
this.float = parse(float, declaration.cssFloat);
|
this.float = parse(float, declaration.cssFloat);
|
||||||
this.fontFamily = parse(fontFamily, declaration.fontFamily);
|
this.fontFamily = parse(fontFamily, declaration.fontFamily);
|
||||||
this.fontSize = parse(fontSize, declaration.fontSize);
|
this.fontSize = parse(fontSize, declaration.fontSize);
|
||||||
|
68
src/css/property-descriptors/filter.ts
Normal file
68
src/css/property-descriptors/filter.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
||||||
|
import {CSSValue, isIdentWithValue, CSSFunction, isCSSFunction} from '../syntax/parser';
|
||||||
|
import {isLength, Length} from '../types/length';
|
||||||
|
|
||||||
|
export interface Filter {
|
||||||
|
contrast: Length | null;
|
||||||
|
'hue-rotate': Length | null;
|
||||||
|
grayscale: Length | null;
|
||||||
|
brightness: Length | null;
|
||||||
|
blur: Length | null;
|
||||||
|
invert: Length | null;
|
||||||
|
saturate: Length | null;
|
||||||
|
sepia: Length | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const filter: IPropertyListDescriptor<Filter | null> = {
|
||||||
|
name: 'filter',
|
||||||
|
initialValue: 'none',
|
||||||
|
prefix: true,
|
||||||
|
type: PropertyDescriptorParsingType.LIST,
|
||||||
|
parse: (tokens: CSSValue[]) => {
|
||||||
|
if (tokens.length === 1 && isIdentWithValue(tokens[0], 'none')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filter: Filter = {
|
||||||
|
contrast: null,
|
||||||
|
'hue-rotate': null,
|
||||||
|
grayscale: null,
|
||||||
|
brightness: null,
|
||||||
|
blur: null,
|
||||||
|
invert: null,
|
||||||
|
saturate: null,
|
||||||
|
sepia: null
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasFilter: boolean = false;
|
||||||
|
|
||||||
|
tokens.filter(isCSSFunction).forEach((token: CSSFunction) => {
|
||||||
|
switch (token.name) {
|
||||||
|
case 'contrast':
|
||||||
|
case 'hue-rotate':
|
||||||
|
case 'grayscale':
|
||||||
|
case 'brightness':
|
||||||
|
case 'blur':
|
||||||
|
case 'invert':
|
||||||
|
case 'saturate':
|
||||||
|
case 'sepia':
|
||||||
|
for (let index = 0; index < token.values.length; index++) {
|
||||||
|
const value: CSSValue = token.values[index];
|
||||||
|
if (isLength(value)) {
|
||||||
|
hasFilter = true;
|
||||||
|
filter[token.name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasFilter) {
|
||||||
|
return filter;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,9 +1,9 @@
|
|||||||
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
||||||
|
|
||||||
export const textStrokeColor: IPropertyTypeValueDescriptor = {
|
export const textStrokeColor: IPropertyTypeValueDescriptor = {
|
||||||
name: `-webkit-text-stroke-color`,
|
name: `text-stroke-color`,
|
||||||
initialValue: 'transparent',
|
initialValue: 'transparent',
|
||||||
prefix: false,
|
prefix: true,
|
||||||
type: PropertyDescriptorParsingType.TYPE_VALUE,
|
type: PropertyDescriptorParsingType.TYPE_VALUE,
|
||||||
format: 'color'
|
format: 'color'
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
||||||
export const textStrokeWidth: IPropertyTypeValueDescriptor = {
|
export const textStrokeWidth: IPropertyTypeValueDescriptor = {
|
||||||
name: '-webkit-text-stroke-width',
|
name: 'text-stroke-width',
|
||||||
initialValue: '0',
|
initialValue: '0',
|
||||||
type: PropertyDescriptorParsingType.TYPE_VALUE,
|
type: PropertyDescriptorParsingType.TYPE_VALUE,
|
||||||
prefix: false,
|
prefix: true,
|
||||||
format: 'length-percentage'
|
format: 'length-percentage'
|
||||||
};
|
};
|
||||||
|
@ -141,6 +141,7 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isCSSFunction = (token: CSSValue): token is CSSFunction => token.type === TokenType.FUNCTION;
|
||||||
export const isDimensionToken = (token: CSSValue): token is DimensionToken => token.type === TokenType.DIMENSION_TOKEN;
|
export const isDimensionToken = (token: CSSValue): token is DimensionToken => token.type === TokenType.DIMENSION_TOKEN;
|
||||||
export const isNumberToken = (token: CSSValue): token is NumberValueToken => token.type === TokenType.NUMBER_TOKEN;
|
export const isNumberToken = (token: CSSValue): token is NumberValueToken => token.type === TokenType.NUMBER_TOKEN;
|
||||||
export const isIdentToken = (token: CSSValue): token is StringValueToken => token.type === TokenType.IDENT_TOKEN;
|
export const isIdentToken = (token: CSSValue): token is StringValueToken => token.type === TokenType.IDENT_TOKEN;
|
||||||
|
@ -302,3 +302,156 @@ export const COLORS: {[key: string]: Color} = {
|
|||||||
YELLOW: 0xffff00ff,
|
YELLOW: 0xffff00ff,
|
||||||
YELLOWGREEN: 0x9acd32ff
|
YELLOWGREEN: 0x9acd32ff
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface RGBColor {
|
||||||
|
r: number;
|
||||||
|
g: number;
|
||||||
|
b: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const contrastRGB = (rgb: RGBColor, value: number): RGBColor => {
|
||||||
|
if (value < 0) value = 0;
|
||||||
|
else if (value > 1) value = 1;
|
||||||
|
return {
|
||||||
|
r: Math.max(0, Math.min(255, value * (rgb.r - 128) + 128)),
|
||||||
|
g: Math.max(0, Math.min(255, value * (rgb.g - 128) + 128)),
|
||||||
|
b: Math.max(0, Math.min(255, value * (rgb.b - 128) + 128))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const grayscaleRGB = (rgb: RGBColor, value: number, mode?: string | null) => {
|
||||||
|
var gray = 0;
|
||||||
|
//different grayscale algorithms
|
||||||
|
switch (mode) {
|
||||||
|
case 'average':
|
||||||
|
gray = (rgb.r + rgb.g + rgb.b) / 3;
|
||||||
|
break;
|
||||||
|
case 'luma:BT601':
|
||||||
|
gray = rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114;
|
||||||
|
break;
|
||||||
|
case 'desaturation':
|
||||||
|
gray = (Math.max(rgb.r, rgb.g, rgb.b) + Math.max(rgb.r, rgb.g, rgb.b)) / 2;
|
||||||
|
break;
|
||||||
|
case 'decompsition:max':
|
||||||
|
gray = Math.max(rgb.r, rgb.g, rgb.b);
|
||||||
|
break;
|
||||||
|
case 'decompsition:min':
|
||||||
|
gray = Math.min(rgb.r, rgb.g, rgb.b);
|
||||||
|
break;
|
||||||
|
case 'luma:BT709':
|
||||||
|
default:
|
||||||
|
gray = rgb.r * 0.2126 + rgb.g * 0.7152 + rgb.b * 0.0722;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rgb.r = value * (gray - rgb.r) + rgb.r;
|
||||||
|
rgb.g = value * (gray - rgb.g) + rgb.g;
|
||||||
|
rgb.b = value * (gray - rgb.b) + rgb.b;
|
||||||
|
return rgb;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const brightnessRGB = (rgb: RGBColor, value: number): RGBColor => {
|
||||||
|
if (value < 0) value = 0;
|
||||||
|
return {
|
||||||
|
r: Math.max(0, Math.min(255, rgb.r * value)),
|
||||||
|
g: Math.max(0, Math.min(255, rgb.g * value)),
|
||||||
|
b: Math.max(0, Math.min(255, rgb.b * value))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const invertRGB = (rgb: RGBColor, value: number): RGBColor => {
|
||||||
|
return {
|
||||||
|
r: value * (255 - 2 * rgb.r) + rgb.r,
|
||||||
|
g: value * (255 - 2 * rgb.g) + rgb.g,
|
||||||
|
b: value * (255 - 2 * rgb.b) + rgb.b
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sepiaRGB = (rgb: RGBColor, value: number): RGBColor => {
|
||||||
|
if (value < 0) value = 0;
|
||||||
|
else if (value > 1) value = 1;
|
||||||
|
return {
|
||||||
|
r: value * Math.min(255, rgb.r * 0.393 + rgb.g * 0.769 + rgb.b * 0.189 - rgb.r) + rgb.r,
|
||||||
|
g: value * Math.min(255, rgb.r * 0.349 + rgb.g * 0.686 + rgb.b * 0.168 - rgb.g) + rgb.g,
|
||||||
|
b: value * Math.min(255, rgb.r * 0.272 + rgb.g * 0.534 + rgb.b * 0.131 - rgb.b) + rgb.b
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hueRotateRGB = (rgb: RGBColor, value: number): RGBColor => {
|
||||||
|
while (value < 0) value += 360;
|
||||||
|
while (value > 360) value -= 360;
|
||||||
|
rgb2hsl(rgb);
|
||||||
|
rgb.r += value;
|
||||||
|
if (rgb.r < 0) rgb.r += 360;
|
||||||
|
if (rgb.r > 359) rgb.r -= 360;
|
||||||
|
hsl2rgb(rgb);
|
||||||
|
return rgb;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saturateRGB = (rgb: RGBColor, value: number): RGBColor => {
|
||||||
|
if (value < 0) value = 0;
|
||||||
|
rgb2hsl(rgb);
|
||||||
|
rgb.g *= value;
|
||||||
|
if (rgb.g > 100) rgb.g = 100;
|
||||||
|
hsl2rgb(rgb);
|
||||||
|
return rgb;
|
||||||
|
};
|
||||||
|
|
||||||
|
function rgb2hsl(rgb: RGBColor) {
|
||||||
|
rgb.r = Math.max(0, Math.min(255, rgb.r)) / 255;
|
||||||
|
rgb.g = Math.max(0, Math.min(255, rgb.g)) / 255;
|
||||||
|
rgb.b = Math.max(0, Math.min(255, rgb.b)) / 255;
|
||||||
|
let h, l;
|
||||||
|
let M = Math.max(rgb.r, rgb.g, rgb.b);
|
||||||
|
let m = Math.min(rgb.r, rgb.g, rgb.b);
|
||||||
|
let d = M - m;
|
||||||
|
if (d == 0) h = 0;
|
||||||
|
else if (M == rgb.r) h = ((rgb.g - rgb.b) / d) % 6;
|
||||||
|
else if (M == rgb.g) h = (rgb.b - rgb.r) / d + 2;
|
||||||
|
else h = (rgb.r - rgb.g) / d + 4;
|
||||||
|
h = Math.round(h * 60);
|
||||||
|
if (h < 0) h += 360;
|
||||||
|
rgb.r = h;
|
||||||
|
l = (M + m) / 2;
|
||||||
|
if (d == 0) rgb.g = 0;
|
||||||
|
else rgb.g = (d / (1 - Math.abs(2 * l - 1))) * 100;
|
||||||
|
rgb.b = l * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hsl2rgb(rgb: RGBColor) {
|
||||||
|
rgb.r = Math.max(0, Math.min(359, rgb.r));
|
||||||
|
rgb.g = Math.max(0, Math.min(100, rgb.g)) / 100;
|
||||||
|
rgb.b = Math.max(0, Math.min(100, rgb.b)) / 100;
|
||||||
|
let C = (1 - Math.abs(2 * rgb.b - 1)) * rgb.g;
|
||||||
|
let h = rgb.r / 60;
|
||||||
|
let X = C * (1 - Math.abs((h % 2) - 1));
|
||||||
|
let l = rgb.b;
|
||||||
|
rgb.r = 0;
|
||||||
|
rgb.g = 0;
|
||||||
|
rgb.b = 0;
|
||||||
|
if (h >= 0 && h < 1) {
|
||||||
|
rgb.r = C;
|
||||||
|
rgb.g = X;
|
||||||
|
} else if (h >= 1 && h < 2) {
|
||||||
|
rgb.r = X;
|
||||||
|
rgb.g = C;
|
||||||
|
} else if (h >= 2 && h < 3) {
|
||||||
|
rgb.g = C;
|
||||||
|
rgb.b = X;
|
||||||
|
} else if (h >= 3 && h < 4) {
|
||||||
|
rgb.g = X;
|
||||||
|
rgb.b = C;
|
||||||
|
} else if (h >= 4 && h < 5) {
|
||||||
|
rgb.r = X;
|
||||||
|
rgb.b = C;
|
||||||
|
} else {
|
||||||
|
rgb.r = C;
|
||||||
|
rgb.b = X;
|
||||||
|
}
|
||||||
|
let m = l - C / 2;
|
||||||
|
rgb.r += m;
|
||||||
|
rgb.g += m;
|
||||||
|
rgb.b += m;
|
||||||
|
rgb.r = Math.round(rgb.r * 255.0);
|
||||||
|
rgb.g = Math.round(rgb.g * 255.0);
|
||||||
|
rgb.b = Math.round(rgb.b * 255.0);
|
||||||
|
}
|
||||||
|
1057
src/css/types/functions/stackBlur.ts
Normal file
1057
src/css/types/functions/stackBlur.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,17 @@
|
|||||||
import {ElementPaint, parseStackingContexts, StackingContext} from '../stacking-context';
|
import {ElementPaint, parseStackingContexts, StackingContext} from '../stacking-context';
|
||||||
import {asString, Color, isTransparent} from '../../css/types/color';
|
import {
|
||||||
|
asString,
|
||||||
|
Color,
|
||||||
|
isTransparent,
|
||||||
|
RGBColor,
|
||||||
|
contrastRGB,
|
||||||
|
hueRotateRGB,
|
||||||
|
grayscaleRGB,
|
||||||
|
brightnessRGB,
|
||||||
|
invertRGB,
|
||||||
|
saturateRGB,
|
||||||
|
sepiaRGB
|
||||||
|
} from '../../css/types/color';
|
||||||
import {Logger} from '../../core/logger';
|
import {Logger} from '../../core/logger';
|
||||||
import {ElementContainer} from '../../dom/element-container';
|
import {ElementContainer} from '../../dom/element-container';
|
||||||
import {BORDER_STYLE} from '../../css/property-descriptors/border-style';
|
import {BORDER_STYLE} from '../../css/property-descriptors/border-style';
|
||||||
@ -38,6 +50,8 @@ import {TextareaElementContainer} from '../../dom/elements/textarea-element-cont
|
|||||||
import {SelectElementContainer} from '../../dom/elements/select-element-container';
|
import {SelectElementContainer} from '../../dom/elements/select-element-container';
|
||||||
import {IFrameElementContainer} from '../../dom/replaced-elements/iframe-element-container';
|
import {IFrameElementContainer} from '../../dom/replaced-elements/iframe-element-container';
|
||||||
import {TextShadow} from '../../css/property-descriptors/text-shadow';
|
import {TextShadow} from '../../css/property-descriptors/text-shadow';
|
||||||
|
import {Filter} from '../../css/property-descriptors/filter';
|
||||||
|
import {stackBlurImage} from '../../css/types/functions/stackBlur';
|
||||||
|
|
||||||
export type RenderConfigurations = RenderOptions & {
|
export type RenderConfigurations = RenderOptions & {
|
||||||
backgroundColor: Color | null;
|
backgroundColor: Color | null;
|
||||||
@ -248,7 +262,8 @@ export class CanvasRenderer {
|
|||||||
renderReplacedElement(
|
renderReplacedElement(
|
||||||
container: ReplacedElementContainer,
|
container: ReplacedElementContainer,
|
||||||
curves: BoundCurves,
|
curves: BoundCurves,
|
||||||
image: HTMLImageElement | HTMLCanvasElement
|
image: HTMLImageElement | HTMLCanvasElement,
|
||||||
|
filter?: Filter | null | undefined
|
||||||
) {
|
) {
|
||||||
if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) {
|
if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) {
|
||||||
const box = contentBox(container);
|
const box = contentBox(container);
|
||||||
@ -267,6 +282,49 @@ export class CanvasRenderer {
|
|||||||
box.width,
|
box.width,
|
||||||
box.height
|
box.height
|
||||||
);
|
);
|
||||||
|
if (filter) {
|
||||||
|
try {
|
||||||
|
let imageData = this.ctx.getImageData(box.left, box.top, box.width, box.height);
|
||||||
|
for (let _j = 0; _j < imageData.height; _j++) {
|
||||||
|
for (let _i = 0; _i < imageData.width; _i++) {
|
||||||
|
let index = _j * 4 * imageData.width + _i * 4;
|
||||||
|
let rgb: RGBColor = {
|
||||||
|
r: imageData.data[index],
|
||||||
|
g: imageData.data[index + 1],
|
||||||
|
b: imageData.data[index + 2]
|
||||||
|
};
|
||||||
|
if (filter.contrast) {
|
||||||
|
rgb = contrastRGB(rgb, filter.contrast.number);
|
||||||
|
}
|
||||||
|
if (filter['hue-rotate']) {
|
||||||
|
rgb = hueRotateRGB(rgb, filter['hue-rotate'].number);
|
||||||
|
}
|
||||||
|
if (filter.grayscale) {
|
||||||
|
rgb = grayscaleRGB(rgb, filter['grayscale'].number, 'luma:BT709');
|
||||||
|
}
|
||||||
|
if (filter.brightness) {
|
||||||
|
rgb = brightnessRGB(rgb, filter['brightness'].number);
|
||||||
|
}
|
||||||
|
if (filter.invert) {
|
||||||
|
rgb = invertRGB(rgb, filter['invert'].number);
|
||||||
|
}
|
||||||
|
if (filter.saturate) {
|
||||||
|
rgb = saturateRGB(rgb, filter.saturate.number);
|
||||||
|
}
|
||||||
|
if (filter.sepia) {
|
||||||
|
rgb = sepiaRGB(rgb, filter.sepia.number);
|
||||||
|
}
|
||||||
|
imageData.data[index] = rgb.r;
|
||||||
|
imageData.data[index + 1] = rgb.g;
|
||||||
|
imageData.data[index + 2] = rgb.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter.blur) {
|
||||||
|
imageData = stackBlurImage(imageData, box.width, box.height, filter.blur.number * 2.2, 1);
|
||||||
|
}
|
||||||
|
this.ctx.putImageData(imageData, box.left, box.top);
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,7 +341,7 @@ export class CanvasRenderer {
|
|||||||
if (container instanceof ImageElementContainer) {
|
if (container instanceof ImageElementContainer) {
|
||||||
try {
|
try {
|
||||||
const image = await this.options.cache.match(container.src);
|
const image = await this.options.cache.match(container.src);
|
||||||
this.renderReplacedElement(container, curves, image);
|
this.renderReplacedElement(container, curves, image, styles.filter);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.getInstance(this.options.id).error(`Error loading image ${container.src}`);
|
Logger.getInstance(this.options.id).error(`Error loading image ${container.src}`);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user