コード例 #1
0
        private ICanvasImage CreateDiscreteTransfer()
        {
            var discreteTransferEffect = new DiscreteTransferEffect
            {
                Source = bitmapTiger
            };

            float[][] tables =
            {
                new float[] { 0, 1 },
                new float[] { 1, 0 },
                new float[] { 0,0.5f, 1 },
            };

            // Animation switches between different quantisation color transfer tables.
            animationFunction = elapsedTime =>
            {
                int t = (int)(elapsedTime * 2);

                discreteTransferEffect.RedTable   = tables[t % tables.Length];
                discreteTransferEffect.GreenTable = tables[(t / tables.Length) % tables.Length];
                discreteTransferEffect.BlueTable  = tables[(t / tables.Length / tables.Length) % tables.Length];
            };

            return(discreteTransferEffect);
        }
コード例 #2
0
        private static ICanvasImage CreateDiscreteTranferEffect(ICanvasImage canvasImage)
        {
            var ef = new DiscreteTransferEffect
            {
                Source     = canvasImage,
                RedTable   = new float[] { 1, 0 },
                GreenTable = new float[] { 1, 0 },
                BlueTable  = new float[] { 0, 1 },
            };

            return(ef);
        }
コード例 #3
0
        private ICanvasImage CreateDiscreteTransfer()
        {
            var discreteTransferEffect = new DiscreteTransferEffect
            {
                Source = bitmapTiger
            };

            float[][] tables =
            {
                new float[] { 0, 1 },
                new float[] { 1, 0 },
                new float[] { 0, 0.5f, 1 },
            };

            // Animation switches between different quantisation color transfer tables.
            animationFunction = elapsedTime =>
            {
                int t = (int)(elapsedTime * 2);

                discreteTransferEffect.RedTable = tables[t % tables.Length];
                discreteTransferEffect.GreenTable = tables[(t / tables.Length) % tables.Length];
                discreteTransferEffect.BlueTable = tables[(t / tables.Length / tables.Length) % tables.Length];
            };

            return discreteTransferEffect;
        }
コード例 #4
0
ファイル: AppIconGenerator.cs プロジェクト: N500/Win2D
        async Task GenerateIcon(AppInfo appInfo, IconInfo iconInfo, StorageFolder folder)
        {
            // Draw the icon image into a command list.
            var commandList = new CanvasCommandList(device);

            using (var ds = commandList.CreateDrawingSession())
            {
                appInfo.DrawIconImage(ds, iconInfo);
            }

            ICanvasImage iconImage = commandList;

            // Rasterize into a rendertarget.
            var renderTarget = new CanvasRenderTarget(device, iconInfo.Width, iconInfo.Height, 96);

            using (var ds = renderTarget.CreateDrawingSession())
            {
                // Initialize with the appropriate background color.
                ds.Clear(iconInfo.TransparentBackground ? Colors.Transparent : appInfo.BackgroundColor);

                // Work out where to position the icon image.
                var imageBounds = iconImage.GetBounds(ds);

                imageBounds.Height *= 1 + iconInfo.BottomPadding;

                float scaleUpTheSmallerIcons = Math.Max(1, 1 + (60f - iconInfo.Width) / 50f);

                float imageScale = appInfo.ImageScale * scaleUpTheSmallerIcons;

                var transform = Matrix3x2.CreateTranslation((float)-imageBounds.X, (float)-imageBounds.Y) *
                                Utils.GetDisplayTransform(renderTarget.Size.ToVector2(), new Vector2((float)imageBounds.Width, (float)imageBounds.Height)) *
                                Matrix3x2.CreateScale(imageScale, renderTarget.Size.ToVector2() / 2);

                if (iconInfo.Monochrome)
                {
                    // Optionally convert to monochrome.
                    iconImage = new DiscreteTransferEffect
                    {
                        Source = new Transform2DEffect
                        {
                            Source = new LuminanceToAlphaEffect {
                                Source = iconImage
                            },
                            TransformMatrix = transform
                        },

                        RedTable   = new float[] { 1 },
                        GreenTable = new float[] { 1 },
                        BlueTable  = new float[] { 1 },
                        AlphaTable = new float[] { 0, 1 }
                    };
                }
                else
                {
                    ds.Transform = transform;

                    // Optional shadow effect.
                    if (appInfo.AddShadow)
                    {
                        var shadow = new ShadowEffect
                        {
                            Source     = iconImage,
                            BlurAmount = 12,
                        };

                        ds.DrawImage(shadow);
                    }
                }

                // draw the main icon image.
                ds.DrawImage(iconImage);
            }

            // Save to a file.
            using (var stream = await folder.OpenStreamForWriteAsync(iconInfo.Filename, CreationCollisionOption.ReplaceExisting))
            {
                await renderTarget.SaveAsync(stream.AsRandomAccessStream(), CanvasBitmapFileFormat.Png);
            }
        }
