Страница 1 из 1

Урок №4 Как сделать свой эффект для paint.net (сложный урок)

Добавлено: 10 июл 2010, 12:22
xmario
Урок №4.
Как сделать свой эффект для paint.net используя стандартные эффекты paint.net


На этом уроке мы рассмотрим, как вызвать стандартный эффект paint.net в коде скрипта вашего собственного плагина или эффекта paint.net. Оригинальная версия этого урока на английском языке есть здесь. Этот урок сложный, поэтому если вы не читали урок №1, урок №2 и урок №3, которые существенно проще, то рекомендую начать с них.

Итак, более сложные эффекты paint.net могут вызывать внутри себя стандартные плагины, которые есть в paint.net, вычислять результаты их действия, которые вы можете использовать как часть вашего собственного эффекта. Что делать, если вам для вашего собственного эффекта нужен, например, стандартный эффект paint.net «Гауссово размытие». Никаких проблем! Именно о подобных случаях мы сейчас и поговорим в этом уроке.

Как программно вызвать встроенный эффект paint.net внутри своего скрипта

Для создания собственных эффектов мы будем использовать Code Lab для paint.net. Прежде всего, конечно, надо сразу оговорить ограничения, которые есть у Code Lab и paint.net. У вас нет ограничений по количеству операций и количеству пользовательского кода. Но внутри своего скрипта вы можете вызвать только один стандартный эффект paint.net до выполнения собственного кода. Зато вы можете использовать различные расчеты к полученному в результате применения стандартного эффекта paint.net результату, а так же встроенные режимы наложения пикселей. Режимы наложения, такие как есть у слоев в paint.net – перекрытие, умножение осветление и т.д.

Принцип действия сложного эффекта в этом случае похож на работу со слоями в paint.net. Вы как бы :implication: применяете стандартный эффект на один слой, а потом накладываете результата эффекта на другой слой, используя доступные в paint.net режимы смешивания слоев или собственные алгоритмы.

Изображение

Обратите внимание, что на самом деле вы работаете только с одним слоем! Эффект paint.net не может видеть содержимое других слоев изображения. Этот рисунок просто показывает технологию действия эффекта, и она очень похожа на пример работы со слоями в paint.net.

Если вы помните терминологию, использованную нами в предыдущих уроках, то у нас при программировании эффекта paint.net есть два холста – холст источник с исходным изображением и холст назначения, это результат выполнения нашего эффекта. Таким образом, вызывая в начале нашего собственного скрипта стандартный эффект paint.net, например, Гауссово размытие, мы получаем на холсте назначение размытое изображение, полученное применением «Гауссова размытия» к исходному изображению с холста источника. На холсте источнике остается исходное изображение.

Таким образом, после вызова стандартного эффекта paint.net, вы располагаете двумя наборами пикселей. На холсте источнике – исходное изображение, на холсте назначение – изображение после эффекта, например, размытое. Дальше вы можете в ходе своего скрипта обрабатывать эти значения любым способом, а так же можете применять к ним стандартные режимы смешивания paint.net.

Вызвать можно любой встроенный (стандартный) эффект paint.net. Ниже приведем коды для настроек параметров и коды вызовов основных встроенных эффектов paint.net.

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

