New project in Haxedevelop, and a very simple code (below) and I’m getting pixel snapping on a basic sprite using graphics. Targets tested:
Flash: Works as expected - there is subpixel rendering and the sprite appears smooth.
html 5: no subpixel rendering, circle jitters across the screen
windows: same as html5
neko: same as html5
All targets anti-alias fine, just none other than flash seem able to do smooth subpixel movement.
Given OpenFL uses the flash API I’d expected it to mirror the flash rendering behaviour. I can find a few posts from 2015-2016 discussing something similar, but they don’t seem to have anything relavent.
What am I doing wrong? How can I get subpixel rendering of graphics working as it does with the flash target?
import openfl.display.Sprite;
import openfl.Lib;
class Main extends Sprite
{
var circle : Sprite;
public function new()
{
super();
circle = new Sprite();
circle.graphics.beginFill(0xFFFF00FF);
circle.graphics.drawCircle(100, 100, 100);
Lib.current.addChild(circle);
Lib.current.addEventListener(openfl.events.Event.ENTER_FRAME, enterFrame, false, 0, true);
}
public function enterFrame(e : openfl.events.Event) {
circle.x += 0.1;
}
}
It seems I can get the same behaviour as Flash by manually rendering my graphics object to a bitmap with smoothing enabled and pixelsnapping disabled, as well as some padding to make sure the subpixels work correctly right on the edge of the Bitmap. Attached the code. Why does the “normal” implementation do anything differently? My understanding is that OpenFL essentially does this internally, it just seems to generate the bitmap with the “wrong” options.
EDIT: In the real application getting decreased quality through rotations with this method. Turns out I’d implemented it slightly differently and doing something like circleBM.bitmapData = xyz smoothing was reset to False.
import openfl.display.Bitmap;
import openfl.display.PixelSnapping;
import openfl.display.Sprite;
import openfl.Lib;
import openfl.geom.Matrix;
class Main extends Sprite
{
var circle : Sprite;
var circleBM : Bitmap;
public function new()
{
super();
circle = new Sprite();
circle.graphics.beginFill(0xFFFF00FF);
circle.graphics.drawCircle(100, 100, 100);
circleBM = new Bitmap(new BitmapData(202, 202, true, 0), PixelSnapping.NEVER, true);
circleBM.bitmapData.draw(circle, new Matrix(1, 0, 0, 1, 1, 1), null, null, null, true);
circleBM.x = -1;
circleBM.y = 0;
Lib.current.addChild(circleBM);
Lib.current.addEventListener(openfl.events.Event.ENTER_FRAME, enterFrame, false, 0, true);
}
public function enterFrame(e : openfl.events.Event) {
circleBM.x += 0.1;
}
}
This might be a caching issue rather than a pixel snapping one. What happens if you try this?
public function enterFrame(e : openfl.events.Event) {
circle.x += 0.1;
circle.graphics.clear();
circle.graphics.beginFill(0xFFFF00FF);
circle.graphics.drawCircle(100, 100, 100);
}
Or make two different sprites, one starting at x=0 and one starting at x=0.5.
If none of that makes a difference, doing circle.graphics.drawCircle(circleX, 100, 100); (where circleX is a variable that increments by 0.1 each frame) should work no problem.
As for why this happens, I’m certain it’s for performance. Much of the community sees OpenFL as its own thing separate from Flash, with some going so far as to argue that we should drop parts of the Flash API to improve performance further.
OpenFL just doesn’t have a vector renderer that can match Flash’s performance. I don’t think even dedicated Flash emulators have managed to catch up. For all its other faults, Flash’s CPU-based vector renderer was the best in class. (Easily surpassed by switching to GPU, of course, but that comes with its own set of issues. Among other things, you’ll be rendering triangles to approximate a circle.)
I understand your points, however as shown by my solution this isn’t a case “flash redraws, OpenFL caches”. I can get the same behaviour from OpenFL by handling the “cache” onto bitmap myself. It feels more like it’s a bad default within OpenFL not setting the automatic caching for graphics to be smoothed.