~ubuntu-branches/ubuntu/lucid/docky/lucid-proposed

« back to all changes in this revision

Viewing changes to Docky/Docky/CairoHelper/DockySurface_Extensions.cs

  • Committer: Bazaar Package Importer
  • Author(s): Christopher James Halse Rogers
  • Date: 2010-02-17 15:10:07 UTC
  • Revision ID: james.westby@ubuntu.com-20100217151007-msxpd0lsj300ndde
Tags: upstream-2.0.0
ImportĀ upstreamĀ versionĀ 2.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//  
 
2
//  Copyright (C) 2009 Jason Smith
 
3
// 
 
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.
 
8
// 
 
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.
 
13
// 
 
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/>.
 
16
// 
 
17
 
 
18
using System;
 
19
using System.Collections.Generic;
 
20
using System.Collections.ObjectModel;
 
21
using System.Linq;
 
22
using System.Text;
 
23
using System.Threading;
 
24
 
 
25
using Cairo;
 
26
using Gdk;
 
27
using Gtk;
 
28
 
 
29
using Docky.Interface;
 
30
 
 
31
namespace Docky.CairoHelper
 
32
{
 
33
 
 
34
 
 
35
        /// <summary>
 
36
        /// Advanced methods for a DockySurface that are not fit for "general" consumption, usually due to
 
37
        /// performance implications or very specific use cases.
 
38
        /// </summary>
 
39
        public static class DockySurface_Extensions
 
40
        {
 
41
 
 
42
                public static void ShowWithOptions (this DockySurface self, DockySurface target, PointD point, double zoom, double rotation, double opacity)
 
43
                {
 
44
                        if (target == null)
 
45
                                throw new ArgumentNullException ("target");
 
46
                        
 
47
                        Cairo.Context cr = target.Context;
 
48
                        
 
49
                        double cos, sin;
 
50
                        cos = Math.Cos (rotation);
 
51
                        sin = Math.Sin (rotation);
 
52
                        Matrix m = new Matrix (cos, sin, 0 - sin, cos, point.X, point.Y);
 
53
                        cr.Transform (m);
 
54
                        
 
55
                        if (zoom != 1)
 
56
                                cr.Scale (zoom, zoom);
 
57
                        
 
58
                        cr.SetSource (self.Internal,
 
59
                                0 - self.Width / 2, 
 
60
                                0 - self.Height / 2);
 
61
                        
 
62
                        cr.PaintWithAlpha (opacity);
 
63
                        
 
64
                        cr.IdentityMatrix ();
 
65
                }
 
66
                
 
67
                public static void ShowAsReflection (this DockySurface self, DockySurface target, PointD point, double zoom, 
 
68
                        double rotation, double opacity, double height, DockPosition position)
 
69
                {
 
70
                        if (target == null)
 
71
                                throw new ArgumentNullException ("target");
 
72
                        
 
73
                        Cairo.Context cr = target.Context;
 
74
                        
 
75
                        switch (position) {
 
76
                        case DockPosition.Left:
 
77
                                point.X -= self.Width * zoom + height;
 
78
                                break;
 
79
                        case DockPosition.Top:
 
80
                                point.Y -= self.Height * zoom + height;
 
81
                                break;
 
82
                        case DockPosition.Right:
 
83
                                point.X += self.Width * zoom + height;
 
84
                                break;
 
85
                        case DockPosition.Bottom:
 
86
                                point.Y += self.Height * zoom + height;
 
87
                                break;
 
88
                        }
 
89
                        
 
90
                        double cos, sin;
 
91
                        cos = Math.Cos (rotation);
 
92
                        sin = Math.Sin (rotation);
 
93
                        Matrix m = new Matrix (cos, sin, 0 - sin, cos, point.X, point.Y);
 
94
                        cr.Transform (m);
 
95
                        
 
96
                        if (zoom != 1)
 
97
                                cr.Scale (zoom, zoom);
 
98
                        
 
99
                        if (position == DockPosition.Left || position == DockPosition.Right)
 
100
                                cr.Scale (-1, 1);
 
101
                        else
 
102
                                cr.Scale (1, -1);
 
103
                        
 
104
                        cr.SetSource (self.Internal, 
 
105
                                0 - self.Width / 2, 
 
106
                                0 - self.Height / 2);
 
107
                        
 
108
                        cr.PaintWithAlpha (opacity * .3);
 
109
                        
 
110
                        cr.IdentityMatrix ();
 
111
                }
 
112
                
 
113
                public static void ShowAtEdge (this DockySurface self, DockySurface target, PointD point, DockPosition position)
 
114
                {
 
115
                        if (target == null)
 
116
                                throw new ArgumentNullException ("target");
 
117
                        
 
118
                        Cairo.Context cr = target.Context;
 
119
                        double x = point.X;
 
120
                        double y = point.Y;
 
121
                        
 
122
                        switch (position) {
 
123
                        case DockPosition.Top:
 
124
                                x -= self.Width / 2;
 
125
                                break;
 
126
                        case DockPosition.Left:
 
127
                                y -= self.Height / 2;
 
128
                                break;
 
129
                        case DockPosition.Right:
 
130
                                x -= self.Width;
 
131
                                y -= self.Height / 2;
 
132
                                break;
 
133
                        case DockPosition.Bottom:
 
134
                                x -= self.Width / 2;
 
135
                                y -= self.Height;
 
136
                                break;
 
137
                        }
 
138
                        
 
139
                        cr.SetSource (self.Internal, (int) x, (int) y);
 
140
                        cr.Paint ();
 
141
                }
 
142
                
 
143
                public static void TileOntoSurface (this DockySurface self, DockySurface target, Gdk.Rectangle area, int edgeBuffer, double tilt, DockPosition orientation)
 
144
                {
 
145
                        if (orientation == DockPosition.Left || orientation == DockPosition.Right) {
 
146
                                
 
147
                                int x = area.X;
 
148
                                if (orientation == DockPosition.Left)
 
149
                                        x -= self.Width - area.Width;
 
150
                                
 
151
                                Cairo.Context cr = target.Context;
 
152
                                
 
153
                                // draw left edge
 
154
                                cr.Rectangle (area.X, area.Y, area.Width, edgeBuffer);
 
155
                                cr.SetSource (self.Internal, x, area.Y);
 
156
                                cr.Fill ();
 
157
                                
 
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);
 
165
                                        cr.Fill ();
 
166
                                        position += height;
 
167
                                }
 
168
                                
 
169
                                cr.Rectangle (area.X, position, area.Width, edgeBuffer);
 
170
                                cr.SetSource (self.Internal, x, area.Y + area.Height - self.Height);
 
171
                                cr.Fill ();
 
172
                                
 
173
                        } else {
 
174
                                if (tilt != 1) {
 
175
                                        area.Y += (int) (area.Height * tilt);
 
176
                                        area.Height -= (int) (area.Height * tilt);
 
177
                                }
 
178
                                
 
179
                                int y = area.Y;
 
180
                                if (orientation == DockPosition.Top)
 
181
                                        y -= self.Height - area.Height;
 
182
                                
 
183
                                Cairo.Context cr = target.Context;
 
184
                                cr.Rectangle (area.X - 100, area.Y, edgeBuffer + 100, area.Height);
 
185
                                
 
186
                                Matrix m = new Matrix (1, 0, -tilt, 1, 0, y);
 
187
                                cr.Transform (m);
 
188
                                
 
189
                                // draw left edge
 
190
                                cr.SetSource (self.Internal, area.X, 0);
 
191
                                cr.Fill ();
 
192
                                
 
193
                                cr.IdentityMatrix ();
 
194
                                
 
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);
 
202
                                        cr.Fill ();
 
203
                                        position += width;
 
204
                                }
 