コード例 #5
0
        void CreateEffects()
        {
            // The Game of Life is a cellular automaton with very simple rules.
            // Each cell (pixel) can be either alive (white) or dead (black).
            // The state is updated by:
            //
            //  - for each cell, count how many of its 8 neighbors are alive
            //  - if less than two, the cell dies from loneliness
            //  - if exactly two, the cell keeps its current state
            //  - if exactly three, the cell become alive
            //  - if more than three, the cell dies from overcrowding

            // Step 1: use a convolve matrix to count how many neighbors are alive. This filter
            // also includes the state of the current cell, but with a lower weighting. The result
            // is an arithmetic encoding where (value / 2) indicates how many neighbors are alive,
            // and (value % 2) is the state of the cell itself. This is divided by 18 to make it
            // fit within 0-1 color range.

            countNeighborsEffect = new ConvolveMatrixEffect
            {
                KernelMatrix = new float[]
                {
                    2, 2, 2,
                    2, 1, 2,
                    2, 2, 2
                },

                Divisor    = 18,
                BorderMode = EffectBorderMode.Hard,
            };

            // Step 2: use a color transfer table to map the different states produced by the
            // convolve matrix to whether the cell should live or die. Each pair of entries in
            // this table corresponds to a certain number of live neighbors. The first of the
            // pair is the result if the current cell is dead, or the second if it is alive.

            float[] transferTable =
            {
                0, 0,   // 0 live neighbors -> dead cell
                0, 0,   // 1 live neighbors -> dead cell
                0, 1,   // 2 live neighbors -> cell keeps its current state
                1, 1,   // 3 live neighbors -> live cell
                0, 0,   // 4 live neighbors -> dead cell
                0, 0,   // 5 live neighbors -> dead cell
                0, 0,   // 6 live neighbors -> dead cell
                0, 0,   // 7 live neighbors -> dead cell
                0, 0,   // 8 live neighbors -> dead cell
            };

            liveOrDieEffect = new DiscreteTransferEffect
            {
                Source = countNeighborsEffect,

                RedTable   = transferTable,
                GreenTable = transferTable,
                BlueTable  = transferTable,
            };

            // Step 3: the algorithm is implemented in terms of white = live,
            // black = dead, but we invert these colors before displaying the
            // result, just 'cause I think it looks better that way.

            invertEffect = new LinearTransferEffect
            {
                RedSlope    = -1,
                RedOffset   = 1,
                GreenSlope  = -1,
                GreenOffset = 1,
                BlueSlope   = -1,
                BlueOffset  = 1,
            };

            // Step 4: insert our own DPI compensation effect to stop the system trying to
            // automatically convert DPI for us. The Game of Life simulation always works
            // in pixels (96 DPI) regardless of display DPI. Normally, the system would
            // handle this mismatch automatically and scale the image up as needed to fit
            // higher DPI displays. We don't want that behavior here, because it would use
            // a linear filter while we want nearest neighbor. So we insert a no-op DPI
            // converter of our own. This overrides the default adjustment by telling the
            // system the source image is already the same DPI as the destination canvas
            // (even though it really isn't). We'll handle any necessary scaling later
            // ourselves, using Transform2DEffect to control the interpolation mode.

            var dpiCompensationEffect = new DpiCompensationEffect
            {
                Source    = invertEffect,
                SourceDpi = new Vector2(canvas.Dpi),
            };

            // Step 5: a transform matrix scales up the simulation rendertarget and moves
            // it to the right part of the screen. This uses nearest neighbor filtering
            // to avoid unwanted blurring of the cell shapes.

            transformEffect = new Transform2DEffect
            {
                Source            = dpiCompensationEffect,
                InterpolationMode = CanvasImageInterpolation.NearestNeighbor,
            };
        }
