Sign in to follow this  
MJW

A Solution to Final Render Flag (?)

Recommended Posts

Earlier this year I said:

 

 

It would be useful if there were a "final render" flag a plugin could test to see if the render code was out of the phase where the user was modifying the controls and instead doing the final render pass. That way, any information in the image that was added to help the user adjust the controls could be omitted. Or the image could be, for instance, rendered with anti-aliasing enabled. I have no idea how had it would be, or if it's even possible, but I do notice there always seems to be a render pass done after the plugin is closed with an OK.

 

I think I figured out a solution. It's based on the fact that when the render code runs under the plugin's menu, it's parent form's name under Property-Based Effects is  EffectConfigDialog, while when it runs as the final actual rendering pass, and when it runs in repeat-effect mode, it's parent form's name is empty. Since I don't want to rely on the exact names, the code is a little more complex.

 

I use the fact that when the effect is called with a dialog, OnCreatePropertyCollection() is called before OnSetRenderInfo(). When OnCreatePropertyCollection(), I set a flag. When OnSetRenderInfo() is called, I save the dialog name if the flag is set. Then when OnSetRenderInfo() is subsequently called, I compare the current dialog name with the saved name, and if they match, I set a flag saying the effect is running under the plugin dialog. The dialog name is initialized to a string the won't match any dialog name. That's so that when the effect is run as a repeated effect, the current dialog name (which will probably be an empty string) won't match.

 

Here's the code.

        protected override PropertyCollection OnCreatePropertyCollection()
        {
            createCalled = true;
                 .
                 .
                 .
        }

        bool createCalled = false;
        string createDialogName = "none";
        bool hasDialog;

        protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
        {
            string dialogName = (Form.ActiveForm != null) ? Form.ActiveForm.Name : "null";
            if (createCalled)
            {
                createCalled = false;
                createDialogName = dialogName;
            }
            hasDialog = (dialogName == createDialogName);
                 .
                 .
                 .
        }

There are probably simpler or cleverer ways to handle setting the flag based on the dialog name comparison, but this seems to work.

 

EDIT: Added test for null Form.ActiveForm because sometimes it would be null when running under the debugger. I can't say I quite understand that, but it seems to work correctly after I added the fix.

 

Another thing I don't understand is what happens on the last render pass after OK is pressed. I always thought it ran a full render phase, first calling OnSetRenderInfo() followed by the multiple calls to Render(). I thought I had good reason to believe it did. But when I modified a plugin to use the "final render flag" feature, and set breakpoints, only OnSetRenderInfo() was called; Render() was never called. I was able to get around this unexpected problem by essentially doing a full-buffer render in OnSetRenderInfo() when hasDialog was false. Even though it would throw a monkey wrench in my plans for this feature, I can see why pressing OK might not do any rendering, and would just keep the current version of dst buffer, I don't understand why the routine that sets up the variables is called, but the Render routine that uses their values isn't called. The (experimental) effect to which I added the feature is rather unorthodox, so perhaps it has something to do with that, though I can't see anything in what I do that would change the behavior of the processing that follows OK.

Edited by MJW

Share this post


Link to post
Share on other sites

This hack is a really bad idea. Relying on Form.ActiveForm is circumstantial and liable to break; and/or, it would tie my hands on future changes, improvements, refactorings.

 

Effects aren't supposed to know if they're running in "preview", final, or repeat rendering mode. I don't want effects to change their rendering behavior between these situations. And, as you can see, Paint.NET takes advantage of this in order to optimize rendering: preview mode's rendering is re-used for the final rendering.

 

I won't allow plugins on the forum that use a hack like this.

Share this post


Link to post
Share on other sites

That's very disappointing to me. I have a very good reason for wanting the the image the user sees while adjusting the controls to be different from what's saved when the plugin exits. The only alternative is to force the user to remember to change the mode before pressing OK, which he or she will likely often forget to do.

 

Even if this method doesn't work correctly, the only ill effect is that the plugin wouldn't produce the desired result. It wouldn't interfere with or change the function of PDN or any other plugin.

Share this post


Link to post
Share on other sites

BolBait provides an excellent example of the type of plugin where this feature is useful.

Edited by MJW

Share this post


Link to post
Share on other sites

I'm not saying it's not useful. I'm saying it's a major hack that takes advantage of circumstantial details about the effect's runtime environment.

 

What you really need is a separate rendering layer that goes on top of the effect.

Share this post


Link to post
Share on other sites

Typically if a plugin has to display something on the canvas it likes to act as a tool and not as an effect. As Rick said you need an additional layer on top all others and the settings should be non-modal so that the user can zoom and scroll the canvas.

 

I requested such a flag some time ago and using it in some plugins but in the meantime I think this is always just a work-around and I'm not happy  with it. I.e. in Boltbaits level plugin the size of the marker and text depends on the zoom level which looks quite strange sometimes. That's the reason why my level plugin shows its own canvas and the effect works like a tool. Independent from the zoom level the markers are displayed in the same size.

Share this post


Link to post
Share on other sites

I agree effects that show different things when the dialog is up are functioning as tools, but I don't see any reason why tools can't run in the main window instead of in a separate form. Running in a separate form often seems like a hack to me. You can't even show the lower layers through the transparent areas, which can be a major disadvantage.

Share this post


Link to post
Share on other sites

For clarity, I'll mention that in the plugin I'm working on, I don't display extra stuff on top of the regular image; I display the data in a different form. The plugin modifies a 24 bit height map. The appearance of the height map is very uninformative, so while its being adjusted, I shade it. Once the adjustments are complete, I want to save it as a height map, not as a shaded image.

Share this post


