Урок №2. Как сделать свой эффект для Paint.NET
Добавлено: 25 апр 2010, 01:15
Как сделать свой собственный plugin в Paint.NET - Часть вторая
Если вы не читали первый урок о том, как создать свой первый плагин для Paint.NET, то рекомендуем сначала прочитать его.
Это второй урок, на котором мы будем создавать более сложный эффект. Оригинальную версию этого урока на английском языке можно найти здесь.
Итак, сейчас мы будем делать более сложный эффект с помощью CodeLab, чем на первом уроке. О том, как работать с CodeLab для Paint.NET, можно прочитать в специальной ветке этого форума.
Сейчас мы будем делать эффект, который требует настраиваемых параметров. В этом уроке будет рассмотрен вариант эффекта, в котором присутствуют три основных вида параметров пользовательского интерфейса.
Итак, открывая Codelab, мы увидим учебный код, с которым мы уже познакомились на первом уроке.
Делать мы будем в этом уроке эффект похожий на «Баланс цвета». Диалоговый интерфейс этого эффекта выглядит так.
Теперь посмотрим на первые три строчки учебного кода CodeLab.
Эти строки CodeLab анализирует, что бы понять, какие элементы пользовательского интерфейса должны присутствовать в диалоге. Каждая из приведенных выше строк описывает ползунок, значения которого можно изменять от нуля до ста. Если в вашем эффекте должны присутствовать только два ползунка, то нужно удалить из кода учебного скрипта нижнюю строчку с «Amount3».
Разберем подробнее, что обозначает эти строки на примере верхней строки.
Число после знака равно «int Amount1=0» означает значение параметра по умолчанию. В нашем случае – это ноль.
Числа в квадратных скобках после знака «//» - знака комментария, означают интервал изменения параметра. В нашем случае «[0,100]» означает изменение параметра от нуля до ста.
Текстовая надпись после квадратных скобок «Slider 1 Description» представляет собой описание параметра. Вместо этого описания по умолчанию. Можно было бы написать, например, «Параметр яркости». Эта надпись будет отображаться в диалоговом окне вашего эффекта рядом с этим параметром.
Для нашего эффекта из примера «баланс цвета» эти параметры должны выглядеть таким образом
или на русском языке
Теперь, после того, как мы сделали меню пользовательского интерфейса, перейдем непосредственно к алгоритму.
Поскольку нам не нужны в этом эффекте ни определение центральной точки, ни основной цвет, ни дополнительный цвет, ни размер кисти, то следующие строчки учебного скрипта можно совершенно спокойно удалить.
Тпереь перейдем к основной сути нашего алгоритма. Как вы уже узнали из первого урока на тему «Как создать свой собственный эффект Paint.NET», то в учебном скрипте есть цикл, который перебирает все пиксели выделенной области изображения. Вот он.
Код внутри цикла получает информацию о пикселе исходного холста «CurrentPixel = src[x,y];» и записывает её в пиксель холста назначения «dst[x,y] = CurrentPixel;».
То что написано между этими строками должно выполнить какие-то действие прежде, чем записать новый пиксель. Сейчас никаких действий между двумя этими событиями: прочтение текущего пикселя и запись нового пикселя не происходит. Все строки между ними закомментированы. А раз так, то можно их удалить. Получится так.
Мы планируем в создаваемом нами эффекте изменять значения трех каналов R, G и B. Поэтому нам понадобятся три временных переменных для хранения этих данных. Так и определим эти переменные int R, G, B;
Ну, наконец приступим о написанию действительно кода выполняемого внутри цикла. То, что находится между получением исходного пикселя и записью нового. Сначала определим наши переменные R,G и B, присвоив им соответствующие значения исходного пикселя.
Для начала вспомним, где на цветовом круге располагаются наши цвкета.
Три стрелки двусторонние стрелки на этом круге представляют собой три наших маркера пользовательского интерфейса. Начнем с первого маркера или с первой горизонтальной стрелки «голубой - Красный».
Итак, при изменении первого параметра Amount1, мы будем добавлять это значение к составляющей красного канала исходного цвета - R.
Поскольку мы добавили это значение к красному каналу R, то мы должны скомпенсировать это значение из двух оставшихся каналов зеленого и синего – G и B. Соответственно, поскольку канала два, то вычтем из каждого из них по половине корректировки канала R.
Аналогичным образом поступим с двумя другими ползунками – с двумя другими стрелками на круге. Вот код, который должен у вас получиться.
Вот мы и написали формулу. Теперь нам осталось собрать наш результирующий пиксель из отдельных частей, рассчитанных нами R, G и B. Для этого надо использовать специальную встроенную функцию Paint.NET.
Что делает эта функция ClampToByte? Она переводит значение наших переменных в тип - байт. Т.е. если значение, например, переменной R в процессе выполнения нашего эффекта станет меньше нуля, то эта функция присвоит ему значение 0. Если значение этой же переменной будет больше, чем 255, то после выполнения этой функции её значение станет 255. Обратите внимание, что значение прозрачности (альфа) мы используем равное этому значению у исходного пикселя. И так наш код теперь выглядит так.
Вот собственно и всё.
С точки зрения программирования этот код можно оптимизировать. Однако, с точки зрения наглядности для чтения кода, в качестве учебного примера получившийся код – это самое то.
Если при написании этого кода CodeLab выдает какие-то ошибки, то дважды щелкните мышью по описанию ошибки в окне ниже теста скрипта и вы перейдете на строку скрипта содержащую ошибку.
Если вы не читали первый урок о том, как создать свой первый плагин для 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
Разберем подробнее, что обозначает эти строки на примере верхней строки.
Код: Выделить всё
int Amount1=0; //[0,100]Slider 1 Description
Числа в квадратных скобках после знака «//» - знака комментария, означают интервал изменения параметра. В нашем случае «[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;
Код: Выделить всё
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;
}
}
То что написано между этими строками должно выполнить какие-то действие прежде, чем записать новый пиксель. Сейчас никаких действий между двумя этими событиями: прочтение текущего пикселя и запись нового пикселя не происходит. Все строки между ними закомментированы. А раз так, то можно их удалить. Получится так.
Код: Выделить всё
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;
}
}
}
Код: Выделить всё
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 = (int)CurrentPixel.R;
G = (int)CurrentPixel.G;
B = (int)CurrentPixel.B;
Три стрелки двусторонние стрелки на этом круге представляют собой три наших маркера пользовательского интерфейса. Начнем с первого маркера или с первой горизонтальной стрелки «голубой - Красный».
Итак, при изменении первого параметра Amount1, мы будем добавлять это значение к составляющей красного канала исходного цвета - R.
Код: Выделить всё
R = R + Amount1;
Код: Выделить всё
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;
}
}
}
Код: Выделить всё
// Reassemble the color from R, G, and B
CurrentPixel = CurrentPixel = ColorBgra.FromBgra(Int32Util.ClampToByte(B),Int32Util.ClampToByte(G),Int32Util.ClampToByte(R),CurrentPixel.A);
Код: Выделить всё
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 выдает какие-то ошибки, то дважды щелкните мышью по описанию ошибки в окне ниже теста скрипта и вы перейдете на строку скрипта содержащую ошибку.