コード例 #6
0
        public void ProcessFrame(ProcessVideoFrameContext context)
        {
            CanvasRenderTarget rt = new CanvasRenderTarget(canvasDevice, context.OutputFrame.Direct3DSurface.Description.Width, context.OutputFrame.Direct3DSurface.Description.Height, 96.0f);

            using (CanvasBitmap input = CanvasBitmap.CreateFromDirect3D11Surface(canvasDevice, context.InputFrame.Direct3DSurface))
                using (CanvasDrawingSession ds = rt.CreateDrawingSession())
                {
                    TimeSpan time = context.InputFrame.RelativeTime.HasValue ? context.InputFrame.RelativeTime.Value : new TimeSpan();

                    float dispX = (float)Math.Cos(time.TotalSeconds) * 75f;
                    float dispY = (float)Math.Sin(time.TotalSeconds) * 75f;

                    ds.Clear(Colors.Black);

                    //var posterizeEffect = new PosterizeEffect()
                    //{
                    //    Source = input,
                    //    BlueValueCount = 2,
                    //    RedValueCount = 2,
                    //    GreenValueCount = 2,
                    //};

                    var transformEffect = new Transform2DEffect()
                    {
                        Source          = input,
                        TransformMatrix = new System.Numerics.Matrix3x2(-1, 0, 0, 1, rt.SizeInPixels.Width, 0),
                    };

                    var discreteTransferEffect = new DiscreteTransferEffect()
                    {
                        Source       = transformEffect,
                        RedTable     = new float[] { 0.0f, 1.0f, 1.0f, 1.0f },
                        GreenTable   = new float[] { 0.0f, 1.0f, 1.0f, 1.0f },
                        BlueTable    = new float[] { 0.0f, 1.0f, 1.0f, 1.0f },
                        AlphaDisable = true,
                    };

                    var dispMap = new ConvolveMatrixEffect()
                    {
                        KernelMatrix = new float[]
                        {
                            0, +1, 0,
                            +1, -4, +1,
                            0, +1, 0,
                        },
                        Source  = discreteTransferEffect,
                        Divisor = 0.2f,
                    };

                    var modEffect = new ArithmeticCompositeEffect()
                    {
                        Source1        = dispMap,
                        Source2        = dispMap,
                        MultiplyAmount = 1,
                    };

                    //var finalPosterizeEffect = new PosterizeEffect()
                    //{
                    //    Source = modEffect,
                    //    BlueValueCount = 2,
                    //    RedValueCount = 2,
                    //    GreenValueCount = 2,
                    //};

                    //var dispMap = new EdgeDetectionEffect()
                    //{
                    //    Source = greyScaleEffect,
                    //    Mode = EdgeDetectionEffectMode.Sobel,
                    //};

                    Rect src = new Rect(rt.SizeInPixels.Width / 3, rt.SizeInPixels.Height / 3, rt.SizeInPixels.Width / 3, rt.SizeInPixels.Height / 3);

                    //ds.DrawImage(input, bottomLeft);
                    //ds.DrawImage(discreteTransferEffect, bottomRight, src);
                    //ds.DrawImage(dispMap, topLeft, src);
                    ds.DrawImage(modEffect, src, src);
                }

            int centerX      = 0;
            int centerY      = 0;
            int cubletWidth  = 0;
            int cubletHeight = 0;

            int analysisAreaX = (int)rt.SizeInPixels.Width * 3 / 10;
            int analysisWidth = (int)rt.SizeInPixels.Width * 4 / 10;

            byte[] analysisHorzBytes = rt.GetPixelBytes(analysisAreaX, (int)rt.SizeInPixels.Height / 2, analysisWidth, 1);

            int analysisAreaY  = (int)rt.SizeInPixels.Height * 3 / 10;
            int analysisHeight = (int)rt.SizeInPixels.Height * 4 / 10;

            byte[] analysisVertBytes = rt.GetPixelBytes((int)rt.SizeInPixels.Width / 2, analysisAreaY, 1, analysisHeight);

            int foundLeft = 0;

            for (int i = 0; i < analysisWidth / 2; i++)
            {
                byte b = analysisHorzBytes[4 * (analysisWidth / 2 - i) + 0];
                byte g = analysisHorzBytes[4 * (analysisWidth / 2 - i) + 1];
                byte r = analysisHorzBytes[4 * (analysisWidth / 2 - i) + 2];
                if ((r > 100 || g > 100 || b > 50) && foundLeft == 0)
                {
                    foundLeft = i;
                }
            }

            int foundRight = 0;

            for (int i = 0; i < analysisWidth / 2; i++)
            {
                byte r = analysisHorzBytes[4 * (analysisWidth / 2 + i) + 0];
                byte g = analysisHorzBytes[4 * (analysisWidth / 2 + i) + 1];
                byte b = analysisHorzBytes[4 * (analysisWidth / 2 + i) + 2];
                if ((r > 100 || g > 100 || b > 50) && foundRight == 0)
                {
                    foundRight = i;
                }
            }

            int foundTop = 0;

            for (int i = 0; i < analysisHeight / 2; i++)
            {
                byte r = analysisVertBytes[4 * (analysisHeight / 2 - i) + 0];
                byte g = analysisVertBytes[4 * (analysisHeight / 2 - i) + 1];
                byte b = analysisVertBytes[4 * (analysisHeight / 2 - i) + 2];
                if ((r > 100 || g > 100 || b > 50) && foundTop == 0)
                {
                    foundTop = i;
                }
            }

            int foundBottom = 0;

            for (int i = 0; i < analysisHeight / 2; i++)
            {
                byte r = analysisVertBytes[4 * (analysisHeight / 2 + i) + 0];
                byte g = analysisVertBytes[4 * (analysisHeight / 2 + i) + 1];
                byte b = analysisVertBytes[4 * (analysisHeight / 2 + i) + 2];
                if ((r > 100 || g > 100 || b > 50) && foundBottom == 0)
                {
                    foundBottom = i;
                }
            }

            centerX      = (-foundLeft + foundRight) / 2 + (int)rt.SizeInPixels.Width / 2;
            centerY      = (-foundTop + foundBottom) / 2 + (int)rt.SizeInPixels.Height / 2;
            cubletWidth  = (int)((foundLeft + foundRight) * 1.2f);
            cubletHeight = (int)((foundTop + foundBottom) * 1.2f);

            // No 2d arrays in WinRT components? Boo.
            // "Error C1113	#using failed on 'rubikscuberecognitionwinrt.winmd'	RubiksCubeRecognitionLib
            Vector2[] cubletCenters = new Vector2[3 * 3];

            cubletCenters[1 * 3 + 1] = new Vector2(centerX, centerY);
            cubletCenters[1 * 3 + 0] = new Vector2(centerX, centerY - cubletHeight);
            cubletCenters[1 * 3 + 2] = new Vector2(centerX, centerY + cubletHeight);
            cubletCenters[0 * 3 + 1] = new Vector2(centerX - cubletWidth, centerY);
            cubletCenters[0 * 3 + 0] = new Vector2(centerX - cubletWidth, centerY - cubletHeight);
            cubletCenters[0 * 3 + 2] = new Vector2(centerX - cubletWidth, centerY + cubletHeight);
            cubletCenters[2 * 3 + 1] = new Vector2(centerX + cubletWidth, centerY);
            cubletCenters[2 * 3 + 0] = new Vector2(centerX + cubletWidth, centerY - cubletHeight);
            cubletCenters[2 * 3 + 2] = new Vector2(centerX + cubletWidth, centerY + cubletHeight);

            cubeletColorsMutex.WaitOne();

            using (CanvasBitmap input = CanvasBitmap.CreateFromDirect3D11Surface(canvasDevice, context.InputFrame.Direct3DSurface))
                using (CanvasRenderTarget output = CanvasRenderTarget.CreateFromDirect3D11Surface(canvasDevice, context.OutputFrame.Direct3DSurface))
                    using (CanvasDrawingSession ds = output.CreateDrawingSession())
                    {
                        var transformEffect = new Transform2DEffect()
                        {
                            Source          = input,
                            TransformMatrix = new System.Numerics.Matrix3x2(-1, 0, 0, 1, output.SizeInPixels.Width, 0),
                        };

                        Rect src = new Rect(0, 0, output.SizeInPixels.Width, output.SizeInPixels.Height);
                        ds.DrawImage(transformEffect, src, src);

                        // Draw a crosshair on the screen
                        ds.FillRectangle(new Rect(output.SizeInPixels.Width / 2 - 3, output.SizeInPixels.Height / 4, 6, output.SizeInPixels.Height / 2), Colors.Gray);
                        ds.FillRectangle(new Rect(output.SizeInPixels.Width / 4, output.SizeInPixels.Height / 2 - 3, output.SizeInPixels.Width / 2, 6), Colors.Gray);

                        if (true)
                        {
                            for (int x = 0; x < 3; x++)
                            {
                                for (int y = 0; y < 3; y++)
                                {
                                    int sampleWidth = 2;

                                    byte[] cubletBytes = input.GetPixelBytes((int)cubletCenters[(2 - x) * 3 + y].X - sampleWidth / 2, (int)cubletCenters[(2 - x) * 3 + y].Y - sampleWidth / 2, sampleWidth, sampleWidth);
                                    int    totalR = 0; int totalG = 0; int totalB = 0;
                                    for (int i = 0; i < sampleWidth * sampleWidth; i++)
                                    {
                                        totalB += cubletBytes[4 * i + 0];
                                        totalG += cubletBytes[4 * i + 1];
                                        totalR += cubletBytes[4 * i + 2];
                                    }

                                    cubletColors[x * 3 + y] = Color.FromArgb(255, (byte)(totalR / (sampleWidth * sampleWidth)), (byte)(totalG / (sampleWidth * sampleWidth)), (byte)(totalB / (sampleWidth * sampleWidth)));
                                }
                            }

                            for (int x = 0; x < 3; x++)
                            {
                                for (int y = 0; y < 3; y++)
                                {
                                    ds.FillRectangle((float)cubletCenters[x * 3 + y].X - 12, (float)cubletCenters[x * 3 + y].Y - 12, 24, 24, Colors.Black);
                                    ds.FillRectangle((float)cubletCenters[x * 3 + y].X - 10, (float)cubletCenters[x * 3 + y].Y - 10, 20, 20, cubletColors[x * 3 + y]);
                                }
                            }
                        }
                    }

            cubeletColorsMutex.ReleaseMutex();
        }
