I'm trying to produce animation file using node.js and the node-canvas package, extracting frame by frame. Part of the animation includes scaling and moving images. My problem is that no anti-aliasing seems to be happening although according to node-canvas and cario (the graphic library behind the canvas) anti-aliasing should be done by default. Also, according to node-canvas anti-aliasing can be controlled using this systex ctx.antialias = 'gray';, but it doesn't seem to do anything either.
If there's any more details I can provide that may help please let me know.
Is there any way, using node-canvas or even cairo directly, to add anti-aliasing to image transformations?
P.S. I'm using ctx.drawImage method to draw the images and I've already also tried using patternQuality = 'best'; with no success.
When upscaling patterns, cairo uses bilinear interpolation by default, which should look reasonable. However, when downscaling, cairo currently (as of 1.12.14) does not properly antialias patterns. There is no way to get this, short of actually adding the feature to cairo itself, though see below for a potential workaround.
The 'antialias' setting in cairo controls the antialiasing of rasterized shapes and text, not patterns. The setting that controls antialiasing for patterns is called 'cairo_pattern_set_filter' and has the following values:
CAIRO_FILTER_FAST, // usually the same as NEAREST
CAIRO_FILTER_GOOD, // usually the same as BILINEAR
CAIRO_FILTER_BEST, // usually the same as BILINEAR
CAIRO_FILTER_NEAREST, // for upscaling, this is pixel-replication
CAIRO_FILTER_BILINEAR, // for upscaling, this is linear interpolation
CAIRO_FILTER_GAUSSIAN // not implemented, should not be used
But as mentioned, none of them work well for downscaling.
A workaround that some people have used is to scale the image down in steps of 2. That is, keep downscaling the image to half the size in both dimensions until it is roughly the desired size. This downscaled image can then be used with a modified transformation that downscales by less than 2.
Related
I'm still gathering the pieces needed to understand a web 2 print editor in HTML5. I'm using FabricJS and a bunch of other 3rd party tools to make it a possibility.
My latest roadblock is my Samsung Chromebook Pro, which has a HiDPI/retina display. The window.devicePixelRatio is different, and I'm trying to figure out how to cope with this higher DPI using FabricJS. I followed what everybody else has been saying is successful, but it doesn't seem to work or help me. Plus my canvas becomes extremely large, and objects are no longer movable.
I'm also using another 3rd party library, PetitoJPEG, which I am using to encode the raw pixel data from the FabricJS canvas.
Because of the different devicePixelRatio from my other laptop, running the encoder causes the red dot on the right side of the canvas to not show on the proof image.
I'm wondering how I can encode the entirety of the canvas on a HiDPI display.
Because of all the 3rd party assets, I'm willing to provide a direct link instead of a JSFiddle: http://xbit.x10host.com/editor2.php
I think I understand what the scaling method people are saying is successful does, but I'm wondering if it isn't working properly because of my CSS transform on the canvas container. I've tried adjusting scaling and the canvas widths/heights and CSS width/heights through the console to no avail though.
Any help would be appreciated. I'll paste the snippet that everybody is saying is working below.
Note: My Samsung Chromebook Pro is logging 26562 bytes written, and my Lenovo x230 Windows laptop is logging 27586 bytes.
if( window.devicePixelRatio !== 1 ){
var c = canvas.getElement(); // canvas = fabric.Canvas
var w = c.width, h = c.height;
// Scale the canvas up by two for retina
// just like for an image
c.setAttribute('width', w*window.devicePixelRatio);
c.setAttribute('height', h*window.devicePixelRatio);
// then use css to bring it back to regular size
// or set it here
c.setAttribute('style', 'width="'+w+'"; height="'+h+'";')
// or jQuery $(c).css('width', w);
// $(c).css('width', w);
// $(c).css('height', h);
// finally set the scale of the context
c.getContext('2d').scale(window.devicePixelRatio, window.devicePixelRatio);
canvas.renderAll();
}
I know the above chunk of code seems to work for most people, but isn't the following line incorrect?
c.setAttribute('style', 'width="'+w+'"; height="'+h+'";')
That isn't how you define inline styles. You need a ":" instead of "=" and there's no unit of measurement, so I'm wondering how this works.
Here is a screenshot of the new isolation page I've created to more effectively isolate the issue: And here is the direct URL: http://xbit.x10host.com/isolation.php
You can see how using the method everybody else is saying works isn't working. The CSS property values are invalid. Maybe I'm just confused, but even if I set the CSS width and height correctly, am I using the 1125x675 for getImageData or the new 4500x1200? Using the latter causes the proof image to be too large; the 4500x1200 contains all the data I want but then there's all this black space. Below is a screenshot of that result.
Now, the 2250x1350 (output using the goto method with correct inline CSS syntax) seems to be correct, but I thought the canvas fix was supposed to help to output the same as my other laptop. Would this mean people with HiDPI displays will get higher quality proof images (and prints) than people who don't have a HiDPI display? I want the dimensions and pixel count to the be same regardless of device DPI.
I've found out how to make images the same number of pixels regardless of DPI.
It involves using an off-screen canvas and re-drawing the on-screen canvas onto it at the desired width and height, and re-encoding the off-screen canvas at 300dpi so that regardless of screen resolution, the output file will always be the same.
Question:
Is canvas more suitable than svg in the following case?
Case:
I'm drawing a chart (using d3js library) similar to this one (but with much more data):
http://mbostock.github.com/d3/talk/20111116/iris-parallel.html
It's based on an svg and it works fine for several thousands of lines (up to 5000), adding more lines (svg path) decreases the performance dramatically (scrolling in the page becomes slow)
Keep in mind: That I need to add mouse events (which is handy in svg)
Generally svg is better suited for vector images, like in your example. However canvas has a lot of benefits in modern browsers such as hardware acceleration, so for drawing the lines, as long as zooming, panning ect. isn't required performance will be using canvas.
Mouse events can be a pain using canvas, since you have to manually keep track of everything, so with 5000+ points using canvas it wont be fun. The trade off however will be once the points are drawn, assuming you only draw them once the page will behave fine regardless of the number of lines, since they are all drawn to a raster image and aren't part of the DOM.
Honestly though the best way to find it is to test what you currently have using canvas.
When performance becomes a problem, switching to canvas might be an option. In this case you can draw the canvas once. Afterwards it's pretty much treated like an image. Drawing might take some time, but afterwards it can be scaled pretty quickly. Note that it is possible to draw a rendered SVG to a canvas using the context.drawImage method (example). So you could keep your SVG generation code to create an SVG in the background, and then draw it to the canvas.
But keep in mind that it won't scale as beautiful as an SVG as soon as it is on the canvas. When the user zooms in, it will get blurry or pixely, depending on how the browser scales graphics.
Click events on canvas can be handled in two ways. Either keep an array of click targets, and add an onclick event handler to the canvas. When a click occurs, iterate the array and check which one is closest to the click coordinates.
The other option is to use hit regions. These have to be defined as polygonal paths.
+1 to everything said above. I've seen some amazing performance increases when using canvas over SVG and over compositing images using the DOM.
About manipulating the canvas image with mouse events, I imagine the best approach for an image such as you are describing is to abstract it away using a library like the following:
http://paperjs.org
http://kineticjs.com
http://www.createjs.com/#!/EaselJS
Keep your code away from the canvas itself and let a library do the thinking for you.
What I am trying to do is create a game that has an extreme amount of zoom-ability on a canvas element. I would like to make use of the advantage that vector graphics have insofar as being able to be programmatically created at runtime, with the high performance of bitmap images.
What I would like to do is programmatically create the first-frame image of a game "sprite"... this would be a vector image. After the first frame though, I do not want to keep wasting CPU cycles on drawing the image though.. i would like to cache it as a bitmap/high performance image for that zoom level.
Following this, if the user zooms in by >20%, I then redraw the image with a higher level of detail vector image. As above, this vector image would then be cached and optimized.
As you can see here, this would be a pretty basic space ship.. I would first render it programmatically as a vector and then.. raster it I guess? Goal is to avoid wasting CPU.
If the user zooms in...
A new vector image of the same shape would be drawn, albeit with a much higher level of detail. This is basically a Level Of Detail system. In this case as well, after the initial programmatic draw, I would "raster" the image for maximum performance.
Does anyone have ideas on what tools I would need to make this a reality inside of a HTML canvas? (The rest of the game will be running inside of the canvas element..)
Thank you very much for your thoughts.
**Edit: I wanted to add... perhaps the route of rendering an image via SVG (programmatically), then pushing that png file into the canvas using drawimage(), might provide some success? Something similar? Hmm...
Check out that article , but it seems there is no standard method to do what you want and it may fail in IE.
http://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/#svg_to_canvas
You should perhaps go with an all SVG game , or provide a maximum zooming rate to your game and use big images as sprite assets. it would not have been a problem using flash,but i guess you wont go with flash anyway.
Maybe there is a framework that can translate SVG into a "canvas drawing sequence" but i would not bet on high performances in that case.
I managed to answer my own question.
The way to do this is to first create an SVG file, and then convert it to a PNG file on the client using "canvg". The PNG can be created at different levels of details based on what you want, and in this way you could create a dynamic LOD system.
Flash does something similar automatically by cashing a bitmap image of the SVG file... it's called "pre-rendering". If the SVG isn't scaled or the alpha isn't changed, flash will just use the bitmap instead (much faster then continuously re-rendering the SVG file, in complex cases). Size (and thus detail) of the PNG output can be modified however you like, and so pre-rendering could be done based on events as well.
From this information, I have decided to implement the LOD system such that SVG is used whilst the user is actively zooming (scaling the target "sprite"), and then as the zoom slows down, compute a PNG pre-render. Also, at extremely high levels of zoom, I simply use the SVG, as it is much easier for the CPU to compute SVG's at high resolution, then bitmap images that cover most of the screen. (just take a look at some of the HTML5 icon tests that put lots of icons on the screen... the bigger the icons are, the slower it runs).
Thanks very much to everyone's comments here and I hope that my question/answer has helped someone.
I need to choose a library for "standard" charting: pies, lines and bars.
From what I've read, it seems to me that the best format is SVG/VML, like Highcharts for example. SVG is becoming standard across all major browsers, now that IE 9 accepts it. It seems easier to rescale and export than Canvas.
Still, I see that several charting libraries rely on Canvas. Am I missing something? Is there any reason for considering Canvas over SVG for such applications?
You can generally achieve the same results with either. Both end up drawing pixels to the screen for the user. The major differentiators are that HTML5 Canvas gives you pixel-level control over the results (both reading and writing), while SVG is a retained-mode graphics API that makes it very easy to handle events or manipulate the artwork with JavaScript or SMIL Animation and have all redrawing taken care of for you.
In general, I'd suggest using HTML5 Canvas if you:
need pixel-level control over effects (e.g. blurring or blending)
have a very large number of data points that will be presented once (and perhaps panned), but are otherwise static
Use SVG if you:
want complex objects drawn on the screen to be associated with events (e.g. move over a data point to see a tooltip)
want the result to print well at high resolution
need to animate the shapes of various graph parts independently
will be including text in your output that you want to be indexed by search engines
want to use XML and/or XSLT to produce the output
Canvas isn't needed unless you want heavy manipulation/animation or are going to have 10,000+ charts. More on performance analysis here.
It is also important to make the distinction: Charting and diagramming are two different things. Displaying a few bar charts is very different from (for instance) making diagramming flowcharts with 10,000+ movable, link-able, potentially-animated objects.
Every SVG element is a DOM element, and adding 10,000 or 100,000 nodes to the DOM causes incredible slowdown. But adding that many elements to Canvas is entirely doable, and can be quite fast.
In case it may have confused you: RaphaelJS (in my opinion the best charting SVG Library) makes use of the word canvas, but that is no way related to the HTML <canvas> element.
In the past two years, my preference has been to use svg, as I mainly deal with relatively small datasets to build pies, column charts or maps.
However one advantage I have found with canvas is the ability to save the chart as an image thanks to the toDataURL method. I haven't found an equivalent for svg, and it seems that the best way to save an svg chart client side is to convert it to canvas first (using for example canvg).
Is there a way to draw an arc using points in JavaScript??
I need to draw an arc when I specify a group of points.
Depending on what platforms you want to support and on the complexity of the drawing, I'd suggest either DHTML, canvas (with ExplorerCanvas for IE).
Although DHTML is cross-browser, canvas seems the way to go for future projects since it is capable of much more and is actually meant to be used for graphics.
Edit: Either I was wrong when I originally wrote this or ExplorerCanvas was actually using flash to render its canvas. but now they've seemed to switch to VML and/or Silverlight. I've edited my answer to exclude that variable dependency. I've also added a stronger suggestion towards canvas.