Possible SVG and colorTransform workaround idea

Hi, I will start to apologize if my english (and ideas ;)) sound a little “wacky”, this isn’t my main language.

I’m a little fresh on haxe/openfl so bear with me if make some bad basic assumptions or coding mistakes.

In my current cross-platform project I’m using a lot of SVG assets in openfl and I stumbled with the need to do color transformation to some of them. I know that right now the colorTransform in display objects only work for the Flash target, due to “technical details” (@singmajesty own words) that affect performance on others targets. My idea for a workaround for a colorTransform to a SVG asset is to create a method in the SVG class that allows to “tint” all fills and strokes of the SVG before render. I don’t know exactly how the parse is made in that particular importer, but I think that maybe it’s not that hard to add some channel increments to all colors when the render method is called

If this is possible, I think this method makes little to none performance impact when we need to do a color transformation before render the asset (pearhaps I’m doing all sort of crazy assumptions in this idea, if so correct me ;))

Another thing that I’m struggling to acomplish with SVG asset is to change dynamically a color of a particular fill (or stroke for that matter). The two workarounds that I’ve came up with are:

  1. Divide the vector art in layers, and then save each layer in it’s own SVG, import them directly to their own sprite and the layer that has the fill that I want to change dynamically, draw it to bitmapdata and then color transform it (ugly process and only doable to a few assets);

  2. Parse the SVG XML before hand, and find the fill by the path id, change it and then feed it again to the SVG instance to be rendered again (much better process, but still a little “ugly”);

My second sugestion is a method that enables to feed an id of a path and a color to the SVG object and it changes it before render to a display object.

Is this two ideas too out of place, or are they doable? Is anyone out there that faces the same problems that I have? @singmajesty, what do you think of this?

Best regards.

1 Like

I’m sorry for double posting, but I have no answer in almost a week…

I was expecting, at least, some sort of “bashing” from you guys (the experienced ones), saying that it’s not like that, I don’t even know what I’m talking about (that my english “sucks”, something ;)).

I understand that HAXE/OpenFL is used manly for game development, and in that context, SVG is rarely used in favour of a more performant solution. The thing is, I manly develop applications and most of them have use of visual graphics, so the use of SVG is welcomed and very designer friendly.

But stopping with the whinning, I’m thinking on develop a solution for this. I can try one of three ideas (if someone have others, please tell me):

  1. Fork the SVG package and introduce the methods that I need;

  2. Extend the SVG Class, and add the methods;

  3. Make a helper class that receive the SVG instance and manipulate it with the methods that i want.

What will be the best doable approach?

Thanks.

I’m not sure if it will still merge directly, but here’s a pull request that implemented something similar to what you described:

I’m afraid of complicating this part of our code too much, and we have work in-progress to apply color transform for any type of object, through cacheAsBitmap. After cacheAsBitmap, though, I do see the value, though, in this type of optimization

Another idea – in the development of OpenFL, we recently improved the readGraphicsData, drawPath or drawGraphicsData methods of Graphics. You might be able to render the SVG to a graphics object, readGraphicsData, then modify the data to use different colors, then drawGraphicsData on another object – if that makes sense.

Psuedo-code:

var shape = new Shape ();
svg.render (shape);
var graphicsData = shape.graphics.readGraphicsData ();
for (data in graphicsData) {
    if (Std.is(data, GraphicsSolidFill)) {
        // modify solid fill
    }
}
var secondShape = new Shape ();
secondShape.graphics.drawGraphicsData (graphicsData);
addChild (secondShape);

Hi, @singmajesty, thanks for your reply, I know how valuable your time is.

I hope you nail the color transform for any type of object, for all the openfl targets.

I’ll take a look at vizanto work, but meanwhile I’ll try your idea, the readGraphicsData -> modify fill color -> drawGraphicsData to another shape. I’ll try to evaluate the performance impact when changing color in a considerable number of SVG. I’ve tried one of yours previous suggestion:

It worked but the end result was not as smooth as we could expect from a SVG.

Regarding my second problem, this is one thing I miss in the pair Flash Pro/Flash builder (actionscript). I would make all my assets in Flash Pro, in each asset I’ll name some symbols/Movieclips and then in Flashbuilder with actionscript embed the assets, then I can set each named symbol to a MovieClip instance and after that I can change the visibility, color, etc - it’s like a SVG with layers (or only paths, strokes, etc) and then easily change the properties of each layer using the ID, that is what I’m planning on doing for the new workflow inkscape/haxedevelop (openfl).

Imagine a simplistic example, an SVG pie chart with 5 diferent colors for each slice, Each layers/path has an ID with a number (1 to 5). Wouldn´t be great if we want to change the color of slice 5 before render, could be as simple as:

svgPieChart.color(0xFF0000, "5");
var shape = new Shape ();
svgPieChar.render (shape);

I believe this change has to happen before render, because after that the id is lost (correct me if I’m wrong).

