Урок №2. Как сделать свой эффект для Paint.NET

Эта ветка форума посвящена вопросу, как самому сделать свой плагин (эффект) для Paint.NET. Для публикации своих эффектов используйте основной форум "Эффекты и плагины к Paint.NET"
Ответить
Аватара пользователя
xmario
Администратор
Сообщения: 3402
Зарегистрирован: 03 апр 2010, 20:12
Репутация: 45
Пол: Мужской
Откуда: Москва

Урок №2. Как сделать свой эффект для Paint.NET

Сообщение xmario » 25 апр 2010, 01:15

Как сделать свой собственный plugin в Paint.NET - Часть вторая

Если вы не читали первый урок о том, как создать свой первый плагин для Paint.NET, то рекомендуем сначала прочитать его.
Это второй урок, на котором мы будем создавать более сложный эффект. Оригинальную версию этого урока на английском языке можно найти здесь.
Итак, сейчас мы будем делать более сложный эффект с помощью CodeLab, чем на первом уроке. О том, как работать с CodeLab для Paint.NET, можно прочитать в специальной ветке этого форума.
Сейчас мы будем делать эффект, который требует настраиваемых параметров. В этом уроке будет рассмотрен вариант эффекта, в котором присутствуют три основных вида параметров пользовательского интерфейса.
Итак, открывая Codelab, мы увидим учебный код, с которым мы уже познакомились на первом уроке.

Код: Выделить всё

int Amount1=0;   //[0,100]Slider 1 Description 
int Amount2=0;   //[0,100]Slider 2 Description 
int Amount3=0;   //[0,100]Slider 3 Description 

void Render(Surface dst, Surface src, Rectangle rect) 
{ 
    // Delete any of these lines you don't need 
    Rectangle selection = this.EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt(); 
    long CenterX = (long)(((selection.Right - selection.Left) / 2)+selection.Left); 
    long CenterY = (long)(((selection.Bottom - selection.Top) / 2)+selection.Top); 
    ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor; 
    ColorBgra SecondaryColor = (ColorBgra)EnvironmentParameters.SecondaryColor; 
    int BrushWidth = (int)EnvironmentParameters.BrushWidth; 

    ColorBgra CurrentPixel; 
    for(int y = rect.Top; y < rect.Bottom; y++) 
    { 
        for (int x = rect.Left; x < rect.Right; x++) 
        { 
            CurrentPixel = src[x,y]; 
            // TODO: Add pixel processing code here 
            // Access RGBA values this way, for example: 
            // CurrentPixel.R = (byte)PrimaryColor.R; 
            // CurrentPixel.G = (byte)PrimaryColor.G; 
            // CurrentPixel.B = (byte)PrimaryColor.B; 
            // CurrentPixel.A = (byte)PrimaryColor.A; 
            dst[x,y] = CurrentPixel; 
        } 
    } 
}
Делать мы будем в этом уроке эффект похожий на «Баланс цвета». Диалоговый интерфейс этого эффекта выглядит так.

Изображение

Теперь посмотрим на первые три строчки учебного кода CodeLab.

Код: Выделить всё

int Amount1=0;   //[0,100]Slider 1 Description 
int Amount2=0;   //[0,100]Slider 2 Description 
int Amount3=0;   //[0,100]Slider 3 Description 
Эти строки CodeLab анализирует, что бы понять, какие элементы пользовательского интерфейса должны присутствовать в диалоге. Каждая из приведенных выше строк описывает ползунок, значения которого можно изменять от нуля до ста. Если в вашем эффекте должны присутствовать только два ползунка, то нужно удалить из кода учебного скрипта нижнюю строчку с «Amount3».
Разберем подробнее, что обозначает эти строки на примере верхней строки.

Код: Выделить всё

int Amount1=0;   //[0,100]Slider 1 Description 
Число после знака равно «int Amount1=0» означает значение параметра по умолчанию. В нашем случае – это ноль.
Числа в квадратных скобках после знака «//» - знака комментария, означают интервал изменения параметра. В нашем случае «[0,100]» означает изменение параметра от нуля до ста.
Текстовая надпись после квадратных скобок «Slider 1 Description» представляет собой описание параметра. Вместо этого описания по умолчанию. Можно было бы написать, например, «Параметр яркости». Эта надпись будет отображаться в диалоговом окне вашего эффекта рядом с этим параметром.
Для нашего эффекта из примера «баланс цвета» эти параметры должны выглядеть таким образом

Код: Выделить всё

int Amount1=0;   //[-100,100]Cyan - Red
int Amount2=0;   //[-100,100]Magenta - Green
int Amount3=0;   //[-100,100]Yellow - Blue
или на русском языке

Код: Выделить всё

