1 /////////////////////////////////////////////////////////////////////////////////
2 // Paint.NET                                                                   //
3 // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors.     //
4 // Portions Copyright (C) Microsoft Corporation. All Rights Reserved.          //
5 // See license-pdn.txt for full licensing and attribution details.             //
6 //                                                                             //
7 // Ported to Pinta by: Marco Rolappe <m_rolappe@gmx.net>                       //
8 /////////////////////////////////////////////////////////////////////////////////
9 
10 using System;
11 using Cairo;
12 using Pinta.Gui.Widgets;
13 using Pinta.Core;
14 using Mono.Unix;
15 
16 namespace Pinta.Effects
17 {
18 	public class FrostedGlassEffect : BaseEffect
19 	{
20 		public override string Icon {
21 			get { return "Menu.Effects.Distort.FrostedGlass.png"; }
22 		}
23 
24 		public override string Name {
25 			get { return Catalog.GetString ("Frosted Glass"); }
26 		}
27 
28 		public override bool IsConfigurable {
29 			get { return true; }
30 		}
31 
32 		public override string EffectMenuCategory {
33 			get { return Catalog.GetString ("Distort"); }
34 		}
35 
36 		public FrostedGlassData Data {
37 			get { return EffectData as FrostedGlassData; }
38 		}
39 
40 		private Random random = new Random ();
41 
FrostedGlassEffect()42 		public FrostedGlassEffect () {
43 			EffectData = new FrostedGlassData ();
44 		}
45 
LaunchConfiguration()46 		public override bool LaunchConfiguration () {
47 			return EffectHelper.LaunchSimpleEffectDialog (this);
48 		}
49 
50 		#region Algorithm Code Ported From PDN
Render(ImageSurface src, ImageSurface dst, Gdk.Rectangle[] rois)51 		unsafe public override void Render (ImageSurface src, ImageSurface dst, Gdk.Rectangle[] rois) {
52 			int width = src.Width;
53 			int height = src.Height;
54 			int r = Data.Amount;
55 			Random localRandom = this.random;
56 
57 			int* intensityCount = stackalloc int[256];
58 			uint* avgRed = stackalloc uint[256];
59 			uint* avgGreen = stackalloc uint[256];
60 			uint* avgBlue = stackalloc uint[256];
61 			uint* avgAlpha = stackalloc uint[256];
62 			byte* intensityChoices = stackalloc byte[(1 + (r * 2)) * (1 + (r * 2))];
63 
64 			int src_width = src.Width;
65 			ColorBgra* src_data_ptr = (ColorBgra*)src.DataPtr;
66 
67 			foreach (var rect in rois) {
68 				int rectTop = rect.Top;
69 				int rectBottom = rect.GetBottom ();
70 				int rectLeft = rect.Left;
71 				int rectRight = rect.GetRight ();
72 
73 				for (int y = rectTop; y <= rectBottom; ++y) {
74 					ColorBgra* dstPtr = dst.GetPointAddress (rect.Left, y);
75 
76 					int top = y - r;
77 					int bottom = y + r + 1;
78 
79 					if (top < 0) {
80 						top = 0;
81 					}
82 
83 					if (bottom > height) {
84 						bottom = height;
85 					}
86 
87 					for (int x = rectLeft; x <= rectRight; ++x) {
88 						int intensityChoicesIndex = 0;
89 
90 						for (int i = 0; i < 256; ++i) {
91 							intensityCount[i] = 0;
92 							avgRed[i] = 0;
93 							avgGreen[i] = 0;
94 							avgBlue[i] = 0;
95 							avgAlpha[i] = 0;
96 						}
97 
98 						int left = x - r;
99 						int right = x + r + 1;
100 
101 						if (left < 0) {
102 							left = 0;
103 						}
104 
105 						if (right > width) {
106 							right = width;
107 						}
108 
109 						for (int j = top; j < bottom; ++j) {
110 							if (j < 0 || j >= height) {
111 								continue;
112 							}
113 
114 							ColorBgra* srcPtr = src.GetPointAddressUnchecked (src_data_ptr, src_width, left, j);
115 
116 							for (int i = left; i < right; ++i) {
117 								byte intensity = srcPtr->GetIntensityByte ();
118 
119 								intensityChoices[intensityChoicesIndex] = intensity;
120 								++intensityChoicesIndex;
121 
122 								++intensityCount[intensity];
123 
124 								avgRed[intensity] += srcPtr->R;
125 								avgGreen[intensity] += srcPtr->G;
126 								avgBlue[intensity] += srcPtr->B;
127 								avgAlpha[intensity] += srcPtr->A;
128 
129 								++srcPtr;
130 							}
131 						}
132 
133 						int randNum;
134 
135 						lock (localRandom) {
136 							randNum = localRandom.Next (intensityChoicesIndex);
137 						}
138 
139 						byte chosenIntensity = intensityChoices[randNum];
140 
141 						byte R = (byte)(avgRed[chosenIntensity] / intensityCount[chosenIntensity]);
142 						byte G = (byte)(avgGreen[chosenIntensity] / intensityCount[chosenIntensity]);
143 						byte B = (byte)(avgBlue[chosenIntensity] / intensityCount[chosenIntensity]);
144 						byte A = (byte)(avgAlpha[chosenIntensity] / intensityCount[chosenIntensity]);
145 
146 						*dstPtr = ColorBgra.FromBgra (B, G, R, A);
147 						++dstPtr;
148 
149 						// prepare the array for the next loop iteration
150 						for (int i = 0; i < intensityChoicesIndex; ++i) {
151 							intensityChoices[i] = 0;
152 						}
153 					}
154 				}
155 			}
156 		}
157 		#endregion
158 
159 		public class FrostedGlassData : EffectData
160 		{
161 			[Caption ("Amount"), MinimumValue(1), MaximumValue(10)]
162 			public int Amount = 1;
163 		}
164 	}
165 }
166