205
                                
 
206
                                cr.Rectangle (position, area.Y, edgeBuffer + 100, area.Height);
 
207
                                
 
208
                                m = new Matrix (1, 0, tilt, 1, 0, y);
 
209
                                cr.Transform (m);
 
210
                                
 
211
                                cr.SetSource (self.Internal, area.X + area.Width - self.Width, 0);
 
212
                                cr.Fill ();
 
213
 
 
214
                                cr.IdentityMatrix ();
 
215
                        }
 
216
                }
 
217
                
 
218
                public static Gdk.Pixbuf LoadToPixbuf (this DockySurface self)
 
219
                {
 
220
                        Gdk.Pixbuf pbuf;
 
221
                        string tmp = System.IO.Path.GetTempFileName ();
 
222
                        self.Internal.WriteToPng (tmp);
 
223
                        pbuf = new Pixbuf (tmp);
 
224
                        System.IO.File.Delete (tmp);
 
225
 
 
226
                        return pbuf;
 
227
                }
 
228
                
 
229
                public static DockySurface CreateMask (this DockySurface self, double cutOff, out Gdk.Rectangle extents)
 
230
                {
 
231
                        ImageSurface original = new ImageSurface (Format.ARGB32, self.Width, self.Height);
 
232
                        
 
233
                        using (Cairo.Context cr = new Cairo.Context (original)) {
 
234
                                cr.Operator = Operator.Source;
 
235
                                cr.SetSource (self.Internal);
 
236
                                cr.Paint ();
 
237
                        }
 
238
                        
 
239
                        int width = original.Width;
 
240
                        int height = original.Height;
 
241
                        byte slice = (byte) (byte.MaxValue * cutOff);
 
242
                        
 
243
                        int left = width;
 
244
                        int right = 0;
 
245
                        int top = height;
 
246
                        int bottom = 0;
 
247
                        
 
248
                        unsafe {
 
249
                                byte* dataPtr = (byte*) original.DataPtr;
 
250
                                
 
251
                                int src;
 
252
                                for (int y = 0; y < height; y++) {
 
253
                                        for (int x = 0; x < width; x++) {
 
254
                                                src = (y * width + x) * 4;
 
255
                                                
 
256
                                                bool mask = dataPtr[src + 3] > slice;
 
257
                                                
 
258
                                                dataPtr[src + 0] = 0;
 
259
                                                dataPtr[src + 1] = 0;
 
260
                                                dataPtr[src + 2] = 0;
 
261
                                                dataPtr[src + 3] = mask ? byte.MaxValue : (byte) 0;
 
262
                                                
 
263
                                                if (mask) {
 
264
                                                        if (y < top)
 
265
                                                                top = y;
 
266
                                                        if (y > bottom)
 
267
                                                                bottom = y;
 
268
                                                        if (x < left)
 
269
                                                                left = x;
 
270
                                                        if (x > right)
 
271
                                                                right = x;
 
272
                                                }
 
273
                                        }
 
274
                                }
 
275
                        }
 
276
                        
 
277
                        extents = new Gdk.Rectangle (left, top, right - left, bottom - top);
 
278
                        
 
279
                        return new DockySurface (original);
 
280
                }
 
