How to draw only the visible pixels which are >0% alpha with a custom color in canvas? - algorithm

I would like to make a good performance hit test for png images and other shapes. I don't really care what shapes they are because with this technique there is no performance issues at checking (not setup).
I intent to collect all the images on the screen in a secondary canvas just for hit test. For each image drawn I will create a new color which is attached to that particular image. Then I draw all of them in the canvas, each image will have a different fill color.
When I click on a pixel (x, y) it will get the color (r, g, b). Every color is mapped to a image, so I get the image clicked with no error (I don't waste with finding what was hit with that click).
I know it will be limited to 256*256*256=16 777 216 items because those are all the colors but I don't think it will be a problem for now...
So what I really need is to know how to put those fill colors on the secondary canvas which is based only on the visible pixels for each image.
UPDATE
As you can see to the right it's the hit test map. So if I click on the black shade (c) I instantly know I've clicked on the blue box without any other calculation.
One improvement it would be to cache the alpha data. Also reuse the same alpha data for each image instance (we must take care about scaling and rotation...).
thanks

Here’s how you would color-mask the non-transparent pixels of a canvas image.
Be sure you replace "PutYourImageHere.png" with your own image url.
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid blue;}
</style>
<script>
$(function(){
var img=new Image();
img.onload=function(){
var red=255;
var blue=0;
var green=0;
var canvasCopy=document.getElementById("canvasCopy");
var ctxCopy=canvasCopy.getContext("2d");
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");
ctx.drawImage(this,0,0);
var imgData=ctx.getImageData(0,0,c.width,c.height);
for (var i=0;i<imgData.data.length;i+=4)
{
if(imgData.data[i+3]>0){
imgData.data[i]=red;
imgData.data[i+1]=green;
imgData.data[i+2]=blue;
imgData.data[i+3]=255;
}
}
ctxCopy.putImageData(imgData,0,0);
}
img.src = "PutYourImageHere.png";
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
<canvas id="canvasCopy" width="300" height="300"></canvas>
</body>
</html>

Related

how to use html content inside a canvas element

Can any one tell me how to place my html content on a canvas.And if we can do that, will the properties and events of those elements works or not, and also I have animations drawn on that canvas.
From this article on MDN:
You can't just draw HTML into a canvas. Instead, you need to use an
SVG image containing the content you want to render. To draw HTML
content, you'd use a element containing the HTML, then
draw that SVG image into your canvas.
It than suggest you follow these steps:
The only really tricky thing here—and that's probably an
overstatement—is creating the SVG for your image. All you need to do
is create a string containing the XML for the SVG and construct a Blob
with the following parts.
The MIME media type of the blob should be "image/svg+xml".
The element.
Inside that, the element.
The (well-formed) HTML itself, nested inside the .
By using a object URL as described above, we can inline our HTML
instead of having to load it from an external source. You can, of
course, use an external source if you prefer, as long as the origin is
the same as the originating document.
The following example is provided (you can see more information about this in this blog by Robert O'Callahan):
DEMO
const ctx = document.getElementById("canvas").getContext("2d");
const data = `
<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'>
<foreignObject width='100%' height='100%'>
<div xmlns='http://www.w3.org/1999/xhtml' style='font-size:40px'>
<em>I</em> like <span style='color:white; text-shadow:0 0 2px blue;'>CANVAS</span>
</div>
</foreignObject>
</svg>
`;
const img = new Image();
const svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
const url = URL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
};
img.src = url;
<canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
This example results in this HTML being rendered to canvas as this:
Will the properties and events of those elements works or not ?
No, everything drawn to a canvas is forgotten as passive pixels - they becomes simply an image.
You will need to provide custom logic that you provide yourselves in order to to handle any such things as clicks, objects, events etc. The logic need to define the areas, objects and anything else.

Virtual area around vml element

this is my first post, so my deepest excuses if something went wrong :)
I have a little html-control to write and biggest problem is ie6-8 support. There are no alternatives to skip ie6-8 support at all :( So after searching a while, I did found Raphael and it allows me to create custom shapes defined in SVG file. I need to attach 'mouseover' event and select element on hover. Event working great but I did find BIG problems in VML hover behavior.
Code was simplified to RAW html with VML shape.
<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<style>v\: * { behavior:url(#default#VML); antialias: false; }</style>
</head>
<body>
<div id="message">hovered: nope</div>
<v:oval id="oval" style="width:100px; height:75px" fillcolor="#bbb"></v:oval>
<script>
var messageElm = document.getElementById('message');
var ovalElm = document.getElementById('oval');
ovalElm.attachEvent('onmouseover', function () { messageElm.innerText = 'hovered: yep'; });
ovalElm.attachEvent('onmouseout', function () { messageElm.innerText = 'hovered: nope'; });
</script>
</body>
</html>
If you try to move mouse over oval element you can noticed that rendered shape is not same as hover shape. I mean, hover triggers 2-3px from rendered shape (not from each side).
So question is: how to disable that virtual area (if it is possible at all)?
i had the same issue and i tried usemap;
first i create a map on a transparent png8 which covered the vml
this.dom.insertAdjacentHTML("AfterBegin",'<map name="'+_id+'"></map><img id="'+_id+'" src="'+transparent.png+
'" style="position:absolute;width:'+dom.clientWidth+';height:'+dom.clientHeight+'" />');
var map = this.dom.getElementsByTagName('map')[0];
this.dom.appendChild(map);
this.map = map;
then get the shape attach to an area; map it;
i made poly demo only;
function _getMap(shape){
this._map = this._map || {};
if(this._map[shape.id]){
}else if(shape.nodeName == 'shape'){
var arrDots = shape.childNodes[0].v.match(/(\d+),(\d+)/g)
this._map[shape.id] = _polyMap(arrDots);
}
return this.map[shape.id]
}
function _polyMap(arrDots){
var map = this.map;
var area = document.createElement('area');
area.setAttribute('shape',"poly");
area.setAttribute('coords',arrDots.join(','));
area.setAttribute('href','##');
area.setAttribute('alt','##');
map.appendChild(area);
}
then you can bind event on it;
function _onIE(shape, evtname, fn){
this._getMap(shape).attachEvent('on'+evtname, fn);
}

HTML Canvas vs Image Memory Usage

If I create a canvas element via:
var canvas = document.createElement('canvas');
And then draw to it. If I then keep a reference to that canvas will that use more memory than converting the canvas content to a data url and creating an image element with that data and releasing the reference to the canvas?
Which is less memory consuming? A canvas element or an image element, both the same dimensions with the same image data?
Using this html test page:
<html>
<head>
<script type="text/javascript">
window.onload = function () {
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var img = new Image;
img.src = "http://blog.buzzbuzzhome.com/wp-content/uploads/2012/06/Canadian-Flag-canada-729711_1280_1024.jpg";
ctx.drawImage(img, 0, 0);
}
</script>
</head>
<body>
<canvas id="canvas" width="1280" height="1024"></canvas>
<img src="http://blog.buzzbuzzhome.com/wp-content/uploads/2012/06/Canadian-Flag-canada-729711_1280_1024.jpg">
</body>
</html>
The memory output from a Google Chrome profile snapshot is as follows:
Google Chrome -> Developer Tools -> Profiles -> Take Head Snapshot -> Class Filter:HTML
The canvas has a smaller retained size (132 to 152 of the img), but a look into what's left over from rendering the image reveals more:
Class Filter:canvas
IMHO you're going to pay an overhead for rendering to the canvas in most major browsers.
Whether the mess gets cleaned up when your reference is released and your final memory usage is lower is anyone's guess.
I realize I didn't do it strictly the way you intended, but I felt a side by side comparison would give you some idea what's involved.
I suppose if loading the image via canvas is the only way to go, perhaps you're performing some manipulation before outputting the final result, then leaving it in the canvas and attempting to nullify all references for garbage collection will be slightly less expensive for the client.
This test was only done in Google Chrome and I cannot verify anything for other browsers.
Try it yourself!

wp7 webbrowser control opening a centered image

I have been looking for a simple way to allow a similar functionality to the built-in Image Viewer (Zooming in and out of an image with 2 fingers, pinch zoom / zoom out, and the floating effect, if someone gently pushes an image in one of the directions).
Initially I was trying to achieve it with the Image control, but It turned out to be extremely difficult.
I decided to go with the Webbrowser control, as it seemed to offer just what I need and it had a benefit of clipping it's content to the parent container ( an Image control wouldn't do that by default ).
I placed the Webbrowser control in the second Row of a 2-row parent Grid container (Its basically the default Page generated by Visual Studio)
<Grid x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<toolkit:PerformanceProgressBar x:Name="ProgressBar" IsIndeterminate="True" />
<phone:WebBrowser Visibility="Collapsed" x:Name="BrowserWindow" IsScriptEnabled="True" />
</Grid>
The C# code backing the View is this:
public partial class ItemDetailView : PhoneApplicationPage
{
public static string HtmlTemplate =
#"<!doctype html>
<html>
<head>
<meta name='viewport' content='initial-scale=0.1,minimum-scale=0.1,user-scalable=yes,width=480' />
<title></title>
<script type='text/javascript'>
function imageLoadFailed() {{
window.external.notify('ImageLoadFailed');
}}
</script>
</head>
<body style='margin:0; padding:0; width:100%; background-color: {0};'>
<div style='margin:0 auto;'><img onerror='imageLoadFailed()' src='{1}' alt='' /></div>
</body>
</html>";
public ItemDetailView() {
InitializeComponent();
this.BrowserWindow.LoadCompleted += (s, e) => {
this.ProgressBar.IsIndeterminate = false;
this.BrowserWindow.Visibility = Visibility.Visible;
};
this.BrowserWindow.ScriptNotify += (s, e) => {
if (e.Value == "ImageLoadFailed") {
MessageBox.Show("Image failed to load");
}
};
}
public ItemDetailViewModel VM {
get {
return this.DataContext as ItemDetailViewModel
}
}
public bool IsBackgroundBlack() {
return Visibility.Visible == (Visibility)Resources["PhoneDarkThemeVisibility"];
}
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
App app = App.Current as App;
this.VM.Item = app.CurrentItem;
string css = "";
if (this.IsBackgroundBlack()) {
css = "black";
}
else {
css = "white";
}
this.BrowserWindow.NavigateToString(string.Format(ItemDetailView.HtmlTemplate, css, app.CurrentItem.Url));
}
}
As you may see, I set the width to 480 in the Viewport meta tag. This is the only value that is close enough to center the image so it appears naturally as if placed in an Image control (it fits within the width of the phones screen and is scaled appropriately)
I tried to set the viewport meta tag width property to 'device-width', but with this setting, the image reaches beyond the phones screen width upon initial load.
I also have noticed that with width set to 480, if a user taps on the Web browser for the first time, the browser centers the image a little bit, so 480 is not a perfect value. I am also concerned that this may not be the greatest solution given multiple screen sizes of other devices.
I am curious, if there is a cross-device way to center the image in the web browser control.
Reference Image:
Left Image - What I want, and what it more or less looks like with Viewport meta tag's width=480 (Entire image is seen)
Right Image - What it looks like with Viewport meta tag's width=device-width;
(A single tap is needed to zoom out and position it in the center like the left image)
It appears that there is no need for setting the meta tag at all (it just caused trouble in my case)
the html template:
<!doctype html>
<html>
<head>
<title></title>
<script type='text/javascript'>
function imageLoadFailed() {
window.external.notify('ImageLoadFailed');
}
</script>
</head>
<body style='background-color: {0};'>
<img width='100%' onerror='imageLoadFailed()' src='{1}' alt='' /></div>
</body>
</html>
does the trick

HTML5 Image Buffer

I am a beginner programmer in javascript. I don't use jQuery! And I want to make a simple game.
I am loading multiple images into canvas using
imageObj.onload = function(){}
I am using a keylistener for multiple keypresses so that the images could move on the diagonal while pressing both up and left keys by using smth like this:
function keydown_handler(e){my_key[String.fromCharCode(e.keyCode)] =
true; Move();}
My problem is that when I press the keys and move the images on the canvas the image flickers. I suppose this is because it loads the image every time I press a key. If this is true how can I load an image ONCE into memory and then RECALL that image from memory and change it's coordinates?
Thank you!
Well, what you really need to do is to create a render loop with javascript using requestAnimationFrame(), and render with the canvas element. Here's a really basic example of rendering with HTML5:
<!DOCTYPE html>
<html>
<body>
<canvas id="gameCanvas" width="800px" height="600px"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('gameCanvas');
var context = canvas.getContext('2d');
var myImage = new Image();
var myImage.onload=function(){init();};
var myImage.src='location/of/image.png';
var imageX = 0, imageY = 0;
function render()
{
window.requestAnimationFrame(render);
// clear canvas
canvas.width = canvas.width;
context.drawImage(myImage, imageX, imageY);
imageX++;
imageY++;
}
function init()
{
window.requestAnimationFrame(render);
}
</script>
</body>
</html>
There will never be flicker when you're rendering through a canvas since the browser is already double buffering that rendering surface; and manually double buffering the canvas will actually produce a significant drop in framerate. What you're probably encountering (if you're rendering through a canvas) is tearing of the frame. Using requestAnimationFrame will resolve the tearing problem by essentially v-syncing the render (since it waits until the end of code execution to render).
Hopefully this will help you get started on the right path for rendering with HTML5.
What you are referring to is a very common problem when dealing with animations. The issue has less to do with what is stored in memory and more to do with the way an animation must be redrawn each time something changes. The most common method for avoiding this flickering issue is known as double buffering.
I have never done this using HTML5 specifically but after a quick search I found this article that may help you.
https://stackoverflow.com/a/2864533/594558

Resources