Skip to content

Commit

Permalink
TheShovel/CanvasEffects: separate x & y scale, add border, add change…
Browse files Browse the repository at this point in the history
… effect (#1262)

Co-authored-by: SharkPool <[email protected]>
  • Loading branch information
GarboMuffin and SharkPool-SP authored Jan 28, 2024
1 parent 195d3e4 commit 93fa5a3
Showing 1 changed file with 157 additions and 40 deletions.
197 changes: 157 additions & 40 deletions extensions/TheShovel/CanvasEffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// ID: theshovelcanvaseffects
// Description: Apply visual effects to the entire stage.
// By: TheShovel
// By: SharkPool

(function (Scratch) {
"use strict";
Expand All @@ -13,7 +14,7 @@

const updateStyle = () => {
// Gotta keep the translation to % because of the stage size, window size and so on
const transform = `rotate(${rotation}deg) scale(${scale}%) skew(${skewX}deg, ${skewY}deg) translate(${offsetX}%, ${
const transform = `rotate(${rotation}deg) scale(${scaleX}%, ${scaleY}%) skew(${skewX}deg, ${skewY}deg) translate(${offsetX}%, ${
0 - offsetY
}%)`;
if (canvas.style.transform !== transform) {
Expand All @@ -27,15 +28,27 @@
if (canvas.style.filter !== filter) {
canvas.style.filter = filter;
}

const cssBorderRadius = borderRadius === 0 ? "" : `${borderRadius}%`;
if (canvas.style.borderRadius !== cssBorderRadius) {
canvas.style.borderRadius = cssBorderRadius;
}

const imageRendering = resizeMode === "pixelated" ? "pixelated" : "";
if (canvas.style.imageRendering !== imageRendering) {
canvas.style.imageRendering = imageRendering;
}

const border = `${borderWidth}px ${borderStyle} ${borderColor}`;
if (canvas.style.border !== border) {
canvas.style.border = border;
}

if (canvas.style.backgroundColor !== backgroundColor) {
canvas.style.backgroundColor = backgroundColor;
}
};

// scratch-gui may reset canvas styles when resizing the window or going in/out of fullscreen
new MutationObserver(updateStyle).observe(canvas, {
attributeFilter: ["style"],
Expand All @@ -48,7 +61,8 @@
let offsetX = 0;
let skewY = 0;
let skewX = 0;
let scale = 100;
let scaleX = 100;
let scaleY = 100;
// Thanks SharkPool for telling me about these
let transparency = 0;
let sepia = 0;
Expand All @@ -59,6 +73,10 @@
let brightness = 100;
let invert = 0;
let resizeMode = "default";
let borderStyle = "solid";
let borderWidth = 0;
let borderColor = "#000000";
let backgroundColor = "transparent";

const resetStyles = () => {
borderRadius = 0;
Expand All @@ -67,7 +85,8 @@
offsetX = 0;
skewY = 0;
skewX = 0;
scale = 100;
scaleX = 100;
scaleY = 100;
transparency = 0;
sepia = 0;
blur = 0;
Expand All @@ -77,6 +96,10 @@
brightness = 100;
invert = 0;
resizeMode = "default";
borderStyle = "solid";
borderWidth = 0;
borderColor = "#000000";
backgroundColor = "transparent";
updateStyle();
};

Expand All @@ -102,6 +125,21 @@
},
},
},
{
opcode: "changeEffect",
blockType: Scratch.BlockType.COMMAND,
text: "change canvas [EFFECT] by [NUMBER]",
arguments: {
EFFECT: {
type: Scratch.ArgumentType.STRING,
menu: "EFFECTMENU",
},
NUMBER: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: 5,
},
},
},
{
opcode: "geteffect",
blockType: Scratch.BlockType.REPORTER,
Expand All @@ -118,10 +156,34 @@
blockType: Scratch.BlockType.COMMAND,
text: "clear canvas effects",
},
"---",
{
opcode: "setBorder",
blockType: Scratch.BlockType.COMMAND,
text: "set canvas border to [WIDTH] pixels [STYLE] with color [COLOR1] and background [COLOR2]",
arguments: {
STYLE: {
type: Scratch.ArgumentType.STRING,
menu: "borderTypes",
},
WIDTH: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: 5,
},
COLOR1: {
type: Scratch.ArgumentType.COLOR,
defaultValue: "#ff0000",
},
COLOR2: {
type: Scratch.ArgumentType.COLOR,
defaultValue: "#0000ff",
},
},
},
{
opcode: "renderscale",
blockType: Scratch.BlockType.COMMAND,
text: "set canvas render size to width:[X] height:[Y]",
text: "set canvas render size to width: [X] height: [Y]",
arguments: {
X: {
type: Scratch.ArgumentType.NUMBER,
Expand All @@ -148,53 +210,60 @@
menus: {
EFFECTMENU: {
acceptReporters: true,
items: [
"blur",
"contrast",
"saturation",
"color shift",
"brightness",
"invert",
"sepia",
"transparency",
"scale",
"skew X",
"skew Y",
"offset X",
"offset Y",
"rotation",
"border radius",
],
items: this._getMenuItems(false),
},
RENDERMODE: {
acceptReporters: true,
items: ["pixelated", "default"],
},
EFFECTGETMENU: {
acceptReporters: true,
// this contains 'resize rendering mode', EFFECTMENU does not
items: this._getMenuItems(true),
},
borderTypes: {
acceptReporters: true,
items: [
"blur",
"contrast",
"saturation",
"color shift",
"brightness",
"invert",
"resize rendering mode",
"sepia",
"transparency",
"scale",
"skew X",
"skew Y",
"offset X",
"offset Y",
"rotation",
"border radius",
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"none",
],
},
},
};
}

_getMenuItems(isGetter) {
return [
"blur",
"contrast",
"saturation",
"color shift",
"brightness",
"invert",
...(isGetter ? ["resize rendering mode"] : []),
"sepia",
"transparency",
...(isGetter ? [] : ["scale"]),
"scale X",
"scale Y",
"skew X",
"skew Y",
"offset X",
"offset Y",
"rotation",
"border radius",
...(isGetter
? ["border width", "border style", "border color", "background"]
: []),
];
}

geteffect({ EFFECT }) {
if (EFFECT === "blur") {
return blur;
Expand All @@ -215,7 +284,12 @@
} else if (EFFECT === "transparency") {
return transparency;
} else if (EFFECT === "scale") {
return scale;
// old extension compatibility
return scaleX;
} else if (EFFECT === "scale X") {
return scaleX;
} else if (EFFECT === "scale Y") {
return scaleY;
} else if (EFFECT === "skew X") {
return skewX;
} else if (EFFECT === "skew Y") {
Expand All @@ -228,6 +302,14 @@
return rotation;
} else if (EFFECT === "border radius") {
return borderRadius;
} else if (EFFECT === "border width") {
return borderWidth;
} else if (EFFECT === "border style") {
return borderStyle;
} else if (EFFECT === "border color") {
return borderColor;
} else if (EFFECT === "background") {
return backgroundColor;
}
return "";
}
Expand All @@ -250,7 +332,12 @@
} else if (EFFECT === "transparency") {
transparency = NUMBER;
} else if (EFFECT === "scale") {
scale = NUMBER;
scaleX = NUMBER;
scaleY = NUMBER;
} else if (EFFECT === "scale X") {
scaleX = NUMBER;
} else if (EFFECT === "scale Y") {
scaleY = NUMBER;
} else if (EFFECT === "skew X") {
skewX = NUMBER;
} else if (EFFECT === "skew Y") {
Expand All @@ -266,6 +353,23 @@
}
updateStyle();
}
changeEffect(args) {
// Scale needs some special treatment to change x & y separately
if (args.EFFECT === "scale") {
scaleX = scaleX + Scratch.Cast.toNumber(args.NUMBER);
scaleY = scaleY + Scratch.Cast.toNumber(args.NUMBER);
updateStyle();
return;
}

// Everything else is really generic
const currentEffect = Scratch.Cast.toNumber(this.geteffect(args));
const newValue = Scratch.Cast.toNumber(args.NUMBER) + currentEffect;
this.seteffect({
EFFECT: args.EFFECT,
NUMBER: newValue,
});
}
cleareffects() {
resetStyles();
}
Expand All @@ -276,6 +380,19 @@
renderscale({ X, Y }) {
Scratch.vm.renderer.resize(X, Y);
}
setBorder(args) {
borderWidth = Scratch.Cast.toNumber(args.WIDTH);
borderStyle = Scratch.Cast.toString(args.STYLE).replace(/[^a-z]/gi, "");
borderColor = Scratch.Cast.toString(args.COLOR1).replace(
/[^#0-9a-z]/gi,
""
);
backgroundColor = Scratch.Cast.toString(args.COLOR2).replace(
/[^#0-9a-z]/gi,
""
);
updateStyle();
}
}
Scratch.extensions.register(new CanvasEffects());
})(Scratch);

0 comments on commit 93fa5a3

Please sign in to comment.