• Content count

  • Joined

  • Last visited

Community Reputation


About maccas

  1. @yellowman: the effect you show there would be super difficult to achieve through a plug-in (not impossible mind!). Essentially your original surface now has the appearance of no longer being flat but is curved and twisted in all sorts of weird and wonderful ways, one section near the top even has both opposing edges showing depth at the same point. Just coding the UI capture what distortions you wanted you be a bit of a challenge. @Red ochre: This is more do-able but on thinking about it, it is not particularly simple either (happy to be proved wrong). I like a challenge so I'll have a crack at it. I don't have loads of spare time so it might take more than a few days. Here's my thoughts on the matter thus far, feel free to chip in if you've got any bright ideas on how to tackle the problem and/or any useful source code: my code doesn't do most of it at all as I only need worry about combining 4 source pixels to get any one destination pixel but perspective, with it's shrinking of the receding planes, could put many source pixels to any one destination pixel. Not the end of the world but will require a different approach also I get to assume constant angle of translation in my current code but it will vary in any perspective enabled routine thinking about the varying angle stuff I'm coming round to the fact that a realistic depth effect is actually intimately tied to the original layer rotation manipulation (i.e. step 2 of my original "how to use..."). The rotation actually constraints where is it reasonable to put the vanishing points so I'm thinking that any 3D perspective effect should include the rotation as well so that it knows how the planes have moved relative to the observer. Any starting image should be assumed to be 2D and face-on to the observer with vanishing points in the middle of the page, to right and left infinity and to top and bottom infinity. That middle of the page point is the sole visible vanishing point of the two front and back vanishing points, which should also be at infinity: you can't see the back point because it's behind you. Since our effect is setting out to cast a flat 2D object directly backwards in space tracking the position of these 6 vanishing points is all (!) we need to do. Our effect therefore needs to allow us to specify:depth of cast into 3D (obviously) rotation of the original layer (as discussed above) ability to bring vanishing points in from infinity to some finite value, you don't get any perspective without this ... and getting inspiration from Pyrochild's work, the ability to fade out or blur the image at greater depth [*]I think the UI coding alone takes me slightly beyond the bounds of what's sensibly do-able in Codelab so it looks like I'll also need to migrate to VS. If anyone has any good tutorials / guides on how someone can do this from scratch (i.e. what programs do I need to install onwards, I'm currently a total beginner) I'd be most obliged otherwise I'll just have to figure it out as I go along. Last point: it feels like this might no longer a post about a published plug-in but a development discussion thread. If the post needs splitting or moving can someone please advise as I don't want to break any etiquette.
  2. Just as I feared ... someone's already done this only way better! Thanks for all your encouragement and the coding pointers. Would it be it useful to add perspective to this plug-in for extra 3D goodness (& to differentiate from Pyrochild's more comprehensive offering)?
  3. ... should also say thanks to BoltBait for his plug-in writing tutorials without which this would have taken me much longer. Love your work.
  4. Hi all, I just picked up Paint.NET the other day after someone at work gave me a recommendation. I've been looking to try use it to create some icons and other computer graphics (as opposed to photo editing). The issue I've come across is trying to add (the appearance of) depth to a 2D shape. I had a look at some of the tutorials to figure out how to do this and they seem to suggest either using: motion blur, which works but is a bit of a hack and you have to process the thing after to get the correct effect, or duplicating, shifting and merging layers repeatedly, which also works but only on the axes or diagonal and is a little laborious So I thought in for penny, in for a pound and had a crack at writing my first plug-in to automate the job. I apologise if someone's already done this as I'm new to PdN. The plug-in is designed to work on an image on transparent background i.e. it doesn't detect white or any other colour as background and in particular if you pass it an image with no transparency there will be no effect! There are three parameters: Depth - how deep the offset fill is calculated at. The higher you put this value the slower the effect is to calculate as the code needs to check along the depth length for every output pixel (I need to have a ponder on whether this is optimal) Angle - the angle at which the shape is filled back at High Quality Mode - you'll get marginally better performance if you uncheck the box but the outer edges of the fill shape are more realistic if this mode is switched on. I put this in so the image was more responsive when you were playing around with angle and depth but to be honest I can't detect much difference in performance on my PC I find it most useful if you duplicate the original surface layer so that after you're done you've got a surface layer and a depth layer play around with separately. In the screenshots below I've done the following: got a bit of text rotated it (Layers -> Rotate / Zoom ...) duplicated the layer recoloured the top layer yellow used Add Depth on the bottom layer Here's the DLL: Add Depth Not sure if this bit's for the code development forum but I attach the CodeLab source code below. I'd appreciate it if anyone can point out how to make the thing more efficient or if I've got any basic errors going on. Pointers on better coding generally are welcomed as this is the first time I've tried out C++. // Title: Add Depth // Author: maccas // Submenu: Render // Name: Add Depth // URL: #region UICode int Amount1 = 10; // [1,100] Depth double Amount2 = 45; // [-180,180] Angle bool Amount3 = false; // [0,1] High Quality Mode #endregion // Global variables to hold the selection bounds (assumes a single rectangle is selected) // Saves having to call GetSelection().GetBoundsInt() with each call to GetTransPixel() private int maxX, maxY, minX, minY; // Routine to ensure intergrity of integer to byte conversion private byte Clamp2Byte(int iValue) { if (iValue < 0) return 0; if (iValue > 255) return 255; return (byte)iValue; } // Routine to ensure intergrity of double to byte conversion private byte Clamp2Byte(double dValue) { if (dValue < 0) return 0; if (dValue > 255) return 255; return (byte)Math.Round(dValue); } // Routine returns the translated pixel that is 'depth' away from [x,y] at an angle of 'angle' private ColorBgra GetTransPixel (Surface src, int x, int y, int depth, double angle) { // Declare initial variables double deltaX, deltaY; // Get the offset amounts // Reverse x offset as [0,0] is top left of canvas but 0 assumed to point horizontal right deltaX = -(double)depth * Math.Cos(angle); deltaY = (double)depth * Math.Sin(angle); // If the offset puts us outside the bounds of the selection then quit out if ((x + deltaX > maxX - 1) || (x + deltaX < minX + 1) || (y + deltaY > maxY - 1) || (y + deltaY < minY + 1)) { // Create pixel with hex colour #FFFFFF and full transparancy return ColorBgra.FromBgra((byte)255,(byte)255,(byte)255,(byte)0); } // Declare more variables as we're in range to do something int iDeltaX, iDeltaY; ColorBgra srcPixel1; // Round down the offsets plus 0.5 to get the principal offset pixel // using the 0.5 as we're measuring to the mid-point of pixels iDeltaX = (int)Math.Floor(deltaX + 0.5); iDeltaY = (int)Math.Floor(deltaY + 0.5); // Get the pricipal offset pixel srcPixel1 = src[x+iDeltaX,y+iDeltaY]; // Only mix the 4 pixels if we're working in high quality mode if (!Amount3) return srcPixel1; // Declare last batch of variables for the full monty calculation ColorBgra srcPixel2, srcPixel3, srcPixel4; double mixX, mixY; bool xUp; byte outR, outG, outB, outA; // Get the x offset pixel if ((deltaX - iDeltaX) > 0) { srcPixel2 = src[x+iDeltaX+1,y+iDeltaY]; mixX = 1 - (deltaX - iDeltaX); xUp = true; } else { srcPixel2 = src[x+iDeltaX-1,y+iDeltaY]; mixX = 1 + (deltaX - iDeltaX); xUp = false; } // Get the y and the xy offset pixel if ((deltaY - iDeltaY) > 0) { srcPixel3 = src[x+iDeltaX,y+iDeltaY+1]; mixY = 1 - (deltaY - iDeltaY); if (xUp) { srcPixel4 = src[x+iDeltaX+1,y+iDeltaY+1]; } else { srcPixel4 = src[x+iDeltaX-1,y+iDeltaY+1]; } } else { srcPixel3 = src[x+iDeltaX,y+iDeltaY-1]; mixY = 1 + (deltaY - iDeltaY); if (xUp) { srcPixel4 = src[x+iDeltaX+1,y+iDeltaY-1]; } else { srcPixel4 = src[x+iDeltaX-1,y+iDeltaY-1]; } } // Compute the output pixel as the mix of the four source pixels outB = Clamp2Byte(mixX*mixY*(double)srcPixel1.B + (1-mixX)*mixY*(double)srcPixel2.B + mixX*(1-mixY)*(double)srcPixel3.B + (1-mixX)*(1-mixY)*(double)srcPixel4.; outG = Clamp2Byte(mixX*mixY*(double)srcPixel1.G + (1-mixX)*mixY*(double)srcPixel2.G + mixX*(1-mixY)*(double)srcPixel3.G + (1-mixX)*(1-mixY)*(double)srcPixel4.G); outR = Clamp2Byte(mixX*mixY*(double)srcPixel1.R + (1-mixX)*mixY*(double)srcPixel2.R + mixX*(1-mixY)*(double)srcPixel3.R + (1-mixX)*(1-mixY)*(double)srcPixel4.R); outA = Clamp2Byte(mixX*mixY*(double)srcPixel1.A + (1-mixX)*mixY*(double)srcPixel2.A + mixX*(1-mixY)*(double)srcPixel3.A + (1-mixX)*(1-mixY)*(double)srcPixel4.A); // Make the Pixel and return the output return ColorBgra.FromBgra(outB,outG,outR,outA); } // Main routine void Render(Surface dst, Surface src, Rectangle rect) { // Record the dimensions of the environment to the global variables Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt(); minX = selection.Left; maxX = selection.Right; minY = selection.Top; maxY = selection.Bottom; // Convert degrees to radians double rad = 2*Math.PI*(Amount2/360); // Declare some variables we're going to need ColorBgra pixel; byte[] outputs = new byte[Amount1]; byte maxAlpha; for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { // Clear the output array & max alpha counter for (int d = 0; d < Amount1; d++) outputs[d] = 0; maxAlpha = 0; // Initialse pixel variable to stop compiler complaining when setting dst[x,y] // Not sure why I need to do this! pixel = ColorBgra.FromBgra((byte)255,(byte)255,(byte)255,(byte)0); // Loop through the offset distances from 1 to depth and // record the alpha of each translated pixel // stop if we get a fully opaque pixel for (int d = 0; d < Amount1; d++) { // Get the state of the translated pixel at depth d pixel = GetTransPixel (src, x, y, d, rad); outputs[d] = (byte)pixel.A; // Record the max alpha value and quit out if fully opaque if (outputs[d] > maxAlpha) maxAlpha = outputs[d]; if (maxAlpha == 255) break; } // Find the first pixel at Max Alpha // No point in running GetTransPixel() again if alpha is at max // as pixel variable already holds correct reference if (maxAlpha != 255) { for (int d = 0; d < Amount1; d++) { if (outputs[d] == maxAlpha) { pixel = GetTransPixel (src, x, y, d, rad); d = Amount1; } } } dst[x,y] = pixel; } } } I also thought it might be useful to get hold of the the bottom surface as a separate layer, which I've coded up as a different plug-in as Code Lab scripts only seem to be able to work within one layer. It's called Offset Shadow and is in the zip file above. In and of itself it's not much to write home about as you'd just duplicate and shift a layer normally but if you use the same settings as the Add Depth plug-in you get the pixel perfect bottom layer for good measure. Enjoy! Maccas