コード例 #7
0
ファイル: AppIconGenerator.cs プロジェクト: fengweijp/Win2D
        async Task GenerateIcon(AppInfo appInfo, IconInfo iconInfo, StorageFolder folder)
        {
            // Draw the icon image into a command list.
            var commandList = new CanvasCommandList(device);

            using (var ds = commandList.CreateDrawingSession())
            {
                appInfo.DrawIconImage(ds, iconInfo);
            }

            ICanvasImage iconImage = commandList;

            // Rasterize into a rendertarget.
            var renderTarget = new CanvasRenderTarget(device, iconInfo.Width, iconInfo.Height, 96);

            using (var ds = renderTarget.CreateDrawingSession())
            {
                // Initialize with the appropriate background color.
                ds.Clear(iconInfo.TransparentBackground ? Colors.Transparent : appInfo.BackgroundColor);

                // Work out where to position the icon image.
                var imageBounds = iconImage.GetBounds(ds);

                imageBounds.Height *= 1 + iconInfo.BottomPadding;

                float scaleUpTheSmallerIcons = Math.Max(1, 1 + (60f - iconInfo.Width) / 50f);

                float imageScale = appInfo.ImageScale * scaleUpTheSmallerIcons;

                var transform = Matrix3x2.CreateTranslation((float)-imageBounds.X, (float)-imageBounds.Y) *
                                Utils.GetDisplayTransform(renderTarget.Size.ToVector2(), new Vector2((float)imageBounds.Width, (float)imageBounds.Height)) *
                                Matrix3x2.CreateScale(imageScale, renderTarget.Size.ToVector2() / 2);

                if (iconInfo.Monochrome)
                {
                    // Optionally convert to monochrome.
                    iconImage = new DiscreteTransferEffect
                    {
                        Source = new Transform2DEffect
                        {
                            Source = new LuminanceToAlphaEffect { Source = iconImage },
                            TransformMatrix = transform
                        },

                        RedTable   = new float[] { 1 },
                        GreenTable = new float[] { 1 },
                        BlueTable  = new float[] { 1 },
                        AlphaTable = new float[] { 0, 1 }
                    };
                }
                else
                {
                    ds.Transform = transform;

                    // Optional shadow effect.
                    if (appInfo.AddShadow)
                    {
                        var shadow = new ShadowEffect
                        {
                            Source = iconImage,
                            BlurAmount = 12,
                        };

                        ds.DrawImage(shadow);
                    }
                }

                // draw the main icon image.
                ds.DrawImage(iconImage);
            }

            // Save to a file.
            using (var stream = await folder.OpenStreamForWriteAsync(iconInfo.Filename, CreationCollisionOption.ReplaceExisting))
            {
                await renderTarget.SaveAsync(stream.AsRandomAccessStream(), CanvasBitmapFileFormat.Png);
            }
        }
