FabricJS HiDPI getImageData - javascript

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.

Related

Node-Canvas image anti-aliasing doesn't seem to work

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.

How to increase the canvas bit depth to create a higher quality picture in javaScript? and how to increate the canvas size when outputting

I'm new to HTML5 and JavaScript, and I'm trying to use the canvas element to create a high(ish) quality output image.
The current purpose is to allow users to all their own images (jpeg, png, svg). this works like a charm. however when I render the image it's always a 32-bit png. how can I create a higher quality picture using JavaScript(preferably) ?
when I output the file, it always seems to keep the same resolution as the canvas, how can I change this using JavaScript(preferably)
Thanks in Advance guys, I looked around for a while but I couldn't find the answer to this :(
If you want to create a larger image with getImageData or toDataURL then you have to:
Create an in-memory canvas that is larger than your normal canvas
Redraw your entire scene onto your in-memory canvas. You will probably want to call ctx.scale(theScale, theScale) on your in-memory context in order to scale your images, but this heavily depends on how they were created in the first place (images? Canvas paths?)
Call getImageData or toDataURL on the in-memory canvas and not your
normal canvas
By in-memory canvas I mean:
var inMem = document.createElement('canvas');
// The larger size
inMem.width = blah;
inMem.height = blah;
Well firstly, when you draw the image to the canvas it's not a png quite yet, it's a simple raw bitmap where the canvas API works on as you call it's methods, then it's converted to a png in order for the browser to display it, and that's what you get when you use toDataURL. When using toDataURL you're able to choose which image format you want the output to be, I think jpeg and bmp are supported along with png. (Don't think of it as a png converted to another format, cause it's not)
And I don't know what exactly do you mean by higher quality by adding more bits to a pixel, you see 32 bits are enough for all RGBA channels to have a true color 8 bits depth giving you way more colors than the human eye can see at once. I know depending on the lighting and angle in which the user is exposed to your picture his perception of the colors may vary but not the quality of it which I'd say only depends on the resolution it has. Anyway the canvas was not designed to work with those deeper colors and honestly that much color information isn't even necessary on any kind of scene you could render on the canvas, that's only relevant for high definition movies and games made by big studios, also, even if you could use deep colors on the canvas it would really depend on the support of the user's videocard and screen which I think the majority of people doesn't have.
If you wish to add information not directly concerned to the color of each pixel but maybe on potencial transformations they could have you better create your own type capable of carrying the imageData acceptable by the canvas API, keeping it's 32-bit format, and the additional information on a corresponding array.
And yes, the output image has the same resolution as the canvas do but there are a couple of ways provided for you to resize your final composition. Just do as Simon Sarris said, create an offscreen canvas which resolution is the final resolution you want your image to be, then, you can either:
Resize the raster image by calling drawImage while providing the toDataURL generated image making use of the resizing parameters
Redraw your scene there, as Simon said, which will reduce quality loss if your composition contains shapes created through the canvas API and not only image files put together
In case you know the final resolution you want it to be beforehand then just set the width and height of the canvas to it, but the CSS width and height can be set differently in order for your canvas to fit in your webpage as you wish.

Best way to access pixels of an image in JavaScript to do Image Processing?

