Reptillian Posted November 6, 2020 Share Posted November 6, 2020 (edited) Hi, yes we do have a filter which does exactly the above and this is actually a translation of my gmic filter. The only difference is this is extended with blending option and more modes. That being said, for some reason I am getting a null reference error. Not sure why. Spoiler // Name: Rotate By Torus Map // Submenu: Distortion // Author: Reptorian // Title: Rotation by Torus Map // Version: 1 // Desc: Rotates images using a Torus Map. // Keywords: distortion // URL: https://forums.getpaint.net/profile/85868-reptillian/ // Help: #region UICode DoubleSliderControl v_circ_a = 0; // [0,100] Circumference A (%) DoubleSliderControl v_circ_b = 0; // [0,100] Circumference B (%) AngleControl angle = 45; // [-180,180] Angle ListBoxControl distortmode = 0; // {!enable_distortion_blend} Distortion Mode|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal ListBoxControl distortmode_a = 0; // {enable_distortion_blend} Distortion Mode A|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal ListBoxControl distortmode_b = 0; // {enable_distortion_blend} Distortion Mode B|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal DoubleSliderControl v_distort_blend = 0; // [0,100] {enable_distortion_blend} Distortion Mode Blending (%) CheckboxControl enable_distortion_blend = true; // Enable Distortion Blending Mode? PanSliderControl distpos = Pair.Create(0.0,0.0); // Distortion Position CheckboxControl wraparound_mode = true; // Wraparound Distortion? CheckboxControl remove_background = false; // Remove Background? #endregion Surface Surface_Mapped; double ww,hh,cx,cy,offx,offy,eps,sx,sy,new_min,new_max,pi,distort_blend; int boundary; double vallim_x(double v){ return v - ww * Math.Floor(v/(ww+eps)); } double vallim_y(double v){ return v - hh * Math.Floor(v/(hh+eps)); } double start_x(double v){ if (wraparound_mode){ return vallim_x(v+offx);} else { return v+offx;} } double start_y(double v){ if (wraparound_mode){ return vallim_y(v+offy);} else { return v+offy;} } double nm(double v){ return (v-new_min)*(1/(new_max - new_min)); } double limcut(double v){ return Math.Max(0,Math.Min(1,v)); } int bndcut(double v){ return (v > 1 || v >=0 ) ? 1 : 0; } double ang2rad(double v){ return Math.PI * (v/180) ; } double rot_x(double a, double b, double c){ return a * Math.Cos(ang2rad(c)) - b * Math.Sin(ang2rad(c)); } double rot_y(double a, double b, double c){ return a * Math.Cos(ang2rad(c)) - b * Math.Sin(ang2rad(c)); } double sur_x(double v){ return (v/ww - .5) * 2 * sx; } double sur_y(double v){ return (v/hh - .5) * 2 * sy; } double unsur_x(double v){ return (v/(2*sx) + .5) * ww; } double unsur_y(double v){ return (v/(2*sy) + .5) * hh; } double lerp(double a,double b){ return a * distort_blend + b * (1 - distort_blend); } double mode_choice(double v,int mode){ switch(mode){ case 0: return (Math.Cos(v*(2*pi)-pi)+1)/2; case 1: return Math.Abs(Math.Cos(v*pi+pi/2)); case 2: return Math.Sqrt(1-Math.Pow(Math.Abs(v-.5)*2,2)); case 3: return Math.Pow(Math.Pow(1-(Math.Abs(v-.5)*2),2),1/(2+(1-v))); case 4: return Math.Cos(v*pi)*boundary; case 5: return Math.Cos(v*pi)*boundary*-1; case 6: return 1 - Math.Abs(v-.5) * 2; case 7: double r = (1-Math.Abs(v-.5)*2)*2; return r > 1 ? 1 : r ; } return 0; } double mode(double v){ if (enable_distortion_blend){ return lerp(distortmode_a,distortmode_b); } else{ return mode_choice(v,distortmode); } } void PreRender(Surface dst,Surface src) { int w= src.Width; int h = src.Height; double d_w=(double)(w); double d_h=(double)(h); double sd = Math.Max(d_w,d_h)/Math.Min(d_w,d_h); if (Surface_Mapped == null) { Surface_Mapped = new Surface(src.Size); } double circ_a = v_circ_a / 100; double circ_b = v_circ_b / 100; double maxang = angle * -1; sx = w > h ? sd : 1 ; sy = w > h ? 1 : sd ; ww = d_w - 1; hh = d_h - 1; cx = ww/2; cy = hh/2; offx = cx * distpos.First * -1; offy = cy * distpos.Second; eps = Math.Pow(10,-10); new_min=Math.Min(circ_a,circ_b); new_max=Math.Max(circ_a,circ_b); distort_blend = v_distort_blend / 100; pi = Math.PI; double xx,yy,radial_gradient,norm_gradient,z_depth,XX,YY,diff_x,diff_y; int Alpha; ColorBgra tempcolor; for (int y = 0; y < h ; y++){ if (IsCancelRequested) return; for (int x = 0 ; x < w ; x++){ xx=start_x((double)(x)); yy=start_y((double)(y)); xx=sur_x(xx); yy=sur_y(yy); radial_gradient=nm(Math.Sqrt(xx*xx+yy*yy)); norm_gradient=limcut(radial_gradient); boundary=bndcut(radial_gradient); z_depth=mode(norm_gradient)*maxang; XX=rot_x(xx,yy,z_depth); YY=rot_y(xx,yy,z_depth); diff_x=XX-xx; diff_y=YY-yy; tempcolor=src.GetBilinearSample(x,y); if (remove_background){ Alpha = tempcolor.A * boundary; Surface_Mapped[x,y]=ColorBgra.FromBgra(tempcolor.B,tempcolor.G,tempcolor.R,(byte)(Alpha)); } else{ Surface_Mapped[x,y] = tempcolor; } } } } void Render(Surface dst, Surface src, Rectangle rect) { for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { dst[x,y] = Surface_Mapped[x,y]; } } } Edited November 6, 2020 by Reptillian Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
toe_head2001 Posted November 6, 2020 Share Posted November 6, 2020 This is caused by a Regex bug in CodeLab. In the meantime, just add a space in between the PreRender parameters, and all your code will start working again. void PreRender(Surface dst,Surface src) Needs to change to: void PreRender(Surface dst, Surface src) Quote My Gallery | My Plugin Pack Layman's Guide to CodeLab Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 (edited) Thank you, I was able to finish up almost. Now, I definitely have a problem here. Mirror Boundary Code here does not work. I will ask @MJW for suggestions. EDIT: I found my solution. I had to change one part of a small code from hh to ww instead. It works. Edited November 7, 2020 by Reptillian Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 One change I believe you could make is to write directly to the destination surface in PreRender() instead of creating an auxiliary surface, and use a do-nothing Render() routine. I understand from some recent discussions that writing to the dst surface in PreRender(), even outside the clip bounds, is allowed. If you create an auxiliary service, it would probably be best to dispose it in an OnDispose() routine, though Rick Brewster has indicated that doesn't really need to be done. Quote Link to comment Share on other sites More sharing options...
BoltBait Posted November 7, 2020 Share Posted November 7, 2020 6 minutes ago, MJW said: write directly to the destination surface in PreRender() instead of creating an auxiliary surface, and use a do-nothing Render() routine. Do NOT do this. Quote Click to play: Download: BoltBait's Plugin Pack | CodeLab | and how about a Computer Dominos Game Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 (edited) 7 minutes ago, MJW said: One change I believe you could make is to write directly to the destination surface in PreRender() instead of creating an auxiliary surface, and use a do-nothing Render routine. I understand from some recent discussions that writing to the dst surface in PreRender(), even outside the clip bounds, is allowed. If you create an auxiliary service, it would probably be best to dispose it in an OnDispose() routine, though Rick Brewster has indicated that's doesn't really need to be done. I'm concerned about performance and the bug I experienced with the creation of Axis-Based Shift. It is something I will look into. I'm already done with the functionality part of the plugin. Spoiler // Name: Rotate By Torus Map // Submenu: Distortion // Author: Reptorian // Title: Rotation by Torus Map // Version: 1 // Desc: Rotates images using a Torus Map. // Keywords: distortion // URL: https://forums.getpaint.net/profile/85868-reptillian/ // Help: #region UICode DoubleSliderControl v_circ_a = 50; // [0,100] Circumference A (%) DoubleSliderControl v_circ_b = 100; // [0,100] Circumference B (%) AngleControl angle = 45; // [-180,180] Angle ListBoxControl distortmode = 3; // {!enable_distortion_blend} Distortion Mode|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal ListBoxControl distortmode_a = 0; // {enable_distortion_blend} Distortion Mode A|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal ListBoxControl distortmode_b = 3; // {enable_distortion_blend} Distortion Mode B|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal DoubleSliderControl v_distort_blend = 50; // [0,100] {enable_distortion_blend} Distortion Mode Blending (%) CheckboxControl enable_distortion_blend = true; // Enable Distortion Blending Mode? PanSliderControl distpos = Pair.Create(0.000, 0.000); // Distortion Position CheckboxControl wraparound_mode = true; // Wraparound Distortion? CheckboxControl remove_background = false; // Remove Background? ListBoxControl outofrange_boundary_choice = 0; // Boundary|None|Neumann|Periodic|Mirror #endregion Surface Surface_Mapped; double ww,hh,cx,cy,offx,offy,eps,sx,sy,new_min,new_max,pi,distort_blend; float f_w,f_h,f_ww,f_hh; int boundary; double vallim_x(double v){ return v - ww * Math.Floor(v/(ww+eps)); } double vallim_y(double v){ return v - hh * Math.Floor(v/(hh+eps)); } double start_x(double v){ if (wraparound_mode){ return vallim_x(v+offx);} else { return v+offx;} } double start_y(double v){ if (wraparound_mode){ return vallim_y(v+offy);} else { return v+offy;} } double nm(double v){ return (v-new_min)*(1/(new_max - new_min)); } double limcut(double v){ return Math.Max(0,Math.Min(1,v)); } int bndcut(double v){ return v>1||v<0?0:1; } double ang2rad(double v){ return Math.PI * (v/180) ; } double rot_x(double a, double b, double c){ return a * Math.Cos(ang2rad(c)) - b * Math.Sin(ang2rad(c)); } double rot_y(double a, double b, double c){ return a * Math.Sin(ang2rad(c)) + b * Math.Cos(ang2rad(c)); } double sur_x(double v){ return (v/ww - .5) * 2 * sx; } double sur_y(double v){ return (v/hh - .5) * 2 * sy; } double unsur_x(double v){ return (v/(2*sx) + .5) * ww; } double unsur_y(double v){ return (v/(2*sy) + .5) * hh; } double lerp(double a,double b){ return a * distort_blend + b * (1 - distort_blend); } float mod(float a,float b){ return a - b * (float)(Math.Floor(a/b)); } float neumann_x(float x){ return Math.Min(f_ww,Math.Max(x,0)); } float neumann_y(float y){ return Math.Min(f_hh,Math.Max(y,0)); } float periodic_x(float x){ return x-f_ww*(float)(Math.Floor(x/f_ww)); } float periodic_y(float y){ return y-f_hh*(float)(Math.Floor(y/f_hh)); } float mirror_x(float x){ float mx = mod(x,f_w*2); if (mx>=f_ww) {return 2*f_w - mx - 1;} else {return mx;} } float mirror_y(float y){ float my = mod(y,f_h*2); if (my>=f_hh) {return 2*f_h - my - 1;} else {return my;} } float out_posx(float posx){ switch(outofrange_boundary_choice){ case 0: return posx; case 1: return neumann_x(posx); case 2: return periodic_x(posx); case 3: return mirror_x(posx); } return 0; } float out_posy(float posy){ switch(outofrange_boundary_choice){ case 0: return posy; case 1: return neumann_y(posy); case 2: return periodic_y(posy); case 3: return mirror_y(posy); } return 0; } double mode_choice(double v,int mode){ switch(mode){ case 0: return (Math.Cos(v*(2*pi)-pi)+1)/2; case 1: return Math.Abs(Math.Cos(v*pi+pi/2)); case 2: return Math.Sqrt(1-Math.Pow(Math.Abs(v-.5)*2,2)); case 3: return Math.Pow(1-Math.Pow(Math.Abs(v-.5)*2,2),1/(2+(1-v))); case 4: return Math.Cos(v*pi)*boundary; case 5: return Math.Cos(v*pi)*boundary*-1; case 6: return 1 - Math.Abs(v-.5) * 2; case 7: double r = (1-Math.Abs(v-.5)*2)*2; return r > 1 ? 1 : r ; } return 0; } double mode(double v){ if (enable_distortion_blend){ return lerp(mode_choice(v,distortmode_a),mode_choice(v,distortmode_b)); } else{ return mode_choice(v,distortmode); } } void PreRender(Surface dst, Surface src) { int w= src.Width; int h = src.Height; f_w=(float)(w); f_h=(float)(h); f_ww=(float)(w)-1; f_hh=(float)(h)-1; double d_w=(double)(w); double d_h=(double)(h); double sd = Math.Max(d_w,d_h)/Math.Min(d_w,d_h); if (Surface_Mapped == null) { Surface_Mapped = new Surface(src.Size); } double circ_a = v_circ_a / 100; double circ_b = v_circ_b / 100; double maxang = angle; sx = w > h ? sd : 1 ; sy = w > h ? 1 : sd ; ww = d_w - 1; hh = d_h - 1; cx = ww/2; cy = hh/2; offx = cx * distpos.First * -1; offy = cy * distpos.Second; eps = Math.Pow(10,-10); new_min=Math.Min(circ_a,circ_b); new_max=Math.Max(circ_a,circ_b); distort_blend = v_distort_blend / 100; pi = Math.PI; double xx,yy,radial_gradient,norm_gradient,z_depth,XX,YY,diff_x,diff_y; float posx,posy; int Alpha; ColorBgra tempcolor; for (int y = 0; y < h ; y++){ if (IsCancelRequested) return; for (int x = 0 ; x < w ; x++){ xx=start_x((double)(x)); yy=start_y((double)(y)); xx=sur_x(xx); yy=sur_y(yy); radial_gradient=nm(Math.Sqrt(xx*xx+yy*yy)); norm_gradient=limcut(radial_gradient); boundary=bndcut(radial_gradient); z_depth=mode(norm_gradient)*maxang; XX=rot_x(xx,yy,z_depth); YY=rot_y(xx,yy,z_depth); XX=unsur_x(XX); YY=unsur_y(YY); xx=unsur_x(xx); yy=unsur_y(yy); diff_x=XX-xx; diff_y=YY-yy; posx=(float)(x)+(float)(diff_x); posy=(float)(y)+(float)(diff_y); posx=out_posx(posx); posy=out_posy(posy); tempcolor=src.GetBilinearSample(posx,posy); if (remove_background){ Alpha = tempcolor.A * boundary; Surface_Mapped[x,y]=ColorBgra.FromBgra(tempcolor.B,tempcolor.G,tempcolor.R,(byte)(Alpha)); } else{ Surface_Mapped[x,y] = tempcolor; } } } } void Render(Surface dst, Surface src, Rectangle rect) { for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { dst[x,y] = Surface_Mapped[x,y]; } } } EDIT: I think I will call it done after @BoltBait warning. Edited November 7, 2020 by Reptillian Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 I refer you to this comment by @toe_head2001, and those that follow. Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 5 minutes ago, MJW said: I refer you to this comment by @toe_head2001, and those that follow. Hmm, it seems that I am writing outside selection. I actually don't know how to fix that issue with my plugins. Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 6 minutes ago, Reptillian said: Hmm, it seems that I am writing outside selection. I actually don't know how to fix that issue with my plugins. Even if writing outside the selection is a problem (which the comment I linked to says it isn't), you aren't doing so. You are only writing to dst in Render(), and the Rectangle it is passed only includes selected pixels. Writing outside the selection in the aux surface could never be a problem. Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 6 minutes ago, MJW said: Even if writing outside the selection is a problem (which the comment I linked to says it isn't), you aren't doing so. You are only writing to dst in Render(), and the Rectangle it is passed only includes selected pixels. Writing outside the selection in the aux surface could never be a problem. Got it. One last thing, do you have any suggestion on speeding up plugin? It is a bit slow. Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 I'll take a look at it later an see if I can see any improvements. I've only glanced at it so far, but I'm somewhat surprised it's slow. Seemed like it was just a loop through all the pixels in PreRender(), with some straightforward computations, and a trivial Render(). I must have overlooked something. Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 Removing the if canceled on render helps and creating tyy for start_y(y) helps. I think that's all that can be done from my side of things. Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 6 minutes ago, Reptillian said: Removing the if canceled on render helps and creating tyy for start_y(y) helps. I think that's all that can be done from my side of things. That seems strange to me. I would expect those changes to have a negligible effect on the run time. Perhaps IsCancelRequested is for some reason inefficient in PreRender(), though I've used it there and never noticed a problem. It's only executed once per row. Quote Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 I could be missing something, but I don't see why the aux surface and the PreRender() computations using it can't be eliminated, and the computations moved to Render(). The same values computed for the aux surface are the ones needed for rendering in Render(). Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 34 minutes ago, MJW said: I could be missing something, but I don't see why the aux surface and the PreRender() computations using it can't be eliminated, and the computations moved to Render(). The same values computed for the aux surface are the ones needed for rendering in Render(). I tested it, even worse. Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted November 7, 2020 Share Posted November 7, 2020 @Reptillian When you publish this plugin, can you change the menu to Distort rather than creating a new sub-menu called Distortion? Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 12 minutes ago, Ego Eram Reputo said: @Reptillian When you publish this plugin, can you change the menu to Distort rather than creating a new sub-menu called Distortion? Consider it done when I do so. 1 Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 12 minutes ago, Reptillian said: I tested it, even worse. I don't know what's going on, but I can't see why the performance would be bad in the first place, and I can't see how that would make it noticeably worse. Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 @MJW I found the solution. It's a strange solution, but it took me a while to find the best code for it just like how it took me time to finish the Thorn Fractal. I took out xx=unsur(xx), and used nxx, and nyy to define that instead. It trimmed off the time it takes to calculate by more than half. I think I will publish it. nxx=start_x((double)(x)); nyy=tyy; xx=sur_x(nxx); yy=sur_y(nyy); radial_gradient=nm(Math.Sqrt(xx*xx+yy*yy)); norm_gradient=limcut(radial_gradient); boundary=bndcut(radial_gradient); z_depth=mode(norm_gradient)*maxang; XX=rot_x(xx,yy,z_depth); YY=rot_y(xx,yy,z_depth); XX=unsur_x(XX); YY=unsur_y(YY); diff_x=XX-nxx; diff_y=YY-nyy; posx=(float)(x)+(float)(diff_x); posy=(float)(y)+(float)(diff_y); posx=out_posx(posx); posy=out_posy(posy); tempcolor=src.GetBilinearSample(posx,posy); if (remove_background){ Alpha = tempcolor.A * boundary; Surface_Mapped[x,y]=ColorBgra.FromBgra(tempcolor.B,tempcolor.G,tempcolor.R,(byte)(Alpha)); } else{ Surface_Mapped[x,y] = tempcolor; } Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 Are you testing performance outside of CodeLab? Those performance issues strike me as the kind of thing that might happen in non-optimized debug code, but not in optimized code. I don't know how CodeLab handles optimization, but I wonder if the optimization is different when it's run under CodeLab then when the code is compiled and run as a separate plugin. Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 I am testing within CodeLab. I use Preview to verify Performance. Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 Perhaps @BoltBait will weigh in on the CodeLab optimization question. I know under Visual Studio, the Debug version often runs much slower than the Release version. Quote Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 1 hour ago, Reptillian said: I tested it, even worse. Even if it is worse, that's how it should be done. If it's done in PreRender(), it can't take advantage of multiprocessing. Also, the progress bar isn't updated correctly. Computing per-pixel data in PreRender() should only be done when the algorithm can't be implemented any other way. I believe (though I'm not certain) that Render() code executed within CodeLab will only run on one core. CodeLab is meant to develop the code, not run it in the most efficient manner. Quote Link to comment Share on other sites More sharing options...
Reptillian Posted November 7, 2020 Author Share Posted November 7, 2020 I just published the plugin. I'm not sure how it could take advantage of multiprocessing. Usually when I do image processing outside of CodeLab like g'mic or c++ with a established code with its own functions, I know that what I'm doing involves multiple calculation in different areas on different threads. I will try to change the code back to render, compile it and report back. Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
MJW Posted November 7, 2020 Share Posted November 7, 2020 3 minutes ago, Reptillian said: I'm not sure how it could take advantage of multiprocessing. Different ROIs are processed by different cores, simultaneously. You don't have to do anything besides include the pixel-processing code in Render() for that to happen. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.