Как получить из класса Effect - PullRequest
0 голосов
/ 28 июня 2019

Textblock не реализует свойство stroke и является закрытым классом. Наиболее распространенным способом решения этой проблемы является создание собственного класса текстового блока из FrameworkElement . Однако недавно я наткнулся на DropShadowEffect и подумал, возможно ли использовать пользовательский эффект для достижения того же самого выделенного текстового результата без работы по реализации всего выделенного текстового блока. (Мне нужен более четкий контур, который мне даст DropShadow.)

С этой целью я попытался создать класс, унаследованный от Effect , но сразу столкнулся с проблемами:

namespace MyNamespace;

public class OutlineEffect : Effect
{
    internal override Channel GetChannelCore(int index)
    {
        //How am I supposed to override an internal class in a Microsoft namespace?
    }

    //...
}

В документации говорится :

Получите из класса Effect, чтобы реализовать собственный растровый эффект. В В большинстве случаев вы будете получать из ShaderEffect

так что я бы предположил, что это возможно. Итак, Как вы получаете от эффекта ?

1 Ответ

0 голосов
/ 02 июля 2019

Вы должны наследовать от ShaderEffect вместо Effect.Вот пример выделения текста с использованием эффекта фильтра обнаружения краев:

Сочетание этого учебного пособия по шейдерам и фильтра распознавания краев Prewitt Мне удалось получить приличный эффект контуравокруг текста.Существуют и другие способы получения аналогичного эффекта, например , создающий собственный текстовый блок контура , но использование эффекта имеет преимущество, заключающееся в рендеринге с использованием графического процессора и общем применении к любому UIElement.Однако мне пришлось много поиграть со свойством EdgeResponse, чтобы получить красивый контур.

Конечный результат в XAML:

<Grid>
    <Grid.Resources>
        <local:EdgeDetectionEffect x:Key="OutlineEffect"
            x:Shared="false"
            EdgeResponse="4.0"
            ActualHeight="{Binding RelativeSource={RelativeSource AncestorType=TextBlock}, Path=ActualHeight}"
            ActualWidth="{Binding RelativeSource={RelativeSource AncestorType=TextBlock}, Path=ActualWidth}"/>
    </Grid.Resources>
    <TextBlock Text="The Crazy Brown Fox Jumped Over the Lazy Dog."
        FontWeight="Bold"
        Foreground="Yellow"
        Effect="{StaticResource OutlineEffect}"/>
</Grid>

Чтобы создать эффект, я сначала создал EdgeDetectionColorEffect.Файл fx (hdld) - это код, который GPU использует для фильтрации изображения.Я скомпилировал его в командной строке Visual Studio с помощью команды:

fxc / T ps_2_0 / E main /Focc.ps EdgeDetectionColorEffect.fx

sampler2D Input : register(s0);
float ActualWidth : register(c0);
float ActualHeight : register(c1);
float4 OutlineColor : register(c2);
float EdgeDetectionResponse : register(c3);

float4 GetNeighborPixel(float2 pixelPoint, float xOffset, float yOffset)
{
    float2 NeighborPoint = {pixelPoint.x + xOffset, pixelPoint.y + yOffset};
    return tex2D(Input, NeighborPoint);
}

