///////////////////////////////////////////////////////////////////////////////// // Paint.NET // // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. // // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // // See license-pdn.txt for full licensing and attribution details. // // // // Ported to Pinta by: Marco Rolappe // ///////////////////////////////////////////////////////////////////////////////// using System; using Cairo; using Pinta.Gui.Widgets; using Pinta.Core; using Mono.Unix; namespace Pinta.Effects { //[System.ComponentModel.Composition (typeof (BaseEffect))] public class TileEffect : BaseEffect { public override string Icon { get { return "Menu.Effects.Distort.Tile.png"; } } public override string Text { get { return Catalog.GetString ("Tile Reflection"); } } public override bool IsConfigurable { get { return true; } } public override string EffectMenuCategory { get { return Catalog.GetString ("Distort"); } } public TileData Data { get { return EffectData as TileData; } } public TileEffect () { EffectData = new TileData (); } public override bool LaunchConfiguration () { return EffectHelper.LaunchSimpleEffectDialog (this); } #region Algorithm Code Ported From PDN unsafe public override void RenderEffect (ImageSurface src, ImageSurface dst, Gdk.Rectangle[] rois) { int width = dst.Width; int height = dst.Height; float hw = width / 2f; float hh = height / 2f; float sin = (float)Math.Sin (Data.Rotation * Math.PI / 180.0); float cos = (float)Math.Cos (Data.Rotation * Math.PI / 180.0); float scale = (float)Math.PI / Data.TileSize; float intensity = Data.Intensity; intensity = intensity * intensity / 10 * Math.Sign (intensity); int aaLevel = 4; int aaSamples = aaLevel * aaLevel + 1; PointD* aaPoints = stackalloc PointD[aaSamples]; for (int i = 0; i < aaSamples; ++i) { double x = (i * aaLevel) / (double)aaSamples; double y = i / (double)aaSamples; x -= (int)x; // RGSS + rotation to maximize AA quality aaPoints[i] = new PointD ((double)(cos * x + sin * y), (double)(cos * y - sin * x)); } int src_width = src.Width; ColorBgra* src_data_ptr = (ColorBgra*)src.DataPtr; foreach (var rect in rois) { for (int y = rect.Top; y < rect.Bottom; y++) { float j = y - hh; ColorBgra* dstPtr = dst.GetPointAddressUnchecked (rect.Left, y); for (int x = rect.Left; x < rect.Right; x++) { int b = 0; int g = 0; int r = 0; int a = 0; float i = x - hw; for (int p = 0; p < aaSamples; ++p) { PointD pt = aaPoints[p]; float u = i + (float)pt.X; float v = j - (float)pt.Y; float s = cos * u + sin * v; float t = -sin * u + cos * v; s += intensity * (float)Math.Tan (s * scale); t += intensity * (float)Math.Tan (t * scale); u = cos * s - sin * t; v = sin * s + cos * t; int xSample = (int)(hw + u); int ySample = (int)(hh + v); xSample = (xSample + width) % width; // This makes it a little faster if (xSample < 0) { xSample = (xSample + width) % width; } ySample = (ySample + height) % height; // This makes it a little faster if (ySample < 0) { ySample = (ySample + height) % height; } ColorBgra sample = *src.GetPointAddressUnchecked (src_data_ptr, src_width, xSample, ySample); b += sample.B; g += sample.G; r += sample.R; a += sample.A; } *(dstPtr++) = ColorBgra.FromBgra ((byte)(b / aaSamples), (byte)(g / aaSamples), (byte)(r / aaSamples), (byte)(a / aaSamples)); } } } } #endregion public class TileData : EffectData { [MinimumValue(-45), MaximumValue(45)] public double Rotation = 30; [MinimumValue(2), MaximumValue(200)] public int TileSize = 40; [MinimumValue(-20), MaximumValue(20)] public int Intensity = 8; } } }