HTML5 canvas - Cannot clear dirty pixels - Retina related - html5-canvas

I have a simple canvas with a mouseover event. As the user moves the mouse, I want to draw a single pixel at the event's x,y coords (in the future it will be more complex than a single pixel). Essentially it's like a custom cursor.
The logic is extremely simple as shown below. Strangely, although I'm cleaning up the old pixel location, there're tiny remnants of the pixel left behind (since I posted this question, I have discovered it's related to the Retina display). I've been able to work around this. Instead of saving 1px and restore 1px, I save 3px and restore 3px. But I don't understand why I need to do this, and in the future when I'm drawing a more complicated cursor, I want the dirty pixel handling to be precise.
Here's a runnable JSFiddle example: http://jsfiddle.net/sbCq3/2/
// cleanup previously drawn pixel
ctx.putImageData(lastImageData, lastImageX, lastImageY);
// save the imageData currently at x,y
lastImageData = ctx.getImageData(x, y, 1, 1);
lastImageX = x;
lastImageY = y;
// draw the dot
var dotData = ctx.createImageData(1, 1);
...
ctx.putImageData(dotData, x, y);
I'm a bit stumped. I'm wondering if it has anything to do with my Retina display. If I draw a single pixel at 5,5, it's a single pixel in the image data - but I can zoom using the DigitalColor Meter (built in zoom tool) and see that single pixel is sub-divided and anti-aliased. Whereas if I view that pixel on a Windows machine it's a nice solid pixel. I haven't tested to see if this problem appears on Windows or non-retina machines yet. (I'm not referring to the normal canvas anti-aliasing problem).
UPDATE: I just tested this on my coworkers non-retina MacBook 17" and it works perfectly fine. So this definitely appears to be related to the Retina display.

I'm not entirely sure, but I think this may have to do with the resolutions of the different screens. If you've ever looked at an html5 canvas on a smartphone, which most have a much greater resolution per sq inch than a monitor, then you'll see that the quality is much degraded because the web browser expands the 100px that you tell it to use to the same physical size, which ends up being around ~130px. I think the same is happening with your retina display because it uses a crazy good resolution. Basically, different screens have different pixel size ratios. To get around this, I added the following to my code.
var PIXEL_RATIO = (function() {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backignStorePixelRatio || 1;
return dpr / bsr;
})();
Now, whenever you make a canvas or draw anything, multiply your constants by the variable PIXEL_RATIO.

Related

Processing 3 Box2D Velocity Not Scaling With Resolution

I'm currently trying to make my simple game scale with the resolution. I've noticed though when I change the resolution not everything works out. For instance from the shift from 1280x720 to 1920x1080 the jumping distance changes slightly. The main problem I've noticed is that when I fire a projectile with a velocity. On lower resolutions it seems to travel across the screen significantly faster and I can't understand why as it should scale down with the size of the window. Here is a snipet of the code that fires a projectile:
m = new Box(l.pos.x+Width/32*direction2, l.pos.y-Height/288, Width/64, Height/72, true, 4);
m.body.setGravityScale(0f);
boxes.add(m);
m.body.setLinearVelocity(new Vec2(Width*direction2, 0));
In this scenario m is a box I'm creating. In new Box(spawn x coordinate, spawn y cooridinate, width of box, height of box, is the box moveable, type of box) l.pos.x and l.pos.y are the positions I'm firing the box from. The Height and Width variables are the size of the current window in pixels being updated in void draw(), direction2 is either 1 or -1 depending on the direction in which the character is facing.
Hard to tell how the rest of code affects the simulation without seeing more of it.
Ideally you would want to keep phyics related properties independent from the Processing sketch dimensions in terms of dimensions but maintain proportion so you can simply scale up the rendering of the same sized world. If you have mouse interaction the coordinates would scale as well, but other than the position, the rest of physical proeprties should be maintained.
From what I can gather in your code if Width is the sketch's width you should de-couple that from linear velocity:
m.body.setLinearVelocity(new Vec2(Width*direction2, 0));
you should use a value that you will keep constant in relation to the sketch dimensions.

THREE.CanvasRenderer rendering issue, circles leave a 'trace' even though setClearColor is set