// Настройка параметров для Гауссова размытия Gaussian Blur
GaussianBlurEffect blurEffect = new GaussianBlurEffect();
PropertyCollection bProps = blurEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken bParameters = new PropertyBasedEffectConfigToken(bProps);
bParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1); // fix
blurEffect.SetRenderInfo(bParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Гауссово размытие Gaussian Blur
blurEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для эффекта Барельеф Emboss 
EmbossEffect embossEffect = new EmbossEffect();
PropertyCollection eProps = embossEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken eParameters = new PropertyBasedEffectConfigToken(eProps);
eParameters.SetPropertyValue(EmbossEffect.PropertyNames.Angle, (double)Amount1); // fix
embossEffect.SetRenderInfo(eParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Барельеф - Emboss 
embossEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для эффекта Уменьшение шума Noise Reduction function
ReduceNoiseEffect noiseEffect = new ReduceNoiseEffect();
PropertyCollection nProps = noiseEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken nParameters = new PropertyBasedEffectConfigToken(nProps);
nParameters.SetPropertyValue(ReduceNoiseEffect.PropertyNames.Radius, 10); // fix
nParameters.SetPropertyValue(ReduceNoiseEffect.PropertyNames.Strength, Amount1); // fix
noiseEffect.SetRenderInfo(nParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Уменьшение шума Reduce Noise function
noiseEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для коррекции Яркость и контраст Brightness and Contrast Adjustment function
BrightnessAndContrastAdjustment bacAdjustment = new BrightnessAndContrastAdjustment();
PropertyCollection bacProps = bacAdjustment.CreatePropertyCollection();
PropertyBasedEffectConfigToken bacParameters = new PropertyBasedEffectConfigToken(bacProps);
bacParameters.SetPropertyValue(BrightnessAndContrastAdjustment.PropertyNames.Brightness, Amount1); // fix
bacParameters.SetPropertyValue(BrightnessAndContrastAdjustment.PropertyNames.Contrast, 45); // fix
bacAdjustment.SetRenderInfo(bacParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов коррекции Яркость и контраст Brightness and Contrast Adjustment 
bacAdjustment.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для эффекта Размытие в движении Motion Blur effect
MotionblurEffect blurEffect = new MotionblurEffect();
PropertyCollection blurProps = blurEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken BlurParameters = new PropertyBasedEffectConfigToken(blurProps);
BlurParameters.SetPropertyValue(MotionblurEffect.PropertyNames.Angle, Amount1); // fix
BlurParameters.SetPropertyValue(MotionblurEffect.PropertyNames.Centered, Amount2); // fix
BlurParameters.SetPropertyValue(MotionblurEffect.PropertyNames.Distance, Amount3); // fix
blurEffect.SetRenderInfo(BlurParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Размытие в движении Motion Blur function
blurEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для эффекта Картина, масло Oil Painting effect
OilPaintingEffect oilpaintEffect = new OilPaintingEffect();
PropertyCollection oilpaintProps = oilpaintEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken oilpaintParameters = new PropertyBasedEffectConfigToken(oilpaintProps);
oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.BrushSize, Amount1); // fix
oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.Coarseness, Amount2); // fix
oilpaintEffect.SetRenderInfo(oilpaintParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Картина, масло Oil Painting function
oilpaintEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для эффекта Подсвеченные края Edge Detect effect
EdgeDetectEffect edgedetectEffect = new EdgeDetectEffect();
PropertyCollection edgeProps = edgedetectEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken EdgeParameters = new PropertyBasedEffectConfigToken(edgeProps);
EdgeParameters.SetPropertyValue(EdgeDetectEffect.PropertyNames.Angle, Amount1); // fix
edgedetectEffect.SetRenderInfo(EdgeParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Подсвеченные края Edge Detect function
edgedetectEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для эффекта Подсветка Relief effect
ReliefEffect reliefEffect = new ReliefEffect();
PropertyCollection reliefProps = reliefEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken ReliefParameters = new PropertyBasedEffectConfigToken(reliefProps);
ReliefParameters.SetPropertyValue(ReliefEffect.PropertyNames.Angle, Amount1); // fix
reliefEffect.SetRenderInfo(ReliefParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов эффекта Подсветка Relief function
reliefEffect.Render(new Rectangle[1] {rect},0,1);

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

// Настройка параметров для коррекции Сепия Sepia effect
SepiaEffect sepiaEffect = new SepiaEffect();
PropertyCollection sepiaProps = sepiaEffect.CreatePropertyCollection();
PropertyBasedEffectConfigToken SepiaParameters = new PropertyBasedEffectConfigToken(sepiaProps);
sepiaEffect.SetRenderInfo(SepiaParameters, new RenderArgs(dst), new RenderArgs(src));
// Вызов коррекции Сепия Sepia function
sepiaEffect.Render(new Rectangle[1] {rect},0,1);

Унарные операции над пикселями

Добавлено: 10 июл 2010, 12:25
xmario
Унарные операции над пикселями в paint.net

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

Что бы включить унарную операцию необходимо сначала в коде определить ее. Вот примеры нескольких строк для определения разных встроенных эффектов paint.net как унарных операций.
Определение унарных операций осуществляется вне функции Render.

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

private UnaryPixelOps.Desaturate desaturateOp = new UnaryPixelOps.Desaturate();
private UnaryPixelOps.HueSaturationLightness saturationOp;
private UnaryPixelOps.Invert invertOp = new UnaryPixelOps.Invert();
А что бы применить определенную выше унарную операцию к пикселю, просто вызовете внутри функции Render метод, который вы определили.

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

CurrentPixel = desaturateOp.Apply(CurrentPixel);
CurrentPixel = invertOp.Apply(CurrentPixel);
В результате первой операции пиксель станет черно-белым. Помните, что унарная операция применяется только к одному пикселю. А так же что бы увидеть результат не забудьте сохранить пиксель на холсте назначения.

В случае с HueSaturationLightness вызов её немного сложнее.

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

// create a saturation operation based on a UI slider
saturationOp = new UnaryPixelOps.HueSaturationLightness(0, Amount1, 0); // fix
CurrentPixel = saturationOp(CurrentPixel);
Дело в том, что определяем мы унарные операции вне функции Render, где параметры пользовательского интерфейса недоступны. Мы же хотим в качестве второго параметра использовать пользовательскую настройку. Поэтому вне функции Render мы просто обозначили эту унарную операцию, а теперь определяем её, используя параметры. В нашем примере в качестве второго параметра передается значение Amount1 из пользовательского интерфейса. Понятно, что и первый и третий параметры, которые сейчас равны нулю, можно определить подобным образом в интерфейсе пользователя.

Режимы наложения (смешивания) в paint.net

Добавлено: 10 июл 2010, 12:28
xmario
Режимы смешивания (наложения) в paint.net

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

Изображение

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

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

private UserBlendOp normalOp = new UserBlendOps.NormalBlendOp(); // Normal
private UserBlendOp multiplyOp = new UserBlendOps.MultiplyBlendOp(); // Multiply
private UserBlendOp additiveOp = new UserBlendOps.AdditiveBlendOp(); // Additive
private UserBlendOp colorburnOp = new UserBlendOps.ColorBurnBlendOp(); // Color Burn
private UserBlendOp colordodgeOp = new UserBlendOps.ColorDodgeBlendOp(); // Color Dodge
private UserBlendOp reflectOp = new UserBlendOps.ReflectBlendOp(); // Reflect
private UserBlendOp glowOp = new UserBlendOps.GlowBlendOp(); // Glow
private UserBlendOp overlayOp = new UserBlendOps.OverlayBlendOp(); // Overlay
private UserBlendOp differenceOp = new UserBlendOps.DifferenceBlendOp(); // Difference
private UserBlendOp negationOp = new UserBlendOps.NegationBlendOp(); // Negation
private UserBlendOp lightenOp = new UserBlendOps.LightenBlendOp(); // Lighten
private UserBlendOp darkenOp = new UserBlendOps.DarkenBlendOp(); // Darken
private UserBlendOp screenOp = new UserBlendOps.ScreenBlendOp(); // Screen
private UserBlendOp xorOp = new UserBlendOps.XorBlendOp(); // Xor
А вот, собственно, как осуществляется вызов режима смешивания, на примере режима умножение.

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

CurrentPixel = multiplyOp.Apply(CurrentPixel, TopPixel);
По аналогии со смешиванием слоев в paint.net, представьте себе как будто первый пиксель (первый параметр) – это нижний слой, а второй пиксель-параметр – это верхний слой.

Пример создания сложного эффекта в paint.net

Добавлено: 10 июл 2010, 12:31
xmario
Пример создания сложного эффекта для paint.net

Попробуем объединить все знания, которые мы получили на этом уроке и создать какой-нибудь сложный эффект для paint.net. Например, давайте сделаем эффект, который сначала размывает изображение с помощью размытия Гаусса, а потом накладывает размытое изображение на исходное, используя режим смешивания «замена темным».

Используем скрипт по умолчанию из Code Lab. Он выглядит так.

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

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

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;
        }
    }
}
Удалим два ненужных параметра потому, что нам нужен только один для радиуса размытия. Установим значение этого параметра по умолчанию 10. Удалим и ненужные переменные из верхней части кода по умолчанию. Получится такой код.

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

#region UICode
int Amount1=10; //[0,20]Radius
#endregion

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
            // 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;
        }
    }
}
Теперь добавим в наш код эффекта paint.net вызов стандартного эффекта paint.net размытие Гаусса. Код вызова стандартного эффекта мы разместим до цикла обработки изображения, что бы на момент начала обработки пикселей в цикле на холсте назначения уже находилось размытое изображение.

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

