Browse Source

Apply correct filters

pull/9993/head
Bartosz Legięć 3 months ago
parent
commit
99ebc29f97
No known key found for this signature in database
GPG Key ID: 30B502838D95B041
  1. 4
      packages/common/src/utils.ts
  2. 57
      packages/element/src/applyFilterToImage.ts
  3. 12
      packages/element/src/renderElement.ts
  4. 15
      packages/excalidraw/renderer/staticScene.ts

4
packages/common/src/utils.ts

@ -1336,3 +1336,7 @@ export const isMobileOrTablet = (): boolean => { @@ -1336,3 +1336,7 @@ export const isMobileOrTablet = (): boolean => {
}
return false;
};
export function isCanvasFilterSupported() {
return "filter" in (CanvasRenderingContext2D.prototype || {});
}

57
packages/element/src/applyFilterToImage.ts

@ -1,13 +1,8 @@ @@ -1,13 +1,8 @@
import {
IMAGE_INVERT_FILTER_HUE_ROTATE_AMOUNT,
IMAGE_INVERT_FILTER_INVERT_AMOUNT,
IMAGE_INVERT_FILTER_SATURATE_AMOUNT,
} from "./renderElement";
export function applyFiltersToImage(
image: CanvasImageSource,
imageWidth: number,
imageHeight: number,
filter: string,
): CanvasImageSource {
const cached = cache.get(image);
if (cached) {
@ -21,9 +16,7 @@ export function applyFiltersToImage( @@ -21,9 +16,7 @@ export function applyFiltersToImage(
const ctx = canvas.getContext("2d")!;
ctx.drawImage(image, 0, 0, imageWidth, imageHeight);
invert(ctx, IMAGE_INVERT_FILTER_INVERT_AMOUNT);
hueRotate(ctx, IMAGE_INVERT_FILTER_HUE_ROTATE_AMOUNT);
saturate(ctx, IMAGE_INVERT_FILTER_SATURATE_AMOUNT);
applyFilter(ctx, filter);
cache.set(image, canvas);
return canvas;
@ -232,3 +225,49 @@ function normalizeAngle(angle: string): number { @@ -232,3 +225,49 @@ function normalizeAngle(angle: string): number {
return normalized;
}
/**
* Taken from @davidenke's context-filter-polyfill
* @see {@link https://github.com/davidenke/context-filter-polyfill/blob/e1a04c24b8f31a0608f5d05155d29544a6d6429a/src/utils/filter.utils.ts#L5}
*/
function parseFilterString(
filterString: string,
): [filterName: string, filterValue: string][] {
// filters are separated by whitespace
const match = filterString.match(/([-a-z]+)(?:\(([\w\d\s.%-]*)\))?/gim);
if (!match) {
return [];
}
return (
match
// filters may have options within appended brackets
?.map(
(filter) =>
filter.match(/([-a-z]+)(?:\((.*)\))?/i)?.slice(1, 3) as [
string,
string,
],
)
);
}
function applyFilter(ctx: CanvasRenderingContext2D, filterString: string) {
const filters = parseFilterString(filterString);
for (const [filterName, filterValue] of filters) {
switch (filterName) {
case "invert":
invert(ctx, filterValue);
break;
case "hue-rotate":
hueRotate(ctx, filterValue);
break;
case "saturate":
saturate(ctx, filterValue);
break;
default:
break;
}
}
}

12
packages/element/src/renderElement.ts

@ -22,6 +22,7 @@ import { @@ -22,6 +22,7 @@ import {
isRTL,
getVerticalOffset,
invariant,
isCanvasFilterSupported,
} from "@excalidraw/common";
import type {
@ -87,10 +88,8 @@ import type { RoughCanvas } from "roughjs/bin/canvas"; @@ -87,10 +88,8 @@ import type { RoughCanvas } from "roughjs/bin/canvas";
// as a temp hack to make images in dark theme look closer to original
// color scheme (it's still not quite there and the colors look slightly
// desatured, alas...)
export const IMAGE_INVERT_FILTER_INVERT_AMOUNT = "100%";
export const IMAGE_INVERT_FILTER_HUE_ROTATE_AMOUNT = "180deg";
export const IMAGE_INVERT_FILTER_SATURATE_AMOUNT = "1.25";
export const IMAGE_INVERT_FILTER = `invert(${IMAGE_INVERT_FILTER_INVERT_AMOUNT}) hue-rotate(${IMAGE_INVERT_FILTER_HUE_ROTATE_AMOUNT}) saturate(${IMAGE_INVERT_FILTER_SATURATE_AMOUNT})`;
export const IMAGE_INVERT_FILTER =
"invert(100%) hue-rotate(180deg) saturate(1.25)";
const isPendingImageElement = (
element: ExcalidrawElement,
@ -99,10 +98,6 @@ const isPendingImageElement = ( @@ -99,10 +98,6 @@ const isPendingImageElement = (
isInitializedImageElement(element) &&
!renderConfig.imageCache.has(element.fileId);
function isCanvasFilterSupported() {
return "filter" in (CanvasRenderingContext2D.prototype || {});
}
const shouldResetImageFilter = (
element: ExcalidrawElement,
renderConfig: StaticCanvasRenderConfig,
@ -497,6 +492,7 @@ const drawElementOnCanvas = ( @@ -497,6 +492,7 @@ const drawElementOnCanvas = (
img,
img.naturalWidth,
img.naturalHeight,
IMAGE_INVERT_FILTER,
);
}

15
packages/excalidraw/renderer/staticScene.ts

@ -1,4 +1,10 @@ @@ -1,4 +1,10 @@
import { FRAME_STYLE, THEME, throttleRAF } from "@excalidraw/common";
import {
FRAME_STYLE,
isCanvasFilterSupported,
THEME,
THEME_FILTER,
throttleRAF,
} from "@excalidraw/common";
import { applyFiltersToImage, isElementLink } from "@excalidraw/element";
import { createPlaceholderEmbeddableLabel } from "@excalidraw/element";
import { getBoundTextElement } from "@excalidraw/element";
@ -459,11 +465,16 @@ const _renderStaticScene = ({ @@ -459,11 +465,16 @@ const _renderStaticScene = ({
}
});
if (isExporting && appState.theme === THEME.DARK) {
if (
isExporting &&
appState.theme === THEME.DARK &&
!isCanvasFilterSupported()
) {
const invertedCanvas = applyFiltersToImage(
canvas,
normalizedWidth,
normalizedHeight,
THEME_FILTER,
);
context.drawImage(invertedCanvas, 0, 0, normalizedWidth, normalizedHeight);
}

Loading…
Cancel
Save