This has been puzzling me all day. I have put together a very simple holding page for a website that can be viewed here. It has a very simple three.js animated scene as a background. This is based on one of the examples. Then the user moves the mouse to the top of the window the animation responds to the input. The issue is that there seems to be rendering artefact (see image below or try online in above link)
It seems that there is an unwanted 'trace' like effect that I just can't seem to prevent. The clear color is set 'renderer.setClearColor(0xEFEFEF, 1). I have tried 'renderer.autoClear = true' explicitly but still the artefacts show. It seems to happen when the circle is at the very edge of the drawn area when moving vertically.
I have tried searching everywhere but failed to find an answer. Can anyone help?
UPDATE:
I looked at the link provided by WestLangley where the context.arc scaling looked like a possible cause. It turned out that it was not. I was using a value < 1. I tried a value 1 but the artefacts still show.
I believe that since the drawing area changes each frame (due to the sinusoidal nature of the wave animation) and the changing viewing angle of the camera, the area that is cleared is incorrect. The drawing area that is cleared is the drawing area of the current frame (to be rendered), leaving an uncleared area from the previous frame (already rendered and visible) and possibly artefacts. This only occurs when the drawing area is shrinking and the camera is moving in the opposite direction to the reduction in drawing area. I believe, in order for these artefacts to not show, the drawing area of the previous frame should be cleared and not the area of the current frame before drawing. I am still looking at this.
UPDATE 2:
I seem to have verified the drawing area clearance theory from update 1. By adding:
renderer.getContext().canvas.getContext("2d").clearRect(0, 0, renderer.getContext().canvas.width, renderer.getContext().canvas.height);
before the renderer.render(scene, camera) call, the entire canvas was cleared to white. Since the renderer.setClearColor(0xEFEFEF, 1) is set to slightly off-white, one can clearly see the drawing area.
I decided to add a plane with a transparent material to force the drawing area to be full size of the canvas. I am not 100% happy with this but it seems to be a workaround.
var geo = new THREE.PlaneBufferGeometry(1920 * 2, 1080 * 2, 1, 1);
var mat = new THREE.MeshBasicMaterial({ opacity: 0.01 });
var plane = new THREE.Mesh(geo, mat);
The artefacts are banished.

External elements slowing down canvas

