Jump to content

BoltBait

Administrator
  • Posts

    15,728
  • Joined

  • Last visited

  • Days Won

    405

Everything posted by BoltBait

  1. Stop opening that file! Seriously, don't open that file. That's the solution.
  2. BTW, to stay safe for high contrast screen modes, don't use any of the color tags in the [v] drop down list. Stick with all the other codes and you'll be fine.
  3. I was able to read your code with my phone Eli. It is a phone number.
  4. Open your gold leaf image Add your QR code to a new layer above the gold leaf image Select the QR layer and Adjustment > Invert Colors so that your bar code is white instead of black Click Layers > Properties, change the blending mode from Normal to Multiply. Flatten the image With Magic Wand, Shift-Click the black area and press Delete key. Save as .PNG format Since I don't know what plugins you have loaded, I gave you a paint.net only way to do it.
  5. I haven't seen any plugins updated with help yet (other than my own plugin pack, of course). I thought this feature would be more popular. Is anyone considering using this?
  6. Thanks! I just clarified a few areas of the tutorial so hopefully it makes more sense now.
  7. All of the controls are visible and not overlapping. So, it looks like your issue is that the drawing area is very small.
  8. Thank you! Desaturate wouldn't work at all. What if you typed white text on a blank canvas? Your code would blur that and desaturate it to... white... your shadow would be white on white text. Secondary color is almost as bad... it is usually white too. Primary color might be a good way to go. But, in this case, your Primary color is probably set to blue (since you just typed the text). Therefore, it would make a blue shadow with blue text. Not good. I think it is best to just hard code it to black. Why hard code it? Well, I simplified the real code down as much as possible for the tutorial. As mentioned in the closing, adding a color wheel is an exercise left up to the reader. That was exactly the reason I chose the function. It keeps the code simple for the demo. Just be careful you don't reuse the same namespace as another effect. That could lead to trouble.
  9. Sometimes you come up with an idea for a plugin and it is just a little too complex for the normal limitations of the plugin system. Normally, you're limited to two surfaces (the source surface and the destination surface), one built-in effect (like Gaussian Blur or Clouds, etc.), and an unlimited number of unary pixel ops and blend ops. What if you need an additional built-in effect? Or, what if you need an additional surface to precompute something? In this tutorial, I thought I'd walk you through using CodeLab to build an effect that requires an additional surface. The first thing you'll need to do is come up with an idea and design it out. In this tutorial, I'll walk you through designing and coding a "drop shadow" plugin. The Design Our plugin will start by using Gaussian blur our source canvas to a working canvas. Then, we will shift that result over and down a couple of pixels to our destination canvas. Finally, we will combine the source canvas with the destination canvas to retain our original object that is casting a shadow: If we didn't need to shift the shadow down and right, we would be able to do this without an extra surface. But, there is no practical way to blur and shift at the same time. So, let's go ahead and use an extra surface. Generating Code The first thing you'll need is an icon. Either design one in paint.net or use this one: (You can save that to your system as a .png file.) Let's get started in CodeLab. Run CodeLab and use the "File > New" menu. This will bring up the New Source (Template) screen. We have already decided based on our design that we'll need Gaussian Blur, Normal Blend Mode, and a WRK surface, so select those options: Click the "Generate Code" button. Your script should look like this: // Name: // Submenu: // Author: // Title: // Version: // Desc: // Keywords: // URL: // Help: #region UICode IntSliderControl Amount1=10; // [0,100] Radius #endregion // Working surface Surface wrk = null; // Setup for calling the Gaussian Blur effect GaussianBlurEffect blurEffect = new GaussianBlurEffect(); PropertyCollection blurProps; // Setup for using Normal blend op private BinaryPixelOp normalOp = LayerBlendModeUtil.CreateCompositionOp(LayerBlendMode.Normal); void PreRender(Surface dst, Surface src) { if (wrk == null) { wrk = new Surface(src.Size); } blurProps = blurEffect.CreatePropertyCollection(); PropertyBasedEffectConfigToken BlurParameters = new PropertyBasedEffectConfigToken(blurProps); BlurParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1); blurEffect.SetRenderInfo(BlurParameters, new RenderArgs(wrk), new RenderArgs(src)); } // Here is the main render loop function void Render(Surface dst, Surface src, Rectangle rect) { // Call the Gaussian Blur function blurEffect.Render(new Rectangle[1] {rect},0,1); // Now in the main render loop, the wrk canvas has a blurred version of the src canvas for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { ColorBgra CurrentPixel = wrk[x,y]; // TODO: Add additional pixel processing code here CurrentPixel = normalOp.Apply(src[x,y], CurrentPixel); dst[x,y] = CurrentPixel; } } } protected override void OnDispose(bool disposing) { // Release any surfaces or effects you've created. if (wrk != null) { wrk.Dispose(); wrk = null; } if (blurEffect != null) { blurEffect.Dispose(); blurEffect = null; } base.OnDispose(disposing); } Our script is nowhere near working yet, so don't worry about that yet. We're just gathering code that we'll need to write our effect. OK, now we have an effect with a slider for blur radius. Next we'll need to add a couple of sliders to handle the offset of the shadow. Use the File > User Interface Designer and add a double vector control: We'll also need a Double Slider for Shadow Strength (Style: White-Black, Min: 0, Default: 0.5, Max: 1) and a Colorwheel (Default: Black) for the shadow color. Go ahead and add those controls as shown above. Click OK to update your script with the new UI control. Your UICode region should now look like this: #region UICode IntSliderControl Amount1 = 10; // [0,100] Radius PanSliderControl Amount2 = Pair.Create(0.000,0.000); // Shadow Offset DoubleSliderControl Amount3 = 0.5; // [0,1,4] Shadow Strength ColorWheelControl Amount4 = ColorBgra.FromBgr(0,0,0); // [Black] Shadow Color #endregion The Shadow Radius of 10 (Amount1) is going to be much too big for most applications. Let's change it to 4. Finally, fill in all of the comment lines at the top of the script. The top of your script should look something like this now: // Name: Drop Shadow Demo // Submenu: Object // Author: BoltBait // Title: Drop Shadow Demo // Desc: Drop Shadow // Keywords: drop|shadow // URL: http://www.BoltBait.com/pdn // Help: #region UICode IntSliderControl Amount1 = 4; // [0,100] Radius PanSliderControl Amount2 = Pair.Create(0.000,0.000); // Shadow Offset DoubleSliderControl Amount3 = 0.5; // [0,1,4] Shadow Strength ColorWheelControl Amount4 = ColorBgra.FromBgr(0,0,0); // [Black] Shadow Color #endregion Let's take a moment to save our work. Use File > Save... command to save your script as "DropShadowDemo.cs" Adjusting Double Vector Defaults In the CodeLab UI Builder screen, we can't adjust the defaults of the Double Vector control. Let's go ahead and edit that right in the CodeLab editor. Change the following Amount2 line from: PanSliderControl Amount2 = Pair.Create(0.000,0.000); // Shadow Offset to: PanSliderControl Amount2 = Pair.Create(0.02,0.02); // Shadow Offset This will eventually make our default shadow 2 pixels to the right and 2 pixels down from our objects by multiplying those values by 100. Move the Blur from Render to PreRender Locate your Render function and cut the following 2 lines from the top of that function: // Call the Gaussian Blur function blurEffect.Render(new Rectangle[1] {rect},0,1); Now, paste those lines into the bottom of your PreRender function. Your PreRender code should now look like this: void PreRender(Surface dst, Surface src) { if (wrk == null) { wrk = new Surface(src.Size); } blurProps = blurEffect.CreatePropertyCollection(); PropertyBasedEffectConfigToken BlurParameters = new PropertyBasedEffectConfigToken(blurProps); BlurParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1); blurEffect.SetRenderInfo(BlurParameters, new RenderArgs(wrk), new RenderArgs(src)); // Call the Gaussian Blur function blurEffect.Render(new Rectangle[1] {rect},0,1); } That won't compile the way it is, so let's fix that last line. Change it to say: blurEffect.Render(new Rectangle[1] { wrk.Bounds }, 0, 1); This tells Gaussian Blur to render the entire work surface instead of just one rectangle of it. Typically, we will want to call Gaussian Blur in the Render function because then the work will be split up between all of your processors (multi-processor). But, in the case of our Object Shadow plugin, we need to PreRender the entire object to the WRK canvas before the main Render function starts. Be careful what you put in the PreRender function as everything in there runs in a single thread, so it will be much slower than stuff running in the Render function. Final Render Now it is time to finish our render function. As you remember from our plan, we've already handled #1 so we only have #2 and #3 to go. Currently, our render function looks like this: // Here is the main render loop function void Render(Surface dst, Surface src, Rectangle rect) { // Now in the main render loop, the wrk canvas has a blurred version of the src canvas for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { ColorBgra CurrentPixel = wrk[x,y]; // TODO: Add additional pixel processing code here CurrentPixel = normalOp.Apply(src[x,y], CurrentPixel); dst[x,y] = CurrentPixel; } } } We will keep the looping structure and only modify the code inside of the innermost loop. Starting with this code, let's implement #2 and #3: ColorBgra CurrentPixel = wrk[x,y]; // TODO: Add additional pixel processing code here CurrentPixel = normalOp.Apply(src[x,y], CurrentPixel); dst[x,y] = CurrentPixel; The first thing we need to calculate is how far to shift the shadow. Using the double vector control is fairly easy. We only need to convert the first slider to the x-offset and the second slider to the y-offset: // the shadow is on the work surface, now offset it int offsetx = (int)(Amount2.First * 100); int offsety = (int)(Amount2.Second * 100); We are multiplying by 100 here to convert from 0.02 to 2. This will offset by 2 pixels. Insert this code at the top of our innermost loop, right above the line that says "ColorBgra CurrentPixel = wrk[x,y];". Next, we will be changing the following line: ColorBgra CurrentPixel = wrk[x,y]; to: ColorBgra CurrentPixel = wrk.GetBilinearSample(x - offsetx, y - offsety); This will get our CurrentPixel off of the working canvas, offset by the amount specified by our double vector control. That's the "Shift right/down" portion of item #2 of our plan. If you recall, that pixel is blurred, but it may have some color. We need to change the shadow to black, so replace the following line: // TODO: Add additional pixel processing code here With: // apply color to our shadow CurrentPixel.R = Amount4.R; CurrentPixel.G = Amount4.G; CurrentPixel.B = Amount4.B; That implements the "color it black" part of item #2 of our plan. That handles the R, G, and B portion of the shadow. But, we still need to calculate the Alpha. We could just leave it alone, but if we did, the shadow would be too dark. So, let's add the following line right after the last lines we just inserted: CurrentPixel.A = (byte)(CurrentPixel.A * Amount3); By default, this lightens the shadow by half. (If Amount3 is equal to 0.5, multiplying the CurrentPixel alpha by Amount3 will lighten it by half.) Only one thing from our original plan left to do, #3. Replace the following line of automatically generated code: CurrentPixel = normalOp.Apply(src[x, y], CurrentPixel); with: CurrentPixel = normalOp.Apply(CurrentPixel, src[x, y]); This will mix the original surface with the shadow to retain the original object. The "normalOp" function mixes the pixel on the right with the pixel on the left as if the pixel on the right is on a higher layer than the pixel on the left. In this case, we want the original pixel to be placed on top of the shadow, so we needed to specify the CurrentPixel (our shadow) on the left and the "src[x,y]" pixel (our original object) on the right. Your final render function should now look like this: // Here is the main render loop function void Render(Surface dst, Surface src, Rectangle rect) { // Now in the main render loop, the wrk canvas has a blurred version of the src canvas for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { // the shadow is on the work surface, now offset it int offsetx = (int)(Amount2.First * 100); int offsety = (int)(Amount2.Second * 100); ColorBgra CurrentPixel = wrk.GetBilinearSample(x - offsetx, y - offsety); // add color to our shadow, default black CurrentPixel.R = Amount4.R; CurrentPixel.G = Amount4.G; CurrentPixel.B = Amount4.B; CurrentPixel.A = (byte)(CurrentPixel.A * Amount3); CurrentPixel = normalOp.Apply(CurrentPixel, src[x,y]); dst[x,y] = CurrentPixel; } } } Let's take another moment to save our work. Use File > Save... command to save your script as "DropShadowDemo.cs" Building a DLL Now that we've completed the code, use CodeLab's Build DLL function (Ctrl+B) to create a real plugin for paint.net. Be sure to select that icon you saved to your system so that it will appear in paint.net's menu with an icon. Once built, check your desktop for two files: DropShadowDemo.dll and Install_DropShadowDemo.bat You can run the install batch file located on your desktop to install your new DLL into paint.net. Restart paint.net to see your new effect in the Effect menu. Final Thoughts I hope you learned something from this tutorial. Now, show me what you can do! If you enjoyed this tutorial and would like to buy me a beer, click this button: If that's too much, how about an up vote?
  10. When first asked about this privately, Rick responded:
  11. Well, if you don't care to use the Shape Maker plugin, perhaps you'd like to design your shapes manually. Here is a tutorial to get you started: http://forums.getpaint.net/index.php?/topic/32101-h
  12. They are 2 different ways to create custom shapes: The Easy Way - Shape Maker The Hard Way - Explained below Most of the shapes in my plugin pack were created the "hard way", that is coded by hand using the Kaxaml editor. Before we begin, download and install Kaxaml: http://www.kaxaml.com/ There are 2 ways to hand code an XAML shape for paint.net: Simple PathGeometry - House GeometryGroup - Smart Phone / Cube Both ways have their good points. However, you should know that there are things you can do in the GeometryGroup method that you can't do with the Simple PathGeometry. I'll explain both and you can choose which method to use. Simple PathGeometry Here is a template / example you can use right in Kaxaml: <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <Canvas> <Path Fill="#D2E9F6" Stroke="#5894C1" StrokeThickness="1" Data="F1 M 21,142 L 160,22 L 300,142 L 300,318 L 21,318 Z" /> </Canvas> </Grid> </Page> You just need to replace the data "F1 M 21,142 ... Z" etc. with the instructions to draw your own shape. Here is the language reference: Language Reference for XAML Mini-Language The string starts with either F0 (default if not specified) or F1 indicating fill mode 0 (even odd) or fill mode 1 (non-zero). The M command moves to the specified point, the L command draws a line to the specified end point, and the Z command closes a shape by drawing a line back to the last M command. Check the reference page for more commands, like curves, etc. Once you are happy with the shape in Kaxaml, take the data string and drop it into the template for a paint.net shape: <ps:SimpleGeometryShape xmlns="clr-namespace:PaintDotNet.UI.Media;assembly=PaintDotNet.Framework" xmlns:ps="clr-namespace:PaintDotNet.Shapes;assembly=PaintDotNet.Framework" DisplayName="House" Geometry="F1 M 21,142 L 160,22 L 300,142 L 300,318 L 21,318 Z" /> The "DisplayName" field is where you specify the shape name that will appear when you hover over the shape in the shape menu. The "Geometry" field is where you put your drawing instructions. Once you've saved this file onto your Desktop, copy it to your shapes directory: C:\Program Files\paint.net\Shapes Restart paint.net and check to see if it appears in the menu. If it doesn't appear, click the gear icon in the upper right corner of the paint.net screen and check in the plugin errors tab. GeometryGroup Here is a template / example you can use right in Kaxaml: <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <Path Fill="#D2E9F6" Stroke="#5894C1" StrokeThickness="1"> <Path.Data> <GeometryGroup> <RectangleGeometry Rect="10,10,184,307" RadiusX="10" RadiusY="10" /> <RectangleGeometry Rect="15,40,174,227" RadiusX="10" RadiusY="10" /> <RectangleGeometry Rect="74,21,60,7" RadiusX="10" RadiusY="10" /> <EllipseGeometry Center="103,290" RadiusX="15" RadiusY="15" /> </GeometryGroup> </Path.Data> </Path> </Grid> </Page> I chose to use basic shapes to create the cell phone because I needed to use rounded rectangles. These are easy here as you can specify RadiusX and RadiusY for rectangle corners... something you can't do using the mini-language. And, here is how it would look as a paint.net shape: <ps:SimpleGeometryShape xmlns="clr-namespace:PaintDotNet.UI.Media;assembly=PaintDotNet.Framework" xmlns:ps="clr-namespace:PaintDotNet.Shapes;assembly=PaintDotNet.Framework" DisplayName="Smart Phone"> <GeometryGroup> <RectangleGeometry Rect="10,10,184,307" RadiusX="10" RadiusY="10" /> <RectangleGeometry Rect="15,40,174,227" RadiusX="10" RadiusY="10" /> <RectangleGeometry Rect="74,21,60,7" RadiusX="10" RadiusY="10" /> <EllipseGeometry Center="103,290" RadiusX="15" RadiusY="15" /> </GeometryGroup> </ps:SimpleGeometryShape> Notice the simple copy-and-paste needed. Then, don't forget to specify a Display Name. Note: paint.net shapes are limited to one path data or one GeometryGroup. Another Example Here is an example of using a GeometryGroup that looks very similar to the Simple PathGeometry we learned first: <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <Path Fill="#D2E9F6" Stroke="#5894C1" StrokeThickness="1"> <Path.Data> <PathGeometry FillRule="Nonzero"> <PathFigure IsClosed="True" IsFilled="True" StartPoint="0,22"> <LineSegment Point="58,0" IsSmoothJoin="True" /> <LineSegment Point="116,22" IsSmoothJoin="True" /> <LineSegment Point="116,93" IsSmoothJoin="True" /> <LineSegment Point="58,116" IsSmoothJoin="True" /> <LineSegment Point="0,93" IsSmoothJoin="True" /> <LineSegment Point="0,22" IsSmoothJoin="True" /> </PathFigure> <PathFigure IsClosed="False" IsFilled="False" StartPoint="58,46"> <LineSegment Point="0,22" /> </PathFigure> <PathFigure IsClosed="False" IsFilled="False" StartPoint="58,46"> <LineSegment Point="58,116" /> </PathFigure> <PathFigure IsClosed="False" IsFilled="False" StartPoint="58,46"> <LineSegment Point="116,22" /> </PathFigure> </PathGeometry> </Path.Data> </Path> </Grid> </Page> Here I chose to use the expanded syntax because I can control the shape of the line endings to make the drawing look better. Here's how it looks as a paint.net shape: <ps:SimpleGeometryShape xmlns="clr-namespace:PaintDotNet.UI.Media;assembly=PaintDotNet.Framework" xmlns:ps="clr-namespace:PaintDotNet.Shapes;assembly=PaintDotNet.Framework" DisplayName="Cube"> <PathGeometry FillRule="Nonzero"> <PathFigure IsClosed="True" IsFilled="True" StartPoint="0,22"> <LineSegment Point="58,0" IsSmoothJoin="True" /> <LineSegment Point="116,22" IsSmoothJoin="True" /> <LineSegment Point="116,93" IsSmoothJoin="True" /> <LineSegment Point="58,116" IsSmoothJoin="True" /> <LineSegment Point="0,93" IsSmoothJoin="True" /> <LineSegment Point="0,22" IsSmoothJoin="True" /> </PathFigure> <PathFigure IsClosed="False" IsFilled="False" StartPoint="58,46"> <LineSegment Point="0,22" /> </PathFigure> <PathFigure IsClosed="False" IsFilled="False" StartPoint="58,46"> <LineSegment Point="58,116" /> </PathFigure> <PathFigure IsClosed="False" IsFilled="False" StartPoint="58,46"> <LineSegment Point="116,22" /> </PathFigure> </PathGeometry> </ps:SimpleGeometryShape> Just copy all of the PathGeometry tag into the paint.net template. Don't forget to specify a Display Name. GeometryGroup and PathFigure are really the same method. You can combine them by putting a PathGeometry inside of a GeometryGroup at the same level as the RectangleGeometry or EllipseGeometry in the first example above. ____________________
  13. Click the Gear symbol in the upper right corner of the paint.net window.
  14. I'm trying to read your mind, but something's blocking me. Maybe you could explain what you don't like so that it could be addressed.
  15. As more people come up with their own shapes, they will be added to the Custom section. BTW, I really love the new custom shape feature. I've already used it when designing graphics for documentation at work. I'm always documenting work flows like this: ____________
  16. "Preloaded shapes"? The preloaded shapes (that come with paint.net) are in the first 5 rows (Basic, Polygons and Stars, Arrows, Callouts, and Symbols). All the shapes in my pack are listed under the 6th row (Custom). Looks to me that everything is working fine.
  17. Please show us a screenshot of your shapes menu, like this:
  18. Paint.NET does have a way to replace color: Recolor Tool. Or, you could try one of the plugins in my plugin pack, Hue / Saturation+ http://forums.getpaint.net/index.php?/topic/28613-
  19. There are some limitations. For example paint.net shapes can only have 1 Geometry Group. BTW, there is a shape editor for paint.net: http://forums.getpaint.net/index.php?/topic/32096- It doesn't work on my shapes, but you can use it to easily make your own paint.net shapes.
  20. Paste Alpha got a new Zoom slider and a Placement control: Fill From Clipboard and Fill From File got a "Tile image" methods. And, the [+] control now works as expected. Thanks goes out to MJW and midora for the mirroring code. Enjoy. Please give me feedback on the built-in help files. Too much? Not enough? Just right? This is the first rev, so let me know! ______________
  21. This tutorial explains how to add help to your plugins.* http://boltbait.com/pdn/CodeLab/help/tutorial6.php ___________________ *This tutorial requires paint.net v4.0.6+ and CodeLab v2.6+
  22. This is now built-in to CodeLab (starting with version 2.6) http://forums.getpaint.net/index.php?/topic/880-codelab-v26-for-advanced-users-released-august-2-2015/?p=429174 Enjoy.
×
×
  • Create New...