Skip to content

Commit dffb5f6

Browse files
authored
Merge pull request #945 from rgantzos/main
webp uploads
2 parents 1948a45 + f19a0ee commit dffb5f6

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

features/features.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
[
2+
{
3+
"version": 2,
4+
"id": "webp-uploads",
5+
"versionAdded": "v4.0.0"
6+
},
27
{
38
"version": 2,
49
"id": "remaining-replies",

features/webp-uploads/data.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"title": "WEBP Image Uploads",
3+
"description": "Allows you to upload webp images in the editor for new sprites, costumes and stage backdrops.",
4+
"credits": [
5+
{ "username": "rgantzos", "url": "https://scratch.mit.edu/users/rgantzos/" }
6+
],
7+
"type": ["Editor"],
8+
"tags": ["New", "Featured"],
9+
"dynamic": true,
10+
"scripts": [{ "file": "script.js", "runOn": "/projects/*" }]
11+
}

features/webp-uploads/script.js

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
export default async function ({ feature, console }) {
2+
let fileInput = {
3+
costume: null,
4+
sprite: null,
5+
stage: null,
6+
};
7+
8+
ScratchTools.waitForElements(
9+
"div[class*='asset-panel_wrapper_'] input[type=file]",
10+
function (input) {
11+
fileInput.costume = input;
12+
13+
fileInput.costume.parentElement.addEventListener("click", function (e) {
14+
if (!feature.self.enabled) return;
15+
16+
e.preventDefault();
17+
e.stopImmediatePropagation();
18+
19+
let input = document.createElement("input");
20+
input.accept = ".svg, .png, .bmp, .jpg, .jpeg, .gif, .webp";
21+
input.setAttribute("multiple", null);
22+
input.type = "file";
23+
24+
let files = [];
25+
26+
input.addEventListener("change", async function () {
27+
const dataTransfer = new DataTransfer();
28+
29+
for (var i in input.files) {
30+
let file = input.files[i];
31+
if (file?.type?.startsWith("image/")) {
32+
if (file.type === "image/webp") {
33+
let blob = await convertWebPFileToPng(file);
34+
file = new File([blob], file.name.split(".")[0] + ".png", {
35+
type: "image/png",
36+
});
37+
dataTransfer.items.add(file);
38+
} else {
39+
dataTransfer.items.add(file);
40+
}
41+
}
42+
}
43+
44+
fileInput.costume.files = dataTransfer.files;
45+
46+
fileInput.costume.dispatchEvent(
47+
new Event("change", { bubbles: true })
48+
);
49+
});
50+
51+
input.click();
52+
});
53+
}
54+
);
55+
56+
ScratchTools.waitForElements(
57+
"div[class*='sprite-selector_sprite-selector_'] input[type=file]",
58+
function (input) {
59+
fileInput.sprite = input;
60+
61+
fileInput.sprite.parentElement.addEventListener("click", function (e) {
62+
if (!feature.self.enabled) return;
63+
64+
e.preventDefault();
65+
e.stopImmediatePropagation();
66+
67+
let input = document.createElement("input");
68+
input.accept = ".svg, .png, .bmp, .jpg, .jpeg, .gif, .webp";
69+
input.setAttribute("multiple", null);
70+
input.type = "file";
71+
72+
let files = [];
73+
74+
input.addEventListener("change", async function () {
75+
const dataTransfer = new DataTransfer();
76+
77+
for (var i in input.files) {
78+
let file = input.files[i];
79+
if (file?.type?.startsWith("image/")) {
80+
if (file.type === "image/webp") {
81+
let blob = await convertWebPFileToPng(file);
82+
file = new File([blob], file.name.split(".")[0] + ".png", {
83+
type: "image/png",
84+
});
85+
dataTransfer.items.add(file);
86+
} else {
87+
dataTransfer.items.add(file);
88+
}
89+
}
90+
}
91+
92+
fileInput.sprite.files = dataTransfer.files;
93+
94+
fileInput.sprite.dispatchEvent(
95+
new Event("change", { bubbles: true })
96+
);
97+
});
98+
99+
input.click();
100+
});
101+
}
102+
);
103+
104+
ScratchTools.waitForElements(
105+
"div[class*='stage-selector_stage-selector_'] input[type=file]",
106+
function (input) {
107+
fileInput.stage = input;
108+
109+
fileInput.stage.parentElement.addEventListener("click", function (e) {
110+
if (!feature.self.enabled) return;
111+
112+
e.preventDefault();
113+
e.stopImmediatePropagation();
114+
115+
let input = document.createElement("input");
116+
input.accept = ".svg, .png, .bmp, .jpg, .jpeg, .gif, .webp";
117+
input.setAttribute("multiple", null);
118+
input.type = "file";
119+
120+
let files = [];
121+
122+
input.addEventListener("change", async function () {
123+
const dataTransfer = new DataTransfer();
124+
125+
for (var i in input.files) {
126+
let file = input.files[i];
127+
if (file?.type?.startsWith("image/")) {
128+
if (file.type === "image/webp") {
129+
let blob = await convertWebPFileToPng(file);
130+
file = new File([blob], file.name.split(".")[0] + ".png", {
131+
type: "image/png",
132+
});
133+
dataTransfer.items.add(file);
134+
} else {
135+
dataTransfer.items.add(file);
136+
}
137+
}
138+
}
139+
140+
fileInput.stage.files = dataTransfer.files;
141+
142+
fileInput.stage.dispatchEvent(new Event("change", { bubbles: true }));
143+
});
144+
145+
input.click();
146+
});
147+
}
148+
);
149+
150+
async function convertWebPFileToPng(webpFile) {
151+
try {
152+
const img = new Image();
153+
const webpUrl = URL.createObjectURL(webpFile);
154+
img.src = webpUrl;
155+
156+
await new Promise((resolve, reject) => {
157+
img.onload = resolve;
158+
img.onerror = reject;
159+
});
160+
161+
const canvas = document.createElement("canvas");
162+
canvas.width = img.width;
163+
canvas.height = img.height;
164+
const ctx = canvas.getContext("2d");
165+
ctx.drawImage(img, 0, 0);
166+
167+
const pngBlob = await new Promise((resolve) => {
168+
canvas.toBlob(resolve, "image/png");
169+
});
170+
171+
URL.revokeObjectURL(webpUrl);
172+
173+
return pngBlob;
174+
} catch (error) {
175+
console.error("Error converting webp to png:", error);
176+
throw error;
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)