Если вы не читали первый урок о том, как создать свой первый плагин для 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;
}
}
}
![Изображение](https://paint-net.ru/img/0523.jpg)
Теперь посмотрим на первые три строчки учебного кода 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;
![Изображение](https://paint-net.ru/img/0524.jpg)
Три стрелки двусторонние стрелки на этом круге представляют собой три наших маркера пользовательского интерфейса. Начнем с первого маркера или с первой горизонтальной стрелки «голубой - Красный».
Итак, при изменении первого параметра 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 выдает какие-то ошибки, то дважды щелкните мышью по описанию ошибки в окне ниже теста скрипта и вы перейдете на строку скрипта содержащую ошибку.