I'm new to use PNG and I get below error unrecognised content at end of stream
cy.readFile("cypress/e2e/Testdata/sample.png", "binary").then((image1) => {
cy.readFile("cypress/downloads/sample.png", "binary").then((image2) => {
const img1 = PNG.sync.read(image1);
const img2 = PNG.sync.read(image2);
const { width, height } = img1;
const diff = new PNG({ width, height });
pixelmatch(img1.data, img2.data, diff.data, width, height, {
threshold: 0.1,
});
fs.writeFileSync("diff.png", PNG.sync.write(diff));
expect(diff.width).to.equal(0);
expect(diff.height).to.equal(0);
});
});
You cannot mix browser commands like cy.readfile() with node commands like fs.writeFileSync().
You will have to create a Cypress task to run this code.
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('task', {
png({image1Path, image2Path}) {
const image1 = fs.readFileSync(image1Path)
const image2 = fs.readFileSync(image2Path)
const img1 = PNG.sync.read(image1);
const img2 = PNG.sync.read(image2);
const { width, height } = img1;
const diff = new PNG({ width, height });
pixelmatch(img1.data, img2.data, diff.data, width, height, {
threshold: 0.1,
});
fs.writeFileSync("diff.png", PNG.sync.write(diff));
return diff
},
})
},
},
})
Related
Steps to reproduce this error:
Download or clone this Satori example:
https://codesandbox.io/s/cover-image-bf6282?from-embed=&file=/src/index.tsx
Run the example (npm start) and press the Download button in the launched page.
You'll get this error:
Uncaught (in promise) Error: Satori is not initialized: expect yoga to be loaded, got undefined
What could be the problem, and how to fix it?
I think the problem is here:
svg-utils.ts:
export const svgToPngURL = (svg: string) =>
new Promise<string>((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext("2d");
ctx!.drawImage(img, 0, 0);
resolve(canvas.toDataURL("image/png"));
URL.revokeObjectURL(img.src);
};
img.onerror = (e) => {
reject(e);
URL.revokeObjectURL(img.src);
};
img.src = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml" }));
});
export const downloadSvgAsPng = async (svg: string) => {
const pngURL = await svgToPngURL(svg);
try {
const a = document.createElement("a");
a.href = pngURL;
a.download = "Image.png";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
} finally {
URL.revokeObjectURL(pngURL);
}
};
I switched from using 2D context to using WebGL context. I'm trying to figure out a way to wait until pixels have been drawn onto the screen.
With 2D context I would do something like this
const handlePaintFrame = () => {
const ctx: CanvasRenderingContext2D | any = videoCanvasRef?.current?.getContext("2d");
if (video.current && videoCanvasRef.current && ctx) {
if (chroma?.keyColor) {
drawChromaFrame(ctx, width, height);
} else {
ctx.drawImage(video.current, 0, 0, width, height);
}
const rendered = () => {
setTimeout(() => {
onReady("video");
}, 20);
};
const startRender = () => {
requestAnimationFrame(rendered);
};
const img = new Image();
img.onload = () => {
requestAnimationFrame(startRender);
};
img.onerror = (e: any) => {
console.log("Image error:", { e });
};
img.src = videoCanvasRef.current.toDataURL();
}
};
Currently my draw code for the WebGL context is something like this
const drawTexture = () => {
// render
if (gl && video.current) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, video.current);
drawScene(locationRef.current, buffersRef.current);
}
};
const handlePaintFrame = () => {
if (video.current) {
if (chroma?.keyColor) {
// drawChromaFrame(ctx, width, height);
} else {
drawTexture();
// TODO: We need to wait until texture is drawn
setTimeout(() => {
onReady("video");
}, 100);
}
}
};
I have an object with links
const urls = {
small: https://image1.com,
medium: https://image2.com,
large: https://image3.com,
};
output should look like that:
{
small: {width: image_width, height: image_height},
medium: {width: image_width, height: image_height},
large: {width: image_width, height: image_height}
}
I tried like this, but it turns out an infinite loop.
Help me fix my function or show how it is possible in another way
const [imageSize, setImageSize] = useState({});
const getImageSize = () => {
for (const url in urls) {
const img = new Image();
img.src = urls[url];
img.onload = () => {
setImageSize((prev) => {
return {
...prev,
[url]: {
width: img.width,
height: img.height,
},
};
});
};
}
};
To trigger image onload listener it should be mounted to the DOM, otherwise your setImageSize not called. To do this you should have valid images and attach it somewhere to the html, like this:
const urls = {
small: 'https://dummyimage.com/600x400/000/fff',
medium: 'https://dummyimage.com/600x400/000/fff',
large: 'https://dummyimage.com/600x400/000/fff',
}
const getImageSize = () => {
for (const url in urls) {
const img = new Image()
img.src = urls[url]
img.onload = () => {
setImageSize(prev => {
return {
...prev,
[url]: {
width: img.width,
height: img.height,
},
}
})
}
document.body.appendChild(img)
}
}
useEffect(() => {
getImageSize()
}, [])
Try this below code:
const [imageSize, setImageSize] = useState({});
const urls = {
small: https://image1.com,
medium: https://image2.com,
large: https://image3.com,
};
const getImageSize = () => {
for (const size in urls) {
var img = new Image();
img.onload = function() {
setImageSize((prev) => {
return {
...prev,
[size]: {width: this.width, height: this.height}
}
})
};
img.src = url;
}
}
useEffect(() => {
getImageSize()
}, [])
check the fiddle link I hope it will work[https://jsfiddle.net/k7bue0ho/][1]
I've created a function that takes care of resizing a image, now i want to return the resized image from the function.
I'm working with react and know i can fix the problem by using the state but i don't like this solution..
Tried returning every different scope, but i still get a undefined response. Also when i return the reader itself, i get the old data.
Interface
interface IHTMLInputEvent extends Event {
target: HTMLInputElement & EventTarget;
}
the resize function
function resizeImage(file: IHTMLInputEvent, width: number) {
const fileName = file.target.files[0].name;
const reader = new FileReader();
reader.readAsDataURL(file.target.files[0]);
reader.onload = (event) => {
const img = new Image();
img.src = event.target.result.toString();
img.onload = () => {
const elem = document.createElement('canvas');
const scaleFactor = width / img.width;
elem.width = width;
elem.height = img.height * scaleFactor;
const ctx = elem.getContext('2d');
ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
ctx.canvas.toBlob((blob) => {
return new File([blob], fileName, { type: 'image/jpeg', lastModified: Date.now() });
}, 'image/jpeg', 1);
};
};
}
function for handling the upload(so far)
function handleUpload(e: IHTMLInputEvent) {
const resizedImage = resizeImage(e, 600);
}
input field
<input
className={classes.inputForUpload}
accept='image/*'
type='file'
ref={uploadImage}
onChange={(e: IHTMLInputEvent) => handleUpload(e)}
/>
I would like to return the new created image.
You can solve this using Promise,
function resizeImage(file: IHTMLInputEvent, width: number) {
return new Promise((resolve, reject) => {
const fileName = file.target.files[0].name;
const reader = new FileReader();
reader.readAsDataURL(file.target.files[0]);
reader.onload = (event) => {
const img = new Image();
img.src = event.target.result.toString();
img.onload = () => {
const elem = document.createElement('canvas');
const scaleFactor = width / img.width;
elem.width = width;
elem.height = img.height * scaleFactor;
const ctx = elem.getContext('2d');
ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
ctx.canvas.toBlob((blob) => {
resolve(new File([blob], fileName, {
type: 'image/jpeg',
lastModified: Date.now()
}));
}, 'image/jpeg', 1);
};
};
});
}
With async, await ,
async function handleUpload(e: IHTMLInputEvent) {
const resizedImage = await resizeImage(e, 600);
// do you suff here
}
JSX,
<input
className={classes.inputForUpload}
accept='image/*'
type='file'
ref={uploadImage}
onChange={async (e: IHTMLInputEvent) => await handleUpload(e)}
/>
May I ask why you don't like the solution of using state? This seems like a pretty standard use case.
Your state could look something like this:
state = {
imageDescription: '',
imageUrl: null
};
Your action handler would simply setState upon success like so:
img.onload = () => {
...
this.setState({ imageDescription: fileName, imageSrc: img.src })
};
Finally your render function would look something like this:
render() {
const { imageDescription, imageUrl } = this.state;
return (
<Fragment>
<input
className={classes.inputForUpload}
accept='image/*'
type='file'
ref={uploadImage}
onChange={(e: IHTMLInputEvent) => handleUpload(e)}
/>
<img src={imageUrl} alt={imageDescription} />
</Fragment>
)
}
P.S. you can delete handleUpload and call resizeImage directly.
I'm looking for the best solution to compress images that's I receive and need to store in my database.
Actually, I convert an image in base64 and then send it to the server.
handleImage = e => {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
file: file,
image: reader.result
});
}
this.setState({ imgchange: true })
}
And then send the current image in state to the server. But with the low quality image it's fine but when I try to upload a medium-high quality I can't save it on the server, I need a method to compress the image.
Have you any idea to achieve this? Can you show me an example?
Best npm package you can use for image compression is browser-image-compression. Tested in React and Redux.
Install npm install browser-image-compression --save
async await syntax:
import React from "react";
import imageCompression from 'browser-image-compression';
function photoUpload() {
async function handleImageUpload(event) {
const imageFile = event.target.files[0];
console.log('originalFile instanceof Blob', imageFile instanceof Blob); // true
console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`);
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true
}
try {
const compressedFile = await imageCompression(imageFile, options);
console.log('compressedFile instanceof Blob', compressedFile instanceof Blob); // true
console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB
await uploadToServer(compressedFile); // write your own logic
} catch (error) {
console.log(error);
}
}
return (
<>
<div>
<input type="file" accept="image/*" onChange={event => handleImageUpload(event)}/>
</div>
</>
);
}
export default photoUpload;
For more in details can check on NPM
I've done this in a React/Redux app before with image libraries that ultimately produce a compressed JPG file - if that works for you, then using something like Jimp is an option. It was made for node, but I installed it for use in the browser, and used it like so:
Jimp.read('image.jpg').then((image) => {
if (image.bitmap.data.length > MAX_IMAGE_SIZE) {
image.quality(80); // some value of 'quality'
}
// do something else with the image
});
You can do some fiddling to figure out what the right quality of JPG is right for your app, and adjust accordingly.
When I was using this, I threw together an onDrop function that processed images a lot like you do - I won't guarantee that this code is super clean or super efficient - it came from a throwaway prototype - but it should get you started on the right path:
handleFileDrop(e) {
e.stopPropagation();
e.preventDefault();
var file = e.dataTransfer.files[0];
var reader = new FileReader();
reader.onload = (function(inputFile) {
return function(e) {
var imageBlob = new Blob([e.target.result], {type: inputFile.type});
var src = URL.createObjectURL(imageBlob)
Jimp.read(src, (err, image) => {
// do stuff here
});
})(file);
reader.readAsArrayBuffer(file);
}
you can use react-image-file-resizer library to compress image
import Resizer from 'react-image-file-resizer';
Resizer.imageFileResizer(
file, //is the file of the new image that can now be uploaded...
maxWidth, // is the maxWidth of the new image
maxHeight, // is the maxHeight of the new image
compressFormat, // is the compressFormat of the new image
quality, // is the quality of the new image
rotation, // is the rotatoion of the new image
responseUriFunc, // is the callBack function of the new image URI
outputType // is the output type of the new image
);
For Example:
import React, { Component } from 'react';
import Resizer from 'react-image-file-resizer';
class App extends Component {
constructor(props) {
super(props);
this.fileChangedHandler = this.fileChangedHandler.bind(this);
}
fileChangedHandler(event) {
var fileInput = false
if(event.target.files[0]) {
fileInput = true
}
if(fileInput) {
Resizer.imageFileResizer(
event.target.files[0],
300,
300,
'JPEG',
100,
0,
uri => {
console.log(uri)
},
'base64'
);
}
}
render() {
return (
<div className="App">
<input type="file" onChange={this.fileChangedHandler}/>
</div>
);
}
}
export default App;
For Details you can read this documentation
import React from "react";
import imageCompression from "browser-image-compression";
function imageOrientation(image) {
var img = new Image();
img.src = image;
return { width: img.naturalWidth, height: img.naturalHeight };
}
const imageCompressionFile = async (image) => {
const options = {
maxSizeMB: 5,
maxWidthOrHeight: 1080,
useWebWorker: true,
};
let { width, height } = imageOrientation(URL.createObjectURL(image));
let compress_file;
if (width > height) {
if (width > 400) {
// landscape
let compressedFile = await imageCompression(image, options);
compress_file = await compressedFile;
} else {
// No landscape
compress_file = await image;
}
} else if (width < height) {
// portrait
if (height > 400) {
let compressedFile = await imageCompression(image, options);
compress_file = await compressedFile;
} else {
// No portrait
compress_file = await image;
}
} else {
const compressedFile = await imageCompression(image, options);
compress_file = await compressedFile;
}
return await compress_file;
};
function APP() {
async function handleImageUpload(event) {
const image = event.target.files[0];
let fileData = await imageCompressionFile(image);
}
return (
<>
<div>
<input
type="file"
accept="image/*"
onChange={(event) => handleImageUpload(event)}
/>
</div>
</>
);
}
export default APP;