@@ -13,7 +13,88 @@ module ffi
1313/// Many of these are image manipulation utilities.
1414/// These are not minimal amount of code, but expose the least amount of
1515/// surface area to and from Effekt/JavaScript.
16- extern js """
16+
17+ extern jsNode """
18+ const { createCanvas, ImageData, Image } = require('canvas');
19+
20+
21+ function $newDrawingCtx(width, height) {
22+ const canvas = createCanvas(width, height);
23+ return canvas.getContext("2d");
24+ }
25+ function $drawInto(ctx, data, x, y) {
26+ ctx.putImageData(data, x, y);
27+ }
28+ function $blitFx(source, sourceX, sourceY, sourceWidth, sourceHeight) {
29+ const src = source.data;
30+ const srcW = source.width;
31+
32+ const result = new ImageData(sourceWidth, sourceHeight);
33+ const dst = result.data;
34+
35+ const rowLength = sourceWidth * 4;
36+
37+ for (let y = 0; y < sourceHeight; y++) {
38+ const srcStart = ((sourceY + y) * srcW + sourceX) * 4;
39+ const dstStart = y * rowLength;
40+
41+ dst.set(src.subarray(srcStart, srcStart + rowLength), dstStart);
42+ }
43+
44+ return result;
45+ }
46+ function $shuffled(array) {
47+ const result = array.slice();
48+
49+ for (let i = result.length - 1; i > 0; i--) {
50+ const j = Math.floor(Math.random() * (i + 1));
51+ [result[i], result[j]] = [result[j], result[i]];
52+ }
53+
54+ return result;
55+ }
56+ function $imageDataFromFile(file, callback) {
57+ throw new Error("Not implemented in Node.js environment");
58+ }
59+ function $downloadImageDataAsPNG(imageData, filename) {
60+ throw new Error("Not implemented in Node.js environment");
61+ }
62+ function $scaleImageData(imageData, scale) {
63+ const srcCanvas = createCanvas(imageData.width, imageData.height);
64+ const srcCtx = srcCanvas.getContext("2d");
65+ srcCtx.putImageData(imageData, 0, 0);
66+
67+ const dstCanvas = createCanvas(imageData.width * scale, imageData.height * scale);
68+ const dstCtx = dstCanvas.getContext("2d");
69+
70+ dstCtx.imageSmoothingEnabled = false;
71+
72+ dstCtx.drawImage(
73+ srcCanvas,
74+ 0, 0, srcCanvas.width, srcCanvas.height,
75+ 0, 0, dstCanvas.width, dstCanvas.height
76+ );
77+
78+ return dstCtx.getImageData(0, 0, dstCanvas.width, dstCanvas.height);
79+ }
80+
81+ function $showMessage(message, color) {
82+ console.log(message);
83+ }
84+
85+ function $imageFromImageData(imageData) {
86+ const canvas = createCanvas(imageData.width, imageData.height);
87+ const ctx = canvas.getContext('2d');
88+ ctx.putImageData(imageData, 0, 0);
89+
90+ const img = new Image();
91+ img.src = canvas.toDataURL("image/png");
92+
93+ return img;
94+ }
95+ """
96+
97+ extern jsWeb """
1798function $newDrawingCtx(width, height) {
1899 const canvas = document.createElement("canvas");
19100 canvas.width = width;
@@ -174,6 +255,10 @@ extern type _Context2D // lib/canvas.effekt
174255
175256namespace FFI {
176257
258+ extern def isBrowserEnv() at io: Bool =
259+ jsWeb "true"
260+ jsNode "false"
261+
177262 namespace Dom {
178263 extern def setupPopupHook() at io: Unit =
179264 jsWeb """
@@ -193,31 +278,40 @@ namespace FFI {
193278 """
194279
195280 extern def getDocumentBody() at {}: _Node =
196- js "(document.body)"
281+ jsWeb "(document.body)"
282+ jsNode "undefined"
197283
198284 extern def getDocumentElement() at {}: _Node =
199- js "(document.documentElement)"
285+ jsWeb "(document.documentElement)"
286+ jsNode "undefined"
200287
201288 extern def getElementByIdUnsafe(id: String) at io: _Node =
202- js "document.getElementById(${id})"
289+ jsWeb "document.getElementById(${id})"
290+ jsNode "undefined"
203291
204292 extern def createElement(tag: String) at io: _Node =
205- js "document.createElement(${tag})"
293+ jsWeb "document.createElement(${tag})"
294+ jsNode "undefined"
206295
207296 extern def createTextNode(text: String) at io: _Node =
208- js "document.createTextNode(${text})"
297+ jsWeb "document.createTextNode(${text})"
298+ jsNode "undefined"
209299
210300 extern def clone(node: _Node) at io: _Node =
211- js "(${node}.cloneNode(true))"
301+ jsWeb "(${node}.cloneNode(true))"
302+ jsNode "undefined"
212303
213304 extern def removeContent(node: _Node) at io: Unit =
214- js "${node}.innerHTML = ''"
305+ jsWeb "${node}.innerHTML = ''"
306+ jsNode "undefined"
215307
216308 extern def appendChild(node: _Node, child: _Node) at io: _Node =
217- js "${node}.appendChild(${child})"
309+ jsWeb "${node}.appendChild(${child})"
310+ jsNode "undefined"
218311
219312 extern def onClick(node: _Node, handler: () => Unit at {io, global}) at io: Unit =
220- js "(${node}.addEventListener('click', () => { $effekt.runToplevel((ks, k) => ${handler}(ks, k)) }))"
313+ jsWeb "(${node}.addEventListener('click', () => { $effekt.runToplevel((ks, k) => ${handler}(ks, k)) }))"
314+ jsNode "undefined"
221315
222316 extern def wait(time: Int, handler: () => Unit at {io, global}) at io: Unit =
223317 js "(setTimeout(() => $effekt.runToplevel((ks, k) => ${handler}(ks, k)), ${time}))"
@@ -231,7 +325,7 @@ namespace FFI {
231325 """
232326
233327 extern def getInputNumber(node: _Node): Int =
234- js """
328+ jsWeb """
235329 (function() {
236330 const num = ${node}.valueAsNumber
237331 if (Number.isNaN(num)) {
@@ -241,40 +335,51 @@ namespace FFI {
241335 }
242336 }) ()
243337 """
338+ jsNode "undefined"
244339
245340 extern def getInputBool(node: _Node): Bool =
246- js "${node}.checked"
341+ jsWeb "${node}.checked"
342+ jsNode "undefined"
247343
248344 extern def onWindowResize(handler: () => Unit at {io, global}) at io: Int =
249345 jsWeb "window.addEventListener('resize', () => { $effekt.runToplevel((ks, k) => ${handler}(ks, k)) })"
250346 jsNode "undefined"
251347
252348 extern def windowWidth() at io: Int =
253- js "(window.innerWidth)"
349+ jsWeb "(window.innerWidth)"
350+ jsNode "undefined"
254351
255352 extern def windowHeight() at io: Int =
256- js "(window.innerHeight)"
353+ jsWeb "(window.innerHeight)"
354+ jsNode "undefined"
257355
258356 extern def setAttribute(node: _Node, key: String, value: String) at io: _Node =
259- js "(function() { ${node}.setAttribute(${key}, ${value}); return ${node} })()"
357+ jsWeb "(function() { ${node}.setAttribute(${key}, ${value}); return ${node} })()"
358+ jsNode "undefined"
260359
261360 extern def style(node: _Node) at io: _Style =
262- js "(${node}.style)"
361+ jsWeb "(${node}.style)"
362+ jsNode "undefined"
263363
264364 extern def setProperty(style: _Style, key: String, value: String) at io: Unit =
265- js "(function() { ${style}.setProperty(${key}, ${value}); return ${style} })()"
365+ jsWeb "(function() { ${style}.setProperty(${key}, ${value}); return ${style} })()"
366+ jsNode "undefined"
266367
267368 extern def clientWidth(node: _Node): Int =
268- js "Number(${node}.clientWidth)"
369+ jsWeb "Number(${node}.clientWidth)"
370+ jsNode "undefined"
269371
270372 extern def clientHeight(node: _Node): Int =
271- js "Number(${node}.clientHeight)"
373+ jsWeb "Number(${node}.clientHeight)"
374+ jsNode "undefined"
272375
273376 extern def setWidth(node: _Node, width: Int): Int =
274- js "${node}.width = ${width}"
377+ jsWeb "${node}.width = ${width}"
378+ jsNode "undefined"
275379
276380 extern def setHeight(node: _Node, height: Int): Int =
277- js "${node}.height = ${height}"
381+ jsWeb "${node}.height = ${height}"
382+ jsNode "undefined"
278383 }
279384 namespace Canvas {
280385 extern def width(img: _Image) at io: Int =
@@ -284,8 +389,7 @@ namespace FFI {
284389 js "(${img}.height)"
285390
286391 extern def loadImageFromInputUnsafe(inputNode: _Node, handler: _Image => Unit at {io, global}) at io: Unit =
287- js
288- """
392+ jsWeb """
289393 (function() {
290394 const files = ${inputNode}.files
291395 const f = imageData => { $effekt.runToplevel((ks, k) => ${handler}(imageData, ks, k)) };
@@ -296,6 +400,7 @@ namespace FFI {
296400 }
297401 }) ()
298402 """
403+ jsNode "undefined"
299404
300405 extern def data(img: _Image) at io: _ImageData =
301406 js "(${img}.data)"
0 commit comments