Jump to content

Does Codelab supports resize function, and some other..


Reptillian

Recommended Posts

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)?

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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. 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

You can, of course, make the auxiliary surface larger than the canvas, but that surface is de-allocated as soon as the plugin exits.

 

Now I think I finally realize what you meant in the first place. You're asking, I think, whether you can use PDN's internal Surface resize methods on a canvas. The answer is, I'm not sure, but I think you may be able to.

Link to comment
Share on other sites

7 minutes ago, MJW said:

You can, of course, make the auxiliary surface larger than the canvas, but that surface is de-allocated as soon as the plugin exits.

This is closer. I would like to be able to resize auxiliary surface with arbitrary w and h, and with interpolation. That auxiliary being a tile surface.

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

I hope someone can provide more certain information, but I think you just call them with the surface you want to copy, like: aux.BicubicFitSurface(src);

 

I don't think you'd do it by actually resizing the Surface. You'd do it by creating a new, larger surface, then using one of the Surface-copying routines to initialize the new surface from the smaller surface's image. Which has the same result.

Link to comment
Share on other sites

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.

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

Looking for this?

 

private static Image ResizeImage(Image image, int width, int height)
{
    var destRect = new Rectangle(0, 0, width+1, height+1);
    var destImage = new Bitmap(width, height);

    destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

    using (var graphics = Graphics.FromImage(destImage))
    using (var wrapMode = new ImageAttributes())
    {
        wrapMode.SetWrapMode(WrapMode.Clamp);
        graphics.CompositingMode = CompositingMode.SourceCopy;
        graphics.CompositingQuality = CompositingQuality.HighQuality;
        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = SmoothingMode.HighQuality;
        graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
        graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
    }

    return (Image)destImage;
}

 

  • You're a Smart Cookie! 1
Link to comment
Share on other sites

If you're working with Surfaces, these are the two overloads for FitSurface:

FitSurface(ResamplingAlgorithm algorithm, Surface source)
FitSurface(ResamplingAlgorithm algorithm, Surface source, Rectangle dstRoi)

 

Example:

auxSuface.FitSurface(ResamplingAlgorithm.Bicubic, srcSurface);

 

5 minutes ago, Reptillian said:

On autocropping, is there a sample code?

 

I have a static method to find the trimmed bounds (remove transparent edges) of a Surface.

https://github.com/toehead2001/pdn-blur-fill/blob/master/BlurFill/BlurFill.cs#L194

 

 

 

In fact my Blur Fill plugin does all this stuff: Trims the source image, uses FitSurface() to enlarge the trimmed surface to the size of the dstSurface.

 

Here's simplified example:

Rectangle selection = this.EnvironmentParameters.SelectionBounds;
Rectangle trimmedBounds = GetTrimmedBounds(src, selection);

Surface trimmedSurface = new Surface(trimmedBounds.Size);
trimmedSurface.CopySurface(src, Point.Empty, trimmedBounds);

dst.FitSurface(ResamplingAlgorithm.Bicubic, trimmedSurface);

 

  • Like 1
  • You're a Smart Cookie! 1

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

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.

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

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;
        }
    }
}

 

 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...