Link to post
Share on other sites
... I don't see any reason why tools can't run in the main window instead of in a separate form

Because from paint.net's perspective, these are not tools. They are effects. And effects are intentionally limited in functionality.

 

It would be quite a bit of work on my part to allow real tool plugins. A significant portion of the work would be in making a safe plugin system so that plugins can't screw up all sorts of important stuff. And I don't mean intentionally screwing things up (that's always going to be easy to some degree) -- it has to be easy to write things that behave correctly. And it would have to not be so complicated that nobody can even use it.

 

Adding a canvas rendering overlay that effects can use is a much simpler solution. This would allow you to use GDI+ or Direct2D to draw things that aren't part of the effect, and which won't ever be persisted. Paint.NET 4.0's newer rendering engine can reasonably support this, but the effect system is much older and really kinda slimey at this point (in terms of code quality and maintainability).

 

For an example of how the "newer rendering engine" can already do this: consider the Move Selection tool. It has a "UI overlay" on top of the canvas (on top of all layers), and it also manages to have a "layer overlay" that replaces the rendering for that layer. So it can easily manage scissoring between the layer's original and new content, while keeping the UI on top (those handles don't have blend modes applied to them, for instance, and won't be occluded by higher layers).

Share this post


Link to post
Share on other sites

I wasn't suggesting allowing tools like those under the main Tool menu -- desirable as that might be. I was just agreeing with midora's characterization of effects which produce a final image that differs from the interactive image as being akin to tools. That sort of "tool" could be achieved simply by allowing the plugin to know whether it's in interactive mode, and providing the plugin the option of requesting a final rendering pass after OK is pressed, if one would not have occurred already. Perhaps that's not a good thing to allow, but I doubt it could screw up anything.

 

The extra-layer method would work for the plugin for which I'd intended to use this final-render-flag feature (aka, "hack").

Edited by MJW

Share this post


Link to post
Share on other sites

If you still need to do something similar without relying on Form.ActiveForm maybe you could iterate over Application.OpenForms and check if one derives from the EffectConfigTokenDialog class to get the effect dialog form. This probably won't be

[...] circumstantial and liable to break; and/or, it would tie my hands on future changes, improvements, refactorings. [...]

 

I did something similar to check whether an effect was truly cancelled or if the IsCancelled flag was just set because the user changed values (to determine if I could discard cached data for good).

Edited by ArgusMagnus

Share this post


Link to post
Share on other sites

Maybe I should not talk about this but I implemented this feature in OptionBasedLibrary a year ago.

 

It works in the way that the token provides a method called IsInteractive() which is true while the dialog is open. The dialog takes care that the render function will be called with a token where the method returns false if you cancel or accept the dialog parameters. This works very well even if you use Paint.NETs "Repeat effect" feature.

Share this post


Link to post
Share on other sites

No, that's fine. That seems like a pretty reasonable way to do this as well, as a "short-term" solution to the larger problem. I can probably add an IsInteractive flag pretty easily and I think that is a better way of phrasing what it's intended to be used for. Querying the flag would probably have to disable the optimization mentioned above (rendering doesn't restart when you press OK in the effect's dialog).

 

I put "short-term" in quotes because it could end up not being so "short." I actually would like to do a full rewrite of the effect plugin system with all sorts of new goodies. (or, rather, create a new effect plugin system while leaving the old one alone)

Share this post


Link to post
Share on other sites

I see nothing wrong with effects knowing whether they're in interactive mode. Not only OptionBased effects, but all effects which implement their own user-interface code using Forms controls, can know when they're in interactive mode. Only Property-Based effects can't tell. Not only do effects know, but many act differently when they have a dialog. Almost all the effects under Effects>Tools act very differently: in interactive mode, they bring up their own canvases, and only update the PDN canvas when they exit. ShapeMaker certainly behaves differently in interactive mode and repeat-effect mode. (For such effects, repeat-effect is essentially meaningless anyway.)

 

EDIT: I wrote this comment before seeing Rick Brewster's last comment. I would love to see the IsInteractive flag.

Edited by MJW

Share this post


Link to post
Share on other sites

That's still a circumstantial hack.

 I fail to see why. Of course, "hack" is not really a well defined term, your definition obviously differs from mine. But given the fact that you already can't remove/refactor the EffectTokenConfigDialog class without braking a lot of plugins and you probably won't move away from windows form either, this doesn't seem to be "liable to break" to me.

 

I don't mean to offend you and I appreciate the time you are investing into this amazing free software, but, as you said yourself, these things can take quite some time. This is understandable, but if we (plugin authors) then are forbidden to use real short-term solutions, simply because you don't seem to like them, this is very frustrating. (e.g. I'm still disappointed that my Plugin Installer Plugin was rejected. I've read posts from you from 2008 or so saying that you are planning to provide a plugin management system, which hasn't happened yet and yet, I am not allowed to provide an intermediate solution for unkown reasosn)

Share this post


Link to post
Share on other sites

Though I won't bother arguing whether ArgusMagnus's method is or isn't a hack, I think he's correct that it's a reliable method of determining whether the effect is in interactive mode, and it depends on factors that seem to be very unlikely to change anytime soon. Also, even if it does fail, the worst that will happen is that the effect will display the wrong image. I wish a short-term solution would be allowed, even if it isn't philosophically perfect. I plan to soon publish two plugins that would be much more convenient to use if they could display the image in interactive mode differently than the saved image (I explained the reason previously). If there's an approved method in the future, I'll modify the effects to use it. Even if I weren't around to do so, I intend to publish the source code, so if the effects are useful, someone else can modify the code. And if they aren't useful, no one will use them, so no one will care.

Edited by MJW

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this