#region UICode
int Amount1=10; //[0,20]Radius
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Call the Gaussian Blur effect
    GaussianBlurEffect blurEffect = new GaussianBlurEffect();
    PropertyCollection bProps = blurEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken bParameters = new PropertyBasedEffectConfigToken(bProps);
    bParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1);
    blurEffect.SetRenderInfo(bParameters, new RenderArgs(dst), new RenderArgs(src));
    blurEffect.Render(new Rectangle[1] {rect},0,1);
    
    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;
        }
    }
}
Что бы иметь возможность воспользоваться функцией смешивания, перед функцией Render нам надо сначала инициализировать этот режим, добавив такую строчку кода.

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

private UserBlendOp darkenOp = new UserBlendOps.DarkenBlendOp();
Теперь заменим код обработки пикселей, вставив в него строку.

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

CurrentPixel = darkenOp.Apply(CurrentPixel, dst[x,y]);
Эта строка означает функцию, которая возвращает результат наложение пикселей размытого слоя на исходный слой в режиме затемнения. Обратите внимание, что первым параметром идет, как бы нижний слой, а вторым, как бы верхний. Если поменять параметры местами результат будет другой. Хозяйке на заметку: если вы не получили нужного результата при смешивании когда делаете собственный скрипт, попробуйте поменять параметры смешивания местами. Может быть ошибка в этом? ;)

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

