The Firefox drawWindow()-Function expects as first parameter a XUL content-window as provided by the low-level api tab utils.
However with the introduction of the multiprocess architecture in Firefox (codenamed electrolysis or e10s) directly accessing tabs via the low-level api is no longer possible. While there are compatibility shims available, it is explicitly stated that they do not support plattform APIs that expect DOM objects.
On the other hand drawWindow() cannot be used in a content script since it is "chrome only".
So my questions are these:
How am I supposed to use drawWindow() if I cannot use it outside of chrome and cannot get a contentWindow-object within chrome?
What are my other options to let my addon take screenshots of websites within multiprocess Firefox?
Our current approach is based on the answer to this SO question. However it will not work with multiprocess Firefox
The solution to using drawWindow() was indeed to use framescripts as Noitidart suggested in the comments. The framescript I use for the screenshots looks like this:
addMessageListener("fs/make_screenshot_from_rectangle", makeScreenshot);
function makeScreenshot(payload) {
var rectangle = payload.data;
var startX = rectangle.startX || 0;
var startY = rectangle.startY || 0;
var width = rectangle.width || content.innerWidth;
var height = rectangle.height || content.innerHeight;
// Create canvas to draw window unto
var canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.width = width;
canvas.height = height;
// Create context for drawing, draw the old window unto the canvas
var context = canvas.getContext("2d");
context.drawWindow(content, startX, startY, width, height, "rgb(255,255,255)");
// Save context as png
var image = canvas.toDataURL('image/png');
sendAsyncMessage("got-screenshot", image);
}
And it is called from a chrome-script with the following function:
function (rectangle) {
var tab = require("sdk/tabs").activeTab;
var xulTab = require("sdk/view/core").viewFor(tab);
var xulBrowser = require("sdk/tabs/utils").getBrowserForTab(xulTab);
var browserMM = xulBrowser.messageManager;
if ( /* framescript not yet attached to tab */ ) {
browserMM.loadFrameScript(require("sdk/self").data.url("content-scripts/frame-script.js"), false);
... // do something to remember that there is a framescript attached to the tab
browserMM.addMessageListener("got-screenshot", function (payload) {
... // handle the screenshot
});
}
browserMM.sendAsyncMessage('fs/make_screenshot_from_rectangle', rectangle);
}
Relevant reading:
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Multiprocess_Firefox_and_the_SDK
https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Browser_messa
Related
https://jsfiddle.net/diegojsrw/od12s9b4/14/
c.font = "100vh sans-serif";
c.fillStyle = "white";
c.fillText(fps.toFixed(0), w/2, 0);
Initially, the text's height seems fine.
However, when I resize the window, the text doesn't resize together. The text is constantly drawn on canvas (using requestAnimationFrame).
The text size is only re-adjusted when I switch to another tab then switch back to this tab.
Any clues?
You are facing a webkit bug. Both latests Chrome and Safari are concerned.
In the Canvas2D API, relative units/values should get computed at setting and this computed value will get saved and used.
This means that your relative 100vw value will get computed to its corresponding value in absolute px unit.
This is one of the reasons it is advised to always use absolute units from the canvas API (among others like tweaks in roundings etc.).
But if you really want to use this unit, then you have to set it everytime it has changed, so you could go blindly and inside your loop just set ctx.font again before calling fillText(), but for performances, I would advise you use a dirty flag, that would get raised in the window's resize event, and only update the ctx.font property when this flag is raised.
But this is only fine for browsers that do follow the specs...
I have no real clue as to when webkit browsers will compute this value, as even resetting the font property to something else won't do, and even setting it to an other value (e.g switching between 20vh and 21vh) won't do either...
So the only workaround I can see for now, is to actually compute yourself this value. For viewport size, that's not so hard (innerWidth / (100 / vw_val)), but even for other relative units, you can actually set this fontSize on the canvas directly and call getComputedStyle() on the canvas.
let dirtySize = true; // this is our resize flag
const ctx = canvas.getContext('2d');
let fontStyle = ' Arial, sans-serif';
anim(0);
// the animation loop
function anim(time) {
// call the drawing methods
draw(Math.round(time/1e2));
// lwoer the flags
dirtySize = false;
// do it again next frame
requestAnimationFrame(anim);
}
function draw(txt) {
// clear
ctx.clearRect(0,0,canvas.width, canvas.height);
// only if needed
if(dirtySize) {
// get the cpmputed style from our DOM element
const fontSize = getComputedStyle(canvas).fontSize;
// or could be
// const fontSize = (innerWidth / (100 / 20)) + 'px';
ctx.font = fontSize + fontStyle;
}
// draw everytime
ctx.fillText(txt, 0, 100);
}
// on window's resize event
window.addEventListener('resize',
evt => dirtySize = true, // raise our flag
{ passive: true }
);
#canvas {
font-size: 20vw;
}
<canvas id="canvas"></canvas>
I am working on an application where the user comes and sees a blank area(div or canvas or whatever, lets call it mycanvas hereafter). Now he drags some images from outside(a div) and drops them on mycanvas. He can also resize them. And, he can also draw something in mycanvas with pencils and colors with erasing feature. Now, as per my research till now, I've figured out that the drawing part is a pure HTML 5 canvas stuff. So, no problem with that. But I'm not sure whether he can drop images from an outside div/canvas to mycanvas. Please tell me how to achieve all the three features(drag-drop from outside, draw with pencil, resize images) in a single area.
I have create a online dnd editor by Html5Canvas.
I will create a loop first
var loop = function(){
// Operation Here
}
self.setInterval(loop, 1000/60);
Create the data model, for example a image
var DndImage = function(x, y, width, height, image){
this.type = "image";
this.image = image;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
Then we draw the image in the looping
var ObjectArray = new Array();
var WIDTH = 800;
var HEIGHT = 600;
var loop = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.clearRect(0, 0, WIDTH, HEIGHT);
for(var x = 0; x < ObjectArray.length; x++){
if(ObjectArray[x].type == "image")
context.drawImage(ObjectArray[x].image,ObjectArray[x].x,ObjectArray[x].y, ObjectArray[x].width, ObjectArray[x].height);
}
}
Function to add New image object
function addImage(src, x, y, width, height){
var img = new Image();
img.src = src;
img.onload = function(){
ObjectArray.push(new DndImage(x, y, width, height, img));
}
}
And now if you want to do a dnd, You need to do is set up a Listener to listen the mouse move event. And set the DndImage Object x and y to follow the mouse position in the image canavs. You can scale the image or changing the size too.
docuemnt.addEventListener("mousedown", function(){ });
docuemnt.addEventListener("mouseup", function(){ });
docuemnt.addEventListener("mousemove", function(){ });
docuemnt.addEventListener("click", function(){ });
Hope I can help you :D
You can achieve all the required features using kinetic js.
To drag, drop and resize
http://www.html5canvastutorials.com/labs/html5-canvas-drag-and-drop-resize-and-invert-images/
To paint using different shapes, say a line:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-line-tutorial/
and dropping from outside canvas is the simplest thing, probably:
http://www.w3schools.com/html/html5_draganddrop.asp
Just check these and let me know if there is any problem in integration.
tried searching for something like this, but I've had no luck. I'm trying to open a new tab with a screenshot of the current state of my webgl image. Basically, it's a 3d model, with the ability to change which objects are displayed, the color of those objects, and the background color. Currently, I am using the following:
var screenShot = window.open(renderer.domElement.toDataURL("image/png"), 'DNA_Screen');
This line succeeds in opening a new tab with a current image of my model, but does not display the current background color. It also does not properly display the tab name. Instead, the tab name is always "PNG 1024x768".
Is there a way to change my window.open such that the background color is shown? The proper tab name would be great as well, but the background color is my biggest concern.
If you open the window with no URL you can access it's entire DOM directly from the JavaScript that opened the window.
var w = window.open('', '');
You can then set or add anything you want
w.document.title = "DNA_screen";
w.document.body.style.backgroundColor = "red";
And add the screenshot
var img = new Image();
img.src = someCanvas.toDataURL();
w.document.body.appendChild(img);
Well it is much longer than your one liner but you can change the background color of the rectangle of the context.
printCanvas (renderer.domElement.toDataURL ("image/png"), width, height,
function (url) { window.open (url, '_blank'); });
// from THREEx.screenshot.js
function printCanvas (srcUrl, dstW, dstH, callback)
{
// to compute the width/height while keeping aspect
var cpuScaleAspect = function (maxW, maxH, curW, curH)
{
var ratio = curH / curW;
if (curW >= maxW && ratio <= 1)
{
curW = maxW;
curH = maxW * ratio;
}
else if (curH >= maxH)
{
curH = maxH;
curW = maxH / ratio;
}
return { width: curW, height: curH };
}
// callback once the image is loaded
var onLoad = function ()
{
// init the canvas
var canvas = document.createElement ('canvas');
canvas.width = dstW;
canvas.height = dstH;
var context = canvas.getContext ('2d');
context.fillStyle = "black";
context.fillRect (0, 0, canvas.width, canvas.height);
// scale the image while preserving the aspect
var scaled = cpuScaleAspect (canvas.width, canvas.height, image.width, image.height);
// actually draw the image on canvas
var offsetX = (canvas.width - scaled.width ) / 2;
var offsetY = (canvas.height - scaled.height) / 2;
context.drawImage (image, offsetX, offsetY, scaled.width, scaled.height);
// notify the url to the caller
callback && callback (canvas.toDataURL ("image/png")); // dump the canvas to an URL
}
// Create new Image object
var image = new Image();
image.onload = onLoad;
image.src = srcUrl;
}
I am completely new to HTML5 and have been reading about it for the past few days mainly because I wanted to create a rotating image to put in a <div>. I found a code that does exactly what I want, but it throws the canvas on to the bottom left corner of my page (I'm not sure why, but I think it has something to do with the very first line of the code below). I'm not sure how to adapt the code to a element so that I can put it where I want. From looking at other people's scripts and trying to emulate them, I know you're supposed to do this sort of thing to hold the canvas "<canvas width="100" height="100" id="pageCanvas"></canvas>," but I don't know how to name the below code in order to do that. I greatly appreciate any help anyone can offer me - thank you so much for reading! :)
<script>
window.addEventListener("load", init);
var counter = 0,
logoImage = new Image(),
TO_RADIANS = Math.PI/180;
logoImage.src = 'IMG URL';
var canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 100;
var context = canvas.getContext('2d');
document.body.appendChild(canvas);
function init(){
setInterval(loop, 1000/30);
}
function loop() {
context.clearRect(0,0,canvas.width, canvas.height);
drawRotatedImage(logoImage,100,100,counter);
drawRotatedImage(logoImage,300,100,counter+90);
drawRotatedImage(logoImage,500,100,counter+180);
counter+=2;
}
function drawRotatedImage(image, x, y, angle) {
// save the current co-ordinate system
// before we screw with it
context.save();
// move to the middle of where we want to draw our image
context.translate(x, y);
// rotate around that point, converting our
// angle from degrees to radians
context.rotate(angle * TO_RADIANS);
// draw it up and to the left by half the width
// and height of the image
context.drawImage(image, -(image.width/2), -(image.height/2));
// and restore the co-ords to how they were when we began
context.restore();
}
</script>
Create a canvas element in your HTML code so you can place it exactly where you want (with html + css) :
<canvas id='canvas' height='100' width='100'> Your browser does not support HTML5 canvas </canvas>
And replace this javascript code :
var canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 100;
var context = canvas.getContext('2d');
document.body.appendChild(canvas);
by this one :
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
I have a project where i'm drawing to a stageSized bitmap by using BitmapData.draw
using the 'show update regions' feature of the debug player, i can see that the player always updates the whole stage although i changed only a small part of the bitmapdata.
Can I somehow make the player only update that part of the screen where there were changes made to the bitmap data?
Would it be possible to use copyPixels instead? It only allows copying from another BitmapData, but most of the time that should be fine (you could always cache animations to bitmapdata once, and then use the cached data instead of the Draw-method). Flash player should be able to redraw only the altered parts when you use copyPixels.
However, the real question; is this really an issue? Don't optimize these things unless you actually need to :)
If you're only redrawing part of the bitmap, then only that part should get updated by Flash Player.
You can see it in action here:
public class Test extends Sprite
{
public function Test()
{
stage.align = "topLeft";
stage.scaleMode = "noScale";
var bitmap:Bitmap = new Bitmap();
addChild(bitmap);
var bd:BitmapData = new BitmapData(400, 400);
bitmap.bitmapData = bd;
// One-second timer.
var timer:Timer = new Timer(1000);
timer.start();
timer.addEventListener("timer", timerHandler);
}
private function timerHandler(event:Event):void
{
var bitmap:Bitmap = Bitmap(getChildAt(0));
var bd:BitmapData = bitmap.bitmapData;
// Draw 100x100 square in random location, with random color.
var xPos:Number = Math.random() * 300;
var yPos:Number = Math.random() * 300;
var matrix:Matrix = new Matrix(1, 0, 0, 1, xPos, yPos);
var color:uint = Math.round(Math.random() * 0xFFFFFF) | 0xFF000000;
var colorTransform:ColorTransform = new ColorTransform();
colorTransform.color = color;
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.beginFill(0xFFFFFF);
g.drawRect(0, 0, 100, 100);
g.endFill();
bd.draw(shape, matrix, colorTransform);
}
}
Try this with "Show Redraw Regions" in the standalone Flash Player, and you'll see it updates only the newly drawn part on each tick.