281
                
 
282
                public unsafe static void GaussianBlur (this DockySurface self, int size)
 
283
                {
 
284
                        // Note: This method is wickedly slow
 
285
                        
 
286
                        int gaussWidth = size * 2 + 1;
 
287
                        double[] kernel = BuildGaussianKernel (gaussWidth);
 
288
                        
 
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);
 
292
                        
 
293
                        double gaussSum = 0;
 
294
                        foreach (double d in kernel)
 
295
                                gaussSum += d;
 
296
                        
 
297
                        for (int i = 0; i < kernel.Length; i++)
 
298
                                kernel[i] = kernel[i] / gaussSum;
 
299
                        
 
300
                        int width = self.Width;
 
301
                        int height = self.Height;
 
302
                        
 
303
                        byte[] src = original.Data;
 
304
                        double[] xbuffer = new double[original.Data.Length];
 
305
                        double[] ybuffer = new double[original.Data.Length];
 
306
                        
 
307
                        int dest, dest2, shift, source;
 
308
                        
 
309
                        byte* srcPtr = (byte*) original.DataPtr;
 
310
                        
 
311
                        fixed (double* xbufferPtr = xbuffer)
 
312
                        fixed (double* ybufferPtr = ybuffer) 
 
313
                        fixed (double* kernelPtr = kernel) {
 
314
                                // Horizontal Pass
 
315
                                for (int y = 0; y < height; y++) {
 
316
                                        for (int x = 0; x < width; x++) {
 
317
                                                dest = y * width + x;
 
318
                                                dest2 = dest * 4;
 
319
                                                
 
320
                                                for (int k = 0; k < gaussWidth; k++) {
 
321
                                                        shift = k - size;
 
322
                                                        
 
323
                                                        source = dest + shift;
 
324
                                                        
 
325
                                                        if (x + shift <= 0 || x + shift >= width) {
 
326
                                                                source = dest;
 
327
                                                        }
 
328
                                                        
 
329
                                                        source = source * 4;
 
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]);
 
334
                                                }
 
335
                                        }
 
