2
// Copyright (C) 2009 Jason Smith
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
14
// You should have received a copy of the GNU General Public License
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
using System.Collections.Generic;
20
using System.Collections.ObjectModel;
23
using System.Threading;
29
using Docky.Interface;
31
namespace Docky.CairoHelper
36
/// Advanced methods for a DockySurface that are not fit for "general" consumption, usually due to
37
/// performance implications or very specific use cases.
39
public static class DockySurface_Extensions
42
public static void ShowWithOptions (this DockySurface self, DockySurface target, PointD point, double zoom, double rotation, double opacity)
45
throw new ArgumentNullException ("target");
47
Cairo.Context cr = target.Context;
50
cos = Math.Cos (rotation);
51
sin = Math.Sin (rotation);
52
Matrix m = new Matrix (cos, sin, 0 - sin, cos, point.X, point.Y);
56
cr.Scale (zoom, zoom);
58
cr.SetSource (self.Internal,
62
cr.PaintWithAlpha (opacity);
67
public static void ShowAsReflection (this DockySurface self, DockySurface target, PointD point, double zoom,
68
double rotation, double opacity, double height, DockPosition position)
71
throw new ArgumentNullException ("target");
73
Cairo.Context cr = target.Context;
76
case DockPosition.Left:
77
point.X -= self.Width * zoom + height;
79
case DockPosition.Top:
80
point.Y -= self.Height * zoom + height;
82
case DockPosition.Right:
83
point.X += self.Width * zoom + height;
85
case DockPosition.Bottom:
86
point.Y += self.Height * zoom + height;
91
cos = Math.Cos (rotation);
92
sin = Math.Sin (rotation);
93
Matrix m = new Matrix (cos, sin, 0 - sin, cos, point.X, point.Y);
97
cr.Scale (zoom, zoom);
99
if (position == DockPosition.Left || position == DockPosition.Right)
104
cr.SetSource (self.Internal,
106
0 - self.Height / 2);
108
cr.PaintWithAlpha (opacity * .3);
110
cr.IdentityMatrix ();
113
public static void ShowAtEdge (this DockySurface self, DockySurface target, PointD point, DockPosition position)
116
throw new ArgumentNullException ("target");
118
Cairo.Context cr = target.Context;
123
case DockPosition.Top:
126
case DockPosition.Left:
127
y -= self.Height / 2;
129
case DockPosition.Right:
131
y -= self.Height / 2;
133
case DockPosition.Bottom:
139
cr.SetSource (self.Internal, (int) x, (int) y);
143
public static void TileOntoSurface (this DockySurface self, DockySurface target, Gdk.Rectangle area, int edgeBuffer, double tilt, DockPosition orientation)
145
if (orientation == DockPosition.Left || orientation == DockPosition.Right) {
148
if (orientation == DockPosition.Left)
149
x -= self.Width - area.Width;
151
Cairo.Context cr = target.Context;
154
cr.Rectangle (area.X, area.Y, area.Width, edgeBuffer);
155
cr.SetSource (self.Internal, x, area.Y);
158
int maxMiddleMove = self.Height - 2 * edgeBuffer;
159
int position = area.Y + edgeBuffer;
160
int edgeTarget = area.Y + area.Height - edgeBuffer;
161
while (position < edgeTarget) {
162
int height = Math.Min (edgeTarget - position, maxMiddleMove);
163
cr.Rectangle (area.X, position, area.Width, height);
164
cr.SetSource (self.Internal, x, position - edgeBuffer);
169
cr.Rectangle (area.X, position, area.Width, edgeBuffer);
170
cr.SetSource (self.Internal, x, area.Y + area.Height - self.Height);
175
area.Y += (int) (area.Height * tilt);
176
area.Height -= (int) (area.Height * tilt);
180
if (orientation == DockPosition.Top)
181
y -= self.Height - area.Height;
183
Cairo.Context cr = target.Context;
184
cr.Rectangle (area.X - 100, area.Y, edgeBuffer + 100, area.Height);
186
Matrix m = new Matrix (1, 0, -tilt, 1, 0, y);
190
cr.SetSource (self.Internal, area.X, 0);
193
cr.IdentityMatrix ();
195
int maxMiddleMove = self.Width - 2 * edgeBuffer;
196
int position = area.X + edgeBuffer;
197
int edgeTarget = area.X + area.Width - edgeBuffer;
198
while (position < edgeTarget) {
199
int width = Math.Min (edgeTarget - position, maxMiddleMove);
200
cr.Rectangle (position, area.Y, width, area.Height);
201
cr.SetSource (self.Internal, position - edgeBuffer, y);
206
cr.Rectangle (position, area.Y, edgeBuffer + 100, area.Height);
208
m = new Matrix (1, 0, tilt, 1, 0, y);
211
cr.SetSource (self.Internal, area.X + area.Width - self.Width, 0);
214
cr.IdentityMatrix ();
218
public static Gdk.Pixbuf LoadToPixbuf (this DockySurface self)
221
string tmp = System.IO.Path.GetTempFileName ();
222
self.Internal.WriteToPng (tmp);
223
pbuf = new Pixbuf (tmp);
224
System.IO.File.Delete (tmp);
229
public static DockySurface CreateMask (this DockySurface self, double cutOff, out Gdk.Rectangle extents)
231
ImageSurface original = new ImageSurface (Format.ARGB32, self.Width, self.Height);
233
using (Cairo.Context cr = new Cairo.Context (original)) {
234
cr.Operator = Operator.Source;
235
cr.SetSource (self.Internal);
239
int width = original.Width;
240
int height = original.Height;
241
byte slice = (byte) (byte.MaxValue * cutOff);
249
byte* dataPtr = (byte*) original.DataPtr;
252
for (int y = 0; y < height; y++) {
253
for (int x = 0; x < width; x++) {
254
src = (y * width + x) * 4;
256
bool mask = dataPtr[src + 3] > slice;
258
dataPtr[src + 0] = 0;
259
dataPtr[src + 1] = 0;
260
dataPtr[src + 2] = 0;
261
dataPtr[src + 3] = mask ? byte.MaxValue : (byte) 0;
277
extents = new Gdk.Rectangle (left, top, right - left, bottom - top);
279
return new DockySurface (original);
282
public unsafe static void GaussianBlur (this DockySurface self, int size)
284
// Note: This method is wickedly slow
286
int gaussWidth = size * 2 + 1;
287
double[] kernel = BuildGaussianKernel (gaussWidth);
289
ImageSurface original = new ImageSurface (Format.Argb32, self.Width, self.Height);
290
using (Cairo.Context cr = new Cairo.Context (original))
291
self.Internal.Show (cr, 0, 0);
294
foreach (double d in kernel)
297
for (int i = 0; i < kernel.Length; i++)
298
kernel[i] = kernel[i] / gaussSum;
300
int width = self.Width;
301
int height = self.Height;
303
byte[] src = original.Data;
304
double[] xbuffer = new double[original.Data.Length];
305
double[] ybuffer = new double[original.Data.Length];
307
int dest, dest2, shift, source;
309
byte* srcPtr = (byte*) original.DataPtr;
311
fixed (double* xbufferPtr = xbuffer)
312
fixed (double* ybufferPtr = ybuffer)
313
fixed (double* kernelPtr = kernel) {
315
for (int y = 0; y < height; y++) {
316
for (int x = 0; x < width; x++) {
317
dest = y * width + x;
320
for (int k = 0; k < gaussWidth; k++) {
323
source = dest + shift;
325
if (x + shift <= 0 || x + shift >= width) {
330
xbufferPtr[dest2 + 0] = xbufferPtr[dest2 + 0] + (srcPtr[source + 0] * kernelPtr[k]);
331
xbufferPtr[dest2 + 1] = xbufferPtr[dest2 + 1] + (srcPtr[source + 1] * kernelPtr[k]);
332
xbufferPtr[dest2 + 2] = xbufferPtr[dest2 + 2] + (srcPtr[source + 2] * kernelPtr[k]);
333
xbufferPtr[dest2 + 3] = xbufferPtr[dest2 + 3] + (srcPtr[source + 3] * kernelPtr[k]);
339
for (int y = 0; y < height; y++) {
340
for (int x = 0; x < width; x++) {
341
dest = y * width + x;
344
for (int k = 0; k < gaussWidth; k++) {
347
source = dest + shift * width;
349
if (y + shift <= 0 || y + shift >= height) {
354
ybufferPtr[dest2 + 0] = ybufferPtr[dest2 + 0] + (xbufferPtr[source + 0] * kernelPtr[k]);
355
ybufferPtr[dest2 + 1] = ybufferPtr[dest2 + 1] + (xbufferPtr[source + 1] * kernelPtr[k]);
356
ybufferPtr[dest2 + 2] = ybufferPtr[dest2 + 2] + (xbufferPtr[source + 2] * kernelPtr[k]);
357
ybufferPtr[dest2 + 3] = ybufferPtr[dest2 + 3] + (xbufferPtr[source + 3] * kernelPtr[k]);
362
for (int i = 0; i < src.Length; i++)
363
srcPtr[i] = (byte) ybufferPtr[i];
366
self.Context.Operator = Operator.Source;
367
self.Context.SetSource (original);
368
self.Context.Paint ();
372
static double[] BuildGaussianKernel (int gaussWidth)
374
if (gaussWidth % 2 != 1)
375
throw new ArgumentException ("Gaussian Width must be odd");
377
double[] kernel = new double[gaussWidth];
379
// Maximum value of curve
383
double range = gaussWidth;
385
// Average value of curve
386
double mean = range / sd;
388
for (int i = 0; i < gaussWidth / 2 + 1; i++) {
389
kernel[i] = Math.Pow (Math.Sin (((i + 1) * (Math.PI / 2) - mean) / range), 2) * sd;
390
kernel[gaussWidth - i - 1] = kernel[i];
396
const int AlphaPrecision = 16;
397
const int ParamPrecision = 7;
399
public unsafe static void ExponentialBlur (this DockySurface self, int radius)
401
self.ExponentialBlur (radius, new Gdk.Rectangle (0, 0, self.Width, self.Height));
404
public unsafe static void ExponentialBlur (this DockySurface self, int radius, Gdk.Rectangle area)
409
area.Intersect (new Gdk.Rectangle (0, 0, self.Width, self.Height));
411
int alpha = (int) ((1 << AlphaPrecision) * (1.0 - Math.Exp (-2.3 / (radius + 1.0))));
412
int height = self.Height;
413
int width = self.Width;
415
ImageSurface original;
417
if (self.Internal is ImageSurface) {
418
original = self.Internal as ImageSurface;
421
original = new ImageSurface (Format.Argb32, width, height);
426
using (Cairo.Context cr = new Cairo.Context (original)) {
427
cr.Operator = Operator.Source;
428
cr.SetSource (self.Internal);
433
byte* pixels = (byte*) original.DataPtr;
436
Thread th = new Thread ((ThreadStart) delegate {
437
ExponentialBlurRows (pixels, width, height, 0, height / 2, area.X, area.Right, alpha);
441
ExponentialBlurRows (pixels, width, height, height / 2, height, area.X, area.Right, alpha);
445
th = new Thread ((ThreadStart) delegate {
446
ExponentialBlurColumns (pixels, width, height, 0, width / 2, area.Y, area.Bottom, alpha);
450
ExponentialBlurColumns (pixels, width, height, width / 2, width, area.Y, area.Bottom, alpha);
453
original.MarkDirty ();
456
self.Context.Operator = Operator.Source;
457
self.Context.SetSource (original);
458
self.Context.Paint ();
459
self.Context.Operator = Operator.Over;
464
unsafe static void ExponentialBlurColumns (byte* pixels, int width, int height, int startCol, int endCol, int startY, int endY, int alpha)
466
for (int columnIndex = startCol; columnIndex < endCol; columnIndex++) {
469
byte *column = pixels + columnIndex * 4;
471
zR = column[0] << ParamPrecision;
472
zG = column[1] << ParamPrecision;
473
zB = column[2] << ParamPrecision;
474
zA = column[3] << ParamPrecision;
477
for (int index = width * (startY + 1); index < (endY - 1) * width; index += width) {
478
ExponentialBlurInner (&column[index * 4], ref zR, ref zG, ref zB, ref zA, alpha);
482
for (int index = (endY - 2) * width; index >= startY; index -= width) {
483
ExponentialBlurInner (&column[index * 4], ref zR, ref zG, ref zB, ref zA, alpha);
488
unsafe static void ExponentialBlurRows (byte* pixels, int width, int height, int startRow, int endRow, int startX, int endX, int alpha)
490
for (int rowIndex = startRow; rowIndex < endRow; rowIndex++) {
492
// Get a pointer to our current row
493
byte* row = pixels + rowIndex * width * 4;
495
zR = row[startX + 0] << ParamPrecision;
496
zG = row[startX + 1] << ParamPrecision;
497
zB = row[startX + 2] << ParamPrecision;
498
zA = row[startX + 3] << ParamPrecision;
500
for (int index = startX + 1; index < endX; index++) {
501
ExponentialBlurInner (&row[index * 4], ref zR, ref zG, ref zB, ref zA, alpha);
505
for (int index = endX - 2; index >= startX; index--) {
506
ExponentialBlurInner (&row[index * 4], ref zR, ref zG, ref zB, ref zA, alpha);
511
unsafe static void ExponentialBlurInner (byte* pixel, ref int zR, ref int zG, ref int zB, ref int zA, int alpha)
519
zR += (alpha * ((R << ParamPrecision) - zR)) >> AlphaPrecision;
520
zG += (alpha * ((G << ParamPrecision) - zG)) >> AlphaPrecision;
521
zB += (alpha * ((B << ParamPrecision) - zB)) >> AlphaPrecision;
522
zA += (alpha * ((A << ParamPrecision) - zA)) >> AlphaPrecision;
524
pixel[0] = (byte) (zR >> ParamPrecision);
525
pixel[1] = (byte) (zG >> ParamPrecision);
526
pixel[2] = (byte) (zB >> ParamPrecision);
527
pixel[3] = (byte) (zA >> ParamPrecision);