update packages
This commit is contained in:
parent
44af557581
commit
8c17f56b79
|
@ -302,10 +302,6 @@ msgstr ""
|
|||
msgid "Failed to establish session after register :("
|
||||
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:32
|
||||
msgid "Mail verification"
|
||||
|
@ -321,6 +317,10 @@ msgid ""
|
|||
"request a new one."
|
||||
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_passwd.js:37
|
||||
#: src/routes/api/auth/delete_account.js:38
|
||||
|
|
|
@ -162,7 +162,7 @@ msgstr ""
|
|||
msgid "Look at past Canvases"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Converter.jsx:615
|
||||
#: src/components/Converter.jsx:586
|
||||
#: src/components/CoordinatesBox.jsx:32
|
||||
msgid "Copy to Clipboard"
|
||||
msgstr ""
|
||||
|
@ -186,6 +186,11 @@ msgstr ""
|
|||
msgid "Restore"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/buttons/CanvasSwitchButton.jsx:23
|
||||
#: src/components/windows/index.js:22
|
||||
msgid "Canvas Selection"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/buttons/ExpandMenuButton.jsx:23
|
||||
msgid "Close Menu"
|
||||
msgstr ""
|
||||
|
@ -202,11 +207,6 @@ msgstr ""
|
|||
msgid "Open Chat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/buttons/CanvasSwitchButton.jsx:23
|
||||
#: src/components/windows/index.js:22
|
||||
msgid "Canvas Selection"
|
||||
msgstr ""
|
||||
|
||||
#: src/actions/fetch.js:40
|
||||
msgid "You made too many requests"
|
||||
msgstr ""
|
||||
|
@ -602,7 +602,7 @@ msgstr ""
|
|||
msgid "For when you are playing on a potato."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Converter.jsx:429
|
||||
#: src/components/Converter.jsx:400
|
||||
#: src/components/windows/Settings.jsx:195
|
||||
msgid "Light Grid"
|
||||
msgstr ""
|
||||
|
@ -631,17 +631,6 @@ msgstr ""
|
|||
msgid "Select Language"
|
||||
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
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
@ -670,39 +659,6 @@ msgstr ""
|
|||
msgid "Consider joining us on Guilded:"
|
||||
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
|
||||
msgid "Register new account here"
|
||||
msgstr ""
|
||||
|
@ -737,6 +693,50 @@ msgstr ""
|
|||
msgid "Submit"
|
||||
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
|
||||
msgid "Channel settings"
|
||||
msgstr ""
|
||||
|
@ -809,30 +809,6 @@ msgstr ""
|
|||
msgid "Password must be shorter than 60 characters."
|
||||
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/ChangeName.jsx:68
|
||||
#: src/components/ChangePassword.jsx:110
|
||||
|
@ -840,35 +816,6 @@ msgstr ""
|
|||
msgid "Save"
|
||||
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
|
||||
msgid "Todays Placed Pixels"
|
||||
msgstr ""
|
||||
|
@ -914,6 +861,30 @@ msgstr ""
|
|||
msgid "Social Settings"
|
||||
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
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
@ -926,77 +897,6 @@ msgstr ""
|
|||
msgid "Ranking updates every 5 min. Daily rankings get reset at midnight UTC."
|
||||
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
|
||||
msgid "Build image on canvas."
|
||||
msgstr ""
|
||||
|
@ -1075,12 +975,104 @@ msgstr ""
|
|||
msgid "User Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/LogInForm.jsx:76
|
||||
msgid "Name or Email"
|
||||
#: src/components/Converter.jsx:260
|
||||
msgid "Choose Canvas"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/LogInForm.jsx:87
|
||||
msgid "LogIn"
|
||||
#: src/components/Converter.jsx:286
|
||||
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 ""
|
||||
|
||||
#: src/components/UserMessages.jsx:28
|
||||
|
@ -1117,14 +1109,14 @@ msgstr ""
|
|||
msgid "Confirm New Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/ChangeName.jsx:64
|
||||
msgid "New Username"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/DeleteAccount.jsx:66
|
||||
msgid "Yes, Delete My Account!"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/ChangeName.jsx:64
|
||||
msgid "New Username"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/ChangeMail.jsx:59
|
||||
msgid ""
|
||||
"Changed Mail successfully. We sent you a verification mail, "
|
||||
|
@ -1147,6 +1139,14 @@ msgstr ""
|
|||
msgid "You have no users blocked"
|
||||
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/Settings.jsx:134
|
||||
msgctxt "keybinds"
|
||||
|
|
25704
package-lock.json
generated
25704
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": {
|
||||
"build": "npm run webpack && 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",
|
||||
"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",
|
||||
"lint": "cd $INIT_CWD && eslint --ext .jsx --ext .js",
|
||||
"lint:src": "eslint --ext .jsx --ext .js src",
|
||||
|
@ -30,53 +30,52 @@
|
|||
"dependencies": {
|
||||
"bcrypt": "^5.0.1",
|
||||
"bluebird": "^3.5.0",
|
||||
"body-parser": "^1.17.2",
|
||||
"bufferutil": "^4.0.3",
|
||||
"body-parser": "^1.19.1",
|
||||
"bufferutil": "^4.0.6",
|
||||
"compression": "^1.7.3",
|
||||
"connect-redis": "^6.0.0",
|
||||
"cookie": "^0.4.1",
|
||||
"core-js": "^3.16.2",
|
||||
"core-js": "^3.20.2",
|
||||
"cors": "^2.8.4",
|
||||
"etag": "^1.8.1",
|
||||
"express": "^4.15.3",
|
||||
"express": "^4.17.2",
|
||||
"express-limiter": "^1.6.0",
|
||||
"express-session": "^1.17.2",
|
||||
"global": "^4.3.2",
|
||||
"http-proxy-agent": "^4.0.1",
|
||||
"image-q": "^2.1.2",
|
||||
"ip-address": "^7.1.0",
|
||||
"image-q": "^3.0.5",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"js-file-download": "^0.4.12",
|
||||
"localforage": "^1.10.0",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.3",
|
||||
"mysql2": "^2.3.0",
|
||||
"nodemailer": "^6.6.3",
|
||||
"passport": "^0.4.0",
|
||||
"multer": "^1.4.4",
|
||||
"mysql2": "^2.3.3",
|
||||
"nodemailer": "^6.7.2",
|
||||
"passport": "^0.5.2",
|
||||
"passport-discord": "^0.1.4",
|
||||
"passport-facebook": "^3.0.0",
|
||||
"passport-google-oauth": "^2.0.0",
|
||||
"passport-json": "^1.2.0",
|
||||
"passport-reddit": "^0.2.4",
|
||||
"passport-vkontakte": "^0.5.0",
|
||||
"ppfun-captcha": "^1.6.5",
|
||||
"ppfun-captcha": "^1.6.6",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-icons": "^4.2.0",
|
||||
"react-redux": "^7.2.4",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-responsive": "^8.2.0",
|
||||
"react-stay-scrolled": "^7.3.1",
|
||||
"react-stay-scrolled": "^7.4.0",
|
||||
"react-toggle-button": "^2.1.0",
|
||||
"redis": "^3.1.2",
|
||||
"redlock": "^4.0.0",
|
||||
"redux": "^4.1.1",
|
||||
"redux": "^4.1.2",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"sequelize": "^6.6.5",
|
||||
"sharp": "^0.27.2",
|
||||
"redux-thunk": "^2.4.1",
|
||||
"sequelize": "^6.12.4",
|
||||
"sharp": "^0.29.3",
|
||||
"startaudiocontext": "^1.2.1",
|
||||
"three": "^0.125.2",
|
||||
"three": "^0.136.0",
|
||||
"three-trackballcontrols": "^0.9.0",
|
||||
"ttag": "^1.7.24",
|
||||
"ttag-po-loader": "0.0.2",
|
||||
|
@ -86,68 +85,65 @@
|
|||
"ws": "^7.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.14.8",
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/node": "^7.14.9",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-proposal-decorators": "^7.14.5",
|
||||
"@babel/plugin-proposal-do-expressions": "^7.14.5",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.14.5",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.14.5",
|
||||
"@babel/plugin-proposal-function-bind": "^7.14.5",
|
||||
"@babel/plugin-proposal-function-sent": "^7.14.5",
|
||||
"@babel/plugin-proposal-json-strings": "^7.14.5",
|
||||
"@babel/plugin-proposal-logical-assignment-operators": "^7.14.5",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.14.5",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.15.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.14.5",
|
||||
"@babel/cli": "^7.16.7",
|
||||
"@babel/core": "^7.16.7",
|
||||
"@babel/node": "^7.16.7",
|
||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||
"@babel/plugin-proposal-decorators": "^7.16.7",
|
||||
"@babel/plugin-proposal-do-expressions": "^7.16.7",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.16.7",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.16.7",
|
||||
"@babel/plugin-proposal-function-bind": "^7.16.7",
|
||||
"@babel/plugin-proposal-function-sent": "^7.16.7",
|
||||
"@babel/plugin-proposal-json-strings": "^7.16.7",
|
||||
"@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.16.7",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.16.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.16.7",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.16.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.14.5",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.14.5",
|
||||
"@babel/plugin-transform-react-inline-elements": "^7.14.5",
|
||||
"@babel/polyfill": "^7.11.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-flow": "^7.14.5",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.16.7",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.16.7",
|
||||
"@babel/plugin-transform-react-inline-elements": "^7.16.7",
|
||||
"@babel/preset-env": "^7.16.7",
|
||||
"@babel/preset-flow": "^7.16.7",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"assets-webpack-plugin": "^7.1.1",
|
||||
"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-remove-prop-types": "^0.4.24",
|
||||
"babel-plugin-ttag": "^1.7.30",
|
||||
"clean-css": "^5.1.5",
|
||||
"copy-webpack-plugin": "^8.1.1",
|
||||
"css-loader": "^5.2.7",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb": "^18.2.1",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-plugin-flowtype": "^5.9.0",
|
||||
"eslint-plugin-import": "^2.24.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"flow-bin": "^0.143.1",
|
||||
"generate-package-json-webpack-plugin": "^2.1.3",
|
||||
"glob": "^7.1.7",
|
||||
"clean-css": "^5.2.2",
|
||||
"copy-webpack-plugin": "^10.2.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.6.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-flowtype": "^8.0.3",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"flow-bin": "^0.168.0",
|
||||
"generate-package-json-webpack-plugin": "^2.5.1",
|
||||
"glob": "^7.2.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"mkdirp": "^1.0.4",
|
||||
"npm-check": "^5.9.2",
|
||||
"parallel-webpack": "^2.6.0",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"react-svg-loader": "^3.0.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "^2.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ttag-cli": "^1.9.3",
|
||||
"webpack": "^5.51.1",
|
||||
"webpack-bundle-analyzer": "^4.4.2",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-middleware": "^4.3.0",
|
||||
"webpack-hot-middleware": "^2.18.0",
|
||||
"webpack-node-externals": "^2.5.2",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-middleware": "^5.3.0",
|
||||
"webpack-hot-middleware": "^2.25.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"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 { useSelector, shallowEqual } from 'react-redux';
|
||||
import fileDownload from 'js-file-download';
|
||||
import iq from 'image-q';
|
||||
import { jt, t } from 'ttag';
|
||||
|
||||
import {
|
||||
ColorDistanceCalculators,
|
||||
ImageQuantizerKernels,
|
||||
quantizeImage,
|
||||
getImageDataOfFile,
|
||||
} from '../utils/image';
|
||||
import printGIMPPalette from '../core/exportGPL';
|
||||
import { copyCanvasToClipboard } from '../utils/clipboard';
|
||||
|
||||
|
@ -43,181 +48,63 @@ function readFile(
|
|||
fr.readAsDataURL(file);
|
||||
}
|
||||
|
||||
const ColorDistanceCalculators = [
|
||||
'Euclidean',
|
||||
'Manhattan',
|
||||
'CIEDE2000',
|
||||
'CIE94Textiles',
|
||||
'CIE94GraphicArts',
|
||||
'EuclideanBT709NoAlpha',
|
||||
'EuclideanBT709',
|
||||
'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 createCanvasFromImageData(imgData) {
|
||||
const { width, height } = imgData;
|
||||
const inputCanvas = document.createElement('canvas');
|
||||
inputCanvas.width = width;
|
||||
inputCanvas.height = height;
|
||||
const inputCtx = inputCanvas.getContext('2d');
|
||||
inputCtx.putImageData(imgData, 0, 0);
|
||||
return inputCanvas;
|
||||
}
|
||||
|
||||
function drawPixels(idxi8, width, height) {
|
||||
const can = document.createElement('canvas');
|
||||
can.width = width;
|
||||
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) {
|
||||
function addGrid(imgData, lightGrid, offsetX, offsetY) {
|
||||
const image = createCanvasFromImageData(imgData);
|
||||
const { width, height } = image;
|
||||
const can = document.createElement('canvas');
|
||||
const ctx = can.getContext('2d');
|
||||
can.width = img.width * 5;
|
||||
can.height = img.height * 5;
|
||||
can.width = width * 5;
|
||||
can.height = height * 5;
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.mozImageSmoothingEnabled = false;
|
||||
ctx.webkitImageSmoothingEnabled = false;
|
||||
ctx.msImageSmoothingEnabled = false;
|
||||
ctx.save();
|
||||
ctx.scale(5.0, 5.0);
|
||||
ctx.drawImage(img, 0, 0);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
ctx.restore();
|
||||
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;
|
||||
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;
|
||||
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 ctxo = can.getContext('2d');
|
||||
can.width = width;
|
||||
can.height = height;
|
||||
const scaleX = width / img.width;
|
||||
const scaleY = height / img.height;
|
||||
const scaleX = width / imgData.width;
|
||||
const scaleY = height / imgData.height;
|
||||
if (doAA) {
|
||||
// scale with canvas for antialiasing
|
||||
const image = createCanvasFromImageData(imgData);
|
||||
ctxo.save();
|
||||
ctxo.scale(scaleX, scaleY);
|
||||
ctxo.drawImage(img, 0, 0);
|
||||
ctxo.drawImage(image, 0, 0);
|
||||
ctxo.restore();
|
||||
return can;
|
||||
return ctxo.getImageData(0, 0, width, height);
|
||||
}
|
||||
// scale manually
|
||||
const imdo = ctxo.createImageData(width, height);
|
||||
const { data: datao } = imdo;
|
||||
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);
|
||||
const { data: datai } = imdi;
|
||||
const { data: datai } = imgData;
|
||||
for (let y = 0; y < height; y += 1) {
|
||||
for (let x = 0; x < width; x += 1) {
|
||||
let posi = (Math.round(x / scaleX) + Math.round(y / scaleY)
|
||||
|
@ -229,30 +116,29 @@ function scaleImage(img, width, height, doAA) {
|
|||
datao[poso] = datai[posi];
|
||||
}
|
||||
}
|
||||
ctxo.putImageData(imdo, 0, 0);
|
||||
return can;
|
||||
return imdo;
|
||||
}
|
||||
|
||||
let newOpts = null;
|
||||
let renderOpts = null;
|
||||
let rendering = false;
|
||||
async function renderOutputImage(opts) {
|
||||
if (!opts.file) {
|
||||
return;
|
||||
}
|
||||
renderOpts = opts;
|
||||
if (rendering) {
|
||||
newOpts = opts;
|
||||
console.log('skip rendering');
|
||||
return;
|
||||
}
|
||||
console.log('render');
|
||||
rendering = true;
|
||||
let renderOpts = opts;
|
||||
while (renderOpts) {
|
||||
newOpts = null;
|
||||
const {
|
||||
file, dither, grid, scaling,
|
||||
} = renderOpts;
|
||||
renderOpts = null;
|
||||
if (file) {
|
||||
let image = file;
|
||||
let pointContainer = null;
|
||||
if (scaling.enabled) {
|
||||
// scale
|
||||
const { width, height, aa } = scaling;
|
||||
|
@ -262,29 +148,18 @@ async function renderOutputImage(opts) {
|
|||
height,
|
||||
aa,
|
||||
);
|
||||
pointContainer = iq.utils.PointContainer.fromHTMLCanvasElement(image);
|
||||
} else {
|
||||
pointContainer = iq.utils.PointContainer.fromHTMLImageElement(image);
|
||||
}
|
||||
// dither
|
||||
const { colors, strategy, colorDist } = dither;
|
||||
const progEl = document.getElementById('qprog');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
pointContainer = await quantize(
|
||||
pointContainer,
|
||||
colors,
|
||||
colorDist,
|
||||
image = await quantizeImage(colors, image, {
|
||||
strategy,
|
||||
(progress) => {
|
||||
colorDist,
|
||||
onProgress: (progress) => {
|
||||
progEl.innerHTML = `Loading... ${Math.round(progress)} %`;
|
||||
},
|
||||
);
|
||||
});
|
||||
progEl.innerHTML = 'Done';
|
||||
image = drawPixels(
|
||||
pointContainer.toUint8Array(),
|
||||
image.width,
|
||||
image.height,
|
||||
);
|
||||
// grid
|
||||
if (grid.enabled) {
|
||||
const { light, offsetX, offsetY } = grid;
|
||||
|
@ -300,10 +175,8 @@ async function renderOutputImage(opts) {
|
|||
output.width = image.width;
|
||||
output.height = image.height;
|
||||
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;
|
||||
}
|
||||
|
@ -321,7 +194,7 @@ function Converter() {
|
|||
], shallowEqual);
|
||||
|
||||
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
||||
const [selectedFile, selectFile] = useState(null);
|
||||
const [inputImageData, setInputImageData] = useState(null);
|
||||
const [selectedStrategy, selectStrategy] = useState('Nearest');
|
||||
const [selectedColorDist, selectColorDist] = useState('Euclidean');
|
||||
const [selectedScaleKeepRatio, selectScaleKeepRatio] = useState(true);
|
||||
|
@ -339,7 +212,7 @@ function Converter() {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedFile) {
|
||||
if (inputImageData) {
|
||||
const canvas = canvases[selectedCanvas];
|
||||
const dither = {
|
||||
colors: canvas.colors.slice(canvas.cli),
|
||||
|
@ -347,13 +220,20 @@ function Converter() {
|
|||
colorDist: selectedColorDist,
|
||||
};
|
||||
renderOutputImage({
|
||||
file: selectedFile,
|
||||
file: inputImageData,
|
||||
dither,
|
||||
grid: gridData,
|
||||
scaling: scaleData,
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [
|
||||
inputImageData,
|
||||
selectedStrategy,
|
||||
selectedColorDist,
|
||||
scaleData,
|
||||
selectedCanvas,
|
||||
gridData,
|
||||
]);
|
||||
|
||||
const {
|
||||
enabled: gridEnabled,
|
||||
|
@ -379,6 +259,7 @@ function Converter() {
|
|||
<div style={{ textAlign: 'center' }}>
|
||||
<div className="modalcotext">{t`Choose Canvas`}:
|
||||
<select
|
||||
value={selectedCanvas}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectCanvas(sel.options[sel.selectedIndex].value);
|
||||
|
@ -391,7 +272,6 @@ function Converter() {
|
|||
? null
|
||||
: (
|
||||
<option
|
||||
selected={canvas === selectedCanvas}
|
||||
value={canvas}
|
||||
>
|
||||
{
|
||||
|
@ -431,28 +311,36 @@ function Converter() {
|
|||
<input
|
||||
type="file"
|
||||
id="imgfile"
|
||||
onChange={(evt) => {
|
||||
onChange={async (evt) => {
|
||||
const fileSel = evt.target;
|
||||
const file = (!fileSel.files || !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`}:
|
||||
<select
|
||||
value={selectedStrategy}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectStrategy(sel.options[sel.selectedIndex].value);
|
||||
}}
|
||||
>
|
||||
{
|
||||
['Nearest',
|
||||
'Riemersma',
|
||||
...Object.keys(iq.image.ErrorDiffusionArrayKernel),
|
||||
].map((strat) => (
|
||||
ImageQuantizerKernels.map((strat) => (
|
||||
<option
|
||||
value={strat}
|
||||
selected={(selectedStrategy === strat)}
|
||||
>{strat}</option>
|
||||
))
|
||||
}
|
||||
|
@ -460,6 +348,7 @@ function Converter() {
|
|||
</p>
|
||||
<p className="modalcotext">{t`Choose Color Mode`}:
|
||||
<select
|
||||
value={selectedColorDist}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectColorDist(sel.options[sel.selectedIndex].value);
|
||||
|
@ -469,7 +358,6 @@ function Converter() {
|
|||
ColorDistanceCalculators.map((strat) => (
|
||||
<option
|
||||
value={strat}
|
||||
selected={(selectedColorDist === strat)}
|
||||
>{strat}</option>
|
||||
))
|
||||
}
|
||||
|
@ -581,8 +469,8 @@ function Converter() {
|
|||
const newWidth = (e.target.value > 1024)
|
||||
? 1024 : e.target.value;
|
||||
if (!newWidth) return;
|
||||
if (selectedScaleKeepRatio && selectedFile) {
|
||||
const ratio = selectedFile.width / selectedFile.height;
|
||||
if (selectedScaleKeepRatio && inputImageData) {
|
||||
const ratio = inputImageData.width / inputImageData.height;
|
||||
const newHeight = Math.round(newWidth / ratio);
|
||||
if (newHeight <= 0) return;
|
||||
setScaleData({
|
||||
|
@ -611,8 +499,8 @@ function Converter() {
|
|||
const nuHeight = (e.target.value > 1024)
|
||||
? 1024 : e.target.value;
|
||||
if (!nuHeight) return;
|
||||
if (selectedScaleKeepRatio && selectedFile) {
|
||||
const ratio = selectedFile.width / selectedFile.height;
|
||||
if (selectedScaleKeepRatio && inputImageData) {
|
||||
const ratio = inputImageData.width / inputImageData.height;
|
||||
const nuWidth = Math.round(ratio * nuHeight);
|
||||
if (nuWidth <= 0) return;
|
||||
setScaleData({
|
||||
|
@ -655,11 +543,11 @@ function Converter() {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (selectedFile) {
|
||||
if (inputImageData) {
|
||||
setScaleData({
|
||||
...scaleData,
|
||||
width: selectedFile.width,
|
||||
height: selectedFile.height,
|
||||
width: inputImageData.width,
|
||||
height: inputImageData.height,
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
@ -669,7 +557,7 @@ function Converter() {
|
|||
</div>
|
||||
)
|
||||
: null}
|
||||
{(selectedFile)
|
||||
{(inputImageData)
|
||||
? (
|
||||
<div>
|
||||
<p id="qprog">...</p>
|
||||
|
@ -704,4 +592,4 @@ function Converter() {
|
|||
);
|
||||
}
|
||||
|
||||
export default React.memo(Converter);
|
||||
export default Converter;
|
||||
|
|
|
@ -14,13 +14,16 @@ const DailyRankings = () => {
|
|||
return (
|
||||
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
||||
<table>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>user</th>
|
||||
<th>Pixels</th>
|
||||
<th># Total</th>
|
||||
<th>Total Pixels</th>
|
||||
</tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>user</th>
|
||||
<th>Pixels</th>
|
||||
<th># Total</th>
|
||||
<th>Total Pixels</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
totalDailyRanking.map((rank) => (
|
||||
<tr>
|
||||
|
@ -32,6 +35,7 @@ const DailyRankings = () => {
|
|||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -35,6 +35,7 @@ function LanguageSelect() {
|
|||
<div style={{ textAlign: 'right' }}>
|
||||
<span>
|
||||
<select
|
||||
value={langSel}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
setLangSel(sel.options[sel.selectedIndex].value);
|
||||
|
@ -43,7 +44,6 @@ function LanguageSelect() {
|
|||
{
|
||||
langs.map(([l]) => (
|
||||
<option
|
||||
selected={l === langSel}
|
||||
value={l}
|
||||
>
|
||||
{l.toUpperCase()}
|
||||
|
|
|
@ -12,13 +12,16 @@ const TotalRankings = () => {
|
|||
return (
|
||||
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
||||
<table>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>user</th>
|
||||
<th>Pixels</th>
|
||||
<th># Today</th>
|
||||
<th>Pixels Today</th>
|
||||
</tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>user</th>
|
||||
<th>Pixels</th>
|
||||
<th># Today</th>
|
||||
<th>Pixels Today</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
totalRanking.map((rank) => (
|
||||
<tr>
|
||||
|
@ -30,6 +33,7 @@ const TotalRankings = () => {
|
|||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@ const WindowManager = () => {
|
|||
|
||||
return (
|
||||
<div id="wm">
|
||||
{
|
||||
{
|
||||
windowIds.map((id) => (<Window key={id} id={id} />))
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,7 @@ const Help = () => {
|
|||
const mailLink = <a href="mailto:pixelplanetdev@gmail.com">pixelplanetdev@gmail.com</a>;
|
||||
|
||||
return (
|
||||
<p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
|
||||
<div style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
|
||||
<p className="modaltext">
|
||||
{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 \
|
||||
|
@ -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 />
|
||||
</div>
|
||||
<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>
|
||||
{(icon) && <img alt="" src={icon} />}
|
||||
<select
|
||||
value={selected}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
onSelect(sel.options[sel.selectedIndex].value);
|
||||
|
@ -63,7 +64,6 @@ const SettingsItemSelect = ({
|
|||
{
|
||||
values.map((value) => (
|
||||
<option
|
||||
selected={value === selected}
|
||||
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 = [
|
||||
'@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-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
|
||||
'@babel/transform-react-constant-elements',
|
||||
'@babel/transform-react-inline-elements',
|
||||
|
@ -69,7 +58,7 @@ export function buildWebpackClientConfig(
|
|||
|
||||
context: __dirname,
|
||||
mode: (development) ? 'development' : 'production',
|
||||
devtool: 'source-map',
|
||||
devtool: (development) ? 'eval' : false,
|
||||
|
||||
entry: {
|
||||
[(locale !== 'default') ? `client-${locale}` : 'client']:
|
||||
|
@ -121,7 +110,7 @@ export function buildWebpackClientConfig(
|
|||
],
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx|ts|tsx)$/,
|
||||
test: /\.(js|jsx)$/,
|
||||
loader: 'babel-loader',
|
||||
include: [
|
||||
path.resolve(__dirname, 'src'),
|
||||
|
@ -136,14 +125,13 @@ export function buildWebpackClientConfig(
|
|||
targets: {
|
||||
browsers: pkg.browserslist,
|
||||
},
|
||||
modules: false,
|
||||
useBuiltIns: 'usage',
|
||||
corejs: {
|
||||
version: 3,
|
||||
},
|
||||
debug: false,
|
||||
}],
|
||||
'@babel/typescript',
|
||||
//'@babel/typescript',
|
||||
'@babel/react',
|
||||
],
|
||||
plugins: babelPlugins,
|
||||
|
@ -214,7 +202,6 @@ export function buildWebpackClientConfig(
|
|||
* maybe some day in the future it might be
|
||||
* better than babel-loader cacheDirectory,
|
||||
* but right now it isn't
|
||||
*
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
cacheDirectory: path.resolve(
|
||||
|
@ -225,6 +212,7 @@ export function buildWebpackClientConfig(
|
|||
),
|
||||
},
|
||||
*/
|
||||
cache: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -250,12 +238,12 @@ function buildWebpackClientConfigAllLangs(development, analyze) {
|
|||
}
|
||||
|
||||
export default ({
|
||||
debug, analyze, extract, locale,
|
||||
development, analyze, extract, locale,
|
||||
}) => {
|
||||
if (extract || locale) {
|
||||
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 CopyPlugin from 'copy-webpack-plugin';
|
||||
|
||||
import patch from './scripts/patch';
|
||||
import pkg from './package.json';
|
||||
|
||||
patch();
|
||||
|
||||
const basePackageValues = {
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
|
@ -29,18 +26,7 @@ const ttag = {};
|
|||
|
||||
const babelPlugins = [
|
||||
'@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-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
|
||||
'@babel/transform-react-constant-elements',
|
||||
'@babel/transform-react-inline-elements',
|
||||
|
@ -51,7 +37,7 @@ const babelPlugins = [
|
|||
|
||||
|
||||
export default ({
|
||||
debug, extract,
|
||||
development, extract,
|
||||
}) => {
|
||||
if (extract) {
|
||||
ttag.extract = {
|
||||
|
@ -65,7 +51,7 @@ export default ({
|
|||
target: 'node',
|
||||
|
||||
context: __dirname,
|
||||
mode: (debug) ? 'development' : 'production',
|
||||
mode: (development) ? 'development' : 'production',
|
||||
|
||||
entry: {
|
||||
web: [path.resolve(__dirname, 'src', 'web.js')],
|
||||
|
@ -140,7 +126,7 @@ export default ({
|
|||
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': debug ? '"development"' : '"production"',
|
||||
'process.env.NODE_ENV': development ? '"development"' : '"production"',
|
||||
'process.env.BROWSER': false,
|
||||
}),
|
||||
// create package.json for deployment
|
||||
|
|
Loading…
Reference in New Issue
Block a user