@singmajesty what is the best way, in your opinion, to acomplish something like the previous pseudo-code? I mean, putting aside the inner logic solution, forking the SVG package, problably it’s not the best idea, because doing so, there’s little chance of a pull request of something this specific be accept for the sake of maintaining the SVG package simple, right? So what’s the best, extend the class or make an helper class, thinking that I’ll be sharing the code in github to help others with the same ordeal?

Thanks once more @singmajesty for your help, and sorry for the long post!

The previous workflow you mentioned – using Flash, SWF assets and rendering with the object ID should work fine in OpenFL. If you need colorTransform, though, then that’s trickier.

Why do you think bitmapData.draw was not very smooth? Did you scale after rendering?

Really!! I didn´t know that, I mean, I’ve read somewhere long ago that inclusion of SWF was in the works, but I thought that, when finished, it will only work with the flash target. That’s great, and being so, I think I’ll port some of my previous project’s to work in diferent targets (with performance increase)!! I’ll will try to find more info about it (If you know just the place to go, please do tell ;)).

So, until the color transform works with any type of object, I’m stuck with the same dilema for my current project. That is, I can use readGraphicsData in the named symbol in the swf assets, and that’s a more focused color transformation. Still, this isn’t the nicest solution for a considerable number of display objects, but it’s a workaround for the time being, thanks.

Yeah, I revisit my test with bitmapData.draw, and your right, after I add it to stage, the same was slightly scaled and I didn’t noticed (tested inside my project). If I don’t scale, it is as smooth as it were before. But with this I have to apply it every time I scale the shape. It not doable for my current project, because we can add shapes to a board (stage), change individual colors and it’s size on the fly, just like we do in a software like inkscape, actually, my project is a software like inkscape without the drawing capabilities and the objects are limited to a restricted library. So we can have on stage more than a hundred shapes, and we could select them and apply a color change/resize to all of them at once, so render perfomance is somewhat important.

I still want to try to do something like changing the color of a SVG before render, as easily as I’ve showned before, because I’ve a freelancer designer colleague that works great with inkscape, and I think it’s a great workflow for someone that doesn’t have a Flash Pro license. So, what do you think, fork, extend or helper class?

The “NyanCat” and “SimpleSWFLayout” samples use SWF assets

These are examples of projects that I know use SWF assets:

http://www.openfl.org/showcase/title/pickm-stickm/
http://www.openfl.org/showcase/title/what-in-the-world/

Perhaps you could use bitmapData.draw, and draw to a higher resolution and scale down? You could also create a Map and store colors you generate, to reuse bitmaps when you need the same color again.

Thank you for the links!

The readGraphicsData worked like a charm, and because this changed the color directly into GraphicsSolidFill, scaling is not a issue. Thanks for the advice, it serves as a good workaround until something better comes along ;).

Yeah, but with over a hundred objects (and in some cases -rare nevertheless- could reach close to a thousand and they will not be the same drawing), with the user able to change the color and size of each one, I don’t know, it could become a little heavy, but I’ll try to do a performance test between all of this workarounds and I’ll post here.

Thanks once more @singmajesty!

@singmajesty, I forgot to try others target’s. I only tried in flash, because it’s fast to build and debug. The readGraphicsData doesn’t work with windows, neko and html5 targets (didn’t try android). In windows target it gives “Null Object Reference”, why is that?

Have you updated to OpenFL 5?

I updated to OpenFL 5, clean my builds, and the issue remains, html5, neko and windows targets builds but in execution the html5 don’t show nothing, the neko crashes with the error “AL lib: (EE) alc_cleanup: 1 device not closed”, and windows with the same “Null Object Reference”.

But I figured that the problem is actually in drawGraphicsData, because if I comment this out, everything works well, even the iteration and modification of the solid fill. Is when I set the graphic data to the new shape that the issue occur.

This still works, with no issues, in flash, after the update to 5.

The code I use is the one you provide some post earlier, but even if I only read the graphics data and set it to a new shape, without messing with it, like the following:

var graphicsData = firstShape.graphics.readGraphicsData();

var secondShape = new Shape ();
secondShape.graphics.drawGraphicsData (graphicsData);
addChild (secondShape);

It has the same issue.

What could it be?

This simple code is working for me:

package;


import openfl.display.Shape;
import openfl.display.Sprite;


class Main extends Sprite {
	
	
	public function new () {
		
		super ();
		
		var shape = new Shape ();
		shape.graphics.beginFill (0xFF0000);
		shape.graphics.drawRect (0, 0, 100, 100);
		
		var graphicsData = shape.graphics.readGraphicsData ();
		
		var shape2 = new Shape ();
		shape2.graphics.drawGraphicsData (graphicsData);
		addChild (shape2);
		
	}
	
	
}

Did you get a file or line number with your null error, so I could know where it is occurring? Perhaps you might have an idea of how to modify the above test so that it breaks on your system as well?

I’m rendering a simple SVG (only a oval shaped path with a fill and a stroke) to a shape. If it renders to graphics data, isn’t supposed to work regardless the content of that data, i mean, once we can read it, isn’t IGraphicsData “safe” for drawing again to graphics?

