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,
Related
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 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);
I'm trying to produce the same base64 data for an image file in both JavaScript and in Ruby. Unfortunately both are outputting two very different values.
In Ruby I do this:
Base64.encode64(File.binread('test.png'));
And then in JavaScript:
var image = new Image();
image.src = 'http://localhost:8000/test.png';
$(image).load(function() {
var canvas, context, base64ImageData;
canvas = document.createElement('canvas');
context = canvas.getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
context.drawImage(this, 0, 0);
imageData = canvas.toDataURL('image/png').replace(/data:image\/[a-z]+;base64,/, '');
console.log(imageData);
});
Any idea why these outputs are different?
When you load the image in Ruby the binary file without any modifications will be encoded directly to base-64.
When you load an image in the browser it will apply some processing to the image before you will be able to use it with canvas:
ICC profile will be applied (if the image file contains that)
Gamma correction (where supported)
By the time you draw the image to canvas, the bitmap values has already been changed and won't necessarily be identical to the bitmap that was encoded before loading it as image (if you have an alpha channel in the file this may affect the color values when drawn to canvas - canvas is a little peculiar at this..).
As the color values are changed the resulting string from canvas will naturally also be different, before you even get to the stage of re-encoding the bitmap (as PNG is loss-less the encoding/compressing should be fairly identical, but factors may exist depending on the browser implementation that will influence that as well. to test, save out a black unprocessed canvas as PNG and compare with a similar image from your application - all values should be 0 incl. alpha and at the same size of course).
The only way to avoid this is to deal with the binary data directly. This is of course a bit overkill (in general at least) and a relative slow process in a browser.
A possible solution that works in some cases, is to remove any ICC profile from the image file. To save an image from Photoshop without ICC choose "Save for web.." in the file menu.
The browser is re-encoding the image as you save the canvas.
It does not generate an identical encoding to the file you rendered.
So I actually ended up solving this...
Fortunately I am using imgcache.js to cache images in the local filesystem using the FileSystem API. My solution is to use this API (and imgcache.js makes it easy) to get the base64 data from the actual cached copy of the file. The code looks like this:
var imageUrl = 'http://localhost:8000/test.png';
ImgCache.init(function() {
ImgCache.cacheFile(imageUrl, function() {
ImgCache.getCachedFile(imageUrl, function(url, fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
console.log($.md5(this.result.replace(/data:image\/[a-z]+;base64,/, '')));
};
reader.readAsDataURL(file);
});
});
});
});
Also, and very importantly, I had to remove line breaks from the base64 in Ruby:
Base64.encode64(File.binread('test.png')).gsub("\n", '');
I would like to know if there is any framework that allow me to store canvas drawn objects, load and manipulate, or if there isnt, how to do such process (if possible).
My objective is to procceed this steps:
Draw on canvas with mouse/touch on mobile devices
Store the drawn object in a way I can manipulate later (not as image file)
(with store I mean to save it remotely on any kind of source)
Load the drawn object to canvas, and be able to manipulate him (bending a line p.ex)
You can use base64 + localStorage:
var canvas = document.getElementsByTagName('canvas')[0];
var pngBase64 = canvas.toDataURL();
localStorage.setItem('myCanvasData', pngBase64);
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.