#region UICode
int Amount1=10; //[0,20]Radius
#endregion

private UserBlendOp darkenOp = new UserBlendOps.DarkenBlendOp();

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Call the Gaussian Blur effect
    GaussianBlurEffect blurEffect = new GaussianBlurEffect();
    PropertyCollection bProps = blurEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken bParameters = new PropertyBasedEffectConfigToken(bProps);
    bParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1);
    blurEffect.SetRenderInfo(bParameters, new RenderArgs(dst), new RenderArgs(src));
    blurEffect.Render(new Rectangle[1] {rect},0,1);
    
    // Loop through all pixels and calculate final pixel values
    ColorBgra CurrentPixel;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        for (int x = rect.Left; x < rect.Right; x++)
        {
            // Get the current pixel from source canvas
            CurrentPixel = src[x,y];
            // Combine source pixel with blured dest pixel
            // using the darken blend operation.
            CurrentPixel = darkenOp.Apply(CurrentPixel, dst[x,y]);
            // Save the results for final display.
            dst[x,y] = CurrentPixel;
        }
    }
}
Вот и все! :Yahoo!:

Как сделать код эффекта эффект Feather – размытие контура

Добавлено: 10 июл 2010, 12:34
xmario
Как самому запрограммировать эффект Feather для paint.net – размытие контура выделенной области

Еще один пример сложного эффекта для paint.net от Bolt Bait. Исходный код эффекта Feather, который можно найти в комплекте дополнительных эффектов paint.net для обработки края выделенной области. В эффекте paint.net Feather Selection стандартный эффект Гауссово размытие используется для размытия контуров объектов.

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

// Title: BoltBait's Feather Object v3.0
// Author: BoltBait
// Submenu: Object
// Name: Feather
// URL: http://www.BoltBait.com/pdn
#region UICode
int Amount1 = 2; // [1,10] Feather Radius
#endregion
unsafe void Render(Surface dst, Surface src, Rectangle rect)
{
    // Call the Gaussian Blur effect
    GaussianBlurEffect blurEffect = new GaussianBlurEffect();
    PropertyCollection bProps = blurEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken bParameters = new PropertyBasedEffectConfigToken(bProps);
    bParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1);
    blurEffect.SetRenderInfo(bParameters, new RenderArgs(dst), new RenderArgs(src));
    blurEffect.Render(new Rectangle[1] {rect},0,1);
    // Loop through all pixels fixing up the alpha values
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        ColorBgra* srcPtr = src.GetPointAddressUnchecked(rect.Left, y);
        ColorBgra* dstPtr = dst.GetPointAddressUnchecked(rect.Left, y);
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra CurrentPixel = *srcPtr;
            ColorBgra DestPixel = *dstPtr;
            // for any source pixel that is not fully transparent...
            if (CurrentPixel.A != 0)
            {
                // combine the source pixel color with the dest pixel alpha
                CurrentPixel.A = DestPixel.A;
                *dstPtr = CurrentPixel;
            }
            srcPtr++;
            dstPtr++;
        }
    }
}
В этом случае автор использует небезопасные указатели. Это ускоряет работу эффекта. Для тех, кто понимает в программировании, этот пример может оказаться полезным.

Ограничения при создании собственных сложных эффектов paint.

Добавлено: 10 июл 2010, 12:36
xmario
Ограничения при создании собственных сложных эффектов paint.net с помощью Code Lab

Огромным плюсом Code Lab для paint.net является то, что создавать с его помощью эффекты для paint.net существенно проще, чем используя Visual Studio. Так же для создания эффектов в Code Lab в принципе достаточно минимальных знаний о программировании. Тем не менее, у Code Lab есть и определенные ограничения.

Во-первых, описанным выше способом нельзя использовать встроенный стандартный эффект paint.net облака. Точнее, вызвать эффект можно, но работать он будет неправильно. Для правильной работы облака эффекта необходимо инициализировать в функцию OnSetRenderInfo, которая недоступна в CodeLab. Если вы хотите создать сложный эффект, который использует облака, вам нужно использовать Visual Studio.

И, во-вторых, как мы выяснили в этом уроке, нам доступны всего две поверхности исходный холст и холст на значения, таким образом вызвать встроенный эффект paint.net мы можем только один. Можно конечно попробовать обойти это ограничение и создать собственную новую поверхность или матрицу, но, как утверждают профессионалы, это не очень хороший путь. Такой эффект будет работать долго и требовать много памяти.