int Amount1=0;   //[-100,100]Голубой - Красный
int Amount2=0;   //[-100,100]Пурпурный -Зеленый
int Amount3=0;   //[-100,100]Желтый - Синий
Теперь, после того, как мы сделали меню пользовательского интерфейса, перейдем непосредственно к алгоритму.

Поскольку нам не нужны в этом эффекте ни определение центральной точки, ни основной цвет, ни дополнительный цвет, ни размер кисти, то следующие строчки учебного скрипта можно совершенно спокойно удалить.

Код: Выделить всё

  // Delete any of these lines you don't need 
    Rectangle selection = this.EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt(); 
    long CenterX = (long)(((selection.Right - selection.Left) / 2)+selection.Left); 
    long CenterY = (long)(((selection.Bottom - selection.Top) / 2)+selection.Top); 
    ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor; 
    ColorBgra SecondaryColor = (ColorBgra)EnvironmentParameters.SecondaryColor; 
    int BrushWidth = (int)EnvironmentParameters.BrushWidth;
Тпереь перейдем к основной сути нашего алгоритма. Как вы уже узнали из первого урока на тему «Как создать свой собственный эффект Paint.NET», то в учебном скрипте есть цикл, который перебирает все пиксели выделенной области изображения. Вот он.

Код: Выделить всё

    for(int y = rect.Top; y < rect.Bottom; y++) 
    { 
        for (int x = rect.Left; x < rect.Right; x++) 
        { 
            CurrentPixel = src[x,y]; 
            // TODO: Add pixel processing code here 
            // Access RGBA values this way, for example: 
            // CurrentPixel.R = (byte)PrimaryColor.R; 
            // CurrentPixel.G = (byte)PrimaryColor.G; 
            // CurrentPixel.B = (byte)PrimaryColor.B; 
            // CurrentPixel.A = (byte)PrimaryColor.A; 
            dst[x,y] = CurrentPixel; 
        } 
    }
Код внутри цикла получает информацию о пикселе исходного холста «CurrentPixel = src[x,y];» и записывает её в пиксель холста назначения «dst[x,y] = CurrentPixel;».

То что написано между этими строками должно выполнить какие-то действие прежде, чем записать новый пиксель. Сейчас никаких действий между двумя этими событиями: прочтение текущего пикселя и запись нового пикселя не происходит. Все строки между ними закомментированы. А раз так, то можно их удалить. Получится так.

Код: Выделить всё

int Amount1=0;   //[-100,100]Cyan - Red
int Amount2=0;   //[-100,100]Magenta - Green
int Amount3=0;   //[-100,100]Yellow - Blue

void Render(Surface dst, Surface src, Rectangle rect) 
{ 
    ColorBgra CurrentPixel; 
    for(int y = rect.Top; y < rect.Bottom; y++) 
    { 
        for (int x = rect.Left; x < rect.Right; x++) 
        { 
            CurrentPixel = src[x,y]; 
            // TODO: Add pixel processing code here 
            dst[x,y] = CurrentPixel; 
        } 
    } 
} 
Мы планируем в создаваемом нами эффекте изменять значения трех каналов R, G и B. Поэтому нам понадобятся три временных переменных для хранения этих данных. Так и определим эти переменные int R, G, B;

Код: Выделить всё

int Amount1=0;   //[-100,100]Cyan - Red
int Amount2=0;   //[-100,100]Magenta - Green
int Amount3=0;   //[-100,100]Yellow - Blue

void Render(Surface dst, Surface src, Rectangle rect) 
{ 
    ColorBgra CurrentPixel; 
    int R, G, B;
    for(int y = rect.Top; y < rect.Bottom; y++) 
    { 
        for (int x = rect.Left; x < rect.Right; x++) 
        { 
            CurrentPixel = src[x,y]; 
            // TODO: Add pixel processing code here 
            dst[x,y] = CurrentPixel; 
        } 
    } 
} 
Ну, наконец приступим о написанию действительно кода выполняемого внутри цикла. То, что находится между получением исходного пикселя и записью нового. Сначала определим наши переменные R,G и B, присвоив им соответствующие значения исходного пикселя.

Код: Выделить всё

R = (int)CurrentPixel.R;
G = (int)CurrentPixel.G;
B = (int)CurrentPixel.B;
Для начала вспомним, где на цветовом круге располагаются наши цвкета.

Изображение

Три стрелки двусторонние стрелки на этом круге представляют собой три наших маркера пользовательского интерфейса. Начнем с первого маркера или с первой горизонтальной стрелки «голубой - Красный».
Итак, при изменении первого параметра Amount1, мы будем добавлять это значение к составляющей красного канала исходного цвета - R.

Код: Выделить всё

R = R + Amount1;
Поскольку мы добавили это значение к красному каналу R, то мы должны скомпенсировать это значение из двух оставшихся каналов зеленого и синего – G и B. Соответственно, поскольку канала два, то вычтем из каждого из них по половине корректировки канала R.

