Add a mutex lock for Litehtml plugin image cache
[claws.git] / src / plugins / litehtml_viewer / container_linux.cpp
1 /*
2  * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
3  * Copyright(C) 2019 the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (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.
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write tothe Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #include "claws-features.h"
21 #endif
22
23 #include "container_linux.h"
24
25 #include <cairo-ft.h>
26
27 #define _USE_MATH_DEFINES
28 #include <math.h>
29
30 #ifndef M_PI
31 #       define M_PI    3.14159265358979323846
32 #endif
33
34 container_linux::container_linux(void)
35 {
36         m_temp_surface  = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
37         m_temp_cr               = cairo_create(m_temp_surface);
38         g_rec_mutex_init(&m_images_lock);
39 }
40
41 container_linux::~container_linux(void)
42 {
43         clear_images();
44         cairo_surface_destroy(m_temp_surface);
45         cairo_destroy(m_temp_cr);
46         g_rec_mutex_clear(&m_images_lock);
47 }
48
49 int container_linux::pt_to_px( int pt )
50 {
51         GdkScreen* screen = gdk_screen_get_default();
52         double dpi = gdk_screen_get_resolution(screen);
53
54         return (int) ((double) pt * dpi / 72.0);
55 }
56
57 void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
58 {
59         if(!marker.image.empty())
60         {
61                 /*litehtml::tstring url;
62                 make_url(marker.image.c_str(), marker.baseurl, url);
63
64                 lock_images_cache();
65                 images_map::iterator img_i = m_images.find(url.c_str());
66                 if(img_i != m_images.end())
67                 {
68                         if(img_i->second)
69                         {
70                                 draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
71                         }
72                 }
73                 unlock_images_cache();*/
74         } else
75         {
76                 switch(marker.marker_type)
77                 {
78                 case litehtml::list_style_type_circle:
79                         {
80                                 draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5);
81                         }
82                         break;
83                 case litehtml::list_style_type_disc:
84                         {
85                                 fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
86                         }
87                         break;
88                 case litehtml::list_style_type_square:
89                         if(hdc)
90                         {
91                                 cairo_t* cr = (cairo_t*) hdc;
92                                 cairo_save(cr);
93
94                                 cairo_new_path(cr);
95                                 cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
96
97                                 set_color(cr, marker.color);
98                                 cairo_fill(cr);
99                                 cairo_restore(cr);
100                         }
101                         break;
102                 default:
103                         /*do nothing*/
104                         break;
105                 }
106         }
107 }
108
109 void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
110 {
111         litehtml::tstring url;
112         make_url(src, baseurl, url);
113         bool found = false;
114
115         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
116                 const image *i = &(*ii);
117
118                 if (!strcmp(i->first.c_str(), url.c_str())) {
119                         found = true;
120                         break;
121                 }
122         }
123
124         if(!found)
125         {
126                 try
127                 {
128                         GdkPixbuf *img = get_image(url.c_str(), true);
129                         if(img)
130                         {
131                                 m_images.push_back(std::make_pair(url, img));
132                         }
133                 } catch(...)
134                 {
135                         int iii=0;
136                         iii++;
137                 }
138         }
139 }
140
141 void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
142 {
143         litehtml::tstring url;
144         make_url(src, baseurl, url);
145         bool found = false;
146         const image *img = NULL;
147
148         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
149                 const image *i = &(*ii);
150                 if (i->first == url) {
151                         img = i;
152                         found = true;
153                         break;
154                 }
155         }
156
157         if(img != NULL)
158         {
159                 sz.width        = gdk_pixbuf_get_width(img->second);
160                 sz.height       = gdk_pixbuf_get_height(img->second);
161         } else
162         {
163                 sz.width        = 0;
164                 sz.height       = 0;
165         }
166 }
167
168 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
169 {
170         cairo_t* cr = (cairo_t*) hdc;
171         cairo_save(cr);
172         apply_clip(cr);
173
174         rounded_rectangle(cr, bg.border_box, bg.border_radius);
175         cairo_clip(cr);
176
177         cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
178         cairo_clip(cr);
179
180         if(bg.color.alpha)
181         {
182                 set_color(cr, bg.color);
183                 cairo_paint(cr);
184         }
185
186         litehtml::tstring url;
187         make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
188
189         lock_images_cache();
190         bool found = false;
191         const image *img_i = NULL;
192
193         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
194                 const image *i = &(*ii);
195                 if (i->first == url) {
196                         img_i = i;
197                         found = true;
198                         break;
199                 }
200         }
201
202         if(img_i != NULL && img_i->second)
203         {
204                 GdkPixbuf *bgbmp = img_i->second;
205
206                 GdkPixbuf *new_img;
207                 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
208                 {
209                         new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
210                         bgbmp = new_img;
211                 }
212
213                 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
214                 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
215                 cairo_matrix_t flib_m;
216                 cairo_matrix_init_identity(&flib_m);
217                 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
218                 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
219                 cairo_pattern_set_matrix (pattern, &flib_m);
220
221                 switch(bg.repeat)
222                 {
223                 case litehtml::background_repeat_no_repeat:
224                         draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
225                         break;
226
227                 case litehtml::background_repeat_repeat_x:
228                         cairo_set_source(cr, pattern);
229                         cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
230                         cairo_fill(cr);
231                         break;
232
233                 case litehtml::background_repeat_repeat_y:
234                         cairo_set_source(cr, pattern);
235                         cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
236                         cairo_fill(cr);
237                         break;
238
239                 case litehtml::background_repeat_repeat:
240                         cairo_set_source(cr, pattern);
241                         cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
242                         cairo_fill(cr);
243                         break;
244                 }
245
246                 cairo_pattern_destroy(pattern);
247                 cairo_surface_destroy(img);
248
249         }
250
251         unlock_images_cache();
252         cairo_restore(cr);
253 }
254
255 void container_linux::make_url(const litehtml::tchar_t* url,    const litehtml::tchar_t* basepath, litehtml::tstring& out)
256 {
257         out = url;
258 }
259
260 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
261 {
262         if(rx > 0 && ry > 0)
263         {
264
265                 cairo_save(cr);
266
267                 cairo_translate(cr, x, y);
268                 cairo_scale(cr, 1, ry / rx);
269                 cairo_translate(cr, -x, -y);
270
271                 if(neg)
272                 {
273                         cairo_arc_negative(cr, x, y, rx, a1, a2);
274                 } else
275                 {
276                         cairo_arc(cr, x, y, rx, a1, a2);
277                 }
278
279                 cairo_restore(cr);
280         } else
281         {
282                 cairo_move_to(cr, x, y);
283         }
284 }
285
286 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
287 {
288         cairo_t* cr = (cairo_t*) hdc;
289         cairo_save(cr);
290         apply_clip(cr);
291
292         cairo_new_path(cr);
293
294         int bdr_top             = 0;
295         int bdr_bottom  = 0;
296         int bdr_left    = 0;
297         int bdr_right   = 0;
298
299         if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
300         {
301                 bdr_top = (int) borders.top.width;
302         }
303         if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
304         {
305                 bdr_bottom = (int) borders.bottom.width;
306         }
307         if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
308         {
309                 bdr_left = (int) borders.left.width;
310         }
311         if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
312         {
313                 bdr_right = (int) borders.right.width;
314         }
315
316         // draw right border
317         if(bdr_right)
318         {
319                 set_color(cr, borders.right.color);
320
321                 double r_top    = borders.radius.top_right_x;
322                 double r_bottom = borders.radius.bottom_right_x;
323
324                 if(r_top)
325                 {
326                         double end_angle        = 2 * M_PI;
327                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);
328
329                         add_path_arc(cr,
330                                 draw_pos.right() - r_top,
331                                 draw_pos.top() + r_top,
332                                 r_top - bdr_right,
333                                 r_top - bdr_right + (bdr_right - bdr_top),
334                                 end_angle,
335                                 start_angle, true);
336
337                         add_path_arc(cr,
338                                 draw_pos.right() - r_top,
339                                 draw_pos.top() + r_top,
340                                 r_top,
341                                 r_top,
342                                 start_angle,
343                                 end_angle, false);
344                 } else
345                 {
346                         cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
347                         cairo_line_to(cr, draw_pos.right(), draw_pos.top());
348                 }
349
350                 if(r_bottom)
351                 {
352                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom() - r_bottom);
353
354                         double start_angle      = 0;
355                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);
356
357                         add_path_arc(cr,
358                                 draw_pos.right() - r_bottom,
359                                 draw_pos.bottom() - r_bottom,
360                                 r_bottom,
361                                 r_bottom,
362                                 start_angle,
363                                 end_angle, false);
364
365                         add_path_arc(cr,
366                                 draw_pos.right() - r_bottom,
367                                 draw_pos.bottom() - r_bottom,
368                                 r_bottom - bdr_right,
369                                 r_bottom - bdr_right + (bdr_right - bdr_bottom),
370                                 end_angle,
371                                 start_angle, true);
372                 } else
373                 {
374                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
375                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
376                 }
377
378                 cairo_fill(cr);
379         }
380
381         // draw bottom border
382         if(bdr_bottom)
383         {
384                 set_color(cr, borders.bottom.color);
385
386                 double r_left   = borders.radius.bottom_left_x;
387                 double r_right  = borders.radius.bottom_right_x;
388
389                 if(r_left)
390                 {
391                         double start_angle      = M_PI / 2.0;
392                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);
393
394                         add_path_arc(cr,
395                                 draw_pos.left() + r_left,
396                                 draw_pos.bottom() - r_left,
397                                 r_left - bdr_bottom + (bdr_bottom - bdr_left),
398                                 r_left - bdr_bottom,
399                                 start_angle,
400                                 end_angle, false);
401
402                         add_path_arc(cr,
403                                 draw_pos.left() + r_left,
404                                 draw_pos.bottom() - r_left,
405                                 r_left,
406                                 r_left,
407                                 end_angle,
408                                 start_angle, true);
409                 } else
410                 {
411                         cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
412                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
413                 }
414
415                 if(r_right)
416                 {
417                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.bottom());
418
419                         double end_angle        = M_PI / 2.0;
420                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);
421
422                         add_path_arc(cr,
423                                 draw_pos.right() - r_right,
424                                 draw_pos.bottom() - r_right,
425                                 r_right,
426                                 r_right,
427                                 end_angle,
428                                 start_angle, true);
429
430                         add_path_arc(cr,
431                                 draw_pos.right() - r_right,
432                                 draw_pos.bottom() - r_right,
433                                 r_right - bdr_bottom + (bdr_bottom - bdr_right),
434                                 r_right - bdr_bottom,
435                                 start_angle,
436                                 end_angle, false);
437                 } else
438                 {
439                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
440                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
441                 }
442
443                 cairo_fill(cr);
444         }
445
446         // draw top border
447         if(bdr_top)
448         {
449                 set_color(cr, borders.top.color);
450
451                 double r_left   = borders.radius.top_left_x;
452                 double r_right  = borders.radius.top_right_x;
453
454                 if(r_left)
455                 {
456                         double end_angle        = M_PI * 3.0 / 2.0;
457                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);
458
459                         add_path_arc(cr,
460                                 draw_pos.left() + r_left,
461                                 draw_pos.top() + r_left,
462                                 r_left,
463                                 r_left,
464                                 end_angle,
465                                 start_angle, true);
466
467                         add_path_arc(cr,
468                                 draw_pos.left() + r_left,
469                                 draw_pos.top() + r_left,
470                                 r_left - bdr_top + (bdr_top - bdr_left),
471                                 r_left - bdr_top,
472                                 start_angle,
473                                 end_angle, false);
474                 } else
475                 {
476                         cairo_move_to(cr, draw_pos.left(), draw_pos.top());
477                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
478                 }
479
480                 if(r_right)
481                 {
482                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.top() + bdr_top);
483
484                         double start_angle      = M_PI * 3.0 / 2.0;
485                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);
486
487                         add_path_arc(cr,
488                                 draw_pos.right() - r_right,
489                                 draw_pos.top() + r_right,
490                                 r_right - bdr_top + (bdr_top - bdr_right),
491                                 r_right - bdr_top,
492                                 start_angle,
493                                 end_angle, false);
494
495                         add_path_arc(cr,
496                                 draw_pos.right() - r_right,
497                                 draw_pos.top() + r_right,
498                                 r_right,
499                                 r_right,
500                                 end_angle,
501                                 start_angle, true);
502                 } else
503                 {
504                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
505                         cairo_line_to(cr, draw_pos.right(),     draw_pos.top());
506                 }
507
508                 cairo_fill(cr);
509         }
510
511         // draw left border
512         if(bdr_left)
513         {
514                 set_color(cr, borders.left.color);
515
516                 double r_top    = borders.radius.top_left_x;
517                 double r_bottom = borders.radius.bottom_left_x;
518
519                 if(r_top)
520                 {
521                         double start_angle      = M_PI;
522                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);
523
524                         add_path_arc(cr,
525                                 draw_pos.left() + r_top,
526                                 draw_pos.top() + r_top,
527                                 r_top - bdr_left,
528                                 r_top - bdr_left + (bdr_left - bdr_top),
529                                 start_angle,
530                                 end_angle, false);
531
532                         add_path_arc(cr,
533                                 draw_pos.left() + r_top,
534                                 draw_pos.top() + r_top,
535                                 r_top,
536                                 r_top,
537                                 end_angle,
538                                 start_angle, true);
539                 } else
540                 {
541                         cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
542                         cairo_line_to(cr, draw_pos.left(), draw_pos.top());
543                 }
544
545                 if(r_bottom)
546                 {
547                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom() - r_bottom);
548
549                         double end_angle        = M_PI;
550                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);
551
552                         add_path_arc(cr,
553                                 draw_pos.left() + r_bottom,
554                                 draw_pos.bottom() - r_bottom,
555                                 r_bottom,
556                                 r_bottom,
557                                 end_angle,
558                                 start_angle, true);
559
560                         add_path_arc(cr,
561                                 draw_pos.left() + r_bottom,
562                                 draw_pos.bottom() - r_bottom,
563                                 r_bottom - bdr_left,
564                                 r_bottom - bdr_left + (bdr_left - bdr_bottom),
565                                 start_angle,
566                                 end_angle, false);
567                 } else
568                 {
569                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom());
570                         cairo_line_to(cr, draw_pos.left() + bdr_left,   draw_pos.bottom() - bdr_bottom);
571                 }
572
573                 cairo_fill(cr);
574         }
575         cairo_restore(cr);
576 }
577
578 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
579 {
580
581 }
582
583 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
584 {
585         litehtml::position clip_pos = pos;
586         litehtml::position client_pos;
587         get_client_rect(client_pos);
588         if(!valid_x)
589         {
590                 clip_pos.x              = client_pos.x;
591                 clip_pos.width  = client_pos.width;
592         }
593         if(!valid_y)
594         {
595                 clip_pos.y              = client_pos.y;
596                 clip_pos.height = client_pos.height;
597         }
598         m_clips.emplace_back(clip_pos, bdr_radius);
599 }
600
601 void container_linux::del_clip()
602 {
603         if(!m_clips.empty())
604         {
605                 m_clips.pop_back();
606         }
607 }
608
609 void container_linux::apply_clip( cairo_t* cr )
610 {
611         for(const auto& clip_box : m_clips)
612         {
613                 rounded_rectangle(cr, clip_box.box, clip_box.radius);
614                 cairo_clip(cr);
615         }
616 }
617
618 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
619 {
620         if(!cr) return;
621         cairo_save(cr);
622
623         apply_clip(cr);
624
625         cairo_new_path(cr);
626
627         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
628         cairo_scale (cr, width / 2.0, height / 2.0);
629         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
630
631         set_color(cr, color);
632         cairo_set_line_width(cr, line_width);
633         cairo_stroke(cr);
634
635         cairo_restore(cr);
636 }
637
638 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
639 {
640         if(!cr) return;
641         cairo_save(cr);
642
643         apply_clip(cr);
644
645         cairo_new_path(cr);
646
647         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
648         cairo_scale (cr, width / 2.0, height / 2.0);
649         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
650
651         set_color(cr, color);
652         cairo_fill(cr);
653
654         cairo_restore(cr);
655 }
656
657 void container_linux::clear_images()
658 {
659         lock_images_cache();
660
661         for(auto i = m_images.begin(); i != m_images.end(); ++i) {
662                 image *img = &(*i);
663
664                 if (img->second) {
665                         g_object_unref(img->second);
666                 }
667         }
668
669         m_images.clear();
670
671         unlock_images_cache();
672 }
673
674 gint container_linux::clear_images(gint desired_size)
675 {
676         gint size = 0;
677         gint num = 0;
678
679         lock_images_cache();
680
681         /* First, tally up size of all the stored GdkPixbufs and
682          * deallocate those which make the total size be above
683          * the desired_size limit. We will remove their list
684          * elements later. */
685         for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
686                 image *img = &(*i);
687                 gint cursize;
688
689                 if (img->second == NULL)
690                         continue;
691
692                 cursize = gdk_pixbuf_get_byte_length(img->second);
693
694                 if (size + cursize > desired_size) {
695                         g_object_unref(img->second);
696                         img->second = NULL;
697                         num++;
698                 } else {
699                         size += cursize;
700                 }
701         }
702
703         /* Remove elements whose GdkPixbuf pointers point to NULL. */
704         m_images.remove_if([&](image _img) -> bool {
705                         if (_img.second == NULL)
706                                 return true;
707                         return false;
708                         });
709
710         unlock_images_cache();
711
712         return num;
713 }
714
715 std::shared_ptr<litehtml::element>      container_linux::create_element(const litehtml::tchar_t *tag_name,
716                                                                                                                                           const litehtml::string_map &attributes,
717                                                                                                                                           const std::shared_ptr<litehtml::document> &doc)
718 {
719         return 0;
720 }
721
722 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
723 {
724         cairo_new_path(cr);
725         if(radius.top_left_x)
726         {
727                 cairo_arc(cr, pos.left() + radius.top_left_x, pos.top() + radius.top_left_x, radius.top_left_x, M_PI, M_PI * 3.0 / 2.0);
728         } else
729         {
730                 cairo_move_to(cr, pos.left(), pos.top());
731         }
732
733         cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
734
735         if(radius.top_right_x)
736         {
737                 cairo_arc(cr, pos.right() - radius.top_right_x, pos.top() + radius.top_right_x, radius.top_right_x, M_PI * 3.0 / 2.0, 2.0 * M_PI);
738         }
739
740         cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
741
742         if(radius.bottom_right_x)
743         {
744                 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
745         }
746
747         cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
748
749         if(radius.bottom_left_x)
750         {
751                 cairo_arc(cr, pos.left() + radius.bottom_left_x, pos.bottom() - radius.bottom_left_x, radius.bottom_left_x, M_PI / 2.0, M_PI);
752         }
753 }
754
755 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,     int y, int cx, int cy)
756 {
757         cairo_save(cr);
758
759         {
760                 cairo_matrix_t flib_m;
761                 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
762
763                 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
764                 {
765                         GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
766                         gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
767                         cairo_paint(cr);
768                 } else
769                 {
770                         gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
771                         cairo_paint(cr);
772                 }
773         }
774
775         cairo_restore(cr);
776 }
777
778 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
779 {
780         cairo_surface_t* ret = NULL;
781
782         if(gdk_pixbuf_get_has_alpha(bmp))
783         {
784                 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
785         } else
786         {
787                 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
788         }
789
790 //      Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
791 //      Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
792 //      Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
793         cairo_t *ctx = cairo_create(ret);
794         cairo_paint(ctx);
795         cairo_destroy(ctx);
796
797         return ret;
798 }
799
800 void container_linux::get_media_features(litehtml::media_features& media) const
801 {
802         litehtml::position client;
803     get_client_rect(client);
804         media.type                      = litehtml::media_type_screen;
805         media.width                     = client.width;
806         media.height            = client.height;
807         media.device_width      = gdk_screen_width();
808         media.device_height     = gdk_screen_height();
809         media.color                     = 8;
810         media.monochrome        = 0;
811         media.color_index       = 256;
812         media.resolution        = 96;
813 }
814
815 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
816 {
817         language = _t("en");
818         culture = _t("");
819 }
820
821 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
822 {
823
824 }