コード例 #8
0
ファイル: GameOfLife.xaml.cs プロジェクト: jiatingxiu/Win2D
        void CreateEffects()
        {
            // The Game of Life is a cellular automaton with very simple rules.
            // Each cell (pixel) can be either alive (white) or dead (black).
            // The state is updated by:
            //
            //  - for each cell, count how many of its 8 neighbors are alive
            //  - if less than two, the cell dies from loneliness
            //  - if exactly two, the cell keeps its current state
            //  - if exactly three, the cell become alive
            //  - if more than three, the cell dies from overcrowding

            // Step 1: use a convolve matrix to count how many neighbors are alive. This filter
            // also includes the state of the current cell, but with a lower weighting. The result
            // is an arithmetic encoding where (value / 2) indicates how many neighbors are alive,
            // and (value % 2) is the state of the cell itself. This is divided by 18 to make it
            // fit within 0-1 color range.

            countNeighborsEffect = new ConvolveMatrixEffect
            {
                KernelMatrix = new float[]
                {
                    2, 2, 2,
                    2, 1, 2,
                    2, 2, 2
                },

                Divisor = 18,
                BorderMode = EffectBorderMode.Hard,
            };

            // Step 2: use a color transfer table to map the different states produced by the
            // convolve matrix to whether the cell should live or die. Each pair of entries in
            // this table corresponds to a certain number of live neighbors. The first of the
            // pair is the result if the current cell is dead, or the second if it is alive.

            float[] transferTable =
            {
                0, 0,   // 0 live neighbors -> dead cell
                0, 0,   // 1 live neighbors -> dead cell
                0, 1,   // 2 live neighbors -> cell keeps its current state
                1, 1,   // 3 live neighbors -> live cell
                0, 0,   // 4 live neighbors -> dead cell
                0, 0,   // 5 live neighbors -> dead cell
                0, 0,   // 6 live neighbors -> dead cell
                0, 0,   // 7 live neighbors -> dead cell
                0, 0,   // 8 live neighbors -> dead cell
            };

            liveOrDieEffect = new DiscreteTransferEffect
            {
                Source = countNeighborsEffect,

                RedTable = transferTable,
                GreenTable = transferTable,
                BlueTable = transferTable,
            };

            // Step 3: the algorithm is implemented in terms of white = live,
            // black = dead, but we invert these colors before displaying the
            // result, just 'cause I think it looks better that way.

            invertEffect = new LinearTransferEffect
            {
                RedSlope = -1,
                RedOffset = 1,
                GreenSlope = -1,
                GreenOffset = 1,
                BlueSlope = -1,
                BlueOffset = 1,
            };

            // Step 4: insert our own DPI compensation effect to stop the system trying to
            // automatically convert DPI for us. The Game of Life simulation always works
            // in pixels (96 DPI) regardless of display DPI. Normally, the system would
            // handle this mismatch automatically and scale the image up as needed to fit
            // higher DPI displays. We don't want that behavior here, because it would use
            // a linear filter while we want nearest neighbor. So we insert a no-op DPI
            // converter of our own. This overrides the default adjustment by telling the
            // system the source image is already the same DPI as the destination canvas
            // (even though it really isn't). We'll handle any necessary scaling later
            // ourselves, using Transform2DEffect to control the interpolation mode.

            var dpiCompensationEffect = new DpiCompensationEffect
            {
                Source = invertEffect,
                SourceDpi = new Vector2(canvas.Dpi),
            };

            // Step 5: a transform matrix scales up the simulation rendertarget and moves
            // it to the right part of the screen. This uses nearest neighbor filtering
            // to avoid unwanted blurring of the cell shapes.

            transformEffect = new Transform2DEffect
            {
                Source = dpiCompensationEffect,
                InterpolationMode = CanvasImageInterpolation.NearestNeighbor,
            };
        }