Add scaling option and remove old image converter

This commit is contained in:
HF 2020-05-09 02:20:10 +02:00
parent 3e86ea25be
commit 8cadbbdac1
4 changed files with 152 additions and 97 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -42,6 +42,8 @@ function downloadOutput() {
function readFile( function readFile(
file, file,
selectFile, selectFile,
selectScaleWidth,
selectScaleHeight,
) { ) {
if (!file) { if (!file) {
return; return;
@ -50,6 +52,8 @@ function readFile(
fr.onload = () => { fr.onload = () => {
const img = new Image(); const img = new Image();
img.onload = () => { img.onload = () => {
selectScaleWidth(img.width);
selectScaleHeight(img.height);
selectFile(img); selectFile(img);
}; };
img.src = fr.result; img.src = fr.result;
@ -89,17 +93,37 @@ function addGrid(img, lightGrid, offsetX, offsetY) {
ctx.drawImage(img, 0, 0); ctx.drawImage(img, 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 <= img.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 <= img.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;
}
function scaleImage(img, width, height, doAA) {
const can = document.createElement('canvas');
const ctx = can.getContext('2d');
can.width = width;
can.height = height;
if (doAA) {
ctx.imageSmoothingEnabled = true;
ctx.mozImageSmoothingEnabled = true;
ctx.webkitImageSmoothingEnabled = true;
ctx.msImageSmoothingEnabled = true;
} else {
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
}
ctx.save();
ctx.scale(width / img.width, height / img.height);
ctx.drawImage(img, 0, 0);
ctx.restore();
return can; return can;
} }
@ -114,10 +138,23 @@ function renderOutputImage(
selectedLightGrid, selectedLightGrid,
selectedGridOffsetX, selectedGridOffsetX,
selectedGridOffsetY, selectedGridOffsetY,
selectedDoScaling,
selectedScaleWidth,
selectedScaleHeight,
selectedScaleAA,
) { ) {
if (!selectedFile) { if (!selectedFile) {
return; return;
} }
let image = selectedFile;
if (selectedDoScaling) {
image = scaleImage(
image,
selectedScaleWidth,
selectedScaleHeight,
selectedScaleAA,
);
}
const rgbQuant = new RgbQuant({ const rgbQuant = new RgbQuant({
colors: colors.length, colors: colors.length,
dithKern: selectedStrategy, dithKern: selectedStrategy,
@ -128,23 +165,23 @@ function renderOutputImage(
useCache: false, useCache: false,
colorDist: selectedColorDist, colorDist: selectedColorDist,
}); });
rgbQuant.sample(selectedFile); rgbQuant.sample(image);
rgbQuant.palette(); rgbQuant.palette();
const pxls = rgbQuant.reduce(selectedFile); const pxls = rgbQuant.reduce(image);
let can = drawPixels(pxls, selectedFile.width, selectedFile.height); image = drawPixels(pxls, image.width, image.height);
const output = document.getElementById('imgoutput'); const output = document.getElementById('imgoutput');
if (selectedAddGrid) { if (selectedAddGrid) {
can = addGrid( image = addGrid(
can, image,
selectedLightGrid, selectedLightGrid,
selectedGridOffsetX, selectedGridOffsetX,
selectedGridOffsetY, selectedGridOffsetY,
); );
} }
output.width = can.width; output.width = image.width;
output.height = can.height; output.height = image.height;
const ctx = output.getContext('2d'); const ctx = output.getContext('2d');
ctx.drawImage(can, 0, 0); ctx.drawImage(image, 0, 0);
} }
@ -162,6 +199,11 @@ function Converter({
const [selectedLightGrid, selectLightGrid] = useState(false); const [selectedLightGrid, selectLightGrid] = useState(false);
const [selectedGridOffsetX, selectGridOffsetX] = useState(0); const [selectedGridOffsetX, selectGridOffsetX] = useState(0);
const [selectedGridOffsetY, selectGridOffsetY] = useState(0); const [selectedGridOffsetY, selectGridOffsetY] = useState(0);
const [selectedDoScaling, selectDoScaling] = useState(false);
const [selectedScaleWidth, selectScaleWidth] = useState(0);
const [selectedScaleHeight, selectScaleHeight] = useState(0);
const [selectedScaleKeepRatio, selectScaleKeepRatio] = useState(true);
const [selectedScaleAA, selectScaleAA] = useState(false);
const input = document.createElement('canvas'); const input = document.createElement('canvas');
useEffect(() => { useEffect(() => {
@ -178,6 +220,10 @@ function Converter({
selectedLightGrid, selectedLightGrid,
selectedGridOffsetX, selectedGridOffsetX,
selectedGridOffsetY, selectedGridOffsetY,
selectedDoScaling,
selectedScaleWidth,
selectedScaleHeight,
selectedScaleAA,
); );
} else { } else {
const output = document.getElementById('imgoutput'); const output = document.getElementById('imgoutput');
@ -245,7 +291,7 @@ function Converter({
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); readFile(file, selectFile, selectScaleWidth, selectScaleHeight);
}} }}
/> />
<p style={textStyle}>Choose Strategy:&nbsp; <p style={textStyle}>Choose Strategy:&nbsp;
@ -318,7 +364,7 @@ function Converter({
selectAddGrid(e.target.checked); selectAddGrid(e.target.checked);
}} }}
/> />
Add Grid Add Grid (uncheck if you need a 1:1 template)
</p> </p>
{(selectedAddGrid) {(selectedAddGrid)
? ( ? (
@ -368,10 +414,103 @@ function Converter({
</div> </div>
) )
: null} : null}
<p style={{ ...textStyle, fontHeight: 16 }}>
<input
type="checkbox"
checked={selectedDoScaling}
onChange={(e) => {
selectDoScaling(e.target.checked);
}}
/>
Scale Image
</p>
{(selectedDoScaling)
? (
<div style={{
borderStyle: 'solid',
borderColor: '#D4D4D4',
borderWidth: 2,
padding: 5,
display: 'inline-block',
}}
>
<span style={textStyle}>Width:&nbsp;
<input
type="number"
step="1"
min="1"
max="16564"
style={{ width: '3em' }}
value={selectedScaleWidth}
onChange={(e) => {
const newWidth = e.target.value;
if (!newWidth) return;
if (selectedScaleKeepRatio && selectedFile) {
const ratio = selectedFile.width / selectedFile.height;
const newHeight = Math.round(newWidth / ratio);
selectScaleHeight(newHeight);
}
selectScaleWidth(newWidth);
}}
/>%
</span>
<span style={textStyle}>Height:&nbsp;
<input
type="number"
step="1"
min="1"
max="16564"
style={{ width: '3em' }}
value={selectedScaleHeight}
onChange={(e) => {
const nuHeight = e.target.value;
if (!nuHeight) return;
if (selectedScaleKeepRatio && selectedFile) {
const ratio = selectedFile.width / selectedFile.height;
const nuWidth = Math.round(ratio * nuHeight);
selectScaleWidth(nuWidth);
}
selectScaleHeight(nuHeight);
}}
/>%
</span>
<p style={{ ...textStyle, fontHeight: 16 }}>
<input
type="checkbox"
checked={selectedScaleKeepRatio}
onChange={(e) => {
selectScaleKeepRatio(e.target.checked);
}}
/>
Keep Ratio
</p>
<p style={{ ...textStyle, fontHeight: 16 }}>
<input
type="checkbox"
onChange={(e) => {
selectScaleAA(e.target.checked);
}}
/>
Anti Aliasing
</p>
<button
type="button"
onClick={() => {
if (selectedFile) {
selectScaleWidth(selectedFile.width);
selectScaleHeight(selectedFile.height);
}
}}
>
Reset
</button>
</div>
)
: null}
<p> <p>
<canvas <canvas
id="imgoutput" id="imgoutput"
style={{ width: '80%' }} style={{ width: '80%', imageRendering: 'crisp-edges' }}
/> />
</p> </p>
<button <button

View File

@ -52,8 +52,6 @@ const HelpModal = () => (
<p>Discord: <a href="./discord" target="_blank">pixelplanet.fun/discord</a></p> <p>Discord: <a href="./discord" target="_blank">pixelplanet.fun/discord</a></p>
<p>Source on <a href="https://github.com/pixelplanetdev/pixelplanet" target="_blank">github</a></p> <p>Source on <a href="https://github.com/pixelplanetdev/pixelplanet" target="_blank">github</a></p>
<p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank">r/PixelPlanetFun</a></p> <p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank">r/PixelPlanetFun</a></p>
<p>Image Converter: <a href="./convert" target="_blank">pixelplanet.fun/convert</a></p>
<p>Image Converter for 2nd Planet: <a href="./convert2" target="_blank">pixelplanet.fun/convert2</a></p>
<p style={titleStyle}>Map Data</p> <p style={titleStyle}>Map Data</p>
<p style={textStyle}>The bare map data that we use, together with converted OpenStreetMap tiles for orientation, <p style={textStyle}>The bare map data that we use, together with converted OpenStreetMap tiles for orientation,
can be downloaded from mega.nz here: <a href="https://mega.nz/#!JpkBwAbJ!EnSLlZmKv3kEBE0HDhakTgAZZycD3ELjduajJxPGaXo">pixelplanetmap.zip</a> (422MB)</p> can be downloaded from mega.nz here: <a href="https://mega.nz/#!JpkBwAbJ!EnSLlZmKv3kEBE0HDhakTgAZZycD3ELjduajJxPGaXo">pixelplanetmap.zip</a> (422MB)</p>