I am trying to perform simple operations on image using javascript. To get the pixels of the image, I am drawing the image on canvas and then get the ImageData from canvas. But for large images, drawing them on the canvas takes a lot of time.
Is there any other way of getting the image pixels without using the canvas element?
I don't think you can have image manipulation in JavaScript with hardware acceleration, so no matter if it's canvas or other technology you won't gain much marginal benefit within JavaScript environment.
If you want your code to be hardware accelerated, your code must be compiled into something that is ran in a GPU and has access to graphics memory. Flash and Silverlight's approach is introducing shading language like AGAL and HLSL. Maybe JavaScript will have something like this in the future.
So my suggestion is to do image processing with:
Server side technology
Flash and Silverlight with shading language
The example below uses MarvinJ. It takes a colored image having 2800x2053 resolution, iterate through each pixel calculating the gray value and setting it back. Above the canvas there is a div to print the processing time. On my machine, it took 115ms including image loading.
var time = new Date().getTime();
var canvas = document.getElementById("canvas");
image = new MarvinImage();
image.load("https://i.imgur.com/iuGXHFj.jpg", imageLoaded);
function imageLoaded(){
for(var y=0; y<image.getHeight(); y++){
for(var x=0; x<image.getWidth(); x++){
var r = image.getIntComponent0(x,y);
var g = image.getIntComponent1(x,y);
var b = image.getIntComponent2(x,y);
var gray = Math.floor(r * 0.21 + g * 0.72 + b * 0.07);
image.setIntColor(x,y,255,gray,gray,gray);
}
}
image.draw(canvas);
$("#time").html("Processing time: "+(new Date().getTime()-time)+"ms");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.marvinj.org/releases/marvinj-0.7.js"></script>
<div id="time"></div>
<canvas id="canvas" width="2800" height="2053"></canvas>
I don't have much experience with Javascript but Max Novakovic(#betamax) wrote a nice a tiny jQuery plugin called $.getImageData which might be helpful.
You can read more about $.getImageData on the disturb blog and the plugin page
You should be able to access pixels through something like context.getImageData(x, y, width, height).data.
Also, you mentioned hardware acceleration, and that should be available in Firefox4 and Chrome. Shadertoy uses GLSL shaders in the browser:
I'm not sure, but might it be possible to write the image data as a string, and manipulate the string, then translate it back into an image before showing the resulting image?
This wouldn't require drawing to canvas, only loading the image data as a string instead of an image. It would, however, involve some complex and possibly difficult to get right string manipulation, to make sure the image data translated correctly and moreso to manipulate the pixels.
It would also mean the string is likely to be very long for larger images, so this might take more memory than canvas would and could potentially need to be split into multiple strings. In exchange it may be faster than drawing to canvas, especially with multiple strings if you only need to manipulate part of the image.
I'm not experienced with image manipulation but theoretically there's no reason a file's data couldn't be written into a string. It is just, again, going to make a very long string and have a possible RAM impact because of it because the string could take up more RAM than the image file itself. But it will be faster loading since it doesn't have to process the data as much as it would to draw to canvas.

Draw Vectors to a Bitmap in EaselJS

I'm working on an EaselJS program that continually draws thousands of vectors. Needless to say, after a short amount of time, performance drops. To solve this in Flash, I would have created a Bitmap behind the vectors and periodically draw the vectors to that Bitmap and remove them from the display list.
I'm having trouble doing this in EaselJS, even though it seems like all the pieces are in place. How can I create an empty Bitmap() and draw the contents of my Container() to it?
I think you can just put everything in a container and cache it.
var vectorContainer=new Container;
for (var i = 0;i<numVectors;i++){
var vector=new Shape();
//draw whatever vector you want to vector.graphics
vectorContainer.addChild(vector);
}
vectorContainer.cache(someX,someY,someW,someH);
However get the newest untagged build as the current release has a bug and doesn't cache container properly.
I've also been adding the 'snapToPixel = true' to vectors because I've read it is supposed to help in some browsers. So, in your example code above, I would add the following line right before the last line where you do your caching.
vectorContainer.snapToPixel = true;
Check out this demo of cache and snap to pixel settings to see a live example.
In my particular browser set up, the snap to pixel does not actually change the performance, but in my reading of the docs it seems like it should.

HTML5 <canvas>: Performance implications of drawImage()

I was wondering if there were any performance differences in using drawImage() on a dynamically created canvas (i.e. document.createElement("canvas")) versus a canvas already created in the DOM (i.e. tags on a HTML page).
My thinking is that the "slow" part of drawImage() occurs when it actually has to display visuals to the user, though I could be wrong (can't find too much information on this).
Would it be substantially more expensive to draw a group of objects to a canvas in memory followed by a final draw to the "main" canvas than just drawing straight to the latter? I feel like it'd be better to have multiple canvases, at least for organizational purposes.
Related: Does the size of the canvas impact performance if you're only drawing to a subsection of it?
Talking about Chrome and Firefox I could not find any difference between static and dynamic canvas elements. Mainly the amount of pixels drawImage() handles makes it slow + the current globalCompositeOperation (copy, source-over are fastest). However, the browser has to render the full page, so it is a bad idea to place a stretched (background) image below the canvas.
There is a difference between the canvas attributes width/height and its style width/height attributes. You may have a 300*200 pixels canvas with a style size set to 100%. Then internal drawing speed is same what ever the browsers window size is. Of course display quality is a concern.
You may want to separate drawing (lines, boxes, arcs etc) from blitting (drawImage) and find out what consumes more time in your application. As long there is no need for multiple canvas (image processing, blending videos, etc.) try to avoid drawImage(). Your code - not elements - should help you to deal with 'organizational purposes'.
A fullscreen drawImage() on a 1 GHZ Netbook with 1024x600 pixels takes about 10msec. Doing it twice means there is no way to achieve a display rate of 50Hz. Things getting worse if you target iPhone or Android smartphones.
There is no need to do good-old-double-buffering with canvas, it's already implemented. You are free to update only relevant (dirty) subparts of your canvas element whenever you want and gain essential msecs.
Instead of using multiple canvas there is an option to do all invisible operations on a huge one in different sections - drawImage() with target and source the same. It is easier then to see what is happening while debugging.

Categories

Resources