I have a question about saving the png to the canvas.
It saves me a picture, but it saves me just what draws. As I loaded the background that no longer sees.
var dataURL = this.canva.toDataURL();
document.getElementById(id).src = dataURL;
background images I have written out so
function leapController()
{
var tracks = new Array();
tracks[0] = {link: 'png/1.png'};
tracks[1] = {link: 'png/2.png'};
tracks[2] = {link: 'png/3.png'};
tracks[3] = {link: 'png/4.png'};
tracks[4] = {link: 'png/5.png'};
I would like to enroll my whole image, along with the background and drawing element
You give precious little code in your question, but the idea is when you're ready to save the canvas you can use Compositing to draw the background image behind the existing pixels.
To get you started ... here's some code applying the idea:
function save(desiredBackgroundIndex){
// set context compositing to draw behind existing pixels
context.globalCompositeOperation='destination-over';
// Draw the desired background on the canvas BEHIND existing pixels
// Assumes tracks[] is in scope
context.drawImage(tracks[desiredBackgroundIndex],0,0);
// always clean up! Change compositing mode back to default
context.globalCompositeOperation='source-over';
// set #id's src to the canvas's data url
document.getElementById(id).src = this.canva.toDataURL();
}
// USAGE example: add png/3.png to the canvas and save the canvas's data url to #id
save(2);
Related
I am creating a webpage that generates in line svg images and then allows the user to download them in various formats. (png,jpg,jpeg,svg) I have an exporting function to convert the images from inline svg to canvas as then canvas to dataURL for download. When I try exporting with Chrome, it takes time to shrink larger images down (7,000x10,000px) to the canvas because of Chrome's data cap. (FF doesn't have any issue and can shrink massive images in a fraction of the time that chrome can)
I need to create a loading progress bar for when the image is taking a while to populate and download from the canvas. I tried the solutions in this answer to no avail because I am using a objectURL created from a svg blob and not a image file on the server.
Is there a way to view the progress of an image load when setting the image src using an objectURL.
canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d',{alpha:false});
// creates a new blank image
var img = new Image();
// encode the svg to a string
var data = (new XMLSerializer()).serializeToString(svg);
// creates a blob from the encoded svg
var svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
// creates an object url for the download
var url = DOMURL.createObjectURL(svgBlob);
// when the image is done being created and its loaded
img.onload = function(){ /* drawImage to canvas and save as dataURL*/ }
// load the image src using the objectURL
img.src = url;
Is there a way to read the progress of the image loading when the url is a objectURL not a image file?
Short answer, there's no way I know of to do what you're asking. As you say, you're not downloading from a server, so there's no 'onprogress' event to hook into. You need something to provide regular alerts from inside the createObjectURL function, and it does not provide an event to hook into that it will fire 20 times during processing to give you a status.
What you could do instead is estimate how long it will take in Chrome, and provide an estimated status bar that way. If you know how big your canvas is, you can estimate how long it will take Chrome to generate your image based on its fixed data cap. If a 7Kx10K image takes x seconds, it should generally take that same time to generate every time, due to the fixed data cap, correct? Do a little math to figure out the seconds based on overall pixels. If it take 100 seconds to process (or whatever it takes), then that's 700,000 px / sec. Again, because of the fixed data cap, this value should remain the same until Chrome changes its data cap in a new version.
You could then provide a simulated progress bar that advances at that rate for how many px total you have. If you have 7M px, then it should take 10 seconds to advance the bar from 0 to 100% (based on my sample rate of 700Kpx/sec).
This is all based on Chrome having a fixed data cap; you can calculate a math rate against that based on the number of px you have to process.
It is possible that I can change the image color of png image by php/js functions?
I would like to change color only on non-transparent area. An example image is available here:
I would like to change colors only on the visible t-shirt not on all area.
Please post your actual transparent PNG input file. In lieu of that, in ImageMagick command line, this I think should work to colorize the white to sky-blue.
convert image.png \
\( -clone 0 -alpha off -fill skyblue -colorize 100 \) \
\( -clone 0 -alpha extract \) \
-compose multiply -composite \
result.png
I do not know PHP Imagick that well. So I will leave my answer to someone to translate to Imagick.
ImageMagick could be a good route to take (check out PHP.net's Imagick reference, and phpimagick.com), as it'll do the operation on the server, and 'theoretically' work no matter what, (e.g., even if the client's JS is turned off).
If you're not able to use it (e.g., your hosting's limited), an option via JavaScript is to use an offscreen canvas, draw the images to it, generate a PNG from it, and then finally add it to the page.
Here's an example of some working code:
// shirtPath: the url to the shirt
// overlayPath: the url to the overlay image
// callback: a callback that is given the image once it's generated
function generateShirtOverlay(shirtPath,overlayPath,callback) {
// load the images
var loadedImages = 0;
function imageLoaded() {
loadedImages++;
if(loadedImages == 2) {
composeImages();
}
}
var shirtImage = new Image(),
overlayImage = new Image();
// set the image onload callbacks
shirtImage.onload = imageLoaded;
overlayImage.onload = imageLoaded;
// load the images
shirtImage.src = shirtPath;
overlayImage.src = overlayPath;
var generatedImage = new Image();
function composeImages() {
// get image sizes
var width = shirtImage.width,
height = shirtImage.height,
overlayWidth = overlayImage.width,
overlayHeight = overlayImage.height;
// create a canvas
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
// draw the shirt to it
var context = canvas.getContext("2d");
context.drawImage(shirtImage,0,0);
// set the global composite operator to multiply
// this overlays the image nicely, keeping the
// dark areas on the shirt dark
context.globalCompositeOperation = "multiply";
// draw the overlay image, centering it if it's
// not the same size
context.drawImage(overlayImage,
(width - overlayWidth) / 2,
(height - overlayHeight) / 2
);
// a bit of masking magic! for details, see:
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
context.globalCompositeOperation = "destination-in";
// draw the shirt again; this clips the overlay image
// at the shirt's edges
context.drawImage(shirtImage,0,0);
// finally, extract a PNG from the canvas
var png = canvas.toDataURL("image/png");
generatedImage.onload = returnComposedImage();
// and load it to an image object
generatedImage.src = png;
}
function returnComposedImage() {
if(typeof callback == "function")
callback(generatedImage);
}
}
This function could be called to generate the image. Ultimately, it'll call a callback, providing the generated image. Then, since it's an Image object, you can add it right to the page, e.g.:
function addImageToPage(image) {
document.body.appendChild(image);
}
generateShirtOverlay("images/shirt.png", "images/design1.png", addImageToPage);
Please note, though, the disadvantage of client-side rendering is that not all browsers support all these canvas features. In addition, it's a little more overhead on the client-side. Fortunately, though, as it's not an extremely complex manipulation, this method uses only image blend modes, and doesn't need to iterate pixels.
You can use the PHP imagick library. I think it's the best way.
Imagick class
For example :
<?php
$imageInPath = __DIR__. '\\image-before.png';
$imageOutPath = __DIR__. '\\image-after.png';
$overlayColor = '#F00'; // Red for example
$fuzz = 0;
$overlay = new Imagick( $imageInPath ); // Create imagick object
$overlay->opaquePaintImage ( $overlay->getImagePixelColor(0, 0) , $overlayColor , $fuzz , true ); // Fill the opaque area
$image = new Imagick($imageInPath); // Create imagick object
$image->compositeImage($overlay, Imagick::COMPOSITE_DARKEN , 0, 0); // Compose the original image with the overlay
$image->writeImage( $imageOutPath ); // Save the image to the given path
?>
This snippet assumes that the first pixel (x=0, y=0) is transparent.
You can change the way to apply the overlay on the picture by using another COMPOSITE constant. You can get the full list of imagick constants here : Imagick constants
Don't forget to install Imagick library and to enable it in the php.ini file to get it work.
i'm trying to use localStorage to save an image, or multiple images for retrieval at a later date to upload to a server.
The current camera code is as follows:
function capturePhoto() {
navigator.camera.getPicture(onCameraSuccess, onCameraFail, {quality: 70, destinationType : Camera.DestinationType.DATA_URL});
}
function onCameraSuccess(imageData) {
//In our success call we want to first process the image to save in our image box on the screen.
var image = document.getElementById('image');
image.src = "data:image/jpeg;base64," + imageData;
//Create a new canvas for our image holder
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
// Make sure canvas is as big as the picture
imgCanvas.width = image.width;
imgCanvas.height = image.height;
// Draw image into canvas element
imgContext.drawImage(image, 0, 0, image.width, image.height);
// Get canvas contents as a data URL
var imgAsDataURL = imgCanvas.toDataURL("image/png");
// Save image into localStorage
try {
// localStorage.setItem(“savedImage”, imgAsDataURL);
localStorage.setItem("savedImage", imageData);
alert('Image Saved');
}
catch (e) {
alert("Storage failed: " + e);
}
var imageStorage = localStorage.getItem("savedImage");
// myCardHolder= document.getElementById(“m1-cardStorage-image1″);
// Reuse existing Data URL from localStorage
var imageInfo = document.getElementById('image');
imageInfo.src = "data:image/jpeg;base64," + imageStorage;
}
This triggers the camera, and the image captured is displayed into
<img id="image" src=""></img>
It also draws a canvas to output the image into. What i'm really trying to achieve is to capture the images base64 data to be able to store it into an array so that it may be uploaded/downloaded from a server.
Ideally i'd like to completely avoid having to display the image to the user, and simply store the images data
I may have misunderstood the localStorage/camera api a little, so any pointers would be great.
Does the image HAVE to be output into an element before the data can be stored? If i could just output it into the canvas that may never have to be shown, and extract the data from the canvas element?
Does the image HAVE to be output into an element before the data can be stored?
Not at all, in this case anyways. You are already receiving the image as base64 data so just store that directly.
Problems:
datauris can be chopped by the browser if too long
if not chopped by browser on string level, the data can be chopped by localstorage itself which has a size limit (i think it's currently around 5 mb for most browsers but there is no standard here)
a string uses two bytes per char so the storage is in effect the half
A better local storage is to use indexedDB.
When you read the base64 data, then you have to use an Image to show the data. Just prefix as you do with data:... etc. and remember to use correct file type.
Last year I was trying to solve the same problem, I don't have the code right now but I followed kind of the approach taken on this answer:
How to convert image into base64 string using javascript
Remember that localStorage has a limit of 5 MB, so if you save a lot of images in b64 you can reach that limit easily. (which was my case), so I had to move my storage to somewhere else, like a sqlite or something like that.
I am trying to create an application like this using JavaScript and canvas.
I have created a draft using kineticjs as the canvas library. The teeth are all png images and the lines are drawn by kinetic js using the Line object.
A friend of mine suggested I draw everything as one large svg (teeth and lines) where each tooth and line is another distinguished path load it on canvas with distinguished id's and manipulate them using the id's.
I tried reading the kineticjs docs and fabricjs object but didn't find something like that. I mean having the svg stored and loading it on canvas using fabricjs or kineticjs. Then the library should parse it and create the svg on canvas which I can manipulate them by id's.
Is it even possible? I am very new to this svg graphics. Is it even possible the way I am thinking it?
I am currently loading Teeth as Images using KineticJS
Periodontogram.prototype.initializeImageObjects = function (){
var obj = this;
if (this.DEBUG){
console.log("initializing ImageObjects:")
}
var check = function (item){return item !== undefined;}
var url = this.options.url;
var imageObj;
var image;
this.options.imageFilenames.forEach(function (filename, index){
if (obj.DEBUG){
console.log("Loading "+filename+" image");
}
var src = url+'/'+filename;
imageObj = new Image();
imageObj.src = src;
imageObj.onload = function (){
if (obj.DEBUG){
console.log("Image "+src+" successfully loaded");
}
image = new Kinetic.Image({
x:0,
y:0,
id: filename.split('.')[0],
width: this.width,
height: this.height,
image:this
});
obj.imageObjects[index] = this;
obj.teethImages[index] = image;
if (obj.imageObjects.filter(check).length === obj.options.imageFilenames.length ) {
if (obj.DEBUG){
console.log("All Images loaded successfully");
console.log("ready to call rest methods")
}
obj.positionImages();
obj.createLineGroups();
obj.createLabelGroups()
obj.createStage();
obj.drawOnScreen();
$("#"+obj.container).width(obj.stage.getWidth());
$("#"+obj.container).height(obj.stage.getHeight ());
obj.callback();
$.event.trigger({
type:'finishLoading',
obj: obj
});
}
};
});
}
I want all this be replaced by loading a whole svg, that consist of teeth and lines.
FabricJS will convert, decompose & recompose an existing single SVG URL/string into a Fabric.PathGroup. Then you can use mySvgGroup.getObjects() to fetch the individual tooth paths which you can manipulate as desired.
http://fabricjs.com/fabric-intro-part-3/#deserialization
KineticJS will accept an individual SVG tooth's path data and display it on canvas.
http://kineticjs.com/docs/Kinetic.Path.html
Still, it's probably more flexible to create individual SVG paths for each tooth and feed those individual SVGs into either KineticJS or FabricJS.
Then you can manipulate them by id's as you desire:
Place each individual tooth using [x,y] coordinates,
Resize without pixelization,
Put each tooth in a Group and make annotations/drawings onto the tooth,
I know EaselJS has an awesome way of managing their sprites, but is it even possible to make it easier?
The following link convinced me to use texture atlasing : http://www.altdevblogaday.com/2012/09/17/building-an-html5-game-dont-shrug-off-atlases/
Few pointers why I want this so badly :
Flash CS6 Generate Spritesheet feature creates the TextureAtlas ready for use
Improves load time
Saves bandwidth
Better Dev time
I have no idea how to kick start this, any help would be appreciated.
Ok, I'll expand on my comment a little.
When you create a display element in EaselJS, you give it a source image:
srcImage = new Image();
srcImage.src = "http://whatever.com/image.png";
...
sprite1 = new Bitmap(srcImage);
By default, the instance of the Bitmap is the whole image. However, you can pinpoint the Bitmap down to a region of that image. For instance, suppose the image contains a row of 32x32 tiles:
sprites = [];
for(i=0; i<8; i++) {
sprites[i] = new Bitmap(srcImage);
sprites[i].sourceRect = { x:i*32, y:0, width:32, height:32 };
}
You've now got eight sprites, each referring to a different 32x32 region of the source image.
You can also change it dynamically:
sprCursor = new Bitmap(srcImage);
sprCursor.sourceRect = ( x:0, y:0, width:32, height:32 };
...
if(cursorOverButton) {
sprCursor.sourceRect = { x:32, y:0, width:32, height:32 };
// or more efficiently, sprCursor.sourceRect.x = 32;
}
I imagine that's pretty much how EaselJS works behind the scenes to handle animated sprites.