I am developing a game using several canvases (3) on top of one another. I am close to finishing the game and I haven't yet optimized the performance.
Regardless, my main concern is that the game has performed pretty well so far, but being close to finish I am building a simple web page around the canvas to give a frame to the game. I am talking just putting the title of the game and a few links here and there, but suddenly the game is now choppy and slow!!! If remove those elements everything is smooth again.
The culprits are:
The game title above the canvas (styled with text-shadow).
four buttons below the canvas to redirect to other sites and credits.
Is it possible that this few static elements interfere with the rendering of the game?
Thank you.
Anything with shadows, rounded corners or expensive effects such as blur cost a lot to render.
Modern browsers try to optimize this in various way but there are special cases which they can't get around just like that (updated render engines using 3D hardware can help in the future).
Shadows are closely related to blurring and needs to be composited per frame due to the possibility that the background, shadow color, blur range etc. could change. Rounded corners forces the browser to create an alpha mask instead of doing just a rectangular clip. The browser may cache some of these operations, but they'll add up in the end.
Text Shadow
A workaround is to "cache" the shadowed text as an image. It can be a pre-made image from Photoshop or it could be made dynamically using a canvas element. Then display this instead of the text+shadow.
Example
var ctx = c.getContext("2d"),
txt = "SHADOW HEADER";
// we need to do this twice as when we set width of canvas, state is cleared
ctx.font = "bold 28px sans-serif";
c.width = ctx.measureText(txt).width + 20; // add space for shadow
c.height = 50; // estimated
// and again...
ctx.font = "bold 28px sans-serif";
ctx.textBaseline = "top";
ctx.textAlign = "left";
ctx.shadowBlur = 9;
ctx.shadowOffsetX = 9;
ctx.shadowOffsetY = 9;
ctx.shadowColor = "rgba(0,0,0,0.8)";
ctx.fillStyle = "#aaa";
ctx.fillText(txt, 0, 0);
body {background:#7C3939}
<canvas id=c></canvas>
The canvas element can now be placed as needed. In addition you could convert the canvas to an image and use that without the extra overhead.
Rounded Corners
Rounded corners on an element is also expensive and there are no easy way around this - the corners need to be cut one way or another and question is which method is fastest.
Let browser do it using CSS
Overlay the element with the outer corners covered in the same color as background - clunky but can be fast as no clipping is needed. However, more data need to be composited.
Use a mask in canvas directly via globalCompositeOperation. The chances are this would be the slowest method. Performance tests must be made for this scenario to find out which one works best overall.
Make a compromise and remove rounded corners all together.
Links
Also these could be replaced by clickable images. It's a bit more tedious but also these could be made dynamically using a canvas allowing the text to change ad-hoc.
CSS
I would also recommend experimenting with position: fixed; for some of the elements. When fixed is used, some browsers renders that element separately (gives it its own bitmap). This may be more efficient in some cases.
But do make some performance tests to see what combination is the best for your scenario.

how to drop shadows under sprites in Top-down view with cocos2d

I just started learning OpenGL and cocos2d and I need an advice.
I'm writing a game in which player is allowed to touch and move rectangles on the screen in a top-down view. Every time a rectangle is touched, it moves up (towards the screen) in z direction and is scaled a bit to look like it's closer than the rest. It drops down to z = 0 after touch ends.
I'd like the risen rectangles to drop shadow under them, but can't get it to work. What approach would you recommend for the best result?
Here's what I have so far.
During setup I turn on the depth buffer and then:
1. all the textures are generated with CCRenderTexture
2. the generated textures are used as an atlas to create CCSpriteBatchNode
3. when a rectangle (tile) is touched:
static const float _raisedScale = 1.2;
static const float _raisedVertexZ = 30;
...
-(void)makeRaised
{
_state = TileStateRaised;
self.scale = _raisedScale;
self.scale = _raisedScale;
self.vertexZ = _raisedVertexZ;
_glowOverlay.vertexZ = _raisedVertexZ;
_glowOverlay.opacity = 255;
}
glow overlay is used to "light up" the rectangle.
After that I animate it using -(void)update:(ccTime)delta
Is there a way to make OpenGl cast the shadow for me using cocos? For example using shaders or OpenGL shadowing. Or do I have to use a texture overlay to simulate the shadow?
What do you recommend? How would you do it?
Sorry for a newbie question, but it's all really new to me and I really need your help
.
EDIT 6th of March
I managed to get sprites with shadow overlay show under the tiles and it looks ok until one tile has to drop shadow on another which has a non-zero vertexZ value. I tried to create additional shadow sprites which would be scaled and shown on top of the other tiles (usually rising or falling down), but I have problems with animation (tile up, tile down).
Why complicate the problem.
Simply create a projections of how the shadow would look like using your favourite graphics editing program and save it as a png. When the object is lifted, insert your shadowSprite behind your lifted object (you can shift it left/right depending on where you think your light source is).
When the user drops the object down, the show can remain under the object and move with it, making it self visible when the item is lifted again.

Flex Mobile GetPixel function of a resized image gets pixel as if the image had its original size

I have an image of 780x585. I am resizing this image to 1246.93x935.19 with scaleMode=ScaleMode.LETTERBOX.
When the user clicks on a graphic and drags it into the picture, I am supposed to get the pixel position where the graphic was dropped.
I do that by listening to the mouse up event and by calling getPixel with the (x,y) coordinates coming from the event when the mouse click is released.
Strange thing is that I get the value of the pixel but not the real one. Instead, I get the value corresponding to the image with it's normal size without being resized.
Does anyone have a clue why this is happening?
Thanks,
Dave
When applying some visual changes to a component (like moving, rotating or stretching as in here), Flex will be applying a transormation matrix to modify only the visual appearance of the object.
Let's say you're moving an image by doing image.width *= 2; the actual image width will be modified. If you do image.matrix.scale(2, 1); (this is pseudo-code), the visual appearance will be modified so you can see the resizing but the the actual width will remain the same.
I think applying a Letterbox resizing is using matrix transformation, so even if your image appears larger, its position and size are still the same. That's why you get the same position as before.
To resolve your problem, you simply have to multiply the coordinates you get (with event.localX or anything else) by the image current scaling and that should be it.
If you already know the final size, you simply have to do something like: newLocalX = event.localX * (resizedImage.width / originalImage.width);
Here some info if you want to know how Matrixes work.

Resources