]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/scout-gfx/include/pt_icon
update
[l4.git] / l4 / pkg / scout-gfx / include / pt_icon
1 // vi:ft=cpp
2 /*
3  * (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #pragma once
11
12 #include <l4/scout-gfx/icon>
13 #include <l4/mag-gfx/canvas>
14
15 #include <cstring>
16
17 namespace Scout_gfx { namespace Pt {
18
19 template <typename PT>
20 class Icon : public Scout_gfx::Icon
21 {
22 private:
23   class Icon_data
24   {
25   public:
26     typedef typename PT::Pixel Pixel;
27
28   private:
29     Pixel *_p;
30     unsigned char *_a;
31
32     void const *_src_id;
33     mutable int _ref_cnt;
34     mutable Icon_data const *_n;
35
36     static Icon_data const *_f;
37
38   public:
39     static Icon_data const *find(void const *src_id)
40     {
41       Icon_data const *x = _f;
42       while (x)
43         {
44           if (x->_src_id == src_id)
45             return x;
46
47           x = x->_n;
48         }
49
50       return 0;
51     }
52
53     int ref_cnt() const { return _ref_cnt; }
54     int put() const { return --_ref_cnt; }
55     void get() const { ++_ref_cnt; }
56     void const *src_id() const { return _src_id; }
57
58
59     Icon_data(void const *src_id, Area const &size)
60     : _src_id(src_id), _n(_f)
61     {
62       unsigned long s = size.pixels() * (sizeof(Pixel) + 1);
63       // printf("create icon data %p for %p (%lu bytes)\n", this, src_id, s);
64       unsigned char *b = (unsigned char*)malloc(s);
65       if (!b)
66         return;
67
68       _p = reinterpret_cast<Pixel*>(b);
69       _a = b + size.pixels() * sizeof(Pixel);
70
71       _f = this;
72       memset(b, 0, s);
73     }
74
75     ~Icon_data()
76     {
77       // printf("release icon data: %p for %p\n", this, _src_id);
78       Icon_data const **x = &_f;
79       while (*x)
80         {
81           if (*x == this)
82             {
83               *x = _n;
84               break;
85             }
86           x = &(*x)->_n;
87         }
88       free(_p);
89     }
90
91     Pixel const *pixel() const { return _p; }
92     Pixel *pixel() { return _p; }
93
94     unsigned char const *alpha() const { return _a; }
95     unsigned char *alpha() { return _a; }
96   };
97
98   typedef typename PT::Pixel Pixel;
99
100   Area _s;
101   Icon_data const *_d;
102
103 public:
104
105   /**
106    * Constructor
107    */
108   Icon();
109
110   void set_geometry(Rect const &s)
111   {
112     _pos = s.p1();
113     _size = _s.max(s.area());
114   }
115
116   /**
117    * Define new icon pixels from rgba buffer
118    *
119    * \param vshift  vertical shift of pixels
120    * \param shadow  shadow divisor, low value -> dark shadow
121    *                special case zero -> no shadow
122    *
123    * The buffer must contains W*H pixels. Each pixels consists
124    * of four bytes, red, green, blue, and alpha.
125    */
126   void rgba(void const *src, Area const &size, int vshift = 0, int shadow = 4);
127
128   /**
129    * Define icon to be a glow of an rgba image
130    *
131    * \param src  source rgba image to extract the glow's shape from
132    * \param c    glow color
133    */
134   void glow(unsigned char const *src, Area const &size, Mag_gfx::Rgba32::Color c);
135
136
137   /**
138    * Element interface
139    */
140   void draw(Canvas *c, Point const &p);
141   Widget *find(Point const &);
142 };
143
144
145 /**********
146  ** Icon **
147  **********/
148
149 template <typename PT>
150 Icon<PT>::Icon() : _d(0)
151 {}
152
153
154 template <typename PT>
155 void
156 Icon<PT>::rgba(void const *_src, Area const &size, int vshift, int shadow)
157 {
158   using Mag_gfx::Rgba32;
159   using Mag_gfx::color_conv;
160
161   typedef typename PT::Pixel Pixel;
162   typedef typename PT::Color Color;
163
164   if (_d && _d->put() == 0)
165     delete const_cast<Icon_data*>(_d);
166
167   _size = size.max(_size);
168
169   char const *id = (char const *)_src + vshift * 10 + shadow;
170   // printf("icon from rgba[%dx%d]: %p %d %d -> %p\n", size.w(), size.h(), _src, vshift, shadow, id);
171   _s = size;
172   if (Icon_data const *d = Icon_data::find(id))
173     {
174       d->get();
175       _d = d;
176       return;
177     }
178
179   Icon_data *n = new Icon_data(id, size);
180   Pixel *_pixel = n->pixel();
181   unsigned char *_alpha = n->alpha();
182   
183   if (shadow == 0)
184     vshift = 0;
185
186   Rgba32::Pixel const *src = reinterpret_cast<Rgba32::Pixel const *>(_src);
187   src += _s.w() * vshift;
188   /* convert rgba values to pixel type and alpha channel */
189   for (int j = (_s.h() - vshift) * _s.w();
190        j > 0;
191        --j, ++src, ++_alpha, ++_pixel)
192     {
193       Rgba32::Color s = *src;
194       *_pixel = color_conv<Color>(s);
195       *_alpha = s.a();
196     }
197
198   n->get();
199   _d = n;
200
201   /* handle special case of no shadow */
202   if (shadow == 0)
203     return;
204
205   _pixel = n->pixel() + 3 * _s.w();
206   _alpha = n->alpha() + 3 * _s.w();
207   /* generate shadow shape from blurred alpha channel */
208   /* apply shadow to pixels */
209   Color shcol(0, 0, 0);
210   src = reinterpret_cast<Rgba32::Pixel const *>(_src);
211   for (int j = 0; j < _s.h() - 5; j++, src+=3, _pixel += 3, _alpha += 3)
212     for (int i = 0; i < _s.w() - 3; i++)
213       {
214         int v = 0;
215         for (int k = 0; k < 3; k++)
216           for (int l = 0; l < 3; l++)
217             v += Rgba32::Color(src[(k * _s.w()) + l]).a();
218
219         ++src;
220         v >>= shadow;
221         *_pixel = PT::mix(shcol, *_pixel, *_alpha);
222         *_alpha = std::min(255, *_alpha + v);
223         ++_pixel;
224         ++_alpha;
225       }
226 }
227
228 namespace {
229
230 static inline
231 void
232 blur(unsigned char *src, unsigned char *dst, int w, int h)
233 {
234   const int kernel = 3;
235   int scale  = (kernel*2 + 1)*(kernel*2 + 1);
236
237   scale = (scale*210)>>8;
238   for (int j = kernel; j < h - kernel; j++)
239     for (int i = kernel; i < w - kernel; i++)
240       {
241         int v = 0;
242         for (int k = -kernel; k <= kernel; k++)
243           for (int l = -kernel; l <= kernel; l++)
244             v += src[w*(j + k) + (i + l)];
245
246         dst[w*j + i] = std::min(v/scale, 255);
247       }
248 }
249
250 /**
251  * Copy pixel with alpha
252  */
253 template <typename PT>
254 static inline
255 void
256 transfer_pixel(PT const *src, int src_a, int alpha, PT *dst)
257 {
258   if (src_a)
259     {
260       int register a = (src_a * alpha)>>8;
261       if (a) *dst = PT::Traits::mix(*dst, *src, a);
262     }
263 }
264
265
266 /*
267  * An Icon has the following layout:
268  *
269  *  P1---+--------+----+
270  *  | cs |   hs   | cs |   top row
271  *  +----P2-------+----+
272  *  |    |        |    |
273  *  | vs |        | vs |   mid row
274  *  |    |        |    |
275  *  +----+--------P3---+
276  *  | cs |   hs   | cs |   low row
277  *  +------------------P4
278  *
279  * cs ... corner slice
280  * hs ... horizontal slice
281  * vs ... vertical slice
282  */
283
284
285 /**
286  * Draw corner slice
287  */
288 template <typename PT>
289 static
290 void
291 draw_cslice(PT const *src, unsigned char const *src_a,
292             int src_pitch, int alpha,
293             char *dst, int dst_pitch, int w, int h)
294 {
295   for (int j = 0; j < h; j++)
296     {
297
298       PT const      *s  = src;
299       unsigned char const *sa = src_a;
300       PT            *d  = reinterpret_cast<PT*>(dst);
301
302       for (int i = 0; i < w; i++, s++, sa++, d++)
303         transfer_pixel(s, *sa, alpha, d);
304
305       src += src_pitch;
306       src_a += src_pitch;
307       dst += dst_pitch;
308     }
309 }
310
311
312 /**
313  * Draw horizontal slice
314  */
315 template <typename PT>
316 static
317 void
318 draw_hslice(PT const *src, unsigned char const *src_a,
319             int src_pitch, int alpha,
320             char *dst, int dst_pitch, int w, int h)
321 {
322   for (int j = 0; j < h; j++)
323     {
324       PT const *s = src;
325       int sa = *src_a;
326       PT  *d =  reinterpret_cast<PT*>(dst);
327
328       for (int i = 0; i < w; i++, d++)
329         transfer_pixel(s, sa, alpha, d);
330
331       src += src_pitch;
332       src_a += src_pitch;
333       dst += dst_pitch;
334     }
335 }
336
337
338 /**
339         for (int j = 0; j < h; j++) {
340
341                 PT   s = *src;
342                 int sa = *src_a;
343                 PT  *d =  dst;
344
345                 for (int i = 0; i < w; i++, d++)
346                         transfer_pixel(s, sa, alpha, d);
347
348                 src += src_pitch, src_a += src_pitch, dst += dst_pitch;
349         }
350  * Draw vertical slice
351  */
352 template <typename PT>
353 static
354 void
355 draw_vslice(PT const *src, unsigned char const *src_a,
356             int, int alpha,
357             char *dst, int dst_pitch, int w, int h)
358 {
359   for (int i = 0; i < w; i++)
360     {
361
362       PT const *s = src;
363       int sa = *src_a;
364       char *d =  dst;
365
366       for (int j = 0; j < h; j++, d += dst_pitch)
367         transfer_pixel(s, sa, alpha, reinterpret_cast<PT*>(d));
368
369       src += 1;
370       src_a += 1;
371       dst += sizeof(PT);
372     }
373 }
374
375
376 /**
377  * Draw center slice
378  */
379 template <typename PT>
380 static
381 void
382 draw_center(PT const *src, unsigned char const *src_a,
383             int, int alpha,
384             char *dst, int dst_pitch, int w, int h)
385 {
386   PT const *s = src;
387   int sa = *src_a;
388
389   for (int j = 0; j < h; j++, dst += dst_pitch)
390     {
391
392       PT  *d =  reinterpret_cast<PT*>(dst);
393
394       for (int i = 0; i < w; i++, d++)
395         transfer_pixel(s, sa, alpha, d);
396     }
397 }
398
399
400 /**
401  * Clip rectangle against clipping region
402  *
403  * The out parameters are the resulting x/y offsets and the
404  * visible width and height.
405  *
406  * \return  1 if rectangle intersects with clipping region,
407  *          0 otherwise
408  */
409 static inline
410 int
411 clip(int px1, int py1, int px2, int py2,
412      Rect const &c,
413      int *out_x, int *out_y, int *out_w, int *out_h)
414 {
415   /* determine intersection of rectangle and clipping region */
416   int x1 = std::max(px1, c.x1());
417   int y1 = std::max(py1, c.y1());
418   int x2 = std::min(px2, c.x2());
419   int y2 = std::min(py2, c.y2());
420
421   *out_w = x2 - x1 + 1;
422   *out_h = y2 - y1 + 1;
423   *out_x = x1 - px1;
424   *out_y = y1 - py1;
425
426   return (*out_w > 0) && (*out_h > 0);
427 }
428
429 } // end of internal stuff
430
431 template <typename PT>
432 void
433 Icon<PT>::glow(unsigned char const *_src, Area const &size,
434                Mag_gfx::Rgba32::Color c)
435 {
436   using Mag_gfx::Rgba32;
437   using Mag_gfx::color_conv;
438
439   typedef typename PT::Color MColor;
440
441   if (_d && _d->put() == 0)
442     delete const_cast<Icon_data*>(_d);
443
444   char const *id = (char const *)_src -10;
445   _s = size;
446   if (Icon_data const *d = Icon_data::find(id))
447     {
448       d->get();
449       _d = d;
450       return;
451     }
452
453   Icon_data *n = new Icon_data(id, size);
454   Pixel *_pixel = n->pixel();
455   unsigned char *_alpha = n->alpha();
456
457   n->get();
458   _d = n;
459
460   Rgba32::Pixel const *src = reinterpret_cast<Rgba32::Pixel const*>(_src);
461   /* extract shape from alpha channel of rgba source image */
462   for (int j = 0; j < _s.pixels(); j++, ++src)
463     *(_alpha++) = Rgba32::Color(*src).a();
464
465   unsigned char *_b = (unsigned char *)malloc(_s.pixels());
466   _alpha = n->alpha();
467   for (int i = 0; i < 2; i++)
468     {
469       blur(_alpha, _b, _s.w(), _s.h());
470       blur(_b, _alpha, _s.w(), _s.h());
471     }
472
473   free(_b);
474
475   /* assign pixels and alpha */
476   MColor s = color_conv<MColor>(c);
477   for (int j = 0; j < _s.pixels(); j++)
478     _pixel[j] = s;
479 }
480
481
482 template <typename PT>
483 void
484 Icon<PT>::draw(Canvas *c, Point const &p)
485 {
486   typedef typename PT::Pixel Pixel;
487   typedef typename PT::Color Color;
488
489   if (!_d)
490     return;
491
492   Pixel const *const _pixel = _d->pixel();
493   unsigned char const *const _alpha = _d->alpha();
494
495   char *addr = (char *)c->buffer();
496
497   if (!addr || (_icon_alpha == 0))
498     return;
499
500   Rect const cl = c->clip();
501   int const bpl = c->bytes_per_line();
502
503   /* determine point positions */
504   Point const p1 = p;
505   Point const p4 = p1 + Point(_size.w(), _size.h());
506   Point const p2 = p1 + Point(_s.w() / 2, _s.h() / 2);
507   Point const p3 = p2.max(p4 - Point(_s.w() / 2, _s.h() / 2));
508
509   const int tx1 = 0;
510   const int ty1 = 0;
511   const int tx4 = _s.w();
512   const int ty4 = _s.h();
513   const int tx2 = _s.w()/2;
514   const int ty2 = _s.h()/2;
515   const int tx3 = std::max(tx4 - _s.w()/2, tx2);
516   const int ty3 = std::max(ty4 - _s.h()/2, ty2);
517
518   Pixel const *src   = _pixel + _s.w()*ty1;
519   unsigned char const *src_a = _alpha + _s.w()*ty1;
520   int dx, dy, w, h;
521
522   /*
523    * top row
524    */
525
526   if (clip(p1.x(), p1.y(), p2.x() - 1, p2.y() - 1, cl, &dx, &dy, &w, &h))
527     draw_cslice(src + tx1 + dy*_s.w() + dx, src_a + tx1 + dy*_s.w() + dx, _s.w(), _icon_alpha,
528         addr + (p1.y() + dy)*bpl + (p1.x() + dx) * sizeof(Pixel), bpl, w, h);
529
530   if (clip(p2.x(), p1.y(), p3.x() - 1, p2.y() - 1, cl, &dx, &dy, &w, &h))
531     draw_hslice(src + tx2 + dy*_s.w(), src_a + tx2 + dy*_s.w(), _s.w(), _icon_alpha,
532         addr + (p1.y() + dy)*bpl + (p2.x() + dx) * sizeof(Pixel), bpl, w, h);
533
534   if (clip(p3.x(), p1.y(), p4.x() - 1, p2.y() - 1, cl, &dx, &dy, &w, &h))
535     draw_cslice(src + tx3 + dy*_s.w() + dx, src_a + tx3 + dy*_s.w() + dx, _s.w(), _icon_alpha,
536         addr + (p1.y() + dy)*bpl + (p3.x() + dx) * sizeof(Pixel), bpl, w, h);
537
538   /*
539    * mid row
540    */
541
542   src   = _pixel + _s.w()*ty2;
543   src_a = _alpha + _s.w()*ty2;
544
545   if (clip(p1.x(), p2.y(), p2.x() - 1, p3.y() - 1, cl, &dx, &dy, &w, &h))
546     draw_vslice(src + tx1 + dx, src_a + tx1 + dx, _s.w(), _icon_alpha,
547         addr + (p2.y() + dy)*bpl + (p1.x() + dx) * sizeof(Pixel), bpl, w, h);
548
549   if (clip(p2.x(), p2.y(), p3.x() - 1, p3.y() - 1, cl, &dx, &dy, &w, &h))
550     draw_center(src + tx2, src_a + tx2, _s.w(), _icon_alpha,
551         addr + (p2.y() + dy)*bpl + (p2.x() + dx) * sizeof(Pixel), bpl, w, h);
552
553   if (clip(p3.x(), p2.y(), p4.x() - 1, p3.y() - 1, cl, &dx, &dy, &w, &h))
554     draw_vslice(src + tx3 + dx, src_a + tx3 + dx, _s.w(), _icon_alpha,
555         addr + (p2.y() + dy)*bpl + (p3.x() + dx) * sizeof(Pixel), bpl, w, h);
556
557   /*
558    * low row
559    */
560
561   src   = _pixel + _s.w()*ty3;
562   src_a = _alpha + _s.w()*ty3;
563
564   if (clip(p1.x(), p3.y(), p2.x() - 1, p4.y() - 1, cl, &dx, &dy, &w, &h))
565     draw_cslice(src + tx1 + dy*_s.w() + dx, src_a + tx1 + dy*_s.w() + dx, _s.w(), _icon_alpha,
566         addr + (p3.y() + dy)*bpl + (p1.x() + dx) * sizeof(Pixel), bpl, w, h);
567
568   if (clip(p2.x(), p3.y(), p3.x() - 1, p4.y() - 1, cl, &dx, &dy, &w, &h))
569     draw_hslice(src + tx2 + dy*_s.w(), src_a + tx2 + dy*_s.w(), _s.w(), _icon_alpha,
570         addr + (p3.y() + dy)*bpl + (p2.x() + dx) * sizeof(Pixel), bpl, w, h);
571
572   if (clip(p3.x(), p3.y(), p4.x() - 1, p4.y() - 1, cl, &dx, &dy, &w, &h))
573     draw_cslice(src + tx3 + dy*_s.w() + dx, src_a + tx3 + dy*_s.w() + dx, _s.w(), _icon_alpha,
574         addr + (p3.y() + dy)*bpl + (p3.x() + dx) * sizeof(Pixel), bpl, w, h);
575 }
576
577
578 template <typename PT>
579 Widget *
580 Icon<PT>::find(Point const &p)
581 {
582   if (!Widget::find(p) || !_d)
583     return 0;
584
585   unsigned char const *_alpha = _d->alpha();
586
587   Point n = p - _pos;
588   // FIXME: also support horizontally scaled icons
589   /* check icon boundaries (the height is flexible) */
590   if (!Rect(_size).contains(n))
591     return 0;
592
593   int x, y;
594   if (n.x() <= _s.w() / 2)
595     x = n.x();
596   else if (n.x() > _size.w() - _s.w() / 2)
597     x = n.x() - _size.w() + _s.w();
598   else
599     x = _s.w()/2;
600
601
602   if (n.y() <= _s.h() / 2)
603     y = n.y();
604   else if (n.y() > _size.h() - _s.h() / 2)
605     y = n.y() - _size.h() + _s.h();
606   else
607     y = _s.h() / 2;
608
609   return _alpha[y * _s.w() + x] ? this : 0;
610 }
611
612 template <typename PT>
613 typename Icon<PT>::Icon_data const *Icon<PT>::Icon_data::_f;
614
615
616 }}