336
                                }
 
337
                        
 
338
                                // Vertical Pass
 
339
                                for (int y = 0; y < height; y++) {
 
340
                                        for (int x = 0; x < width; x++) {
 
341
                                                dest = y * width + x;
 
342
                                                dest2 = dest * 4;
 
343
                                                
 
344
                                                for (int k = 0; k < gaussWidth; k++) {
 
345
                                                        shift = k - size;
 
346
                                                        
 
347
                                                        source = dest + shift * width;
 
348
                                                        
 
349
                                                        if (y + shift <= 0 || y + shift >= height) {
 
350
                                                                source = dest;
 
351
                                                        }
 
352
                                                        
 
353
                                                        source = source * 4;
 
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]);
 
358
                                                }
 
359
                                        }
 
360
                                }
 
361
                                
 
362
                                for (int i = 0; i < src.Length; i++)
 
363
                                        srcPtr[i] = (byte) ybufferPtr[i];
 
364
                        }
 
365
                        
 
366
                        self.Context.Operator = Operator.Source;
 
367
                        self.Context.SetSource (original);
 
368
                        self.Context.Paint ();
 
369
                        original.Destroy ();
 
370
                }
 
371
                
 
372
                static double[] BuildGaussianKernel (int gaussWidth)
 
373
                {
 
374
                        if (gaussWidth % 2 != 1)
 
375
                                throw new ArgumentException ("Gaussian Width must be odd");
 
376
                        
 
377
                        double[] kernel = new double[gaussWidth];
 
378
                        
 
379
                        // Maximum value of curve
 
380
                        double sd = 255;
 
381
                        
 
382
                        // Width of curve
 
383
                        double range = gaussWidth;
 
384
                        
 
385
                        // Average value of curve
 
386
                        double mean = range / sd;
 
387
                        
 
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];
 
391
                        }
 
392
                        
 
393
                        return kernel;
 
394
                }
 
395
                
 
396
                const int AlphaPrecision = 16; 
 
397
                const int ParamPrecision = 7;
 
398
                
 
399
                public unsafe static void ExponentialBlur (this DockySurface self, int radius)
 
400
                {
 
401
                        self.ExponentialBlur (radius, new Gdk.Rectangle (0, 0, self.Width, self.Height));
 
402
                }
 
403
                
 
404
                public unsafe static void ExponentialBlur (this DockySurface self, int radius, Gdk.Rectangle area)
 
