forked from ppfun/pixelplanet
update packages
This commit is contained in:
parent
44af557581
commit
8c17f56b79
|
@ -302,10 +302,6 @@ msgstr ""
|
||||||
msgid "Failed to establish session after register :("
|
msgid "Failed to establish session after register :("
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/routes/api/auth/logout.js:13
|
|
||||||
msgid "You are not even logged in."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/routes/api/auth/verify.js:25
|
#: src/routes/api/auth/verify.js:25
|
||||||
#: src/routes/api/auth/verify.js:32
|
#: src/routes/api/auth/verify.js:32
|
||||||
msgid "Mail verification"
|
msgid "Mail verification"
|
||||||
|
@ -321,6 +317,10 @@ msgid ""
|
||||||
"request a new one."
|
"request a new one."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/routes/api/auth/logout.js:13
|
||||||
|
msgid "You are not even logged in."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/routes/api/auth/change_mail.js:41
|
#: src/routes/api/auth/change_mail.js:41
|
||||||
#: src/routes/api/auth/change_passwd.js:37
|
#: src/routes/api/auth/change_passwd.js:37
|
||||||
#: src/routes/api/auth/delete_account.js:38
|
#: src/routes/api/auth/delete_account.js:38
|
||||||
|
|
|
@ -162,7 +162,7 @@ msgstr ""
|
||||||
msgid "Look at past Canvases"
|
msgid "Look at past Canvases"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Converter.jsx:615
|
#: src/components/Converter.jsx:586
|
||||||
#: src/components/CoordinatesBox.jsx:32
|
#: src/components/CoordinatesBox.jsx:32
|
||||||
msgid "Copy to Clipboard"
|
msgid "Copy to Clipboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -186,6 +186,11 @@ msgstr ""
|
||||||
msgid "Restore"
|
msgid "Restore"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/buttons/CanvasSwitchButton.jsx:23
|
||||||
|
#: src/components/windows/index.js:22
|
||||||
|
msgid "Canvas Selection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/buttons/ExpandMenuButton.jsx:23
|
#: src/components/buttons/ExpandMenuButton.jsx:23
|
||||||
msgid "Close Menu"
|
msgid "Close Menu"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -202,11 +207,6 @@ msgstr ""
|
||||||
msgid "Open Chat"
|
msgid "Open Chat"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/buttons/CanvasSwitchButton.jsx:23
|
|
||||||
#: src/components/windows/index.js:22
|
|
||||||
msgid "Canvas Selection"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/actions/fetch.js:40
|
#: src/actions/fetch.js:40
|
||||||
msgid "You made too many requests"
|
msgid "You made too many requests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -602,7 +602,7 @@ msgstr ""
|
||||||
msgid "For when you are playing on a potato."
|
msgid "For when you are playing on a potato."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Converter.jsx:429
|
#: src/components/Converter.jsx:400
|
||||||
#: src/components/windows/Settings.jsx:195
|
#: src/components/windows/Settings.jsx:195
|
||||||
msgid "Light Grid"
|
msgid "Light Grid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -631,17 +631,6 @@ msgstr ""
|
||||||
msgid "Select Language"
|
msgid "Select Language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/windows/CanvasSelect.jsx:32
|
|
||||||
msgid ""
|
|
||||||
"Select the canvas you want to use. Every canvas is unique and has "
|
|
||||||
"different palettes, cooldown and requirements. Archive of closed canvases "
|
|
||||||
"can be accessed here:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/CanvasSelect.jsx:40
|
|
||||||
msgid "Archive"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/UserArea.jsx:27
|
#: src/components/windows/UserArea.jsx:27
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -670,39 +659,6 @@ msgstr ""
|
||||||
msgid "Consider joining us on Guilded:"
|
msgid "Consider joining us on Guilded:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/windows/Archive.jsx:20
|
|
||||||
msgid ""
|
|
||||||
"While we tend to not delete canvases, some canvases are started for fun or "
|
|
||||||
"as a request by users who currently like a meme. Those canvases can get "
|
|
||||||
"boring after a while and after weeks of no major change and if they really "
|
|
||||||
"aren't worth being kept active, we decide to remove them."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/Archive.jsx:22
|
|
||||||
msgid ""
|
|
||||||
"Here we collect those canvases to archive them in a proper way (which is "
|
|
||||||
"currently just one)."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/Archive.jsx:24
|
|
||||||
msgid "Political Compass Canvas"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/Archive.jsx:31
|
|
||||||
msgid ""
|
|
||||||
"This canvas got requested during a time of political conflicts on the main "
|
|
||||||
"Earth canvas. It was a 1024x1024 representation of the political compass "
|
|
||||||
"with a 5s cooldown and 60s stacking. It got launched on May 11th and "
|
|
||||||
"remained active for months till it got shut down on November 30th."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/Archive.jsx:32
|
|
||||||
msgid ""
|
|
||||||
"We decided to archive it as a timelapse with lossless encoded webm. Taking "
|
|
||||||
"a screenshot from the timelapse results in a perfect 1:1 representation of "
|
|
||||||
"how the canvas was at that time."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/windows/Register.jsx:81
|
#: src/components/windows/Register.jsx:81
|
||||||
msgid "Register new account here"
|
msgid "Register new account here"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -737,6 +693,50 @@ msgstr ""
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/CanvasSelect.jsx:32
|
||||||
|
msgid ""
|
||||||
|
"Select the canvas you want to use. Every canvas is unique and has "
|
||||||
|
"different palettes, cooldown and requirements. Archive of closed canvases "
|
||||||
|
"can be accessed here:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/CanvasSelect.jsx:40
|
||||||
|
msgid "Archive"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/Archive.jsx:20
|
||||||
|
msgid ""
|
||||||
|
"While we tend to not delete canvases, some canvases are started for fun or "
|
||||||
|
"as a request by users who currently like a meme. Those canvases can get "
|
||||||
|
"boring after a while and after weeks of no major change and if they really "
|
||||||
|
"aren't worth being kept active, we decide to remove them."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/Archive.jsx:22
|
||||||
|
msgid ""
|
||||||
|
"Here we collect those canvases to archive them in a proper way (which is "
|
||||||
|
"currently just one)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/Archive.jsx:24
|
||||||
|
msgid "Political Compass Canvas"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/Archive.jsx:31
|
||||||
|
msgid ""
|
||||||
|
"This canvas got requested during a time of political conflicts on the main "
|
||||||
|
"Earth canvas. It was a 1024x1024 representation of the political compass "
|
||||||
|
"with a 5s cooldown and 60s stacking. It got launched on May 11th and "
|
||||||
|
"remained active for months till it got shut down on November 30th."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/windows/Archive.jsx:32
|
||||||
|
msgid ""
|
||||||
|
"We decided to archive it as a timelapse with lossless encoded webm. Taking "
|
||||||
|
"a screenshot from the timelapse results in a perfect 1:1 representation of "
|
||||||
|
"how the canvas was at that time."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/windows/Chat.jsx:146
|
#: src/components/windows/Chat.jsx:146
|
||||||
msgid "Channel settings"
|
msgid "Channel settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -809,30 +809,6 @@ msgstr ""
|
||||||
msgid "Password must be shorter than 60 characters."
|
msgid "Password must be shorter than 60 characters."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/LogInArea.jsx:21
|
|
||||||
msgid "Login to access more features and stats."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/LogInArea.jsx:23
|
|
||||||
msgid "Login with Name or Mail:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/LogInArea.jsx:30
|
|
||||||
msgid "I forgot my Password."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/LogInArea.jsx:31
|
|
||||||
msgid "or login with:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/LogInArea.jsx:72
|
|
||||||
msgid "or register here:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/LogInArea.jsx:79
|
|
||||||
msgid "Register"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/ChangeMail.jsx:91
|
#: src/components/ChangeMail.jsx:91
|
||||||
#: src/components/ChangeName.jsx:68
|
#: src/components/ChangeName.jsx:68
|
||||||
#: src/components/ChangePassword.jsx:110
|
#: src/components/ChangePassword.jsx:110
|
||||||
|
@ -840,35 +816,6 @@ msgstr ""
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:27
|
|
||||||
msgid "Cooldown"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:33
|
|
||||||
msgid "Stacking till"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:35
|
|
||||||
msgid "Ranked"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:37
|
|
||||||
msgid "Requirements"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:39
|
|
||||||
msgid "User Account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:41
|
|
||||||
#, javascript-format
|
|
||||||
msgid "and ${ canvas.req } Pixels set"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/CanvasItem.jsx:45
|
|
||||||
msgid "Dimensions"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/UserAreaContent.jsx:63
|
#: src/components/UserAreaContent.jsx:63
|
||||||
msgid "Todays Placed Pixels"
|
msgid "Todays Placed Pixels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -914,6 +861,30 @@ msgstr ""
|
||||||
msgid "Social Settings"
|
msgid "Social Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInArea.jsx:21
|
||||||
|
msgid "Login to access more features and stats."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInArea.jsx:23
|
||||||
|
msgid "Login with Name or Mail:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInArea.jsx:30
|
||||||
|
msgid "I forgot my Password."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInArea.jsx:31
|
||||||
|
msgid "or login with:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInArea.jsx:72
|
||||||
|
msgid "or register here:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInArea.jsx:79
|
||||||
|
msgid "Register"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Rankings.jsx:28
|
#: src/components/Rankings.jsx:28
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -926,77 +897,6 @@ msgstr ""
|
||||||
msgid "Ranking updates every 5 min. Daily rankings get reset at midnight UTC."
|
msgid "Ranking updates every 5 min. Daily rankings get reset at midnight UTC."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/Converter.jsx:280
|
|
||||||
msgid "Choose Canvas"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:306
|
|
||||||
msgid "Palette Download"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:308
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Palette for ${ gimpLink }"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:326
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Credit for the Palette of the Moon goes to ${ starhouseLink }."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:329
|
|
||||||
msgid "Image Converter"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:330
|
|
||||||
msgid "Convert an image to canvas colors"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:341
|
|
||||||
msgid "Choose Strategy"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:368
|
|
||||||
msgid "Choose Color Mode"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:406
|
|
||||||
msgid "Add Grid (uncheck if you need a 1:1 template)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:431
|
|
||||||
#: src/components/Converter.jsx:447
|
|
||||||
msgid "Offset"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:477
|
|
||||||
msgid "Scale Image"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:489
|
|
||||||
msgid "Width"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:519
|
|
||||||
msgid "Height"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:557
|
|
||||||
msgid "Keep Ratio"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:570
|
|
||||||
msgid "Anti Aliasing"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:584
|
|
||||||
msgid "Reset"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Converter.jsx:603
|
|
||||||
msgid "Download Template"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/Admintools.jsx:184
|
#: src/components/Admintools.jsx:184
|
||||||
msgid "Build image on canvas."
|
msgid "Build image on canvas."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -1075,12 +975,104 @@ msgstr ""
|
||||||
msgid "User Name"
|
msgid "User Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/LogInForm.jsx:76
|
#: src/components/Converter.jsx:260
|
||||||
msgid "Name or Email"
|
msgid "Choose Canvas"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/LogInForm.jsx:87
|
#: src/components/Converter.jsx:286
|
||||||
msgid "LogIn"
|
msgid "Palette Download"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:288
|
||||||
|
#, javascript-format
|
||||||
|
msgid "Palette for ${ gimpLink }"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:306
|
||||||
|
#, javascript-format
|
||||||
|
msgid "Credit for the Palette of the Moon goes to ${ starhouseLink }."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:309
|
||||||
|
msgid "Image Converter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:310
|
||||||
|
msgid "Convert an image to canvas colors"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:332
|
||||||
|
msgid "Choose Strategy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:349
|
||||||
|
msgid "Choose Color Mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:377
|
||||||
|
msgid "Add Grid (uncheck if you need a 1:1 template)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:402
|
||||||
|
#: src/components/Converter.jsx:418
|
||||||
|
msgid "Offset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:448
|
||||||
|
msgid "Scale Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:460
|
||||||
|
msgid "Width"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:490
|
||||||
|
msgid "Height"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:528
|
||||||
|
msgid "Keep Ratio"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:541
|
||||||
|
msgid "Anti Aliasing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:555
|
||||||
|
msgid "Reset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/Converter.jsx:574
|
||||||
|
msgid "Download Template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:27
|
||||||
|
msgid "Cooldown"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:33
|
||||||
|
msgid "Stacking till"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:35
|
||||||
|
msgid "Ranked"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:37
|
||||||
|
msgid "Requirements"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:39
|
||||||
|
msgid "User Account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:41
|
||||||
|
#, javascript-format
|
||||||
|
msgid "and ${ canvas.req } Pixels set"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/CanvasItem.jsx:45
|
||||||
|
msgid "Dimensions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/UserMessages.jsx:28
|
#: src/components/UserMessages.jsx:28
|
||||||
|
@ -1117,14 +1109,14 @@ msgstr ""
|
||||||
msgid "Confirm New Password"
|
msgid "Confirm New Password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/ChangeName.jsx:64
|
|
||||||
msgid "New Username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: src/components/DeleteAccount.jsx:66
|
#: src/components/DeleteAccount.jsx:66
|
||||||
msgid "Yes, Delete My Account!"
|
msgid "Yes, Delete My Account!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/ChangeName.jsx:64
|
||||||
|
msgid "New Username"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/ChangeMail.jsx:59
|
#: src/components/ChangeMail.jsx:59
|
||||||
msgid ""
|
msgid ""
|
||||||
"Changed Mail successfully. We sent you a verification mail, "
|
"Changed Mail successfully. We sent you a verification mail, "
|
||||||
|
@ -1147,6 +1139,14 @@ msgstr ""
|
||||||
msgid "You have no users blocked"
|
msgid "You have no users blocked"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInForm.jsx:76
|
||||||
|
msgid "Name or Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/components/LogInForm.jsx:87
|
||||||
|
msgid "LogIn"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/windows/Help.jsx:15
|
#: src/components/windows/Help.jsx:15
|
||||||
#: src/components/windows/Settings.jsx:134
|
#: src/components/windows/Settings.jsx:134
|
||||||
msgctxt "keybinds"
|
msgctxt "keybinds"
|
||||||
|
|
25624
package-lock.json
generated
25624
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
136
package.json
136
package.json
|
@ -11,9 +11,9 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run webpack && npm run minify-css",
|
"build": "npm run webpack && npm run minify-css",
|
||||||
"build-en": "npm run extract && npm run minify-css",
|
"build-en": "npm run extract && npm run minify-css",
|
||||||
"webpack": "webpack --config ./webpack.config.web.babel.js && parallel-webpack --config ./webpack.config.client.babel.js",
|
"webpack": "webpack --config ./webpack.config.web.babel.js && webpack --config ./webpack.config.client.babel.js",
|
||||||
"minify-css": "babel-node scripts/minifyCss.js",
|
"minify-css": "babel-node scripts/minifyCss.js",
|
||||||
"extract": "webpack --env extract --config ./webpack.config.web.babel.js && webpack --env extract --config ./webpack.config.client.babel.js",
|
"extract": "webpack --env extract --config ./webpack.config.web.babel.js && webpack --env extract --env development --config ./webpack.config.client.babel.js",
|
||||||
"babel-node": "cd $INIT_CWD && babel-node",
|
"babel-node": "cd $INIT_CWD && babel-node",
|
||||||
"lint": "cd $INIT_CWD && eslint --ext .jsx --ext .js",
|
"lint": "cd $INIT_CWD && eslint --ext .jsx --ext .js",
|
||||||
"lint:src": "eslint --ext .jsx --ext .js src",
|
"lint:src": "eslint --ext .jsx --ext .js src",
|
||||||
|
@ -30,53 +30,52 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bluebird": "^3.5.0",
|
"bluebird": "^3.5.0",
|
||||||
"body-parser": "^1.17.2",
|
"body-parser": "^1.19.1",
|
||||||
"bufferutil": "^4.0.3",
|
"bufferutil": "^4.0.6",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"connect-redis": "^6.0.0",
|
"connect-redis": "^6.0.0",
|
||||||
"cookie": "^0.4.1",
|
"cookie": "^0.4.1",
|
||||||
"core-js": "^3.16.2",
|
"core-js": "^3.20.2",
|
||||||
"cors": "^2.8.4",
|
"cors": "^2.8.4",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"express": "^4.15.3",
|
"express": "^4.17.2",
|
||||||
"express-limiter": "^1.6.0",
|
"express-limiter": "^1.6.0",
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"http-proxy-agent": "^4.0.1",
|
"http-proxy-agent": "^4.0.1",
|
||||||
"image-q": "^2.1.2",
|
"image-q": "^3.0.5",
|
||||||
"ip-address": "^7.1.0",
|
|
||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"multer": "^1.4.3",
|
"multer": "^1.4.4",
|
||||||
"mysql2": "^2.3.0",
|
"mysql2": "^2.3.3",
|
||||||
"nodemailer": "^6.6.3",
|
"nodemailer": "^6.7.2",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.5.2",
|
||||||
"passport-discord": "^0.1.4",
|
"passport-discord": "^0.1.4",
|
||||||
"passport-facebook": "^3.0.0",
|
"passport-facebook": "^3.0.0",
|
||||||
"passport-google-oauth": "^2.0.0",
|
"passport-google-oauth": "^2.0.0",
|
||||||
"passport-json": "^1.2.0",
|
"passport-json": "^1.2.0",
|
||||||
"passport-reddit": "^0.2.4",
|
"passport-reddit": "^0.2.4",
|
||||||
"passport-vkontakte": "^0.5.0",
|
"passport-vkontakte": "^0.5.0",
|
||||||
"ppfun-captcha": "^1.6.5",
|
"ppfun-captcha": "^1.6.6",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.3.1",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.6",
|
||||||
"react-responsive": "^8.2.0",
|
"react-responsive": "^8.2.0",
|
||||||
"react-stay-scrolled": "^7.3.1",
|
"react-stay-scrolled": "^7.4.0",
|
||||||
"react-toggle-button": "^2.1.0",
|
"react-toggle-button": "^2.1.0",
|
||||||
"redis": "^3.1.2",
|
"redis": "^3.1.2",
|
||||||
"redlock": "^4.0.0",
|
"redlock": "^4.0.0",
|
||||||
"redux": "^4.1.1",
|
"redux": "^4.1.2",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.4.1",
|
||||||
"sequelize": "^6.6.5",
|
"sequelize": "^6.12.4",
|
||||||
"sharp": "^0.27.2",
|
"sharp": "^0.29.3",
|
||||||
"startaudiocontext": "^1.2.1",
|
"startaudiocontext": "^1.2.1",
|
||||||
"three": "^0.125.2",
|
"three": "^0.136.0",
|
||||||
"three-trackballcontrols": "^0.9.0",
|
"three-trackballcontrols": "^0.9.0",
|
||||||
"ttag": "^1.7.24",
|
"ttag": "^1.7.24",
|
||||||
"ttag-po-loader": "0.0.2",
|
"ttag-po-loader": "0.0.2",
|
||||||
|
@ -86,68 +85,65 @@
|
||||||
"ws": "^7.5.3"
|
"ws": "^7.5.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.14.8",
|
"@babel/cli": "^7.16.7",
|
||||||
"@babel/core": "^7.15.0",
|
"@babel/core": "^7.16.7",
|
||||||
"@babel/node": "^7.14.9",
|
"@babel/node": "^7.16.7",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||||
"@babel/plugin-proposal-decorators": "^7.14.5",
|
"@babel/plugin-proposal-decorators": "^7.16.7",
|
||||||
"@babel/plugin-proposal-do-expressions": "^7.14.5",
|
"@babel/plugin-proposal-do-expressions": "^7.16.7",
|
||||||
"@babel/plugin-proposal-export-default-from": "^7.14.5",
|
"@babel/plugin-proposal-export-default-from": "^7.16.7",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.14.5",
|
"@babel/plugin-proposal-export-namespace-from": "^7.16.7",
|
||||||
"@babel/plugin-proposal-function-bind": "^7.14.5",
|
"@babel/plugin-proposal-function-bind": "^7.16.7",
|
||||||
"@babel/plugin-proposal-function-sent": "^7.14.5",
|
"@babel/plugin-proposal-function-sent": "^7.16.7",
|
||||||
"@babel/plugin-proposal-json-strings": "^7.14.5",
|
"@babel/plugin-proposal-json-strings": "^7.16.7",
|
||||||
"@babel/plugin-proposal-logical-assignment-operators": "^7.14.5",
|
"@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||||
"@babel/plugin-proposal-numeric-separator": "^7.14.5",
|
"@babel/plugin-proposal-numeric-separator": "^7.16.7",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
|
"@babel/plugin-proposal-object-rest-spread": "^7.16.7",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
||||||
"@babel/plugin-proposal-pipeline-operator": "^7.15.0",
|
"@babel/plugin-proposal-pipeline-operator": "^7.16.7",
|
||||||
"@babel/plugin-proposal-throw-expressions": "^7.14.5",
|
"@babel/plugin-proposal-throw-expressions": "^7.16.7",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||||
"@babel/plugin-transform-flow-strip-types": "^7.14.5",
|
"@babel/plugin-transform-flow-strip-types": "^7.16.7",
|
||||||
"@babel/plugin-transform-react-constant-elements": "^7.14.5",
|
"@babel/plugin-transform-react-constant-elements": "^7.16.7",
|
||||||
"@babel/plugin-transform-react-inline-elements": "^7.14.5",
|
"@babel/plugin-transform-react-inline-elements": "^7.16.7",
|
||||||
"@babel/polyfill": "^7.11.5",
|
"@babel/preset-env": "^7.16.7",
|
||||||
"@babel/preset-env": "^7.15.0",
|
"@babel/preset-flow": "^7.16.7",
|
||||||
"@babel/preset-flow": "^7.14.5",
|
"@babel/preset-react": "^7.16.7",
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
"@babel/preset-typescript": "^7.15.0",
|
|
||||||
"assets-webpack-plugin": "^7.1.1",
|
"assets-webpack-plugin": "^7.1.1",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.3",
|
||||||
"babel-plugin-transform-react-pure-class-to-function": "^1.0.1",
|
"babel-plugin-transform-react-pure-class-to-function": "^1.0.1",
|
||||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
||||||
"babel-plugin-ttag": "^1.7.30",
|
"babel-plugin-ttag": "^1.7.30",
|
||||||
"clean-css": "^5.1.5",
|
"clean-css": "^5.2.2",
|
||||||
"copy-webpack-plugin": "^8.1.1",
|
"copy-webpack-plugin": "^10.2.0",
|
||||||
"css-loader": "^5.2.7",
|
"css-loader": "^6.5.1",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^8.6.0",
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-airbnb-base": "^14.2.1",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-plugin-flowtype": "^5.9.0",
|
"eslint-plugin-flowtype": "^8.0.3",
|
||||||
"eslint-plugin-import": "^2.24.1",
|
"eslint-plugin-import": "^2.25.3",
|
||||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-react": "^7.24.0",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"flow-bin": "^0.143.1",
|
"flow-bin": "^0.168.0",
|
||||||
"generate-package-json-webpack-plugin": "^2.1.3",
|
"generate-package-json-webpack-plugin": "^2.5.1",
|
||||||
"glob": "^7.1.7",
|
"glob": "^7.2.0",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"npm-check": "^5.9.2",
|
|
||||||
"parallel-webpack": "^2.6.0",
|
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
"react-svg-loader": "^3.0.3",
|
"react-svg-loader": "^3.0.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^3.3.1",
|
||||||
"ttag-cli": "^1.9.3",
|
"ttag-cli": "^1.9.3",
|
||||||
"webpack": "^5.51.1",
|
"webpack": "^5.65.0",
|
||||||
"webpack-bundle-analyzer": "^4.4.2",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
"webpack-cli": "^4.8.0",
|
"webpack-cli": "^4.9.1",
|
||||||
"webpack-dev-middleware": "^4.3.0",
|
"webpack-dev-middleware": "^5.3.0",
|
||||||
"webpack-hot-middleware": "^2.18.0",
|
"webpack-hot-middleware": "^2.25.1",
|
||||||
"webpack-node-externals": "^2.5.2",
|
"webpack-node-externals": "^3.0.0",
|
||||||
"write-file-webpack-plugin": "^4.0.2"
|
"write-file-webpack-plugin": "^4.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* @flow
|
|
||||||
*/
|
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
|
||||||
|
|
||||||
function patchImageQ() {
|
|
||||||
try {
|
|
||||||
/* fix image-q imports here
|
|
||||||
* Pretty dirty, but we did write an issue and they might
|
|
||||||
* update one day
|
|
||||||
*/
|
|
||||||
console.log('Patching image-q set-immediate import');
|
|
||||||
const regex = /core-js\/fn\/set-immediate/g;
|
|
||||||
const files = [
|
|
||||||
path.resolve(
|
|
||||||
__dirname, '..', 'node_modules',
|
|
||||||
'image-q', 'dist', 'esm', 'basicAPI.js',
|
|
||||||
),
|
|
||||||
path.resolve(
|
|
||||||
__dirname, '..', 'node_modules',
|
|
||||||
'image-q', 'dist', 'esm', 'helper.js',
|
|
||||||
),
|
|
||||||
];
|
|
||||||
files.forEach((file) => {
|
|
||||||
let fileContent = fs.readFileSync(file, 'utf8');
|
|
||||||
fileContent = fileContent.replace(
|
|
||||||
regex,
|
|
||||||
'core-js/features/set-immediate',
|
|
||||||
);
|
|
||||||
fs.writeFileSync(file, fileContent);
|
|
||||||
});
|
|
||||||
console.log('Patching image-q done');
|
|
||||||
} catch {
|
|
||||||
console.log('Error while patching image-q');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default function patch() {
|
|
||||||
patchImageQ();
|
|
||||||
}
|
|
|
@ -6,9 +6,14 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useSelector, shallowEqual } from 'react-redux';
|
import { useSelector, shallowEqual } from 'react-redux';
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
import iq from 'image-q';
|
|
||||||
import { jt, t } from 'ttag';
|
import { jt, t } from 'ttag';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ColorDistanceCalculators,
|
||||||
|
ImageQuantizerKernels,
|
||||||
|
quantizeImage,
|
||||||
|
getImageDataOfFile,
|
||||||
|
} from '../utils/image';
|
||||||
import printGIMPPalette from '../core/exportGPL';
|
import printGIMPPalette from '../core/exportGPL';
|
||||||
import { copyCanvasToClipboard } from '../utils/clipboard';
|
import { copyCanvasToClipboard } from '../utils/clipboard';
|
||||||
|
|
||||||
|
@ -43,181 +48,63 @@ function readFile(
|
||||||
fr.readAsDataURL(file);
|
fr.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorDistanceCalculators = [
|
function createCanvasFromImageData(imgData) {
|
||||||
'Euclidean',
|
const { width, height } = imgData;
|
||||||
'Manhattan',
|
const inputCanvas = document.createElement('canvas');
|
||||||
'CIEDE2000',
|
inputCanvas.width = width;
|
||||||
'CIE94Textiles',
|
inputCanvas.height = height;
|
||||||
'CIE94GraphicArts',
|
const inputCtx = inputCanvas.getContext('2d');
|
||||||
'EuclideanBT709NoAlpha',
|
inputCtx.putImageData(imgData, 0, 0);
|
||||||
'EuclideanBT709',
|
return inputCanvas;
|
||||||
'ManhattanBT709',
|
|
||||||
'CMetric',
|
|
||||||
'PNGQuant',
|
|
||||||
'ManhattanNommyde',
|
|
||||||
];
|
|
||||||
|
|
||||||
function quantize(
|
|
||||||
pointContainer,
|
|
||||||
colors,
|
|
||||||
colorDist,
|
|
||||||
strategy,
|
|
||||||
onProgress,
|
|
||||||
) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// read colors into palette
|
|
||||||
const palette = new iq.utils.Palette();
|
|
||||||
palette.add(iq.utils.Point.createByRGBA(0, 0, 0, 0));
|
|
||||||
colors.forEach((clr) => {
|
|
||||||
const [r, g, b] = clr;
|
|
||||||
const point = iq.utils.Point.createByRGBA(r, g, b, 255);
|
|
||||||
palette.add(point);
|
|
||||||
});
|
|
||||||
// construct color distance calculator
|
|
||||||
let distance;
|
|
||||||
switch (colorDist) {
|
|
||||||
case 'Euclidean':
|
|
||||||
distance = new iq.distance.Euclidean();
|
|
||||||
break;
|
|
||||||
case 'Manhattan':
|
|
||||||
distance = new iq.distance.Manhattan();
|
|
||||||
break;
|
|
||||||
case 'CIEDE2000':
|
|
||||||
distance = new iq.distance.CIEDE2000();
|
|
||||||
break;
|
|
||||||
case 'CIE94Textiles':
|
|
||||||
distance = new iq.distance.CIE94Textiles();
|
|
||||||
break;
|
|
||||||
case 'CIE94GraphicArts':
|
|
||||||
distance = new iq.distance.CIE94GraphicArts();
|
|
||||||
break;
|
|
||||||
case 'EuclideanBT709NoAlpha':
|
|
||||||
distance = new iq.distance.EuclideanBT709NoAlpha();
|
|
||||||
break;
|
|
||||||
case 'EuclideanBT709':
|
|
||||||
distance = new iq.distance.EuclideanBT709();
|
|
||||||
break;
|
|
||||||
case 'ManhattanBT709':
|
|
||||||
distance = new iq.distance.ManhattanBT709();
|
|
||||||
break;
|
|
||||||
case 'CMetric':
|
|
||||||
distance = new iq.distance.CMetric();
|
|
||||||
break;
|
|
||||||
case 'PNGQuant':
|
|
||||||
distance = new iq.distance.PNGQuant();
|
|
||||||
break;
|
|
||||||
case 'ManhattanNommyde':
|
|
||||||
distance = new iq.distance.ManhattanNommyde();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
distance = new iq.distance.Euclidean();
|
|
||||||
}
|
|
||||||
// construct image quantizer
|
|
||||||
let imageQuantizer;
|
|
||||||
if (strategy === 'Nearest') {
|
|
||||||
imageQuantizer = new iq.image.NearestColor(distance);
|
|
||||||
} else if (strategy === 'Riemersma') {
|
|
||||||
imageQuantizer = new iq.image.ErrorDiffusionRiemersma(distance);
|
|
||||||
} else {
|
|
||||||
imageQuantizer = new iq.image.ErrorDiffusionArray(
|
|
||||||
distance,
|
|
||||||
iq.image.ErrorDiffusionArrayKernel[strategy],
|
|
||||||
true,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// quantize
|
|
||||||
let outPointContainer;
|
|
||||||
const iterator = imageQuantizer.quantize(pointContainer, palette);
|
|
||||||
const next = () => {
|
|
||||||
try {
|
|
||||||
const result = iterator.next();
|
|
||||||
if (result.done) {
|
|
||||||
resolve(outPointContainer);
|
|
||||||
} else {
|
|
||||||
if (result.value.pointContainer) {
|
|
||||||
outPointContainer = result.value.pointContainer;
|
|
||||||
}
|
|
||||||
if (onProgress) onProgress(result.value.progress);
|
|
||||||
setTimeout(next, 10);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
setTimeout(next, 10);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawPixels(idxi8, width, height) {
|
function addGrid(imgData, lightGrid, offsetX, offsetY) {
|
||||||
const can = document.createElement('canvas');
|
const image = createCanvasFromImageData(imgData);
|
||||||
can.width = width;
|
const { width, height } = image;
|
||||||
can.height = height;
|
|
||||||
const ctx = can.getContext('2d');
|
|
||||||
ctx.imageSmoothingEnabled = false;
|
|
||||||
ctx.mozImageSmoothingEnabled = false;
|
|
||||||
ctx.webkitImageSmoothingEnabled = false;
|
|
||||||
ctx.msImageSmoothingEnabled = false;
|
|
||||||
|
|
||||||
const imgd = ctx.createImageData(can.width, can.height);
|
|
||||||
const { data } = imgd;
|
|
||||||
for (let i = 0, len = data.length; i < len; ++i) data[i] = idxi8[i];
|
|
||||||
|
|
||||||
ctx.putImageData(imgd, 0, 0);
|
|
||||||
return can;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addGrid(img, lightGrid, offsetX, offsetY) {
|
|
||||||
const can = document.createElement('canvas');
|
const can = document.createElement('canvas');
|
||||||
const ctx = can.getContext('2d');
|
const ctx = can.getContext('2d');
|
||||||
can.width = img.width * 5;
|
can.width = width * 5;
|
||||||
can.height = img.height * 5;
|
can.height = height * 5;
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
ctx.mozImageSmoothingEnabled = false;
|
ctx.mozImageSmoothingEnabled = false;
|
||||||
ctx.webkitImageSmoothingEnabled = false;
|
ctx.webkitImageSmoothingEnabled = false;
|
||||||
ctx.msImageSmoothingEnabled = false;
|
ctx.msImageSmoothingEnabled = false;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.scale(5.0, 5.0);
|
ctx.scale(5.0, 5.0);
|
||||||
ctx.drawImage(img, 0, 0);
|
ctx.drawImage(image, 0, 0);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
ctx.fillStyle = (lightGrid) ? '#DDDDDD' : '#222222';
|
ctx.fillStyle = (lightGrid) ? '#DDDDDD' : '#222222';
|
||||||
for (let i = 0; i <= img.width; i += 1) {
|
for (let i = 0; i <= width; i += 1) {
|
||||||
const thick = ((i + (offsetX * 1)) % 10 === 0) ? 2 : 1;
|
const thick = ((i + (offsetX * 1)) % 10 === 0) ? 2 : 1;
|
||||||
ctx.fillRect(i * 5, 0, thick, can.height);
|
ctx.fillRect(i * 5, 0, thick, can.height);
|
||||||
}
|
}
|
||||||
for (let j = 0; j <= img.height; j += 1) {
|
for (let j = 0; j <= height; j += 1) {
|
||||||
const thick = ((j + (offsetY * 1)) % 10 === 0) ? 2 : 1;
|
const thick = ((j + (offsetY * 1)) % 10 === 0) ? 2 : 1;
|
||||||
ctx.fillRect(0, j * 5, can.width, thick);
|
ctx.fillRect(0, j * 5, can.width, thick);
|
||||||
}
|
}
|
||||||
return can;
|
return ctx.getImageData(0, 0, can.width, can.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
function scaleImage(img, width, height, doAA) {
|
function scaleImage(imgData, width, height, doAA) {
|
||||||
const can = document.createElement('canvas');
|
const can = document.createElement('canvas');
|
||||||
const ctxo = can.getContext('2d');
|
const ctxo = can.getContext('2d');
|
||||||
can.width = width;
|
can.width = width;
|
||||||
can.height = height;
|
can.height = height;
|
||||||
const scaleX = width / img.width;
|
const scaleX = width / imgData.width;
|
||||||
const scaleY = height / img.height;
|
const scaleY = height / imgData.height;
|
||||||
if (doAA) {
|
if (doAA) {
|
||||||
// scale with canvas for antialiasing
|
// scale with canvas for antialiasing
|
||||||
|
const image = createCanvasFromImageData(imgData);
|
||||||
ctxo.save();
|
ctxo.save();
|
||||||
ctxo.scale(scaleX, scaleY);
|
ctxo.scale(scaleX, scaleY);
|
||||||
ctxo.drawImage(img, 0, 0);
|
ctxo.drawImage(image, 0, 0);
|
||||||
ctxo.restore();
|
ctxo.restore();
|
||||||
return can;
|
return ctxo.getImageData(0, 0, width, height);
|
||||||
}
|
}
|
||||||
// scale manually
|
// scale manually
|
||||||
const imdo = ctxo.createImageData(width, height);
|
const imdo = ctxo.createImageData(width, height);
|
||||||
const { data: datao } = imdo;
|
const { data: datao } = imdo;
|
||||||
const cani = document.createElement('canvas');
|
const { data: datai } = imgData;
|
||||||
cani.width = img.width;
|
|
||||||
cani.height = img.height;
|
|
||||||
const ctxi = cani.getContext('2d');
|
|
||||||
ctxi.drawImage(img, 0, 0);
|
|
||||||
const imdi = ctxi.getImageData(0, 0, img.width, img.height);
|
|
||||||
const { data: datai } = imdi;
|
|
||||||
for (let y = 0; y < height; y += 1) {
|
for (let y = 0; y < height; y += 1) {
|
||||||
for (let x = 0; x < width; x += 1) {
|
for (let x = 0; x < width; x += 1) {
|
||||||
let posi = (Math.round(x / scaleX) + Math.round(y / scaleY)
|
let posi = (Math.round(x / scaleX) + Math.round(y / scaleY)
|
||||||
|
@ -229,30 +116,29 @@ function scaleImage(img, width, height, doAA) {
|
||||||
datao[poso] = datai[posi];
|
datao[poso] = datai[posi];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctxo.putImageData(imdo, 0, 0);
|
return imdo;
|
||||||
return can;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let newOpts = null;
|
let renderOpts = null;
|
||||||
let rendering = false;
|
let rendering = false;
|
||||||
async function renderOutputImage(opts) {
|
async function renderOutputImage(opts) {
|
||||||
if (!opts.file) {
|
if (!opts.file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
renderOpts = opts;
|
||||||
if (rendering) {
|
if (rendering) {
|
||||||
newOpts = opts;
|
console.log('skip rendering');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log('render');
|
||||||
rendering = true;
|
rendering = true;
|
||||||
let renderOpts = opts;
|
|
||||||
while (renderOpts) {
|
while (renderOpts) {
|
||||||
newOpts = null;
|
|
||||||
const {
|
const {
|
||||||
file, dither, grid, scaling,
|
file, dither, grid, scaling,
|
||||||
} = renderOpts;
|
} = renderOpts;
|
||||||
|
renderOpts = null;
|
||||||
if (file) {
|
if (file) {
|
||||||
let image = file;
|
let image = file;
|
||||||
let pointContainer = null;
|
|
||||||
if (scaling.enabled) {
|
if (scaling.enabled) {
|
||||||
// scale
|
// scale
|
||||||
const { width, height, aa } = scaling;
|
const { width, height, aa } = scaling;
|
||||||
|
@ -262,29 +148,18 @@ async function renderOutputImage(opts) {
|
||||||
height,
|
height,
|
||||||
aa,
|
aa,
|
||||||
);
|
);
|
||||||
pointContainer = iq.utils.PointContainer.fromHTMLCanvasElement(image);
|
|
||||||
} else {
|
|
||||||
pointContainer = iq.utils.PointContainer.fromHTMLImageElement(image);
|
|
||||||
}
|
}
|
||||||
// dither
|
// dither
|
||||||
const { colors, strategy, colorDist } = dither;
|
const { colors, strategy, colorDist } = dither;
|
||||||
const progEl = document.getElementById('qprog');
|
const progEl = document.getElementById('qprog');
|
||||||
// eslint-disable-next-line no-await-in-loop
|
image = await quantizeImage(colors, image, {
|
||||||
pointContainer = await quantize(
|
|
||||||
pointContainer,
|
|
||||||
colors,
|
|
||||||
colorDist,
|
|
||||||
strategy,
|
strategy,
|
||||||
(progress) => {
|
colorDist,
|
||||||
|
onProgress: (progress) => {
|
||||||
progEl.innerHTML = `Loading... ${Math.round(progress)} %`;
|
progEl.innerHTML = `Loading... ${Math.round(progress)} %`;
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
progEl.innerHTML = 'Done';
|
progEl.innerHTML = 'Done';
|
||||||
image = drawPixels(
|
|
||||||
pointContainer.toUint8Array(),
|
|
||||||
image.width,
|
|
||||||
image.height,
|
|
||||||
);
|
|
||||||
// grid
|
// grid
|
||||||
if (grid.enabled) {
|
if (grid.enabled) {
|
||||||
const { light, offsetX, offsetY } = grid;
|
const { light, offsetX, offsetY } = grid;
|
||||||
|
@ -300,10 +175,8 @@ async function renderOutputImage(opts) {
|
||||||
output.width = image.width;
|
output.width = image.width;
|
||||||
output.height = image.height;
|
output.height = image.height;
|
||||||
const ctx = output.getContext('2d');
|
const ctx = output.getContext('2d');
|
||||||
ctx.drawImage(image, 0, 0);
|
ctx.putImageData(image, 0, 0);
|
||||||
}
|
}
|
||||||
// render again if requested in the meantime
|
|
||||||
renderOpts = newOpts;
|
|
||||||
}
|
}
|
||||||
rendering = false;
|
rendering = false;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +194,7 @@ function Converter() {
|
||||||
], shallowEqual);
|
], shallowEqual);
|
||||||
|
|
||||||
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
||||||
const [selectedFile, selectFile] = useState(null);
|
const [inputImageData, setInputImageData] = useState(null);
|
||||||
const [selectedStrategy, selectStrategy] = useState('Nearest');
|
const [selectedStrategy, selectStrategy] = useState('Nearest');
|
||||||
const [selectedColorDist, selectColorDist] = useState('Euclidean');
|
const [selectedColorDist, selectColorDist] = useState('Euclidean');
|
||||||
const [selectedScaleKeepRatio, selectScaleKeepRatio] = useState(true);
|
const [selectedScaleKeepRatio, selectScaleKeepRatio] = useState(true);
|
||||||
|
@ -339,7 +212,7 @@ function Converter() {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedFile) {
|
if (inputImageData) {
|
||||||
const canvas = canvases[selectedCanvas];
|
const canvas = canvases[selectedCanvas];
|
||||||
const dither = {
|
const dither = {
|
||||||
colors: canvas.colors.slice(canvas.cli),
|
colors: canvas.colors.slice(canvas.cli),
|
||||||
|
@ -347,13 +220,20 @@ function Converter() {
|
||||||
colorDist: selectedColorDist,
|
colorDist: selectedColorDist,
|
||||||
};
|
};
|
||||||
renderOutputImage({
|
renderOutputImage({
|
||||||
file: selectedFile,
|
file: inputImageData,
|
||||||
dither,
|
dither,
|
||||||
grid: gridData,
|
grid: gridData,
|
||||||
scaling: scaleData,
|
scaling: scaleData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}, [
|
||||||
|
inputImageData,
|
||||||
|
selectedStrategy,
|
||||||
|
selectedColorDist,
|
||||||
|
scaleData,
|
||||||
|
selectedCanvas,
|
||||||
|
gridData,
|
||||||
|
]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
enabled: gridEnabled,
|
enabled: gridEnabled,
|
||||||
|
@ -379,6 +259,7 @@ function Converter() {
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div style={{ textAlign: 'center' }}>
|
||||||
<div className="modalcotext">{t`Choose Canvas`}:
|
<div className="modalcotext">{t`Choose Canvas`}:
|
||||||
<select
|
<select
|
||||||
|
value={selectedCanvas}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const sel = e.target;
|
const sel = e.target;
|
||||||
selectCanvas(sel.options[sel.selectedIndex].value);
|
selectCanvas(sel.options[sel.selectedIndex].value);
|
||||||
|
@ -391,7 +272,6 @@ function Converter() {
|
||||||
? null
|
? null
|
||||||
: (
|
: (
|
||||||
<option
|
<option
|
||||||
selected={canvas === selectedCanvas}
|
|
||||||
value={canvas}
|
value={canvas}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
@ -431,28 +311,36 @@ function Converter() {
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="imgfile"
|
id="imgfile"
|
||||||
onChange={(evt) => {
|
onChange={async (evt) => {
|
||||||
const fileSel = evt.target;
|
const fileSel = evt.target;
|
||||||
const file = (!fileSel.files || !fileSel.files[0])
|
const file = (!fileSel.files || !fileSel.files[0])
|
||||||
? null : fileSel.files[0];
|
? null : fileSel.files[0];
|
||||||
readFile(file, selectFile, setScaleData);
|
if (!file) {
|
||||||
|
setInputImageData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const imageData = await getImageDataOfFile(file);
|
||||||
|
setScaleData({
|
||||||
|
enabled: false,
|
||||||
|
width: imageData.width,
|
||||||
|
height: imageData.height,
|
||||||
|
aa: true,
|
||||||
|
});
|
||||||
|
setInputImageData(imageData);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p className="modalcotext">{t`Choose Strategy`}:
|
<p className="modalcotext">{t`Choose Strategy`}:
|
||||||
<select
|
<select
|
||||||
|
value={selectedStrategy}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const sel = e.target;
|
const sel = e.target;
|
||||||
selectStrategy(sel.options[sel.selectedIndex].value);
|
selectStrategy(sel.options[sel.selectedIndex].value);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
['Nearest',
|
ImageQuantizerKernels.map((strat) => (
|
||||||
'Riemersma',
|
|
||||||
...Object.keys(iq.image.ErrorDiffusionArrayKernel),
|
|
||||||
].map((strat) => (
|
|
||||||
<option
|
<option
|
||||||
value={strat}
|
value={strat}
|
||||||
selected={(selectedStrategy === strat)}
|
|
||||||
>{strat}</option>
|
>{strat}</option>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -460,6 +348,7 @@ function Converter() {
|
||||||
</p>
|
</p>
|
||||||
<p className="modalcotext">{t`Choose Color Mode`}:
|
<p className="modalcotext">{t`Choose Color Mode`}:
|
||||||
<select
|
<select
|
||||||
|
value={selectedColorDist}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const sel = e.target;
|
const sel = e.target;
|
||||||
selectColorDist(sel.options[sel.selectedIndex].value);
|
selectColorDist(sel.options[sel.selectedIndex].value);
|
||||||
|
@ -469,7 +358,6 @@ function Converter() {
|
||||||
ColorDistanceCalculators.map((strat) => (
|
ColorDistanceCalculators.map((strat) => (
|
||||||
<option
|
<option
|
||||||
value={strat}
|
value={strat}
|
||||||
selected={(selectedColorDist === strat)}
|
|
||||||
>{strat}</option>
|
>{strat}</option>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -581,8 +469,8 @@ function Converter() {
|
||||||
const newWidth = (e.target.value > 1024)
|
const newWidth = (e.target.value > 1024)
|
||||||
? 1024 : e.target.value;
|
? 1024 : e.target.value;
|
||||||
if (!newWidth) return;
|
if (!newWidth) return;
|
||||||
if (selectedScaleKeepRatio && selectedFile) {
|
if (selectedScaleKeepRatio && inputImageData) {
|
||||||
const ratio = selectedFile.width / selectedFile.height;
|
const ratio = inputImageData.width / inputImageData.height;
|
||||||
const newHeight = Math.round(newWidth / ratio);
|
const newHeight = Math.round(newWidth / ratio);
|
||||||
if (newHeight <= 0) return;
|
if (newHeight <= 0) return;
|
||||||
setScaleData({
|
setScaleData({
|
||||||
|
@ -611,8 +499,8 @@ function Converter() {
|
||||||
const nuHeight = (e.target.value > 1024)
|
const nuHeight = (e.target.value > 1024)
|
||||||
? 1024 : e.target.value;
|
? 1024 : e.target.value;
|
||||||
if (!nuHeight) return;
|
if (!nuHeight) return;
|
||||||
if (selectedScaleKeepRatio && selectedFile) {
|
if (selectedScaleKeepRatio && inputImageData) {
|
||||||
const ratio = selectedFile.width / selectedFile.height;
|
const ratio = inputImageData.width / inputImageData.height;
|
||||||
const nuWidth = Math.round(ratio * nuHeight);
|
const nuWidth = Math.round(ratio * nuHeight);
|
||||||
if (nuWidth <= 0) return;
|
if (nuWidth <= 0) return;
|
||||||
setScaleData({
|
setScaleData({
|
||||||
|
@ -655,11 +543,11 @@ function Converter() {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (selectedFile) {
|
if (inputImageData) {
|
||||||
setScaleData({
|
setScaleData({
|
||||||
...scaleData,
|
...scaleData,
|
||||||
width: selectedFile.width,
|
width: inputImageData.width,
|
||||||
height: selectedFile.height,
|
height: inputImageData.height,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -669,7 +557,7 @@ function Converter() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
{(selectedFile)
|
{(inputImageData)
|
||||||
? (
|
? (
|
||||||
<div>
|
<div>
|
||||||
<p id="qprog">...</p>
|
<p id="qprog">...</p>
|
||||||
|
@ -704,4 +592,4 @@ function Converter() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.memo(Converter);
|
export default Converter;
|
||||||
|
|
|
@ -14,6 +14,7 @@ const DailyRankings = () => {
|
||||||
return (
|
return (
|
||||||
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
||||||
<table>
|
<table>
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>user</th>
|
<th>user</th>
|
||||||
|
@ -21,6 +22,8 @@ const DailyRankings = () => {
|
||||||
<th># Total</th>
|
<th># Total</th>
|
||||||
<th>Total Pixels</th>
|
<th>Total Pixels</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
{
|
{
|
||||||
totalDailyRanking.map((rank) => (
|
totalDailyRanking.map((rank) => (
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -32,6 +35,7 @@ const DailyRankings = () => {
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -35,6 +35,7 @@ function LanguageSelect() {
|
||||||
<div style={{ textAlign: 'right' }}>
|
<div style={{ textAlign: 'right' }}>
|
||||||
<span>
|
<span>
|
||||||
<select
|
<select
|
||||||
|
value={langSel}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const sel = e.target;
|
const sel = e.target;
|
||||||
setLangSel(sel.options[sel.selectedIndex].value);
|
setLangSel(sel.options[sel.selectedIndex].value);
|
||||||
|
@ -43,7 +44,6 @@ function LanguageSelect() {
|
||||||
{
|
{
|
||||||
langs.map(([l]) => (
|
langs.map(([l]) => (
|
||||||
<option
|
<option
|
||||||
selected={l === langSel}
|
|
||||||
value={l}
|
value={l}
|
||||||
>
|
>
|
||||||
{l.toUpperCase()}
|
{l.toUpperCase()}
|
||||||
|
|
|
@ -12,6 +12,7 @@ const TotalRankings = () => {
|
||||||
return (
|
return (
|
||||||
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
||||||
<table>
|
<table>
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>user</th>
|
<th>user</th>
|
||||||
|
@ -19,6 +20,8 @@ const TotalRankings = () => {
|
||||||
<th># Today</th>
|
<th># Today</th>
|
||||||
<th>Pixels Today</th>
|
<th>Pixels Today</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
{
|
{
|
||||||
totalRanking.map((rank) => (
|
totalRanking.map((rank) => (
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -30,6 +33,7 @@ const TotalRankings = () => {
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,7 +36,7 @@ const Help = () => {
|
||||||
const mailLink = <a href="mailto:pixelplanetdev@gmail.com">pixelplanetdev@gmail.com</a>;
|
const mailLink = <a href="mailto:pixelplanetdev@gmail.com">pixelplanetdev@gmail.com</a>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
|
<div style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
|
||||||
<p className="modaltext">
|
<p className="modaltext">
|
||||||
{t`Place color pixels on a large canvas with other players online!`}<br />
|
{t`Place color pixels on a large canvas with other players online!`}<br />
|
||||||
{t`Our main canvas is a huge worldmap, you can place wherever you like, but you will have to wait a specific \
|
{t`Our main canvas is a huge worldmap, you can place wherever you like, but you will have to wait a specific \
|
||||||
|
@ -85,7 +85,7 @@ can be downloaded from mega.nz here: `}<a href="https://mega.nz/#!JpkBwAbJ!EnSLl
|
||||||
{jt`Click ${mouseSymbol} middle mouse button or ${touchSymbol} long-tap to select current hovering color`}<br />
|
{jt`Click ${mouseSymbol} middle mouse button or ${touchSymbol} long-tap to select current hovering color`}<br />
|
||||||
</div>
|
</div>
|
||||||
<p>{t`Partners:`} <a href="https://www.crazygames.com/c/io" target="_blank" rel="noopener noreferrer">crazygames.com</a></p>
|
<p>{t`Partners:`} <a href="https://www.crazygames.com/c/io" target="_blank" rel="noopener noreferrer">crazygames.com</a></p>
|
||||||
</p>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ const SettingsItemSelect = ({
|
||||||
<h3 style={titleStyles} className="modaltitle">{title}</h3>
|
<h3 style={titleStyles} className="modaltitle">{title}</h3>
|
||||||
{(icon) && <img alt="" src={icon} />}
|
{(icon) && <img alt="" src={icon} />}
|
||||||
<select
|
<select
|
||||||
|
value={selected}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const sel = e.target;
|
const sel = e.target;
|
||||||
onSelect(sel.options[sel.selectedIndex].value);
|
onSelect(sel.options[sel.selectedIndex].value);
|
||||||
|
@ -63,7 +64,6 @@ const SettingsItemSelect = ({
|
||||||
{
|
{
|
||||||
values.map((value) => (
|
values.map((value) => (
|
||||||
<option
|
<option
|
||||||
selected={value === selected}
|
|
||||||
value={value}
|
value={value}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
|
|
186
src/utils/image.js
Normal file
186
src/utils/image.js
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/**
|
||||||
|
* Basic image manipulation and quantization
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { utils, distance, image } from 'image-q';
|
||||||
|
|
||||||
|
|
||||||
|
export const ColorDistanceCalculators = [
|
||||||
|
'Euclidean',
|
||||||
|
'Manhattan',
|
||||||
|
'CIEDE2000',
|
||||||
|
'CIE94Textiles',
|
||||||
|
'CIE94GraphicArts',
|
||||||
|
'EuclideanBT709NoAlpha',
|
||||||
|
'EuclideanBT709',
|
||||||
|
'ManhattanBT709',
|
||||||
|
'CMetric',
|
||||||
|
'PNGQuant',
|
||||||
|
'ManhattanNommyde',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ImageQuantizerKernels = [
|
||||||
|
'Nearest',
|
||||||
|
'Riemersma',
|
||||||
|
'FloydSteinberg',
|
||||||
|
'FalseFloydSteinberg',
|
||||||
|
'Stucki',
|
||||||
|
'Atkinson',
|
||||||
|
'Jarvis',
|
||||||
|
'Burkes',
|
||||||
|
'Sierra',
|
||||||
|
'TwoSierra',
|
||||||
|
'SierraLite',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getImageDataOfFile(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fr = new FileReader();
|
||||||
|
fr.onload = () => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const cani = document.createElement('canvas');
|
||||||
|
cani.width = img.width;
|
||||||
|
cani.height = img.height;
|
||||||
|
const ctxi = cani.getContext('2d');
|
||||||
|
ctxi.drawImage(img, 0, 0);
|
||||||
|
const imdi = ctxi.getImageData(0, 0, img.width, img.height);
|
||||||
|
resolve(imdi);
|
||||||
|
};
|
||||||
|
img.onerror = (error) => reject(error);
|
||||||
|
img.src = fr.result;
|
||||||
|
};
|
||||||
|
fr.onerror = (error) => reject(error);
|
||||||
|
fr.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPointContainerFromImageData(imageData) {
|
||||||
|
const { width, height, data } = imageData;
|
||||||
|
console.log('create container from image data', data);
|
||||||
|
const pointContainer = new utils.PointContainer();
|
||||||
|
pointContainer.setWidth(width);
|
||||||
|
pointContainer.setHeight(height);
|
||||||
|
const pointArray = pointContainer.getPointArray();
|
||||||
|
let i = 0;
|
||||||
|
while (i < data.length) {
|
||||||
|
const point = utils.Point.createByRGBA(
|
||||||
|
data[i++],
|
||||||
|
data[i++],
|
||||||
|
data[i++],
|
||||||
|
data[i++],
|
||||||
|
);
|
||||||
|
pointArray.push(point);
|
||||||
|
}
|
||||||
|
return pointContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createImageDataFromPointContainer(pointContainer) {
|
||||||
|
const width = pointContainer.getWidth();
|
||||||
|
const height = pointContainer.getHeight();
|
||||||
|
const data = pointContainer.toUint8Array();
|
||||||
|
const can = document.createElement('canvas');
|
||||||
|
can.width = width;
|
||||||
|
can.height = height;
|
||||||
|
const ctx = can.getContext('2d');
|
||||||
|
const idata = ctx.createImageData(width, height);
|
||||||
|
idata.data.set(data);
|
||||||
|
return idata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function quantizeImage(colors, imageData, opts) {
|
||||||
|
console.log('quantize image');
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const pointContainer = createPointContainerFromImageData(imageData);
|
||||||
|
const strategy = opts.strategy || 'Nearest';
|
||||||
|
const colorDist = opts.colorDist || 'Euclidean';
|
||||||
|
// create palette
|
||||||
|
const palette = new utils.Palette();
|
||||||
|
palette.add(utils.Point.createByRGBA(0, 0, 0, 0));
|
||||||
|
for (let i = 0; i < colors.length; i += 1) {
|
||||||
|
const [r, g, b] = colors[i];
|
||||||
|
const point = utils.Point.createByRGBA(r, g, b, 255);
|
||||||
|
palette.add(point);
|
||||||
|
}
|
||||||
|
console.log('palette', palette);
|
||||||
|
// construct color distance calculator
|
||||||
|
let distCalc;
|
||||||
|
switch (colorDist) {
|
||||||
|
case 'Euclidean':
|
||||||
|
distCalc = new distance.Euclidean();
|
||||||
|
break;
|
||||||
|
case 'Manhattan':
|
||||||
|
distCalc = new distance.Manhattan();
|
||||||
|
break;
|
||||||
|
case 'CIEDE2000':
|
||||||
|
distCalc = new distance.CIEDE2000();
|
||||||
|
break;
|
||||||
|
case 'CIE94Textiles':
|
||||||
|
distCalc = new distance.CIE94Textiles();
|
||||||
|
break;
|
||||||
|
case 'CIE94GraphicArts':
|
||||||
|
distCalc = new distance.CIE94GraphicArts();
|
||||||
|
break;
|
||||||
|
case 'EuclideanBT709NoAlpha':
|
||||||
|
distCalc = new distance.EuclideanBT709NoAlpha();
|
||||||
|
break;
|
||||||
|
case 'EuclideanBT709':
|
||||||
|
distCalc = new distance.EuclideanBT709();
|
||||||
|
break;
|
||||||
|
case 'ManhattanBT709':
|
||||||
|
distCalc = new distance.ManhattanBT709();
|
||||||
|
break;
|
||||||
|
case 'CMetric':
|
||||||
|
distCalc = new distance.CMetric();
|
||||||
|
break;
|
||||||
|
case 'PNGQuant':
|
||||||
|
distCalc = new distance.PNGQuant();
|
||||||
|
break;
|
||||||
|
case 'ManhattanNommyde':
|
||||||
|
distCalc = new distance.ManhattanNommyde();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
distCalc = new distance.Euclidean();
|
||||||
|
}
|
||||||
|
// idk why i need this :/
|
||||||
|
if (distCalc._setDefaults) distCalc._setDefaults();
|
||||||
|
// construct image quantizer
|
||||||
|
let imageQuantizer;
|
||||||
|
if (strategy === 'Nearest') {
|
||||||
|
imageQuantizer = new image.NearestColor(distCalc);
|
||||||
|
} else if (strategy === 'Riemersma') {
|
||||||
|
imageQuantizer = new image.ErrorDiffusionRiemersma(distCalc);
|
||||||
|
} else {
|
||||||
|
imageQuantizer = new image.ErrorDiffusionArray(
|
||||||
|
distCalc,
|
||||||
|
image.ErrorDiffusionArrayKernel[strategy],
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log('quantizer', imageQuantizer);
|
||||||
|
// quantize
|
||||||
|
let outPointContainer;
|
||||||
|
const iterator = imageQuantizer.quantize(pointContainer, palette);
|
||||||
|
const next = () => {
|
||||||
|
try {
|
||||||
|
const result = iterator.next();
|
||||||
|
if (result.done) {
|
||||||
|
resolve(createImageDataFromPointContainer(outPointContainer));
|
||||||
|
} else {
|
||||||
|
if (result.value.pointContainer) {
|
||||||
|
outPointContainer = result.value.pointContainer;
|
||||||
|
}
|
||||||
|
if (opts.onProgress) opts.onProgress(result.value.progress);
|
||||||
|
setTimeout(next, 0);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setTimeout(next, 0);
|
||||||
|
});
|
||||||
|
}
|
|
@ -43,18 +43,7 @@ export function buildWebpackClientConfig(
|
||||||
|
|
||||||
const babelPlugins = [
|
const babelPlugins = [
|
||||||
'@babel/plugin-transform-flow-strip-types',
|
'@babel/plugin-transform-flow-strip-types',
|
||||||
['@babel/plugin-proposal-decorators', { legacy: true }],
|
|
||||||
'@babel/plugin-proposal-function-sent',
|
|
||||||
'@babel/plugin-proposal-export-namespace-from',
|
|
||||||
'@babel/plugin-proposal-numeric-separator',
|
|
||||||
'@babel/plugin-proposal-throw-expressions',
|
'@babel/plugin-proposal-throw-expressions',
|
||||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
|
||||||
['@babel/plugin-proposal-private-methods', { loose: true }],
|
|
||||||
[
|
|
||||||
'@babel/plugin-proposal-private-property-in-object',
|
|
||||||
{ loose: true },
|
|
||||||
],
|
|
||||||
'@babel/proposal-object-rest-spread',
|
|
||||||
// react-optimize
|
// react-optimize
|
||||||
'@babel/transform-react-constant-elements',
|
'@babel/transform-react-constant-elements',
|
||||||
'@babel/transform-react-inline-elements',
|
'@babel/transform-react-inline-elements',
|
||||||
|
@ -69,7 +58,7 @@ export function buildWebpackClientConfig(
|
||||||
|
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
mode: (development) ? 'development' : 'production',
|
mode: (development) ? 'development' : 'production',
|
||||||
devtool: 'source-map',
|
devtool: (development) ? 'eval' : false,
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
[(locale !== 'default') ? `client-${locale}` : 'client']:
|
[(locale !== 'default') ? `client-${locale}` : 'client']:
|
||||||
|
@ -121,7 +110,7 @@ export function buildWebpackClientConfig(
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(js|jsx|ts|tsx)$/,
|
test: /\.(js|jsx)$/,
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
include: [
|
include: [
|
||||||
path.resolve(__dirname, 'src'),
|
path.resolve(__dirname, 'src'),
|
||||||
|
@ -136,14 +125,13 @@ export function buildWebpackClientConfig(
|
||||||
targets: {
|
targets: {
|
||||||
browsers: pkg.browserslist,
|
browsers: pkg.browserslist,
|
||||||
},
|
},
|
||||||
modules: false,
|
|
||||||
useBuiltIns: 'usage',
|
useBuiltIns: 'usage',
|
||||||
corejs: {
|
corejs: {
|
||||||
version: 3,
|
version: 3,
|
||||||
},
|
},
|
||||||
debug: false,
|
debug: false,
|
||||||
}],
|
}],
|
||||||
'@babel/typescript',
|
//'@babel/typescript',
|
||||||
'@babel/react',
|
'@babel/react',
|
||||||
],
|
],
|
||||||
plugins: babelPlugins,
|
plugins: babelPlugins,
|
||||||
|
@ -214,7 +202,6 @@ export function buildWebpackClientConfig(
|
||||||
* maybe some day in the future it might be
|
* maybe some day in the future it might be
|
||||||
* better than babel-loader cacheDirectory,
|
* better than babel-loader cacheDirectory,
|
||||||
* but right now it isn't
|
* but right now it isn't
|
||||||
*
|
|
||||||
cache: {
|
cache: {
|
||||||
type: 'filesystem',
|
type: 'filesystem',
|
||||||
cacheDirectory: path.resolve(
|
cacheDirectory: path.resolve(
|
||||||
|
@ -225,6 +212,7 @@ export function buildWebpackClientConfig(
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
cache: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,12 +238,12 @@ function buildWebpackClientConfigAllLangs(development, analyze) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
debug, analyze, extract, locale,
|
development, analyze, extract, locale,
|
||||||
}) => {
|
}) => {
|
||||||
if (extract || locale) {
|
if (extract || locale) {
|
||||||
return buildWebpackClientConfig(
|
return buildWebpackClientConfig(
|
||||||
debug, analyze, locale || 'default', extract,
|
development, analyze, locale || 'default', extract,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return buildWebpackClientConfigAllLangs(debug, analyze);
|
return buildWebpackClientConfigAllLangs(development, analyze);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,11 +7,8 @@ import nodeExternals from 'webpack-node-externals';
|
||||||
import GeneratePackageJsonPlugin from 'generate-package-json-webpack-plugin';
|
import GeneratePackageJsonPlugin from 'generate-package-json-webpack-plugin';
|
||||||
import CopyPlugin from 'copy-webpack-plugin';
|
import CopyPlugin from 'copy-webpack-plugin';
|
||||||
|
|
||||||
import patch from './scripts/patch';
|
|
||||||
import pkg from './package.json';
|
import pkg from './package.json';
|
||||||
|
|
||||||
patch();
|
|
||||||
|
|
||||||
const basePackageValues = {
|
const basePackageValues = {
|
||||||
name: pkg.name,
|
name: pkg.name,
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
|
@ -29,18 +26,7 @@ const ttag = {};
|
||||||
|
|
||||||
const babelPlugins = [
|
const babelPlugins = [
|
||||||
'@babel/plugin-transform-flow-strip-types',
|
'@babel/plugin-transform-flow-strip-types',
|
||||||
['@babel/plugin-proposal-decorators', { legacy: true }],
|
|
||||||
'@babel/plugin-proposal-function-sent',
|
|
||||||
'@babel/plugin-proposal-export-namespace-from',
|
|
||||||
'@babel/plugin-proposal-numeric-separator',
|
|
||||||
'@babel/plugin-proposal-throw-expressions',
|
'@babel/plugin-proposal-throw-expressions',
|
||||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
|
||||||
['@babel/plugin-proposal-private-methods', { loose: true }],
|
|
||||||
[
|
|
||||||
'@babel/plugin-proposal-private-property-in-object',
|
|
||||||
{ loose: true },
|
|
||||||
],
|
|
||||||
'@babel/proposal-object-rest-spread',
|
|
||||||
// react-optimize
|
// react-optimize
|
||||||
'@babel/transform-react-constant-elements',
|
'@babel/transform-react-constant-elements',
|
||||||
'@babel/transform-react-inline-elements',
|
'@babel/transform-react-inline-elements',
|
||||||
|
@ -51,7 +37,7 @@ const babelPlugins = [
|
||||||
|
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
debug, extract,
|
development, extract,
|
||||||
}) => {
|
}) => {
|
||||||
if (extract) {
|
if (extract) {
|
||||||
ttag.extract = {
|
ttag.extract = {
|
||||||
|
@ -65,7 +51,7 @@ export default ({
|
||||||
target: 'node',
|
target: 'node',
|
||||||
|
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
mode: (debug) ? 'development' : 'production',
|
mode: (development) ? 'development' : 'production',
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
web: [path.resolve(__dirname, 'src', 'web.js')],
|
web: [path.resolve(__dirname, 'src', 'web.js')],
|
||||||
|
@ -140,7 +126,7 @@ export default ({
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env.NODE_ENV': debug ? '"development"' : '"production"',
|
'process.env.NODE_ENV': development ? '"development"' : '"production"',
|
||||||
'process.env.BROWSER': false,
|
'process.env.BROWSER': false,
|
||||||
}),
|
}),
|
||||||
// create package.json for deployment
|
// create package.json for deployment
|
||||||
|
|
Loading…
Reference in New Issue
Block a user