// pixel locations:
// 00 01 02
// 10 11 12
// 20 21 22
float main(float2 pixelPoint : TEXCOORD) : COLOR
{

     float wo = 1 / ActualWidth; //WidthOffset
     float ho = 1 / ActualHeight; //HeightOffset

    float4 c00 = GetNeighborPixel(pixelPoint, -wo, -ho); // color of the pixel up and to the left of me.
    float4 c01 = GetNeighborPixel(pixelPoint,  00, -ho);        
    float4 c02 = GetNeighborPixel(pixelPoint,  wo, -ho);
    float4 c10 = GetNeighborPixel(pixelPoint, -wo,   0);
    float4 c11 = tex2D(Input, pixelPoint); // this is the current pixel
    float4 c12 = GetNeighborPixel(pixelPoint,  wo,   0);
    float4 c20 = GetNeighborPixel(pixelPoint, -wo,  ho);
    float4 c21 = GetNeighborPixel(pixelPoint,   0,  ho);
    float4 c22 = GetNeighborPixel(pixelPoint,  wo,  ho);

    float t00 = c00.r + c00.g + c00.b; //total of color channels
    float t01 = c01.r + c01.g + c01.b;
    float t02 = c02.r + c02.g + c02.b;
    float t10 = c10.r + c10.g + c10.b;
    float t11 = c11.r + c11.g + c11.b;
    float t12 = c12.r + c12.g + c12.b;
    float t20 = c20.r + c20.g + c20.b;
    float t21 = c21.r + c21.g + c21.b;
    float t22 = c22.r + c22.g + c22.b;

    //Prewitt - convolve the 9 pixels with:
    //       01 01 01        01 00 -1
    // Gy =  00 00 00   Gx = 01 00 -1
    //       -1 -1 -1        01 00 -1

    float gy = 0.0;  float gx = 0.0;
    gy += t00;       gx += t00;
    gy += t01;       gx += t10;
    gy += t02;       gx += t20;
    gy -= t20;       gx -= t02;
    gy -= t21;       gx -= t12;
    gy -= t22;       gx -= t22;

    if((gy*gy + gx*gx) > EdgeDetectionResponse)
    {
        return OutlineColor;
    }

    return c11;
}

Вот класс эффектов wpf:

public class EdgeDetectionEffect : ShaderEffect
{
    private static PixelShader _shader = new PixelShader { UriSource = new Uri("path to your compiled shader probably called cc.ps", UriKind.Absolute) };

public EdgeDetectionEffect()
{
    PixelShader = _shader;
    UpdateShaderValue(InputProperty);
    UpdateShaderValue(ActualHeightProperty);
    UpdateShaderValue(ActualWidthProperty);
    UpdateShaderValue(OutlineColorProperty);
    UpdateShaderValue(EdgeResponseProperty);
}

public Brush Input
{
     get => (Brush)GetValue(InputProperty);
     set => SetValue(InputProperty, value);
}
public static readonly DependencyProperty InputProperty = 
    ShaderEffect.RegisterPixelShaderSamplerProperty(nameof(Input), 
    typeof(EdgeDetectionEffect), 0);

public double ActualWidth
{
     get => (double)GetValue(ActualWidthProperty);
     set => SetValue(ActualWidthProperty, value);
}
public static readonly DependencyProperty ActualWidthProperty =
    DependencyProperty.Register(nameof(ActualWidth), typeof(double), typeof(EdgeDetectionEffect),
    new UIPropertyMetadata(1.0, PixelShaderConstantCallback(0)));

//notice the PixelShaderConstantCallback(#) - this tells it which GPU register to use (compare the number here to the first few lines of the EdgeDetectionColorEffect.fx file above.

public double ActualHeight
{
     get => (double)GetValue(ActualHeightProperty);
     set => SetValue(ActualHeightProperty, value);
}
public static readonly DependencyProperty ActualHeightProperty =
    DependencyProperty.Register(nameof(ActualHeight), typeof(double), typeof(EdgeDetectionEffect),
    new UIPropertyMetadata(1.0, PixelShaderConstantCallback(1)));

public Color OutlineColor
{
     get => (Color)GetValue(OutlineColorProperty);
     set => SetValue(OutlineColorProperty, value);
}
public static readonly DependencyProperty OutlineColorProperty=
    DependencyProperty.Register(nameof(OutlineColor), typeof(Color), typeof(EdgeDetectionEffect),
    new UIPropertyMetadata(Colors.Black, PixelShaderConstantCallback(2)));

public double EdgeResponse
{
     get => (double)GetValue(EdgeResponseProperty);
     set => SetValue(EdgeResponseProperty, value);
}
public static readonly DependencyProperty EdgeResponseProperty =
    DependencyProperty.Register(nameof(EdgeResponse), typeof(double), typeof(EdgeDetectionEffect),
    new UIPropertyMetadata(4.0, PixelShaderConstantCallback(3)));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...