Regarding the line number, I’m only debugging in flash right now, but perhaps it’s better that I also debug using something like hxscout or, I don’t recall if it is the right name, hxcpp debugger, because outside the flash target there are somethings that don’t work as intended. Are those debugger mature enough for regular use? I’m asking because it’s been a while that I read about them…

edit—
I’ve tried your code and it only render the square in the flash target, the other targets it did not render nothing on screen (nevertheless in neko and windows didn’t crash either)

Huh, the latest Lime and OpenFL are rendering the square for me here on Windows and HTML5

It’s probably because I’m newbie/fresh on haxe/OpenFL, but it seems that I can´t make it work on any other target than flash. What I’ve tried:

  • update to OpenFL 5.0.0/lime 4.1.0 using haxelib;
  • set each library to the latest version using haxelib;
  • open command-line prompt with openfl and lime and verifying that it’s the latest version at play;
  • create a fresh OpenFL project on haxedevelop, paste the code to main;
  • build and run inside haxedevelop, nothing - only flash;
  • use openfl command-line prompt to build and run - the same;
  • clean, build and run again using IDE or command-line - no change;

I think the only thing that I’ve not tried yet is to reinstall everything. I don´t know what I’m doing wrong or what’s left to try, but it seems that I can´t make it work. Do you have any advice on this?

Is this with my sample project code, or is it with the shape that you are testing?

There’s a chance your shape wouldn’t work on my system either – improvements to readGraphicsData and drawGraphicsData are quite new, and there’s a chance you’re hitting a code path that needs a little patch.

How do you think I could recreate what you are doing? Could you figure out what the contents of your graphics data are, so I could try and recreate it?

Thank you :slight_smile:

The previous post is referring to your sample code, that is, new project, paste your sample code, and it only renders the square in the flash target, the others (windows, neko and html5) only shows a black stage with no errors and crashes. I’ve tried build and run from inside haxedevelop and using openfl command line.

I don’t know if I can upload the svg file that I use, but I could try, but perhaps my problem is regardless the svg asset that I load, because I can´t even make your sample work…

I’ll problably have to reinstall everything, don’t I?

No, it is me that have to thank you, big time! :smiley:

Here is the svg that I use for my own tests, but I think my problem lies elsewhere…
https://we.tl/0l8YXCyhNR

Thanks for the file :slight_smile:

I discovered a rendering discrepancy with this file, which should be fixed in the development version of OpenFL now

package;


import format.SVG;
import openfl.Assets;
import openfl.display.Shape;
import openfl.display.Sprite;


class Main extends Sprite {
	
	
	public function new () {
		
		super ();
		
		var shape = new Shape ();
		//shape.graphics.beginFill (0xFF0000);
		//shape.graphics.drawRect (0, 0, 100, 100);
		
		var svg = new SVG (Assets.getText ("assets/svg_test_asset.svg"));
		svg.render (shape.graphics);
		
		addChild (shape);
		
		var graphicsData = shape.graphics.readGraphicsData ();
		
		var shape2 = new Shape ();
		//shape2.graphics.copyFrom (shape.graphics);
		shape2.graphics.drawGraphicsData (graphicsData);
		shape2.y = 200;
		addChild (shape2);
		
	}
	
	
}

If Windows and Neko are not working properly for you, can you try openfl test neko -Dcairo or openfl test html5 -Dcanvas, in case it is an OpenGL issue on your system?

Thanks

Cool, at least my doubts lead to a bug fix!

But in my case I still can’t render your red square code sample in neko/windows/html, what I’ve tried since my last post:

  • Reinstall haxe and openfl (haxe 3.4.2 / openfl 5.0.0 / lime 4.1.0);
  • Tried straight away openfl test neko -Dcairo - didn’t show the red square;
  • Tried openfl test html5 -Dcanvas- no red square;
  • Tried openfl test neko, openfl test windows, openfl test html5 - still no red square;
  • Tried openfl test flash - showed the red square, no issues here.

I can’t figure out what is happennig in my end, I think the only thing I’ve not tried yet is build and run on other machine. I don’t know if anyone else that is reading this, could try the following sample, with the latest OpenFL, and test the neko/windows/html5 targets and see if the red square appears normally:

package;


import openfl.display.Shape;
import openfl.display.Sprite;


class Main extends Sprite {
	
	
	public function new () {
		
		super ();
		
		var shape = new Shape ();
		shape.graphics.beginFill (0xFF0000);
		shape.graphics.drawRect (0, 0, 100, 100);
		
		var graphicsData = shape.graphics.readGraphicsData ();
		
		var shape2 = new Shape ();
		shape2.graphics.drawGraphicsData (graphicsData);
		addChild (shape2);
		
	}
	
		
}

Something is definitely wrong, I’ve tested in other machine, made a clean install of haxe and openfl (and haxe never was installed there before). Copied only the sample project without the bin folder. So new builds from scratch for all the targets, and the red square only appears in flash target, regardless I use -Dcairo in neko.

I don’t what is happening, but I starting to think that the problem couldn’t be only in my end. @singmajesty, could you please give me some advices in how I could sort this out?

And please, could other good soul try the previous sample code and see if the red square appears in other target besides flash?