Jump to content

midora

Members
  • Posts

    1,782
  • Joined

  • Last visited

  • Days Won

    26

Everything posted by midora

  1. I will keep the example up-to-date once a month. The access to the controls is shown in SetRenderInfo (propXXX=...). They are just not used in the RenderRect() function because the main focus of this example was to show all available controls. But something simple could be added.
  2. The developers know that the content of the destination surface is declared as undefined. Main reason: It's a waste of time if Paint.NET would prepare the surface and the effect plugin does its own filling. Some additional EffectFlags may be used to inform Paint.NET how the surface could be prepared: EffectFlags.FillDestinationWithSource: The plugin can expect that the destination contains the content of the source EffectFlags.FillDestinationWithTransparentWhite: The destination is filled with transparent white EffectFlags.FillDestinationWithPrimary: The destination is filled with the primary color EffectFlags.FillDestinationWithSecondary: -"- secondary color If none of the flags is set then we are in the undefined content mode (like now). This may speed up the effect because Paint.NET could select the optimal method to do the basic fill operation.
  3. Let us collect some feedback like yours and then update the example. Makes no sense to add any variation in an example, but we may fill some more 'good to know' gaps.
  4. It would be nice if we could get some controls to structure the content. Like tabs, groupboxes, or just a void element whicht provides DisplayName and Description element only. I implemented something simular now in the OptionBasedLibrary und used it in development. Here is a code example which shows how simple this could be in the code (even easier as PropertyBased implementation because there is no context, which is used for translation and more): OptionControlList options = new OptionControlList { new OptionContainerTabControl(OptionNames.Tab, optContext) { new OptionContainerTabPage(OptionNames.Page1, optContext) { new OptionPathDrawingBoard(OptionNames.DrawingBoard, optContext) { SourceSize = EnvironmentParameters.SourceSurface.Size, }, }, new OptionContainerTabPage(OptionNames.Page2, optContext) { new OptionInt32Slider(OptionNames.LineWidth, optContext, 2, 1, 100) { Unit = "px", }, new OptionInt32ColorWheel(OptionNames.LineColor, optContext) { ColorSelect = ColorSelect.Any | ColorSelect.Primary | ColorSelect.Secondary, Primary = (int)EnvironmentParameters.PrimaryColor.Bgra, Secondary = (int)EnvironmentParameters.SecondaryColor.Bgra, }, new OptionEnumDropDown<LineStyleEnum>(OptionNames.LineStyle, optContext, LineStyleEnum.Solid), new OptionEnumDropDown<LineDashCapEnum>(OptionNames.LineDashCap, optContext, LineDashCapEnum.Round), new OptionEnumDropDown<LineCapEnum>(OptionNames.LineStartCap, optContext, LineCapEnum.Round), new OptionEnumDropDown<LineCapEnum>(OptionNames.LineEndCap, optContext, LineCapEnum.Round), new OptionEnumDropDown<LineJoinEnum>(OptionNames.LineJoin, optContext, LineJoinEnum.Round), }, }, new OptionEnumCheckBoxes<ShowEnum>(OptionNames.ShowWhileEditing, optContext, new bool[] { true, true}), }; which produces the following dialog:
  5. A simple template showing how to add properties (IndirectUi controls) to an effect dialog. The template contains all available variants of properties in Paint.NET 3.5.11. Microsoft Visual C# 2010 project: ExamplePropertyBasedEffect.zip Screenshot: In the meantime Rick released Paint.NET 4 Alpha. So here is a second project containing the additional controls available in Paint.NET 4 (DoubleVector3Property as PropertyControlType Slider and RollBallAndSliders). Remember this is an alpha version. Everything may change. This second project needs references to the libs in Paint.NET 4 and the Target Framework must be '.NET Framework 4'. Plugins using this framework will not run in Paint.NET 3.5.11. So if you do not need the additional controls then stay with the references to the libs in Paint.NET 3.5.11 (Ricks advice). Microsoft Visual C# 2010 project: ExamplePropertyBasedEffectPdn4.zip Screenshot: using System; using System.Collections.Generic; using System.Drawing; using PaintDotNet; using PaintDotNet.IndirectUI; using PaintDotNet.Effects; using PaintDotNet.PropertySystem; namespace ExamplePropertyBased { public sealed class ExamplePropertyBasedEffect : PropertyBasedEffect { // ---------------------------------------------------------------------- /// <summary> /// Defines an abstract name for the effect class /// </summary> private static Type ClassType = typeof(ExamplePropertyBasedEffect); // ---------------------------------------------------------------------- /// <summary> /// Defines a user friendly name used for menu and dialog caption /// </summary> private static string StaticName { get { return ClassType.Name; } } // ---------------------------------------------------------------------- /// <summary> /// Defines an image used for for menu and dialog caption (may be null) /// </summary> private static Bitmap StaticImage { get { return Properties.Resources.EffectIcon; } } // ---------------------------------------------------------------------- /// <summary> /// Defines the submenu name where the effect should be placed. /// Prefered is one of the SubmenuNames constants (i.e. SubmenuNames.Render) /// (may be null) /// </summary> private static string StaticSubMenuName { get { return "Development"; } } // ---------------------------------------------------------------------- /// <summary> /// Constructs an ExamplePropertyBasedEffect instance /// </summary> public ExamplePropertyBasedEffect() : base(StaticName, StaticImage, StaticSubMenuName, EffectFlags.Configurable) { } // ---------------------------------------------------------------------- /// <summary> /// Destroys the ExamplePropertyBasedEffect instance /// Remove me. Only needed for special cases. /// If resources must be freed then the prefered way is to override OnDispose() /// </summary> ~ExamplePropertyBasedEffect() { } // ---------------------------------------------------------------------- /// <summary> /// Identifiers of the properties used by the effect /// </summary> private enum PropertyNames { EnumDropDown, EnumRadioButtons, BooleanCheckBox, StringTextBox, Int32Slider, Int32IncrementButton, Int32ColorWheel, DoubleSlider, DoubleAngleChooser, DoubleVectorSlider, DoubleVectorPanAndSlider } /// <summary> /// Identifiers used in a dropdown choice property /// </summary> private enum DropDownEnum { DropDown1, DropDown2, DropDown3 } /// <summary> /// Identifiers used in a radiobutton choice property /// </summary> private enum RadioButtonEnum { RadioButton1, RadioButton2, RadioButton3 } // Settings of the properties private DropDownEnum propEnumDropDown; private RadioButtonEnum propEnumRadioButtons; private bool propBooleanCheckBox; private string propStringTextBox; private int propInt32Slider; private int propInt32IncrementButton; private int propInt32ColorWheel; private double propDoubleSlider; private double propDoubleAngleChooser; private Pair<double, double> propDoubleVectorSlider; private Pair<double, double> propDoubleVectorPanAndSlider; // ---------------------------------------------------------------------- /// <summary> /// Configure the properties of the effect. /// This just creates the properties not the controls used in the dialog. /// These properties are defining the content of the EffectToken. /// </summary> protected override PropertyCollection OnCreatePropertyCollection() { // Add properties of all types and control types (always the variant with minimal parameters) List<Property> props = new List<Property> { StaticListChoiceProperty.CreateForEnum<DropDownEnum>( // is PropertyControlType.DropDown PropertyNames.EnumDropDown, DropDownEnum.DropDown1, false), StaticListChoiceProperty.CreateForEnum<RadioButtonEnum>( // set PropertyControlType.RadioButton PropertyNames.EnumRadioButtons, RadioButtonEnum.RadioButton1, false), new BooleanProperty(PropertyNames.BooleanCheckBox), // is PropertyControlType.CheckBox new StringProperty(PropertyNames.StringTextBox), // is PropertyControlType.TextBox new Int32Property(PropertyNames.Int32Slider), // is PropertyControlType.Slider new Int32Property(PropertyNames.Int32IncrementButton), // set PropertyControlType.IncrementButton new Int32Property(PropertyNames.Int32ColorWheel, // set PropertyControlType.ColorWheel 0, 0x000000, 0xffffff), // range can not be changed! new DoubleProperty(PropertyNames.DoubleSlider, // is PropertyControlType.Slider 0.0, -1.0, 1.0), new DoubleProperty(PropertyNames.DoubleAngleChooser, // set PropertyControlType.AngleChooser 0.0, -180.0, 180.0), // only two ranges allowed [-180 180] or [0 360]! new DoubleVectorProperty(PropertyNames.DoubleVectorSlider, // set PropertyControlType.Slider Pair.Create(0.0, 0.0), Pair.Create(-1.0, -1.0), Pair.Create(1.0, 1.0)), new DoubleVectorProperty(PropertyNames.DoubleVectorPanAndSlider, // is PropertyControlType.PanAndSlider Pair.Create(0.0, 0.0), Pair.Create(-1.0, -1.0), Pair.Create(1.0, 1.0)) }; // Add rules List<PropertyCollectionRule> propRules = new List<PropertyCollectionRule>(); // no rules defined (eliminate parameter if you like) return new PropertyCollection(props, propRules); } /* OnCreatePropertyCollection */ // ---------------------------------------------------------------------- /// <summary> /// Configure the user interface of the effect. /// You may change the default control type of your properties or /// modify/suppress the default texts in the controls. /// </summary> protected override ControlInfo OnCreateConfigUI(PropertyCollection props) { ControlInfo configUI = CreateDefaultConfigUI(props); // Set control types of some properties to change them to the expected controls configUI.SetPropertyControlType(PropertyNames.EnumRadioButtons, PropertyControlType.RadioButton); configUI.SetPropertyControlType(PropertyNames.Int32IncrementButton, PropertyControlType.IncrementButton); configUI.SetPropertyControlType(PropertyNames.Int32ColorWheel, PropertyControlType.ColorWheel); configUI.SetPropertyControlType(PropertyNames.DoubleAngleChooser, PropertyControlType.AngleChooser); configUI.SetPropertyControlType(PropertyNames.DoubleVectorSlider, PropertyControlType.Slider); // Change individual properties (see ControlInfoPropertyNames) #if false configUI.SetPropertyControlValue(PropertyNames.EnumDropDown, ControlInfoPropertyNames.DisplayName, "My DisplayName); // Change DisplayName (default is the PropertyNames identifier) configUI.SetPropertyControlValue(PropertyNames.EnumDropDown, ControlInfoPropertyNames.DisplayName, String.Empty); // Suppress display of DisplayName PropertyControlInfo pci = configUI.FindControlForPropertyName(PropertyNames.EnumDropDown); pci.SetValueDisplayName(DropDownEnum.DropDown1, "My DropDown1 Text"); // #endif return configUI; } /* OnCreateConfigUI */ // ---------------------------------------------------------------------- /// <summary> /// Configure the dialog of the effect. /// </summary> protected override void OnCustomizeConfigUIWindowProperties(PropertyCollection props) { #if false props[ControlInfoPropertyNames.WindowTitle].Value = "Hello world!"; // props[ControlInfoPropertyNames.WindowWidthScale].Value = 2.0; // WindowWidthScale must be a double, range seems to be [1.0, 2.0] (Default 1.0) #endif props[ControlInfoPropertyNames.WindowIsSizable].Value = true; // don't set this property if your dialog does not contain a lot of controls base.OnCustomizeConfigUIWindowProperties(props); } /* OnCustomizeConfigUIWindowProperties */ // ---------------------------------------------------------------------- /// <summary> /// Called after the token of the effect changed. /// This method is used to read all values of the token to instance variables. /// These instance variables are then used to render the surface. /// </summary> protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken effectToken, RenderArgs dstArgs, RenderArgs srcArgs) { // Read the current settings of the properties propEnumDropDown = (DropDownEnum)effectToken.GetProperty<StaticListChoiceProperty>(PropertyNames.EnumDropDown).Value; propEnumRadioButtons = (RadioButtonEnum)effectToken.GetProperty<StaticListChoiceProperty>(PropertyNames.EnumRadioButtons).Value; propBooleanCheckBox = effectToken.GetProperty<BooleanProperty>(PropertyNames.BooleanCheckBox).Value; propStringTextBox = effectToken.GetProperty<StringProperty>(PropertyNames.StringTextBox).Value; propInt32Slider = effectToken.GetProperty<Int32Property>(PropertyNames.Int32Slider).Value; propInt32IncrementButton = effectToken.GetProperty<Int32Property>(PropertyNames.Int32IncrementButton).Value; propInt32ColorWheel = effectToken.GetProperty<Int32Property>(PropertyNames.Int32ColorWheel).Value; propDoubleSlider = effectToken.GetProperty<DoubleProperty>(PropertyNames.DoubleSlider).Value; propDoubleAngleChooser = effectToken.GetProperty<DoubleProperty>(PropertyNames.DoubleAngleChooser).Value; propDoubleVectorSlider = effectToken.GetProperty<DoubleVectorProperty>(PropertyNames.DoubleVectorSlider).Value; propDoubleVectorPanAndSlider = effectToken.GetProperty<DoubleVectorProperty>(PropertyNames.DoubleVectorPanAndSlider).Value; base.OnSetRenderInfo(effectToken, dstArgs, srcArgs); } /* OnSetRenderInfo */ // ---------------------------------------------------------------------- /// <summary> /// Render an area defined by a list of rectangles /// This function may be called multiple times to render the area of // the selection on the active layer /// </summary> protected override void OnRender(Rectangle[] rois, int startIndex, int length) { for (int i = startIndex; i < startIndex + length; ++i) { RenderRectangle(DstArgs.Surface, SrcArgs.Surface, rois[i]); } } // ---------------------------------------------------------------------- /// <summary> /// The function to render one rectangle of the surface /// </summary> private void RenderRectangle(Surface dst, Surface src, Rectangle renderRect) { // Add your render code here // Uncomment the basic example you like to see // Copy the original content of the active layer to the selection //dst.CopySurface(src,renderRect.Location,renderRect); // Fill the selection of the active layer with transparent white (0x00FFFFFF) //dst.Clear(renderRect, ColorBgra.Transparent); // Fill the selection of the active layer with the active primary color dst.Clear(renderRect, EnvironmentParameters.PrimaryColor); } } }
  6. Happy coding :-) Have a look to PropertyBased Effect plugins. For the most plugins it is the easiest way to create a dialog.
  7. That's an interesting problem. OnRender() will be called the expected number of times after Ok but the length parameter is always zero which means that Render() will never be called. (I tested the Null54 original project). GraphicsPath should be there if you are adding "using "System.Drawing.Drawing2D". If you are creating a new object than you should dispose it. I guess you are mixing something here. Clip() just defines the area of output not the gdi problem that two threads may share the same object like a pen. BTW g.SetClip( rect) does the same, there is no need to create a new region. I never used GDI+ single threaded. But this means that each OnRender has to use its own set of objects. There are bugs and flaws in GDI+ but at the end it is easy to use.
  8. One additional tip: Use a GraphicsPath for the closed curves to avoid the caps at the end of the line (you mentioned this issue in the description of the original plugin). using (GraphicsPath gp = new GraphicsPath()) { gp.StartFigure(); gp.AddBeziers(closedBezPoints); gp.CloseFigure(); g.DrawPath(priPen, gp); }
  9. This raises a general question, which may be of interest for the developers. What's the proposed method to copy the source: - doing one call in OnSetRenderInfo() to copy the whole surface - doing dst.CopySurface(src, renderRect.Location, renderRect); in OnRender() for each rectangle in the list. The first one may be a little bit faster on a single core but the second one on multi core plus it is faster to abort (I guess Paint.NET aborts current rendering if the UI forces a new FinishTokenUpdate()).
  10. If you are creating temporary object like pens of file handles you may use the 'using' directive using (var pen = (Pen)Pens.Red.Clone()) { ... } because its scope controls the lifetime of these objects. Means they are disposed at the end of the block. Nice to see the construction lines. But now there are too many ;-) The tangents are the important ones because they are defining the black control points. In a typical UI you would activate a black control point which then shows the tangent of this point including the two end points of the tangent. Rick stalked in his blog about the new shape tool. There ou can see a curve shape. But I do not expect that 4.0 allows to control the amount of control points and tangents. One additional remark: I would allow to control the points on the curve and just one end of the tangents. This way the user already knows that half of the points are on the curve (see attachment). And you avoid the difference between the open and the closed curve. Moving one control point of the line should move the associated control point by the same amount. Moving the associated point relativ to it curve point changes curvature and flatness.
  11. If I could see the tangent lines formed by the control points then it would help me to use the tool. That would be great. For sure an IsInteractive property would just be a workaround for tool-like effect plugins like this one. An interactive surface which would be rendered without clipping on top of all other surfaces would be better. But WYSIWYG is a software design decission, so nothing to discuss just to think about.
  12. Just to say I wouldn't have metioned the fix if this issue wouldn't exist in all of my multi-monitor environments. And it solved my issue. Maybe there is an issue with the catch of the rectangle (Screen.FromRectangle ?). The video of Zagna demonstrates the effect quite good. As I said earlier I would not spend time now. Let's check this again in 4.0. If you like to get ideas about how to handle different screen configurations and anchor placement of effect dialogs then check version 1.1 of the fix.
  13. I will use it, thanks :-) Maybe set the dialog to resizable. Srolling is always a mess if your screen is large enough. protected override void OnCustomizeConfigUIWindowProperties(PropertyCollection props) { props[ControlInfoPropertyNames.WindowIsSizable].Value = true; base.OnCustomizeConfigUIWindowProperties(props); } /* OnCustomizeConfigUIWindowProperties */ I like the trick to show temporary infos in the image which are not needed for the final rendering. But it's a little bit a pain to remember to switch them off. So if Rick could provide an effect property in the future which tells that the userinterface is in use (I.e. Effect.Interactive) would help to distinguish if the help information should be rendered or not. This is available in OptionBasedLibrary.
  14. No direct response from the developer does not mean that is is out of view. The issue is a little bit disturbing for some of us, but from my point of view working on Paint.NET 4.0 makes more people happy...
  15. Rick no issue. Its for sure no effect but there is no other way than using effect plugins to provide a user interface. The plugin is just a temporary fix for something I would expect that you will change in a future version of Paint.NET. The solution is quite clean because its just managed code (no unmanaged hooking). At least much better than to use any external solution to fix the issue. I'm not complaining, just giving an explanation. I would allow the plugin as an exception. At the end all the devlopers here just like to support the community. And some positive feedback is (hopefully) what we are getting for. I take your comment as positive. So if you like the name, why not keeping it and just removing the link? ;-)
  16. For those having an issue with this. There is a plugin link removed by Rick -- see comments below
  17. I talked about step 1 to 5 but starting with a small selection on the right.
  18. You can' t. A rectangle stays a rectangle. Maybe in 4.0. Use 'Quadrilateral Reshape'. For the example in the top row. Start with a small rectangle (maybe just one pixel wide) at the right side. Expand it and move the top right corner back. If you have to do more complex object manipulations then you should use a vector editor (i.e. Inkscape).
  19. Maybe not the easiest way but try this: 1. Select the object 2. Use 'Move Pixels' to extend the size 3. Copy and paste to a new image 4. Use plugin 'Quadrilateral Reshape' to move the top right corner back 5. Copy and paste back to the original.
  20. Fair comments are always fine. This is a never ending discussion and there are a lot of pros and cons. Typically I'm more on the pro side. But the reason for this is easy. If you allow the user to translate the plugin via the the .dlc file then you have to allow to translate the submenu name too. Changing the location is a side-effect of the translation feature. BTW: It is possible to add one of standard Paint.NET names which requests Paint.NET to do the translation. I.e. MeasureSelection.SubmenuName=SubmenuNames.Render We may open a discussion thread about OptionBasedLibrary features and discuss it there.
  21. Update 1.2 (9.5.2013) - Added 'Centroid of selection' information - Moved the OptionBased part of the plugin to a separate dll. You may change the submenu of the effect in the MeasureSelection.dlc file (key MeasureSelection.SubmenuName)
  22. If you are searching the forum you will see that there are rare cases of corrupted png output. You uploaded to a share which forces you to download an exe. That's not a good idea...
  23. Hooking is an old concept provided by Microsoft in the WindowManager. Not so easy to use in a managed environment like Paint.NET but it should work. See SetWindowsHookEx(WH_CALLWNDPROC,..).
×
×
  • Create New...