Код: Выделить всё

G = G-(Amount1 / 2);
B = B-(Amount1 / 2);
Аналогичным образом поступим с двумя другими ползунками – с двумя другими стрелками на круге. Вот код, который должен у вас получиться.

Код: Выделить всё

int Amount1=0;   //[-100,100]Cyan - Red
int Amount2=0;   //[-100,100]Magenta - Green
int Amount3=0;   //[-100,100]Yellow - Blue

void Render(Surface dst, Surface src, Rectangle rect) 
{ 
    ColorBgra CurrentPixel; 
    int R, G, B;
    for(int y = rect.Top; y < rect.Bottom; y++) 
    { 
        for (int x = rect.Left; x < rect.Right; x++) 
        { 
            CurrentPixel = src[x,y]; 
            R = (int)CurrentPixel.R;
            G = (int)CurrentPixel.G;
            B = (int)CurrentPixel.B;
            // Cyan - Red adjustment
            R = R + Amount1;
            G = G - (Amount1 / 2);
            B = B - (Amount1 / 2);
            // Magenta - Green adjustment
            G = G + Amount2;
            R = R - (Amount2 / 2);
            B = B - (Amount2 / 2);
            // Yellow - Blue adjustment
            B = B + Amount3;
            R = R - (Amount3 / 2);
            G = G - (Amount3 / 2);
            dst[x,y] = CurrentPixel; 
        } 
    } 
}
Вот мы и написали формулу. Теперь нам осталось собрать наш результирующий пиксель из отдельных частей, рассчитанных нами R, G и B. Для этого надо использовать специальную встроенную функцию Paint.NET.

Код: Выделить всё

// Reassemble the color from R, G, and B
CurrentPixel = CurrentPixel = ColorBgra.FromBgra(Int32Util.ClampToByte(B),Int32Util.ClampToByte(G),Int32Util.ClampToByte(R),CurrentPixel.A);
Что делает эта функция ClampToByte? Она переводит значение наших переменных в тип - байт. Т.е. если значение, например, переменной R в процессе выполнения нашего эффекта станет меньше нуля, то эта функция присвоит ему значение 0. Если значение этой же переменной будет больше, чем 255, то после выполнения этой функции её значение станет 255. Обратите внимание, что значение прозрачности (альфа) мы используем равное этому значению у исходного пикселя. И так наш код теперь выглядит так.

Код: Выделить всё

int Amount1=0;   //[-100,100]Cyan - Red
int Amount2=0;   //[-100,100]Magenta - Green
int Amount3=0;   //[-100,100]Yellow - Blue

void Render(Surface dst, Surface src, Rectangle rect) 
{ 
    ColorBgra CurrentPixel; 
    int R, G, B;
    for(int y = rect.Top; y < rect.Bottom; y++) 
    { 
        for (int x = rect.Left; x < rect.Right; x++) 
        { 
            CurrentPixel = src[x,y]; 
            R = (int)CurrentPixel.R;
            G = (int)CurrentPixel.G;
            B = (int)CurrentPixel.B;
            // Cyan - Red adjustment
            R = R + Amount1;
            G = G - (Amount1 / 2);
            B = B - (Amount1 / 2);
            // Magenta - Green adjustment
            G = G + Amount2;
            R = R - (Amount2 / 2);
            B = B - (Amount2 / 2);
            // Yellow - Blue adjustment
            B = B + Amount3;
            R = R - (Amount3 / 2);
            G = G - (Amount3 / 2);
            // Reassemble the color from R, G, and B
           CurrentPixel = CurrentPixel = ColorBgra.FromBgra(Int32Util.ClampToByte(B),Int32Util.ClampToByte(G),Int32Util.ClampToByte(R),CurrentPixel.A);
            dst[x,y] = CurrentPixel; 
        } 
    } 
}
Вот собственно и всё.
С точки зрения программирования этот код можно оптимизировать. Однако, с точки зрения наглядности для чтения кода, в качестве учебного примера получившийся код – это самое то.

Если при написании этого кода CodeLab выдает какие-то ошибки, то дважды щелкните мышью по описанию ошибки в окне ниже теста скрипта и вы перейдете на строку скрипта содержащую ошибку.

Аватара пользователя
BorzoyExist
Сообщения: 87
Зарегистрирован: 04 апр 2010, 19:42
Репутация: 0
Откуда: Петербург
Контактная информация:

Re: Урок №2. Как сделать свой эффект для Paint.NET

Сообщение BorzoyExist » 02 май 2010, 14:51

Спасибо, растолковано понятно :good:
Изображение

Ответить

Вернуться в «Как сделать свой эффект (плагин) для Paint.NET»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя