Jump to content

Reptillian

Members
  • Posts

    1,239
  • Joined

  • Last visited

  • Days Won

    18

Posts posted by Reptillian

  1. I'm adding a new feature to Tiled Form. It is going to take a while as I will factor into the existence of alpha channel and cmyka.

     

    b187cf9315ac7abc896b81f9107f4ad13855090e

     

    The left is original, the middle left, the middle right is max, and the right is average.

     

    Also, here is how the min/max features work after getting some help from @G'MIC - https://discuss.pixls.us/t/return-maximum-value-coordinates-based-from-the-sum-of-channel-in-a-window-in-context-of-fill-block/21370/6

     

    • Like 1
  2. I had tried converting g'mic-qt `Tiled Form` which is under Testing->Reptorian into a paint.net plugin. Not able to code in shape, so I went with clipboard to create a proof of concept plugin.

     

    The pseudo code for this plugin

    1. Load Two Images (Target Image, Shape Image)

    2. On Shape Image, transformation are applied such as mirror x/mirror y. Then it is cropped and fitted inside each rectangles. Form ratio less than 1 will ensure shape boundary has a length on at least one dimension being always smaller than each rectangle. It should be normalized though that's for later.

    3. Create tile surface which will reflect only on x or y, or both or periodic mode.

    4. Find weighed averages for rectangle using shapes.

    5. Use Tile Surface to interpolate between weighed averages of all pixels that resides within rectangles. Then, it's done.

     

    For evidence to support this proof of concept, insert a shape into your clipboard. Black Background and white shape. Then run this plugin. You will see a different version of TR's Tiled Pixels.

     

    Some bugs still remain on this proof of concept though.

     

    Spoiler
    
    // Name:
    // Submenu:
    // Author:
    // Title:
    // Version:
    // Desc:
    // Keywords:
    // URL:
    // Help:
    #region UICode
    IntSliderControl shape_width = 30; // [4,100] Shape Width
    IntSliderControl shape_height = 30; // [4,100] Shape Height
    DoubleSliderControl shape_ratio = 90; // [0.1,100] Shape Ratio (%)
    AngleControl shape_rotation = 45; // [-180,180] Shape Rotation
    ListBoxControl shape_mirror = 0; // Shape Mirror Axis|None|X|Y
    ListBoxControl tile_boundary = 0; // Tile Boundary|Periodic|Mirror X|Mirror Y|Mirror XY
    #endregion
    #if DEBUG
    #endif
    
    int tile_width,tile_height;
    
    // Ref_Shape_Clipboard surface
    Surface Ref_Shape_Clipboard = null;
    Surface Rotated_Surface = null;
    Surface Crop_Surface = null;
    Surface Resized_Surface = null;
    Surface Inside_Shape_Multiply = null;
    Surface Outside_Shape_Multiply = null;
    Surface Mini_Inside_Shape_Multiply = null;
    Surface Mini_Outside_Shape_Multiply = null;
    
    int [,] Mini_Inside_Shape_R;
    int [,] Mini_Inside_Shape_G;
    int [,] Mini_Inside_Shape_B;
    int [,] Mini_Inside_Shape_A;
    int [,] Mini_Outside_Shape_R;
    int [,] Mini_Outside_Shape_G;
    int [,] Mini_Outside_Shape_B;
    int [,] Mini_Outside_Shape_A;
    
    float [,] End_Surface;
    float [,] Tile_Surface;
    
    private Surface clipboardSurface = null;
    private bool readClipboard = false;
    private bool activate_fill = true;
    bool activate_pixelate_mode;
    
    protected override void OnDispose(bool disposing)
    {
        if (disposing)
        {
            // Release any surfaces or effects you've created
            if (Ref_Shape_Clipboard != null) Ref_Shape_Clipboard.Dispose();
            Ref_Shape_Clipboard = null;
            if (clipboardSurface != null) clipboardSurface.Dispose();
            clipboardSurface = null;
        }
    
        base.OnDispose(disposing);
    }
    
    // This single-threaded function is called after the UI changes and before the Render function is called
    // The purpose is to prepare anything you'll need in the Render function
    void PreRender(Surface dst, Surface src)
    {
        int w = src.Width;
        int h = src.Height;
        int ww = w - 1;
        int hh = h - 1;
        double f_w = (double)(w);
        double f_h = (double)(h);
        int mini_width = (int)(Math.Ceiling(f_w/(double)(shape_width)));
        int mini_height = (int)(Math.Ceiling(f_h/(double)(shape_height)));
        int nearest_width = mini_width*shape_width;
        int nearest_height = mini_height*shape_height;
        double f_ww,f_hh,f_nw,f_nh,half_f_nw,half_f_nh,f_x,f_y,nx,ny,f_form_w,f_form_h;
        int nw,nh,nwx,nhy,min_x,max_x,min_y,max_y,crop_w,crop_h,form_w,form_h,off_x,off_y,end_off_x,end_off_y,tx,ty,sx,sy,posx,posy;
        double abs_shape_rotation = Math.Abs(shape_rotation);
        double ang = Math.PI * shape_rotation/180;
        double pos_ang = Math.Abs(Math.PI * shape_rotation/180);
        double absang = Math.Abs(pos_ang);
        double cos_ang = Math.Cos(ang);
        double sin_ang = Math.Sin(ang);
        double pos_cos_ang = Math.Cos(pos_ang);
        double pos_sin_ang = Math.Sin(pos_ang);
        double half_pi = Math.PI / 2;
        double alt_pos_cos_ang = Math.Cos(pos_ang - half_pi);
        double alt_pos_sin_ang = Math.Sin(pos_ang - half_pi);
        bool use_180;
        bool neg_90 = shape_rotation == -90;
        ColorBgra Rotated_Surface_Color;
        ColorBgra Resized_Surface_Color;
        ColorBgra Current_Color;
        int R,G,B,A;
        float f_offx,f_offy,F_AVG,F_R,F_G,F_B,F_A,shade,t_ir,t_ig,t_ib,t_ia,t_or,t_og,t_ob,t_oa;
        float w_avg = 0;
        float i_w_avg=0;
        activate_pixelate_mode = false;
    
        Mini_Inside_Shape_R = new int [mini_width,mini_height];
        Mini_Inside_Shape_G = new int [mini_width,mini_height];
        Mini_Inside_Shape_B = new int [mini_width,mini_height];
        Mini_Inside_Shape_A = new int [mini_width,mini_height];
        Mini_Outside_Shape_R= new int [mini_width,mini_height];
        Mini_Outside_Shape_G= new int [mini_width,mini_height];
        Mini_Outside_Shape_B= new int [mini_width,mini_height];
        Mini_Outside_Shape_A= new int [mini_width,mini_height];
    
        if (abs_shape_rotation==180) { use_180 = true;}
        else {use_180 = false;}
    
        if (Ref_Shape_Clipboard == null)
        {
            Ref_Shape_Clipboard = new Surface(src.Size);
        }
    
        if (!readClipboard)
        {
            readClipboard = true;
            clipboardSurface = Services.GetService<IClipboardService>().TryGetSurface();
        }
    
        // Copy from the Clipboard to the Ref_Shape_Clipboard surface
        if (readClipboard){
            for (int y = 0; y < Ref_Shape_Clipboard.Size.Height; y++)
            {
                if (IsCancelRequested) return;
                for (int x = 0; x < Ref_Shape_Clipboard.Size.Width; x++)
                {
                    if (clipboardSurface != null)
                    {
                        switch(shape_mirror){
                            case 0:
                                Ref_Shape_Clipboard[x,y] = clipboardSurface.GetBilinearSampleWrapped(x,y);
                                break;
                            case 1:
                                Ref_Shape_Clipboard[x,y] = clipboardSurface.GetBilinearSampleWrapped(ww-x,y);
                                break;
                            case 2:
                                Ref_Shape_Clipboard[x,y] = clipboardSurface.GetBilinearSampleWrapped(x,hh-y);
                                break;
                    
                        }
                    }
    
                    else
                    {
                    Ref_Shape_Clipboard[x,y] = Color.Transparent;
                    }
                }
            }   
        }
    
        //Copy rotated clipboard image from rotated clipboardsurface into rotated_surface.
        if (abs_shape_rotation>0 && abs_shape_rotation!=180){
            if(abs_shape_rotation==90){
                nw = h; 
                nh = w;
                nwx = nw - 1;
                nhy = nh - 1;
                Rotated_Surface = new Surface(nw,nh);
                
                for (int y = 0 ; y < nh ; y++){
                    for(int x = 0 ; x < nw ; x++){
                        if (neg_90) {Rotated_Surface[x,y] = Ref_Shape_Clipboard[nhy-y,nwx-x];}
                        else {Rotated_Surface[x,y] = Ref_Shape_Clipboard[y,x];}
                    }
                }
            }
            else{
                if(abs_shape_rotation<90){
                    f_nw = Math.Abs(f_w*pos_cos_ang+f_h*pos_sin_ang);
                    f_nh = Math.Abs(f_w*pos_sin_ang+f_h*pos_cos_ang);
                }
                else{
                    f_nw = Math.Abs(f_h*alt_pos_cos_ang+f_w*alt_pos_sin_ang);
                    f_nh = Math.Abs(f_h*alt_pos_sin_ang+f_w*alt_pos_cos_ang);
    
                }
                nw = (int)(Math.Round(f_nw));
                nh = (int)(Math.Round(f_nh));
                Debug.WriteLine(nw);
                Debug.WriteLine(nh);
                half_f_nw = f_nw / 2;
                half_f_nh = f_nh / 2;
                f_ww=(double)(nw-1);
                f_hh=(double)(nh-1);
                Rotated_Surface = new Surface(nw,nh);
                for (int y = 0 ; y < nh ; y++){
                    f_y = (double)(y)-half_f_nh;
                    for(int x = 0 ; x < nw ; x++){
                        f_x = (double)(x)-half_f_nw;
                        nx = f_x * cos_ang - f_y * sin_ang + half_f_nw;
                        ny = f_x * sin_ang + f_y * cos_ang + half_f_nh;
                        if((nx>=0&&nx<=f_ww)&&(ny>=0&&ny<=f_hh)){
                            Rotated_Surface[x,y]=Ref_Shape_Clipboard.GetBilinearSample((float)(nx),(float)(ny));
                        }
                        else {Rotated_Surface[x,y]=ColorBgra.Transparent;}
                    }
                }
            }
        }
        else{
            nw = w;
            nh = h;
            nwx = nw - 1;
            nhy = nh - 1;
            Rotated_Surface = new Surface(nw,nh);
    
            for (int y = 0 ; y < nh ; y++){
                for(int x = 0 ; x < nw ; x++){
                    if (use_180){Rotated_Surface[x,y] = Ref_Shape_Clipboard[nwx-x,nhy-y];}
                    else{Rotated_Surface[x,y] = Ref_Shape_Clipboard[x,y];}
                }
            }
    
        }
        nwx = nw - 1;
        nhy = nh - 1;
    
        //find(min_x)
        min_x = nw-1;
        for (int y = 0 ; y < nh ; y++){
            for (int x = 0; x < nw ; x++){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(x<min_x){min_x=x;break;}
                    }
                }
            }
            if(min_x==0){break;}
        }
    
        //find(max_x)
        max_x = 0;
        for (int y = 0 ; y < nh ; y++){
            for (int x = nwx; x >= 0 ; x--){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(x>max_x){max_x=x;break;}
                    }
                }
            }
            if(max_x==nwx){break;}
        }
    
        //find(min_y)
        min_y=nh-1;
        for (int x = 0 ; x < nw ; x++){
            for (int y = 0 ; y < nh ; y++){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(y<min_y){min_y=y;break;}
                    }
                }
            }
            if(min_y==0){break;}
        }
    
        //find(min_y)
        max_y=0;
        for (int x = 0 ; x < nw ; x++){
            for (int y = nhy ; y >= 0 ; y--){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(y>max_y){max_y=y;break;}
                    }
                }
            }
            if(max_y==nhy){break;}
        }
        crop_w = max_x - min_x + 1;
        crop_h = max_y - min_y + 1;
    
        if (crop_w>0){
            Crop_Surface = new Surface(crop_w,crop_h);
            for (int x=0 ; x < crop_w ; x++){
                for(int y=0 ; y < crop_h ; y++){
                    Crop_Surface[x,y]=Rotated_Surface.GetPoint(x+min_x,y+min_y);
                }
            }
            if (shape_ratio<100){
                f_form_w = (double)(shape_width) * shape_ratio/100;
                f_form_h = (double)(shape_height) * shape_ratio/100;
                form_w = (int)(f_form_w);
                form_h = (int)(f_form_h);
                Resized_Surface = new Surface(form_w,form_h);
                Resized_Surface.FitSurface(ResamplingAlgorithm.Bicubic,Crop_Surface);
                f_offx = (float)(shape_width) - (float)(f_form_w);
                f_offy = (float)(shape_height) - (float)(f_form_h);
                f_offx/=2;
                f_offy/=2;
                off_x = (int)(Math.Round(f_offx));
                off_y = (int)(Math.Round(f_offy));
                Debug.WriteLine(off_x);
                end_off_x = off_x + form_w ;
                end_off_y = off_y + form_h ;
                End_Surface = new float[shape_width,shape_height];
                for (int x=0 ; x < shape_width ; x++){
                   for (int y=0 ; y< shape_height ; y++){
                       if((x>=off_x&&x<end_off_x)&&(y>=off_x&&y<end_off_x)){
                        Resized_Surface_Color = Resized_Surface[x-off_x,y-off_y];
                        F_R = (float)(Resized_Surface_Color.R);
                        F_G = (float)(Resized_Surface_Color.G);
                        F_B = (float)(Resized_Surface_Color.B);
                        F_A = (float)(Resized_Surface_Color.A)/255;
                        F_AVG = (F_R+F_G+F_B)/765;
                        shade = F_AVG * F_A;
                        End_Surface[x,y] = shade;
                        w_avg+=shade;
                       }
                       else{
                           End_Surface[x,y]=0;
                       }
                   }
                }
                w_avg = w_avg / ((float)(shape_width) * (float)(shape_height));
                i_w_avg=1-w_avg;
            }
            else{
                Resized_Surface = new Surface(shape_width,shape_height);
                Resized_Surface.FitSurface(ResamplingAlgorithm.Bicubic,Crop_Surface);
                End_Surface = new float[shape_width,shape_height];
                for (int x=0 ; x < shape_width ; x++){
                    for (int y=0 ; y< shape_height ; y++){
                        Resized_Surface_Color = Resized_Surface[x,y];
                        F_R = (float)(Resized_Surface_Color.R);
                        F_G = (float)(Resized_Surface_Color.G);
                        F_B = (float)(Resized_Surface_Color.B);
                        F_A = (float)(Resized_Surface_Color.A)/255;
                        F_AVG = (F_R+F_G+F_B)/765;
                        shade = F_AVG * F_A;
                        End_Surface[x,y] = shade;
                        w_avg+=shade;
                    }
                }
                w_avg = w_avg / ((float)(shape_width) * (float)(shape_height));
                i_w_avg=1-w_avg;
            }
            tile_width = shape_width;
            tile_height = shape_height;
            switch(tile_boundary){
                case 0:
                    Tile_Surface = End_Surface;
                    break;
                case 1:
                    tile_width = shape_width*2;
                    tile_height = shape_height;
                    Tile_Surface = new float [tile_width,tile_height];
                    for (int x = 0 ; x < tile_width ; x++){
                        if(x < shape_width){tx = x;}
                        else { tx = (shape_width - 1) - x % shape_width;}
                        for (int y = 0 ; y < tile_height ; y++){
                            Tile_Surface[x,y] = End_Surface[tx,y];
                        }
                    }
                    break;
                case 2:
                    tile_width = shape_width;
                    tile_height = shape_height*2;
                    Tile_Surface = new float [tile_width,tile_height];
                    for (int y = 0 ; y < tile_height ; y++){
                        if(y < shape_height){ty = y;}
                        else { ty = (shape_height - 1) - y % shape_height;}
                        for (int x = 0 ; x < tile_width ; x++){
                            Tile_Surface[x,y] = End_Surface[x,ty];
                        }
                    }
                    break;
                case 3:
                    tile_width = shape_width*2;
                    tile_height = shape_height*2;
                    Tile_Surface = new float [tile_width,tile_height];
                    for (int y = 0 ; y < tile_height ; y++){
                        if(y < shape_height){ty = y;}
                        else { ty = (shape_height - 1) - y % shape_height;}
                        for (int x = 0 ; x < tile_width ; x++){
                            if(x < shape_width){tx = x;}
                            else { tx = (shape_width - 1) - x % shape_width;}
                            Tile_Surface[x,y] = End_Surface[tx,ty];
                        }
                    }
                    break;
            }
            Inside_Shape_Multiply = new Surface(nearest_width,nearest_height);
            for ( int y = 0 ; y < nearest_height ; y++){
                sy = Math.Min(hh,y);
                for (int x = 0 ; x < nearest_width ; x++){
                    sx = Math.Min(ww,x);
                    Inside_Shape_Multiply[x,y]=src[sx,sy];
                }
            }
            Outside_Shape_Multiply = Inside_Shape_Multiply;
            for (int y = 0 ; y < nearest_height ; y++){
                for (int x = 0; x < nearest_width ; x++){
                    Current_Color = Inside_Shape_Multiply[x,y];
                    t_ir = (float)(Current_Color.R) * Tile_Surface[x%tile_width,y%tile_height];
                    t_ig = (float)(Current_Color.G) * Tile_Surface[x%tile_width,y%tile_height];
                    t_ib = (float)(Current_Color.B) * Tile_Surface[x%tile_width,y%tile_height];
                    t_ia = (float)(Current_Color.A) * Tile_Surface[x%tile_width,y%tile_height];
                    t_or = (float)(Current_Color.R) * (1-Tile_Surface[x%tile_width,y%tile_height]);
                    t_og = (float)(Current_Color.G) * (1-Tile_Surface[x%tile_width,y%tile_height]);
                    t_ob = (float)(Current_Color.B) * (1-Tile_Surface[x%tile_width,y%tile_height]);
                    t_oa = (float)(Current_Color.A) * (1-Tile_Surface[x%tile_width,y%tile_height]);
                    posx = x / shape_width; posy = y / shape_height;
                    Mini_Inside_Shape_R[posx,posy]+=(int)(Math.Round(t_ir));
                    Mini_Inside_Shape_G[posx,posy]+=(int)(Math.Round(t_ig));
                    Mini_Inside_Shape_B[posx,posy]+=(int)(Math.Round(t_ib));
                    Mini_Inside_Shape_A[posx,posy]+=(int)(Math.Round(t_ia));
                    Mini_Outside_Shape_R[posx,posy]+=(int)(Math.Round(t_or));
                    Mini_Outside_Shape_G[posx,posy]+=(int)(Math.Round(t_og));
                    Mini_Outside_Shape_B[posx,posy]+=(int)(Math.Round(t_ob));
                    Mini_Outside_Shape_A[posx,posy]+=(int)(Math.Round(t_oa));
                }
            }
            float d_shape_area = (float)(shape_width)*(float)(shape_height)*w_avg;
            float d_shape_area_2 = (float)(shape_width)*(float)(shape_height)*i_w_avg;
            for (int y = 0 ; y < mini_height ; y++){
                for (int x = 0 ; x < mini_width ; x++){
                    Mini_Inside_Shape_R[x,y]=(int)(Math.Round((float)(Mini_Inside_Shape_R[x,y])/(d_shape_area)));
                    Mini_Inside_Shape_G[x,y]=(int)(Math.Round((float)(Mini_Inside_Shape_G[x,y])/(d_shape_area)));
                    Mini_Inside_Shape_B[x,y]=(int)(Math.Round((float)(Mini_Inside_Shape_B[x,y])/(d_shape_area)));
                    Mini_Inside_Shape_A[x,y]=(int)(Math.Round((float)(Mini_Inside_Shape_A[x,y])/(d_shape_area)));
                    Mini_Outside_Shape_R[x,y]=(int)(Math.Round((float)(Mini_Outside_Shape_R[x,y])/(d_shape_area_2)));
                    Mini_Outside_Shape_G[x,y]=(int)(Math.Round((float)(Mini_Outside_Shape_G[x,y])/(d_shape_area_2)));
                    Mini_Outside_Shape_B[x,y]=(int)(Math.Round((float)(Mini_Outside_Shape_B[x,y])/(d_shape_area_2)));
                    Mini_Outside_Shape_A[x,y]=(int)(Math.Round((float)(Mini_Outside_Shape_A[x,y])/(d_shape_area_2)));
                }
            }
        }
        else{
            activate_pixelate_mode = true;
            int shape_area = shape_width*shape_height;
            int current_x,current_y,window_x,window_y;
            ColorBgra Window_Color;
            for (int y = 0 ; y < mini_height ; y++){
                current_y = y * shape_height;
                for (int x = 0 ; x < mini_width ; x++){
                    current_x = x * shape_width;
                    for (int py = 0 ; py < shape_height ; py++){
                        window_y = Math.Min(hh,py+current_y);
                        for (int px = 0 ; px < shape_width ; px++){
                            window_x = Math.Min(ww,px+current_x);
                            Window_Color = src[window_x,window_y];
                            Mini_Inside_Shape_R[x,y]+=Window_Color.R;
                            Mini_Inside_Shape_G[x,y]+=Window_Color.G;
                            Mini_Inside_Shape_B[x,y]+=Window_Color.B;
                            Mini_Inside_Shape_A[x,y]+=Window_Color.A;
                        }
                    }
                    Mini_Inside_Shape_R[x,y]/=shape_area;
                    Mini_Inside_Shape_G[x,y]/=shape_area;
                    Mini_Inside_Shape_B[x,y]/=shape_area;
                    Mini_Inside_Shape_A[x,y]/=shape_area;
                }
            }
        }
    }
    
    // Here is the main multi-threaded render function
    // The dst canvas is broken up into rectangles and
    // your job is to write to each pixel of that rectangle
    void Render(Surface dst, Surface src, Rectangle rect)
    {
        float i_r,i_g,i_b,i_a,o_r,o_g,o_b,o_a,r,g,b,a;
        bool debug_mode=false;
        Debug.WriteLine(activate_pixelate_mode);
        // Step through each row of the current rectangle
        for (int y = rect.Top; y < rect.Bottom; y++)
        {
            if (IsCancelRequested) return;
            for (int x = rect.Left; x < rect.Right; x++)
            {
                if (debug_mode){
                    dst[x,y]=Rotated_Surface[x,y];
                }
                else
                {
                if (activate_pixelate_mode){
                    dst[x,y]=ColorBgra.FromBgraClamped(Mini_Inside_Shape_B[x/shape_width,y/shape_height],Mini_Inside_Shape_G[x/shape_width,y/shape_height],Mini_Inside_Shape_R[x/shape_width,y/shape_height],Mini_Inside_Shape_A[x/shape_width,y/shape_height]);
                }
                else{
                    i_r=(float)(Mini_Inside_Shape_R[x/shape_width,y/shape_height]);
                    i_g=(float)(Mini_Inside_Shape_G[x/shape_width,y/shape_height]);
                    i_b=(float)(Mini_Inside_Shape_B[x/shape_width,y/shape_height]);
                    i_a=(float)(Mini_Inside_Shape_A[x/shape_width,y/shape_height]);
                    o_r=(float)(Mini_Outside_Shape_R[x/shape_width,y/shape_height]);
                    o_g=(float)(Mini_Outside_Shape_G[x/shape_width,y/shape_height]);
                    o_b=(float)(Mini_Outside_Shape_B[x/shape_width,y/shape_height]);
                    o_a=(float)(Mini_Outside_Shape_A[x/shape_width,y/shape_height]);
                    r=i_r*Tile_Surface[x%tile_width,y%tile_height]+o_r*(1-Tile_Surface[x%tile_width,y%tile_height]);
                    g=i_g*Tile_Surface[x%tile_width,y%tile_height]+o_g*(1-Tile_Surface[x%tile_width,y%tile_height]);
                    b=i_b*Tile_Surface[x%tile_width,y%tile_height]+o_b*(1-Tile_Surface[x%tile_width,y%tile_height]);
                    a=i_a*Tile_Surface[x%tile_width,y%tile_height]+o_a*(1-Tile_Surface[x%tile_width,y%tile_height]);
                    dst[x,y]=ColorBgra.FromBgraClamped((int)(Math.Round(b)),(int)(Math.Round(g)),(int)(Math.Round(r)),(int)(Math.Round(a)));
                }
                }
            }
        }
    }

     

     

  3. I think I"m going with clipboard surface. Looks like the coding to do all of what I want with a shape is exceedingly difficult. See here.

     

    Note : It's not finished, and I'm not adding shapes, but this is to demonstrate.

     

    Spoiler
    
    // Name:
    // Submenu:
    // Author:
    // Title:
    // Version:
    // Desc:
    // Keywords:
    // URL:
    // Help:
    #region UICode
    IntSliderControl shape_width = 30; // [4,100] Shape Width
    IntSliderControl shape_height = 30; // [4,100] Shape Height
    DoubleSliderControl shape_ratio = 100; // [0.1,100] Shape Ratio (%)
    AngleControl shape_rotation = 45; // [-180,180] Shape Rotation
    ListBoxControl shape_mirror = 0; // Shape Mirror Axis|X|Y
    ListBoxControl tile_boundary = 0; // Tile Boundary|Periodic|Mirror X|Mirror Y|Mirror XY
    CheckboxControl fit_tile = true; // Fit Tile?
    #endregion
    
    // Ref_Shape_Clipboard surface
    Surface Ref_Shape_Clipboard = null;
    Surface Rotated_Surface = null;
    Surface Crop_Surface = null;
    Surface Resized_Surface = null;
    Surface Inside_Shape_Multiply = null;
    Surface Outside_Shape_Multiply = null;
    Surface Mini_Inside_Shape_Multiply = null;
    Surface Mini_Outside_Shape_Multiply = null;
    
    int [,] Mini_Inside_Shape_R;
    int [,] Mini_Inside_Shape_G;
    int [,] Mini_Inside_Shape_B;
    int [,] Mini_Inside_Shape_A;
    int [,] Mini_Outside_Shape_R;
    int [,] Mini_Outside_Shape_G;
    int [,] Mini_Outside_Shape_B;
    int [,] Mini_Outside_Shape_A;
    
    float [,] End_Surface;
    float [,] Tile_Surface;
    
    private Surface clipboardSurface = null;
    private bool readClipboard = false;
    private bool activate_fill = true;
    bool activate_pixelate_mode;
    
    protected override void OnDispose(bool disposing)
    {
        if (disposing)
        {
            // Release any surfaces or effects you've created
            if (Ref_Shape_Clipboard != null) Ref_Shape_Clipboard.Dispose();
            Ref_Shape_Clipboard = null;
            if (clipboardSurface != null) clipboardSurface.Dispose();
            clipboardSurface = null;
        }
    
        base.OnDispose(disposing);
    }
    
    // This single-threaded function is called after the UI changes and before the Render function is called
    // The purpose is to prepare anything you'll need in the Render function
    void PreRender(Surface dst, Surface src)
    {
        int w = src.Width;
        int h = src.Height;
        int ww = w - 1;
        int hh = h - 1;
        double f_w = (double)(w);
        double f_h = (double)(h);
        int mini_width = (int)(Math.Ceiling(f_w/(double)(shape_width)));
        int mini_height = (int)(Math.Ceiling(f_h/(double)(shape_height)));
        int nearest_width = mini_width*shape_width;
        int nearest_height = mini_height*shape_height;
        double f_ww,f_hh,f_nw,f_nh,half_f_nw,half_f_nh,f_x,f_y,nx,ny,f_form_w,f_form_h;
        int nw,nh,nwx,nhy,min_x,max_x,min_y,max_y,crop_w,crop_h,form_w,form_h,off_x,off_y,end_off_x,end_off_y,tx,ty,tile_width,tile_height,sx,sy;
        double abs_shape_rotation = Math.Abs(shape_rotation);
        double ang = Math.PI * shape_rotation/180;
        double pos_ang = Math.PI * shape_rotation/180;
        double absang = Math.Abs(pos_ang);
        double cos_ang = Math.Cos(ang);
        double sin_ang = Math.Sin(ang);
        double pos_cos_ang = Math.Cos(pos_ang);
        double pos_sin_ang = Math.Sin(pos_ang);
        double half_pi = Math.PI / 2;
        double alt_pos_cos_ang = Math.Cos(pos_ang - half_pi);
        double alt_pos_sin_ang = Math.Sin(pos_ang - half_pi);
        bool use_180;
        bool neg_90 = shape_rotation == -90;
        ColorBgra Rotated_Surface_Color;
        ColorBgra Resized_Surface_Color;
        int R,G,B,A;
        float f_offx,f_offy,F_AVG,F_R,F_G,F_B,F_A,shade;
        float w_avg = 0;
        activate_pixelate_mode = false;
    
        Mini_Inside_Shape_R = new int [mini_width,mini_height];
        Mini_Inside_Shape_G = new int [mini_width,mini_height];
        Mini_Inside_Shape_B = new int [mini_width,mini_height];
        Mini_Inside_Shape_A = new int [mini_width,mini_height];
        Mini_Outside_Shape_R= new int [mini_width,mini_height];
        Mini_Outside_Shape_G= new int [mini_width,mini_height];
        Mini_Outside_Shape_B= new int [mini_width,mini_height];
        Mini_Outside_Shape_A= new int [mini_width,mini_height];
    
        if (abs_shape_rotation==180) { use_180 = true;}
        else {use_180 = false;}
    
        if (Ref_Shape_Clipboard == null)
        {
            Ref_Shape_Clipboard = new Surface(src.Size);
        }
    
        if (!readClipboard)
        {
            readClipboard = true;
            clipboardSurface = Services.GetService<IClipboardService>().TryGetSurface();
        }
    
        // Copy from the Clipboard to the Ref_Shape_Clipboard surface
        if (activate_fill){
            for (int y = 0; y < Ref_Shape_Clipboard.Size.Height; y++)
            {
                if (IsCancelRequested) return;
                for (int x = 0; x < Ref_Shape_Clipboard.Size.Width; x++)
                {
                    if (clipboardSurface != null)
                    {
                        switch(shape_mirror){
                            case 0:
                                Ref_Shape_Clipboard[x,y] = clipboardSurface.GetBilinearSampleWrapped(x,y);
                                break;
                            case 1:
                                Ref_Shape_Clipboard[x,y] = clipboardSurface.GetBilinearSampleWrapped(ww-x,y);
                                break;
                            case 2:
                                Ref_Shape_Clipboard[x,y] = clipboardSurface.GetBilinearSampleWrapped(x,hh-y);
                                break;
                    
                        }
                    }
    
                    else
                    {
                    Ref_Shape_Clipboard[x,y] = Color.Transparent;
                    }
                }
            }   
            activate_fill = false;
        }
    
        //Copy rotated clipboard image from rotated clipboardsurface into rotated_surface.
        if (abs_shape_rotation>0 && abs_shape_rotation!=180){
            if(abs_shape_rotation==90){
                nw = h; 
                nh = w;
                nwx = nw - 1;
                nhy = nh - 1;
                Rotated_Surface = new Surface(nw,nh);
                
                for (int y = 0 ; y < nh ; y++){
                    for(int x = 0 ; x < nw ; x++){
                        if (neg_90) {Rotated_Surface[x,y] = Ref_Shape_Clipboard[nhy-y,nwx-x];}
                        else {Rotated_Surface[x,y] = Ref_Shape_Clipboard[y,x];}
                    }
                }
            }
            else{
                if(abs_shape_rotation<90){
                    f_nw = Math.Abs(f_w*pos_cos_ang+f_h*pos_sin_ang);
                    f_nh = Math.Abs(f_w*pos_sin_ang+f_h*pos_cos_ang);
                }
                else{
                    f_nw = Math.Abs(f_h*alt_pos_cos_ang+f_w*alt_pos_sin_ang);
                    f_nh = Math.Abs(f_h*alt_pos_sin_ang+f_w*alt_pos_cos_ang);
    
                }
                nw = (int)(Math.Round(f_nw));
                nh = (int)(Math.Round(f_nh));
                half_f_nw = f_nw / 2;
                half_f_nh = f_nh / 2;
                f_ww=(double)(nw-1);
                f_hh=(double)(nh-1);
                Rotated_Surface = new Surface(nw,nh);
                for (int y = 0 ; y < nh ; y++){
                    f_y = (double)(y)-half_f_nh;
                    for(int x = 0 ; x < nw ; x++){
                        f_x = (double)(x)-half_f_nw;
                        nx = f_x * cos_ang - f_y * sin_ang + half_f_nw;
                        ny = f_x * sin_ang + f_y * cos_ang + half_f_nh;
                        if((nx>=0&&nx<=f_ww)&&(ny>=0&&ny<=f_hh)){
                            Rotated_Surface[x,y]=Ref_Shape_Clipboard.GetBilinearSample((float)(nx),(float)(ny));
                        }
                        else {Rotated_Surface[x,y]=ColorBgra.Transparent;}
                    }
                }
            }
        }
        else{
            nw = w;
            nh = h;
            nwx = nw - 1;
            nhy = nh - 1;
            Rotated_Surface = new Surface(nw,nh);
    
            for (int y = 0 ; y < nh ; y++){
                for(int x = 0 ; x < nw ; x++){
                    if (use_180){Rotated_Surface[x,y] = Ref_Shape_Clipboard[nwx-x,nhy-y];}
                    else{Rotated_Surface[x,y] = Ref_Shape_Clipboard[x,y];}
                }
            }
    
        }
        nwx = nw - 1;
        nhy = nh - 1;
    
        //find(min_x)
        min_x = nw-1;
        for (int y = 0 ; y < nh ; y++){
            for (int x = 0; x < nw ; x++){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(x<min_x){min_x=x;break;}
                    }
                }
            }
            if(min_x==0){break;}
        }
    
        //find(max_x)
        max_x = 0;
        for (int y = 0 ; y < nh ; y++){
            for (int x = nwx; x >= 0 ; x--){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(x>max_x){max_x=x;break;}
                    }
                }
            }
            if(max_x==nwx){break;}
        }
    
        //find(min_y)
        min_y=nh-1;
        for (int x = 0 ; x < nw ; x++){
            for (int y = 0 ; y < nh ; y++){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(y<min_y){min_y=y;break;}
                    }
                }
            }
            if(min_y==0){break;}
        }
    
        //find(min_y)
        max_y=0;
        for (int x = 0 ; x < nw ; x++){
            for (int y = nhy ; y >= 0 ; y--){
                Rotated_Surface_Color = Rotated_Surface[x,y];
                A = Rotated_Surface_Color.A;
                if(A>0){
                    R=Rotated_Surface_Color.R;
                    G=Rotated_Surface_Color.G;
                    B=Rotated_Surface_Color.B;
                    if(R>0||G>0||B>0){
                        if(y>max_y){max_y=y;break;}
                    }
                }
            }
            if(max_y==nhy){break;}
        }
        crop_w = max_x - min_x + 1;
        crop_h = max_y - min_y + 1;
    
        if (crop_w>0){
            Crop_Surface = new Surface(crop_w,crop_h);
            for (int x=0 ; x < crop_w ; x++){
                for(int y=0 ; y < crop_h ; y++){
                    Crop_Surface[x,y]=Rotated_Surface.GetPoint(x+min_x,y+min_y);
                }
            }
            if (shape_ratio<100){
                f_form_w = (double)(shape_width) * shape_ratio/100;
                f_form_h = (double)(shape_width) * shape_ratio/100;
                form_w = (int)(f_form_w);
                form_h = (int)(f_form_h);
                Resized_Surface = new Surface(form_w,form_h);
                Resized_Surface.FitSurface(ResamplingAlgorithm.Bicubic,Crop_Surface);
                f_offx = (float)(shape_width) - (float)(f_form_w);
                f_offy = (float)(shape_height) - (float)(f_form_h);
                f_offx/=2;
                f_offy/=2;
                off_x = (int)(Math.Round(f_offx));
                off_y = (int)(Math.Round(f_offy));
                end_off_x = off_x + form_w ;
                end_off_y = off_y + form_h ;
                End_Surface = new float[shape_width,shape_height];
                for (int x=0 ; x < shape_width ; x++){
                   for (int y=0 ; y< shape_height ; y++){
                       if((x>=off_x&&x<=end_off_x)&&(y>=off_x&&y<=end_off_x)){
                        Resized_Surface_Color = Resized_Surface[x-off_x,y-off_y];
                        F_R = (float)(Resized_Surface_Color.R);
                        F_G = (float)(Resized_Surface_Color.G);
                        F_B = (float)(Resized_Surface_Color.B);
                        F_A = (float)(Resized_Surface_Color.A)/255;
                        F_AVG = (F_R+F_G+F_B)/765;
                        shade = F_AVG * F_A;
                        End_Surface[x,y] = shade;
                        w_avg+=shade;
                       }
                       else{
                           End_Surface[x,y]=0;
                       }
                   }
                }
                w_avg = w_avg / ((float)(shape_width) * (float)(shape_height));
            }
            else{
                activate_pixelate_mode = true;
                Resized_Surface = new Surface(shape_width,shape_height);
                Resized_Surface.FitSurface(ResamplingAlgorithm.Bicubic,Crop_Surface);
                End_Surface = new float[shape_width,shape_height];
                for (int x=0 ; x < shape_width ; x++){
                    for (int y=0 ; y< shape_height ; y++){
                        Resized_Surface_Color = Resized_Surface[x,y];
                        F_R = (float)(Resized_Surface_Color.R);
                        F_G = (float)(Resized_Surface_Color.G);
                        F_B = (float)(Resized_Surface_Color.B);
                        F_A = (float)(Resized_Surface_Color.A)/255;
                        F_AVG = (F_R+F_G+F_B)/765;
                        shade = F_AVG * F_A;
                        End_Surface[x,y] = shade;
                        w_avg+=shade;
                    }
                }
                w_avg = w_avg / ((float)(shape_width) * (float)(shape_height));
            }
            switch(shape_mirror){
                case 0:
                    tile_width = shape_width;
                    tile_height = shape_height;
                    Tile_Surface = End_Surface;
                    break;
                case 1:
                    tile_width = shape_width*2;
                    tile_height = shape_height;
                    Tile_Surface = new float [tile_width,tile_height];
                    for (int x = 0 ; x < tile_width ; x++){
                        if(x < shape_width){tx = x;}
                        else { tx = (shape_width - 1) - x % shape_width;}
                        for (int y = 0 ; y < tile_height ; y++){
                            Tile_Surface[x,y] = End_Surface[tx,y];
                        }
                    }
                    break;
                case 2:
                    tile_width = shape_width;
                    tile_height = shape_height*2;
                    Tile_Surface = new float [tile_width,tile_height];
                    for (int y = 0 ; y < tile_height ; y++){
                        if(y < shape_height){ty = y;}
                        else { ty = (shape_height - 1) - y % shape_height;}
                        for (int x = 0 ; x < tile_width ; x++){
                            Tile_Surface[x,ty] = End_Surface[x,ty];
                        }
                    }
                    break;
                case 3:
                    tile_width = shape_width*2;
                    tile_height = shape_height*2;
                    Tile_Surface = new float [tile_width,tile_height];
                    for (int y = 0 ; y < tile_height ; y++){
                        if(y < shape_height){ty = y;}
                        else { ty = (shape_height - 1) - y % shape_height;}
                        for (int x = 0 ; x < tile_width ; x++){
                            if(x < shape_width){tx = x;}
                            else { tx = (shape_width - 1) - x % shape_width;}
                            Tile_Surface[tx,ty] = End_Surface[tx,ty];
                        }
                    }
                    break;
            }
            Inside_Shape_Multiply = new Surface(nearest_width,nearest_height);
            for ( int y = 0 ; y < nearest_height ; y++){
                sy = Math.Min(hh,y);
                for (int x = 0 ; x < nearest_width ; x++){
                    sx = Math.Min(ww,x);
                    Inside_Shape_Multiply[x,y]=src[sx,sy];
                }
            }
            Outside_Shape_Multiply = Inside_Shape_Multiply;
        }
        else{
            activate_pixelate_mode = true;
            End_Surface = new float[shape_width,shape_height];
            for (int x=0 ; x < shape_width ; x++){
                for (int y=0 ; y< shape_height ; y++){
                    End_Surface[x,y]=1;
                }
            }
            w_avg = 1;
        }
    }
    
    // Here is the main multi-threaded render function
    // The dst canvas is broken up into rectangles and
    // your job is to write to each pixel of that rectangle
    void Render(Surface dst, Surface src, Rectangle rect)
    {
    
        // Step through each row of the current rectangle
        for (int y = rect.Top; y < rect.Bottom; y++)
        {
            if (IsCancelRequested) return;
            // Step through each pixel on the current row of the rectangle
            for (int x = rect.Left; x < rect.Right; x++)
            {
                ColorBgra SrcPixel = src[x,y];
                ColorBgra Ref_Shape_ClipboardPixel = Ref_Shape_Clipboard[x,y];
                ColorBgra DstPixel = dst[x,y];
    
                ColorBgra CurrentPixel = Ref_Shape_ClipboardPixel;
    
                // TODO: Add additional pixel processing code here
    
    
                dst[x,y] = CurrentPixel;
            }
        }
    }
    

     

     

  4. 2 minutes ago, MJW said:

     

    By definition, a Bezier curve has two end points and two control points. (Technically, that's only true for cubic Bezier curves, but cubic Bezier curves are by far the most commonly used.) The examples shown in the original comment appear to be multiple cubic Bezier curves attached end to end. That's how Bezier curves are often used; but it doesn't change the fact than PDN's Bezier curves are genuine cubic Bezier curves.

     

    You get no arguments from me there.  My point is that the OP wanted multiple bezier curves attached to each other, and to be able to manipulate aligned curves at once.

  5. @MJW The main difference is that you have more control with Blender/Maya/Inkscape/Krita vector curves than you do with PDN curves. Personally, I don't see a issue since PDN was never meant to be focused on vector. 3D programs has a clear purpose with vector with more control, and raster programs that already support vectors are usually meant for professionals (Not saying pdn can't be used by pros).

  6. Thanks @MJW, @BoltBait, @toe_head2001. After thinking long about it, I determined it is theoretically feasible, but rather difficult due to the nature of processing tile surface (auxiliary surface) taking into account of transformations (rotation part bothers me). Not to mention the headache of creating shape functions.

     

    Everything after that is straightforward. You have four more auxiliary surface after this, and one of those are multiplied by repeated tile surface, and the other is multiplied by the inverted repeated surface. Then you find weighed average of the color within each rectangle of tile and insert them into two small auxiliary surface, and finally use the tile surface to interpolate colors within two small auxiliary surface.

  7. Yes, that is what I want! And I will definitely wait for more info. One last thing, I am concerned about autocropping and rotation on auxiliary surface.

     

    On autocropping, is there a sample code?

     

    On rotation, I'm not sure on how to rotate and still keep all of the image within auxiliary. I know that I have to resize auxiliary surface just to fit it in.

     

    After that, I can assess the feasibility of converting Tiled Form gmic filter.

  8. 7 minutes ago, MJW said:

    What you're saying seems so strange that doesn't even not make sense to me. CodeLab plugins, like all plugins, get the pixels in the source image, and produce the pixels in the destination image. How you describe what you want doesn't fit in with anything I know about plugins.

     

    I know that is how codelab works. Let me explain, you noticed how I added surface within my c# code, I consider that as a vector or image. I would like to know what option do I have to stimulate pdn resize on the surface, and canvas resize as well. The Surface is basically a tile image. 

  9. For content, I would like to recreate my filter "Tiled Form" from Testing->Reptorian found within G'MIC-QT. So, I plan to recycle TR's Tiled Pixels shape (I will credit him for sure, and to save time), and use a faster algorithm based on Tiled Form. So, if you tested Tiled Form, you would observe that shapes have been normalized, and clamped to 0,1. There is also transformations option as well as how tiles are reflected, and how it get resized to fit boundary. That being said, does Codelab support resize function? And is the recreation feasible (dynamic shapes are out of question since it would take too long, and it requires dynamic gui)?

  10. Well, I'm still stuck working with my laptop. I did managed to upgraded Tiled Form. There is only one new option - `Fit Tile?` which would preserve the shape of your chosen form. It is 2x faster now.

     

    In addition, Popcorn Fractal has been upgraded to be faster a while ago, but you'd probably know that if you visited my Popcorn Fractal thread. Popcorn use unsafe multithreaded processing, so I have yet to see if there's going to be a problem here. On larger image, it can be 150% to 200% faster than c# version.

     

    Commit - Upgraded TIled Form/rep_form_pixel

  11. Thanks to the help of @MJW, I was able to complete this plugin and it is a near complete conversion of my g'mic filter named "Rotate By Torus Map" found within Testing->Reptorian. That being said, it can be seen as a upgraded version of Donut Distortion by MadJik from the functional point of view, but that's where the similarity ends. The new functional features are wraparound mode, blending between distortion, and more distortion modes.

     

    Sample Picture:

     

    285f594503a92e0cdf4184554e2610d61563bec6

    Note: Mind the seams on the edges of the picture. That doesn't happen with the plugin. I will replace this picture once I can sign back into imgur.

     

    How to use:

     

    1. Download Plugin - Rotate by Torus Map.zip

    2. Install into Paint.NET

    3. Load Paint.NET and go into Effect->Render.

    4. See how parameters work and change them until your desired result comes.

     

    - Source Codes -

    License: CeCILL v2.0 - http://cecill.info/licences/Licence_CeCILL_V2-en.html

     

    C# CodeLab

    Spoiler
    
    
    
    
    
    // Name: Rotate By Torus Map
    // Submenu: Distort
    // 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 = 45; // [0,100] Circumference A (%)
    DoubleSliderControl v_circ_b = 100; // [0,100] Circumference B (%)
    DoubleSliderControl angle = 360; // [-720,720] 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
    
    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;
    
    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_w) {return 2*f_w - mx - 1;}
        else {return Math.Min(mx,f_ww);}
    }
    
    float mirror_y(float y){
        float my = mod(y,f_h*2);
        if (my>=f_h) {return 2*f_h - my - 1;}
        else {return Math.Min(my,f_hh);}
    }
    
    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,int boundary){
        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,int boundary){
        if (enable_distortion_blend){
            return lerp(mode_choice(v,distortmode_a,boundary),mode_choice(v,distortmode_b,boundary));
        }
        else{
            return mode_choice(v,distortmode,boundary);
        }
    }
    
    void Render(Surface dst, Surface src, Rectangle rect)
    {
        int boundary;
        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);
        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 * -1;
        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,tyy,nxx,nyy;
        float posx,posy;
        ColorBgra tempcolor;
    
        for (int y = rect.Top; y < rect.Bottom; y++)
        {
            if (IsCancelRequested) return;
            tyy=start_y((double)(y));
            for (int x = rect.Left; x < rect.Right; x++)
            {
                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,boundary)*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);
                tempcolor=(remove_background && (boundary == 0)) ? ColorBgra.Transparent : src.GetBilinearSample(out_posx(posx),out_posy(posy));
                dst[x,y]=tempcolor;
            }
               
        }
    }

     

     

    G'MIC-QT

    Spoiler
    
    
    
    
    
    #@cli rep_rbtt: eq. to 'rep_rotate_by_torus_top' : (+)
    rep_rbtt: rep_rotate_by_torus_top $*
    #@cli rep_rotate_by_torus_top: 0<=_torus_circu_min_dimension_percent_1(%)<=100,0<=_torus_circu_min_dimension_percent_2(%)<=100,_distort_ang,_mode_1={ 0=softmode | 1=midmode | 2=hardmode | 3=hardmode_alt | 4=distroymode | 5=invdistroymode | 6=quadextrudemode | 7=hexextrudemode },_mode_2={ 0=softmode | 1=midmode | 2=hardmode | 3=hardmode_alt | 4=distroymode | 5=invdistroymode | 6=quadextrudemode | 7=hexextrudemode },0<=_mode_percent_comb(%)<=100,_offx(%),_offy(%),_off_dup={ 0=no_duplicate_dist | 1=duplicate_dist },_bgremove={ 0=keep_background | 1=remove_background },_cent_isolat_torus_mode={ 0=do_not_center | 1=center },_interpolation={ 0=nearest | 1=linear },_boundary={ 0=none | 1=neumann | 2=periodic | 3=mirror }
    #@cli : Inspired by the Donut Distortion plugin made by MadJik for Paint.NET, this version of donut distortion adds new features such as more modes, mode blending, isolated torus mode.
    #@cli : (eq. to 'rep_rbtt')\n
    #@cli : _torus_circu_min_dimension_percent_1 refers to the primary circumference of circle relative to the minimum image dimension.
    #@cli : _torus_circu_min_dimension_percent_2 refers to the secondary circumference of circle relative to the minimum image dimension.
    #@cli : _distort_ang refers to the maximum angle of distortion.
    #@cli : _mode_1 refers to the first mode of donut distortion.
    #@cli : _mode_1 refers to the second mode of donut distortion.
    #@cli : _mode_percent_comb refers to the weighed average of combination of modes. This only works if and only if _mode_1 and _mode_2 are specified!
    #@cli : _offx refers to the offset of donut distortion relative to the center in -1,1 x-coordinates. 100% means the center will be located in 1.
    #@cli : _offy refers to the offset of donut distortion relative to the center in -1,1 y-coordinates. 100% means the center will be located in 1.
    #@cli : _off_dup option defines whether distortion will wrap around when it reach out of bound.
    #@cli : _bgremove defines whether the background is removed.
    #@cli : _cent_isolat_torus_mode is a special mode used to aid into creating torus from image. This only works if _bgremove is active.
    #@cli : _interpolation defines the smoothness of the image.
    #@cli : _boundary defines how out-of-range values are wrapped as.
    #@cli : Default values: '_torus_circu_min_dimension_percent_1=1','_torus_circu_min_dimension_percent_2=.5','_distort_ang=45','_mode_1=0','_mode_2=','_mode_percent_comb=50%','_offx=0%','_offy=0%','_off_dup=1','_bgremove=0','_cent_isolat_torus_mode=0','_interpolation=1','_boundary=2'
    rep_rotate_by_torus_top:
    skip ${1=1},${2=0},${3=45},${4=0},${5=},${6=.5},${7=0},${8=0},${9=1},${10=1},${11=0}${12=1},${13=2}
    
    if $1==$2 error ""$"1!="$"2=F" fi
    if ($1>1||$1<0)||($2>1||$2<0) error "(0>="$"1<=1)||(0>="$"2<=1)=F" fi
    if $4<0||$4>7 error "0<="$"4<=7=F" fi
    if narg($5) if $5<0||$5>7 error "0<="$"5<=7=F" fi fi
    if $6<0||$6>1 error "0<="$"6<=1=f" fi
    
    if $12>0 r2dx 200%,1 fi
    f "
    begin(
     boundary=0;
    
     const s_id=s-1;
    
     const sd=max(w,h)/min(w,h);
     const sx=w>h?sd:1;
     const sy=w>h?1:sd;
    
     const ww=w-1;
     const hh=h-1;
     const cx=ww/2;
     const cy=hh/2;
     const offx=cx*$7*-1;
     const offy=cy*$8;
     const eps=10^-10;
    
     vallim_x(v)=v-ww*floor(v/(ww+eps));
     vallim_y(v)=v-hh*floor(v/(hh+eps));
    
     if($9,
      start_x(v)=vallim_x(v+offx);
      start_y(v)=vallim_y(v+offy);
     ,
      start_x(v)=v+offx;
      start_y(v)=v+offy;
     );
    
     const torus_val_1=$1;
     const torus_val_2=$2;
     new_min=min(torus_val_1,torus_val_2);
     new_max=max(torus_val_1,torus_val_2);
     nm(v)=(v-new_min)*(1/(new_max-new_min));
     limcut(v)=v>1?1:(v<0?0:v);
     bndcut(v)=v>1||v<0?0:1;
    
     const maxang=$3*-1;
     ang2rad(v)=pi*(v/180);
     rot_x(a,b,c)=a*cos(ang2rad(c))-b*sin(ang2rad(c));
     rot_y(a,b,c)=a*sin(ang2rad(c))+b*cos(ang2rad(c));
    
     sur_x(v)=(v/ww-.5)*2*sx;
     sur_y(v)=(v/hh-.5)*2*sy;
     unsur_x(v)=(v/(2*sx)+.5)*ww;
     unsur_y(v)=(v/(2*sy)+.5)*hh;
    
     softmode(v)=(cos(v*(2*pi)-pi)+1)/2;
     midmode(v)=abs(cos(v*pi+pi/2));
     hardmode(v)=sqrt(1-(abs(v-.5)*2)^2);
     hardmode_alt(v)=(1-(abs(v-.5)*2)^2)^(1/(2+(1-v)));
     distroymode(v)=cos(v*pi)*boundary;
     invdistroymode(v)=(cos(v*pi)*-1)*boundary;
     quadextrudemode(v)=1-abs(v-.5)*2;
     hexextrudemode(v)=(r=(1-abs(v-.5)*2)*2;r>1?1:r);
    
      if($4==0,mode_1(v)=softmode(v);
     ,if($4==1,mode_1(v)=midmode(v);
     ,if($4==2,mode_1(v)=hardmode(v);
     ,if($4==3,mode_1(v)=hardmode_alt(v);
     ,if($4==4,mode_1(v)=distroymode(v);
     ,if($4==5,mode_1(v)=invdistroymode(v);
     ,if($4==6,mode_1(v)=quadextrudemode(v);
     ,if($4==7,mode_1(v)=hexextrudemode(v);
     );
     );
     );
     );
     );
     );
     );
     );
    
     if(narg($5),
    
       if($5==0,mode_2(v)=softmode(v);
      ,if($5==1,mode_2(v)=midmode(v);
      ,if($5==2,mode_2(v)=hardmode(v);
      ,if($5==3,mode_2(v)=hardmode_alt(v);
      ,if($5==4,mode_2(v)=distroymode(v);
      ,if($5==5,mode_2(v)=invdistroymode(v);
      ,if($5==6,mode_2(v)=quadextrudemode(v);
      ,if($5==7,mode_2(v)=hexextrudemode(v);
      );
      );
      );
      );
      );
      );
      );
      );
    
      if($6==0||$6==1
      ,if($6,mode(v)=mode_2(v);,mode(v)=mode_1(v););
      ,mode(v)=$6*mode_2(v)+(1-$6)*mode_1(v);
      );
     ,
      mode(v)=mode_1(v);
     );
    
     if($10&&(s==2||s>3)
      ,out(a,b)=[vectors_id(J(a,b,z,abs($12)?2:0,$13)),j(a,b,z,s_id,abs($12)?2:0,$13)*boundary]
      ,out(a,b)=J(a,b,z,abs($12)?2:0,$13);
     );
    );
    xx=start_x(x);
    yy=start_y(y);
    xx=sur_x(xx);
    yy=sur_y(yy);
    radial_gradient=nm(sqrt(xx^2+yy^2));
    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;
    out(diff_x,diff_y);
    "
    
    if $11&&$10
     offx={($7/2)*100*-1}
     offy={($8/2)*100}
     repeat $! l[$>]
      if s==2||s>3
       shift $offx%,$offy%,0,0,2,0
      fi
     endl done
    fi
    
    if $12>0 r2dx 50%,3 fi
    #@gui Rotate by Torus Map:fx_rep_rbtt,fx_rep_rbtt_preview(0)
    #@gui :_=note("<b>Main</b>")
    #@gui :Circumference A (%)=float(100,0,100)
    #@gui :Circumference B (%)=float(50,0,100)
    #@gui :_=separator()_=note("<b>Distortion</b>")
    #@gui :Distortion Angle=float(-180,-720,720)
    #@gui :Distortion Mode=choice(3,"Soft","Medium","Hard","Alternative Hard","Distroy","Inverse-Distroy","Quad Extrude","Hexagonal Extrude")
    #@gui :Distortion Mode A=choice(3,"Soft","Medium","Hard","Alternative Hard","Distroy","Inverse-Distroy","Quad Extrude","Hexagonal Extrude")
    #@gui :Distortion Mode B=choice(7,"Soft","Medium","Hard","Alternative Hard","Distroy","Inverse-Distroy","Quad Extrude","Hexagonal Extrude")
    #@gui :Distortion Mode Blending (%)=float(50,0,100)
    #@gui :Enable Distortion Mode Blending=bool(1)
    #@gui :Distortion Position=point(50,50,0,1,255,255,255,255)
    #@gui :Wraparound Distortion?=bool(1)
    #@gui :_=separator(),_=note("<b>Additional</b>")
    #@gui :Remove Background?=bool(0)
    #@gui :Activate Isolated Torus Mode?=bool(0)
    #@gui :_=separator(),_=note("<b>Rendering</b>")
    #@gui :Interpolation=choice(1,"None","Linear")
    #@gui :Boundary Condition=choice(2,"None","Neumann","Periodic","Mirror")
    #@gui :_=separator(),_=note("<b>Preview</b>"), Preview Type=choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"), Preview Split = point(50,50,0,0,200,200,200,0,10)_0
    #@gui :_=separator(),_=note("<b>Information</b>\n"),_=note("This filter is inspired by <a href="https://forums.getpaint.net/topic/23400-donut-distortion-effect-plugin/">Donut Distortion Paint.NET plugin</a> by <a href="https://forums.getpaint.net/profile/45895-madjik/">MadJik</a>.\n\n This G'MIC version includes additional modes,wraparound mode, and special mode which isolates the distortion.")
    #@gui :_=separator(),_=note("<small>Author: Reptorian. Latest Update: <i>2020/2/29</i>.</small>")
    fx_rep_rbtt:
    to_a
    if $8 rep_rbtt {$1/100},{$2/100},$3,$5,$6,{$7/100},{($9/100-.5)*2},{($10/100-.5)*-2},$11,$12,$13,$14,$15
    else  rep_rbtt {$1/100},{$2/100},$3,$4,,,{($9/100-.5)*2},{($10/100-.5)*-2},$11,$12,$13,$14,$15
    fi
    fx_rep_rbtt_preview: gui_split_preview "fx_rep_rbtt ${1-13},{$14*-1},$15",${-3--1}
    u "{$1}"\
    "{$2}"\
    "{$3}"\
    "{$4}_"{!$8?2:1}\
    "{$5}_"{$8?2:1}\
    "{$6}_"{$8?2:1}\
    "{$7}_"{$8?2:1}\
    "{$8}"\
    "{$9,$10}"\
    "{$11}"\
    "{$12}"\
    "{$13}_"{$12?2:1}\
    "{$14}"\
    "{$15}"\
    "{$16}"\
    "{$17,$18}"

     

     

    • Like 2
    • Upvote 2
  12. 1 minute ago, MJW said:

    If you can make the program work in PreRender(), you can make it work in Render(). What you might have to do, though, is add a few extra arguments to routines that get called, instead of relying of "global" (i.e, class-level) variables to communicate.  As I mentioned, global variable can never be used for values that might be different between Render() instances.

    So, that is why it does not work how I like yet. In that case, I will address those.

  13. Just now, MJW said:

     

    Why wouldn't it be possible?

    I tested different ways to use boundary since when boundary is at 1, that means there is a distortion. No matter how I structure the conditional, and the result, the background problem will stay there. There were 10 different ways I coded it, and exhausted all options I could think of.

  14. Spoiler
    
    // Name: Rotate By Torus Map
    // Submenu: Distort
    // 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 = 45; // [0,100] Circumference A (%)
    DoubleSliderControl v_circ_b = 100; // [0,100] Circumference B (%)
    DoubleSliderControl angle = 45; // [-720,720] 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
    
    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_w) {return 2*f_w - mx - 1;}
        else {return Math.Abs(mx);}
    }
    
    float mirror_y(float y){
        float my = mod(y,f_h*2);
        if (my>=f_h) {return 2*f_h - my - 1;}
        else {return Math.Abs(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 Render(Surface dst, Surface src, Rectangle rect)
    {
        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);
        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 * -1;
        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,tyy,nxx,nyy;
        float posx,posy;
        ColorBgra tempcolor;
    
        for (int y = rect.Top; y < rect.Bottom; y++)
        {
            if (IsCancelRequested) return;
            tyy=start_y((double)(y));
            for (int x = rect.Left; x < rect.Right; x++)
            {
                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);
                if(outofrange_boundary_choice>0){
                    posx=Math.Max(0,Math.Min(f_ww,out_posx(posx)));
                    posy=Math.Max(0,Math.Min(f_hh,out_posy(posy)));
                }
                tempcolor=src.GetBilinearSample(posx,posy);
                if (remove_background){
                    dst[x,y]=ColorBgra.FromBgra(tempcolor.B,tempcolor.G,tempcolor.R,boundary == 1 ? tempcolor.A : (byte)(0));
                }
                else{
                   dst[x,y] = tempcolor;
                }
            }
        }
    }

     

     

  15. 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.

×
×
  • Create New...