405
                {
 
406
                        if (radius < 1)
 
407
                                return;
 
408
                        
 
409
                        area.Intersect (new Gdk.Rectangle (0, 0, self.Width, self.Height));
 
410
                        
 
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;
 
414
                        
 
415
                        ImageSurface original;
 
416
                        bool owned;
 
417
                        if (self.Internal is ImageSurface) {
 
418
                                original = self.Internal  as ImageSurface;
 
419
                                owned = true;
 
420
                        } else {
 
421
                                original = new ImageSurface (Format.Argb32, width, height);
 
422
                                owned = false;
 
423
                        }
 
424
                        
 
425
                        if (!owned) {
 
426
                                using (Cairo.Context cr = new Cairo.Context (original)) {
 
427
                                        cr.Operator = Operator.Source;
 
428
                                        cr.SetSource (self.Internal);
 
429
                                        cr.Paint ();
 
430
                                }
 
431
                        }
 
432
                        
 
433
                        byte* pixels = (byte*) original.DataPtr;
 
434
                        
 
435
                        // Process Rows
 
436
                        Thread th = new Thread ((ThreadStart) delegate {
 
437
                                ExponentialBlurRows (pixels, width, height, 0, height / 2, area.X, area.Right, alpha);
 
438
                        });
 
439
                        th.Start ();
 
440
                        
 
441
                        ExponentialBlurRows (pixels, width, height, height / 2, height, area.X, area.Right, alpha);
 
442
                        th.Join ();
 
443
                        
 
444
                        // Process Columns
 
445
                        th = new Thread ((ThreadStart) delegate {
 
446
                                ExponentialBlurColumns (pixels, width, height, 0, width / 2, area.Y, area.Bottom, alpha);
 
447
                        });
 
448
                        th.Start ();
 
449
                        
 
450
                        ExponentialBlurColumns (pixels, width, height, width / 2, width, area.Y, area.Bottom, alpha);
 
451
                        th.Join ();
 
452
                        
 
453
                        original.MarkDirty ();
 
454
                        
 
455
                        if (!owned) {
 
456
                                self.Context.Operator = Operator.Source;
 
457
                                self.Context.SetSource (original);
 
458
                                self.Context.Paint ();
 
459
                                self.Context.Operator = Operator.Over;
 
460
                                original.Destroy ();
 
461
                        }
 
462
                }
 
463
                
 
464
                unsafe static void ExponentialBlurColumns (byte* pixels, int width, int height, int startCol, int endCol, int startY, int endY, int alpha)
 
465
                {
 
466
                        for (int columnIndex = startCol; columnIndex < endCol; columnIndex++) {
 
467
                                int zR, zG, zB, zA;
 
468
                                // blur columns
 
469
                                byte *column = pixels + columnIndex * 4;
 
470
                                
 
471
                                zR = column[0] << ParamPrecision;
 
472
                                zG = column[1] << ParamPrecision;
 
473
                                zB = column[2] << ParamPrecision;
 
474
                                zA = column[3] << ParamPrecision;
 
475
                                
 
476
                                // Top to Bottom
 
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);
 
479
                                }
 
480
                                
 
481
                                // Bottom to Top
 
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);
 
484
                                }
 
485
                        }
 
486
                }
 
487
                
 
488
                unsafe static void ExponentialBlurRows (byte* pixels, int width, int height, int startRow, int endRow, int startX, int endX, int alpha)
 
489
                {
 
490
                        for (int rowIndex = startRow; rowIndex < endRow; rowIndex++) {
 
491
                                int zR, zG, zB, zA;
 
492
                                // Get a pointer to our current row
 
493
                                byte* row = pixels + rowIndex * width * 4;
 
494
                                
 
495
                                zR = row[startX + 0] << ParamPrecision;
 
496
                                zG = row[startX + 1] << ParamPrecision;
 
497
                                zB = row[startX + 2] << ParamPrecision;
 
498
                                zA = row[startX + 3] << ParamPrecision;
 
499
                                // Left to Right
 
500
                                for (int index = startX + 1; index < endX; index++) {
 
501
                                        ExponentialBlurInner (&row[index * 4], ref zR, ref zG, ref zB, ref zA, alpha);
 
502
                                }
 
503
                                
 
504
                                // Right to Left
 
505
                                for (int index = endX - 2; index >= startX; index--) {
 
506
                                        ExponentialBlurInner (&row[index * 4], ref zR, ref zG, ref zB, ref zA, alpha);
 
507
                                }
 
508
                        }
 
509
                }
 
510
                
 
511
                unsafe static void ExponentialBlurInner (byte* pixel, ref int zR, ref int zG, ref int zB, ref int zA, int alpha)
 
512
                {
 
513
                        int R, G, B, A;
 
514
                        R = pixel[0];
 
515
                        G = pixel[1];
 
516
                        B = pixel[2];
 
517
                        A = pixel[3];
 
518
                        
 
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;
 
523
                        
 
524
                        pixel[0] = (byte) (zR >> ParamPrecision);
 
525
                        pixel[1] = (byte) (zG >> ParamPrecision);
 
526
                        pixel[2] = (byte) (zB >> ParamPrecision);
 
527
                        pixel[3] = (byte) (zA >> ParamPrecision);
 
528
                }
 
529
        }
 
530
}