]> git.vomp.tv Git - vompclient.git/blob - osdopenvg.cc
Bitmap and VPictureBanner CWFs
[vompclient.git] / osdopenvg.cc
1 /*
2     Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <chrono>
21 #include <sys/syscall.h>
22 #include <fontconfig/fontconfig.h>
23 #include <math.h>
24 #include <bcm_host.h>
25
26 #include "videoomx.h"
27 #include "surface.h"
28 #include "message.h"
29 #include "messagequeue.h"
30 #include "teletxt/txtfont.h"
31
32 #include "osdopenvg.h"
33
34 #define EXTERNALPICTURE(name, fname, fileextension)  extern uint8_t name ## _data[]  asm("_binary_other_"#fname"_"#fileextension"_start"); \
35                                                                                                          extern uint8_t name ## _data_end[]  asm("_binary_other_"#fname"_"#fileextension"_end");
36
37 EXTERNAL_PICTS
38
39 #undef EXTERNALPICTURE
40
41 #define  BACKBUFFER_WIDTH 1280
42 #define  BACKBUFFER_HEIGHT 720
43
44 OsdOpenVG::OsdOpenVG()
45 {
46   vgmutex.lock();
47   taskmutex.lock();
48   lastrendertime = getTimeMS();
49
50   const char* fontname = "Droid Sans:style=Regular";
51   cur_fontname = static_cast<char*>(malloc(strlen(fontname) + 1));
52   strcpy(cur_fontname, fontname);
53
54 #define EXTERNALPICTURE(name, fname, fileextension) static_artwork_begin[sa_ ## name]=name ## _data;
55
56   EXTERNAL_PICTS
57
58 #undef EXTERNALPICTURE
59 #define EXTERNALPICTURE(name, fname, fileextension) static_artwork_end[sa_ ## name]=name ## _data_end;
60
61   EXTERNAL_PICTS
62
63 #undef EXTERNALPICTURE
64 }
65
66 OsdOpenVG::~OsdOpenVG()
67 {
68   if (initted) shutdown();
69
70   if (cur_fontname) free(cur_fontname);
71
72   if (freetype_inited) FT_Done_Face(ft_face);
73
74   for (char* c : fontnames) free(c);
75   for (char* c : fontnames_keys) free(c);
76
77   vgmutex.unlock();
78   taskmutex.unlock();
79 }
80
81 int OsdOpenVG::init()
82 {
83   if (initted) return 0;
84
85   reader.init();
86
87   //First get connection to egl
88   egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
89   if (egl_display == EGL_NO_DISPLAY)
90   {
91     Log::getInstance()->log("OSD", Log::WARN, "Could not get egl display! %x", eglGetError());
92     vgmutex.unlock();
93     return 0;
94   }
95
96   if (eglInitialize(egl_display, NULL, NULL) == EGL_FALSE)
97   {
98     Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %x", eglGetError());
99     vgmutex.unlock();
100     return 0;
101   }
102
103   const char* query_str = eglQueryString(egl_display, EGL_CLIENT_APIS);
104
105   if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
106   else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x", eglGetError());
107
108   query_str = eglQueryString(egl_display, EGL_EXTENSIONS);
109
110   if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
111   else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x", eglGetError());
112
113   if (eglBindAPI(EGL_OPENVG_API) == EGL_FALSE)
114   {
115     Log::getInstance()->log("OSD", Log::WARN, "Binding openvg api failed! %x", eglGetError());
116     vgmutex.unlock();
117     return 0;
118   }
119
120   const EGLint attributs[] =
121   {
122     EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
123     EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
124     EGL_CONFORMANT, EGL_OPENVG_BIT,
125     EGL_NONE
126   }; // Here, we might have to select the resolution!
127
128   EGLint number;
129
130   if (eglChooseConfig(egl_display, attributs, &egl_ourconfig, 1, &number) == EGL_FALSE)
131   {
132     Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %x", eglGetError());
133     vgmutex.unlock();
134     return 0;
135   }
136
137   egl_context = eglCreateContext(egl_display, egl_ourconfig, NULL, NULL);
138
139   if (egl_context == EGL_NO_CONTEXT)
140   {
141     Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %x", eglGetError());
142     vgmutex.unlock();
143     return 0;
144   }
145
146   // warning broadcom specific, get display size!
147   display_width = display_height = 0;
148
149   if (graphics_get_display_size(0, reinterpret_cast<uint32_t*>(&display_width), reinterpret_cast<uint32_t*>(&display_height)) < 0)
150   {
151     Log::getInstance()->log("OSD", Log::WARN, "Getting display size failed! (BCM API) ");
152     vgmutex.unlock();
153     return 0;
154   }
155
156   Log::getInstance()->log("OSD", Log::NOTICE, "Displaysize is %d x %d ", display_width, display_height);
157   VC_RECT_T dst_rect = {0, 0, display_width, display_height};
158   VC_RECT_T src_rect = {0, 0, BACKBUFFER_WIDTH << 16, BACKBUFFER_HEIGHT << 16};
159   VC_RECT_T src_rect_bg = {0, 0, 16 << 16, 16 << 16};
160   //   VC_RECT_T src_rect_im={0,0,16,16};
161
162   uint32_t back_image_ptr;
163   bcm_backres = vc_dispmanx_resource_create(VC_IMAGE_RGBX32, 16, 16, &back_image_ptr);
164
165   updateBackgroundColor(DrawStyle::WALLPAPER);
166
167   DISPMANX_UPDATE_HANDLE_T  bcm_update;
168   bcm_display = vc_dispmanx_display_open(0);
169   bcm_update = vc_dispmanx_update_start(0);
170   bcm_element = vc_dispmanx_element_add(bcm_update, bcm_display,
171                                         2, &dst_rect, 0,
172                                         &src_rect, DISPMANX_PROTECTION_NONE, 0, 0, static_cast<DISPMANX_TRANSFORM_T>(0));
173
174
175   bcm_background = vc_dispmanx_element_add(bcm_update, bcm_display,
176                    0, &dst_rect, bcm_backres,
177                    &src_rect_bg, DISPMANX_PROTECTION_NONE, 0, 0, static_cast<DISPMANX_TRANSFORM_T>(0));
178
179   vc_dispmanx_update_submit_sync(bcm_update);
180
181   static EGL_DISPMANX_WINDOW_T nativewindow;
182   nativewindow.element = bcm_element;
183   nativewindow.height = BACKBUFFER_HEIGHT;
184   nativewindow.width = BACKBUFFER_WIDTH;
185
186   egl_surface = eglCreateWindowSurface(egl_display, egl_ourconfig, &nativewindow, NULL);
187   if (egl_surface == EGL_NO_SURFACE)
188   {
189     Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!");
190     vgmutex.unlock();
191     return 0;
192   }
193
194   Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current in1%d", syscall(SYS_gettid));
195
196   if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE)
197   {
198     Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed");
199     vgmutex.unlock();
200     return 0;
201   }
202
203   // Test stuff
204
205   query_str = reinterpret_cast<const char*>(vgGetString(VG_VERSION));
206
207   if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
208   else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x", vgGetError());
209
210   query_str = reinterpret_cast<const char*>(vgGetString(VG_VENDOR));
211
212   if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
213   else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x", vgGetError());
214
215   query_str = reinterpret_cast<const char*>(vgGetString(VG_RENDERER));
216
217   if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
218   else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x", vgGetError());
219
220   query_str = reinterpret_cast<const char*>(vgGetString(VG_EXTENSIONS));
221
222   if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
223   else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x", vgGetError());
224
225   aspect_correction = ((float)BACKBUFFER_HEIGHT) / 576.f / (((float)BACKBUFFER_WIDTH) / 720.f);
226   initPaths();
227
228   if (!fontnames.size())
229   {
230     //inspired by and copied from vdr's code
231     FcInit();
232     FcObjectSet* objset = FcObjectSetBuild(FC_FAMILY, FC_STYLE, NULL);
233     FcPattern* pattern = FcPatternCreate();
234     FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
235     FcFontSet* fonts = FcFontList(NULL, pattern, objset);
236
237     for (int i = 0; i < fonts->nfont; i++)
238     {
239       char* s = reinterpret_cast<char*>(FcNameUnparse(fonts->fonts[i]));
240
241       if (s)
242       {
243         char* c = strchr(s, ':');
244
245         if (c)
246         {
247           char* s2 = strchr(c + 1, ',');
248
249           if (s2) *s2 = 0;
250         }
251
252         char* p = strchr(s, ',');
253
254         if (p)
255         {
256           if (!c) *p = 0;
257           else memmove(p, c, strlen(c) + 1);
258         }
259
260         char* key = (char*)malloc(strlen(s) + 1);
261         strcpy(key, s);
262         fontnames_keys.push_back(key);
263         char* s2 = strstr(s, "style=");
264
265         if (s2)
266         {
267           memmove(s2, s2 + 6, strlen(s2 + 6) + 1);
268         }
269
270         fontnames.push_back(s);
271       }
272     }
273
274     FcFontSetDestroy(fonts);
275     FcPatternDestroy(pattern);
276     FcObjectSetDestroy(objset);
277   }
278
279   if (!loadFont(false))
280   {
281     return 0;
282   }
283
284   vgttfont = vgCreateFont(0);
285   vgttpaint = vgCreatePaint();
286   vgSetParameteri( vgttpaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
287   vgSetColor(vgttpaint, 0xffffffff);
288
289   eglSwapInterval(egl_display, 1);
290
291   Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out 1%d", syscall(SYS_gettid));
292   eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
293   //Now we will create the Screen
294   initted = true; // must set this here or create surface won't work
295
296   /*if (((VideoOMX*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init  stuff
297     return 0;
298   }*/
299
300
301   renderThreadMutex.lock(); // start-protect
302   renderThreadReq = 0;
303   renderThread = std::thread( [this]
304   {
305     renderThreadMutex.lock();
306     renderThreadMutex.unlock();
307     renderLoop();
308   });
309   renderThreadMutex.unlock();
310
311
312   taskmutex.unlock();
313   vgmutex.unlock();
314
315 #ifdef PICTURE_DECODER_OMX
316   imageomx = new ImageOMX(&reader);
317   reader.addDecoder(imageomx);
318 #endif
319
320   return 1;
321 }
322
323 void OsdOpenVG::renderThreadStop()
324 {
325   Log::getInstance()->log("OSDOpenVG", Log::WARN, "renderThreadStop");
326
327   renderThreadMutex.lock();
328   renderThreadReq = 2; // req-stop
329   renderThreadCond.notify_one(); // in case renderLoop is in cond_wait
330   renderThreadMutex.unlock();
331   renderThread.join();
332 }
333
334 int OsdOpenVG::stopUpdate()
335 {
336   renderThreadStop();
337   processOpenVGCommands();
338   return 1;
339 }
340
341 int OsdOpenVG::shutdown()
342 {
343   reader.shutdown();
344
345 #ifdef PICTURE_DECODER_OMX
346   if (imageomx) reader.removeDecoder(imageomx);
347   imageomx = NULL;
348 #endif
349
350   if (!initted) return 0;
351
352   initted = false;
353   Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark1");
354   renderThreadStop();
355   Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark1a");
356
357   //(((VideoOMX*)Video::getInstance())->shutdownUsingOSDObjects());
358   if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE)
359   {
360     Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in shutdown %x", eglGetError());
361   }
362
363   if (eglBindAPI(EGL_OPENVG_API) == EGL_FALSE)
364   {
365     Log::getInstance()->log("OSD", Log::WARN, "Binding openvg api thread failed! %x", eglGetError());
366   }
367
368   Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark2");
369   processOpenVGCommands();
370   Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark3");
371
372   taskmutex.lock();
373   vgmutex.lock();
374
375   //purgeAllReferences();
376
377   vgDestroyFont(vgfont);
378   vgDestroyFont(vgttfont);
379   vgDestroyPaint(vgttpaint);
380   destroyPaths();
381   float colclear[] = {0.8f, 0.8f, 0.8f, 1.f};
382   vgSetfv(VG_CLEAR_COLOR, 4, colclear);
383   vgClear(0, 0, BACKBUFFER_WIDTH, BACKBUFFER_HEIGHT);
384   eglSwapBuffers(egl_display, egl_surface);
385   Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out final");
386   eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
387
388   if (eglDestroySurface(egl_display, egl_surface) == EGL_FALSE)
389   {
390     Log::getInstance()->log("OSD", Log::ERR, "eglDestroySurface failed %x", eglGetError());
391   }
392
393   if (eglDestroyContext(egl_display, egl_context) == EGL_FALSE)
394   {
395     Log::getInstance()->log("OSD", Log::ERR, "eglDestroyContext failed %x", eglGetError());
396   }
397
398   if (eglTerminate(egl_display) == EGL_FALSE)
399   {
400     Log::getInstance()->log("OSD", Log::ERR, "eglTerminate failed %x", eglGetError());
401   }
402
403   DISPMANX_UPDATE_HANDLE_T  bcm_update;
404   bcm_update = vc_dispmanx_update_start(0);
405
406   vc_dispmanx_element_remove(bcm_update, bcm_element);
407   vc_dispmanx_element_remove(bcm_update, bcm_background);
408   vc_dispmanx_update_submit_sync(bcm_update);
409   vc_dispmanx_resource_delete(bcm_backres);
410   bcm_backres = 0;
411   vc_dispmanx_display_close(bcm_display);
412   return 1;
413 }
414
415 // OSDOVG-ROD-EXPERIMENT - temp hack to allow OsdVector to signal the thread in OsdOpenVG
416 void OsdOpenVG::nudgeRenderThread()
417 {
418   Log::getInstance()->log("OSD", Log::DEBUG, "Nudge render thread");
419
420   // signal renderLoop to go-around (from putOpenVGCommand)
421   renderThreadMutex.lock();
422   if (renderThreadReq == 0) renderThreadReq = 1;
423   renderThreadCond.notify_one();
424   renderThreadMutex.unlock();
425 }
426
427 /*
428  * OSDOVG-ROD-EXPERIMENT
429  *
430  * To switch OsdOpenVG to render-on-demand instead of rendering in a loop
431  *
432  * Pros:
433  *   More responsive OSD
434  *   Cuts 5% constant CPU usage
435  *   Seems to have tickled the GFX corruption bug, might make it easier to find that
436  *
437  * Cons:
438  *   There's a comment about video tearing if render is called too often.
439  *     Will tearing show up in on-demand mode?
440  *   Moves away from conventional FPS type render loop. VOMP doesn't use 3D GFX / animations
441  *     currently, so this doesn't matter at the moment?
442  *   Code needs updating to call nudgeRenderThread,
443  *   e.g. FIXME backing out of the options screen doesn't redraw VWelcome
444  *
445  *   Maybe have a hybrid system. One that normally works on-demand but can
446  *   also switch to looping on the fly if necessary.
447  */
448
449
450 void OsdOpenVG::renderLoop()
451 {
452   // We have to claim the egl context for this thread
453   Log::getInstance()->log("OSD", Log::NOTICE, "Entering drawing thread");
454
455   if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE)
456   {
457     Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in thread %x", eglGetError());
458     return;
459   }
460
461   if (eglBindAPI(EGL_OPENVG_API) == EGL_FALSE)
462   {
463     Log::getInstance()->log("OSD", Log::WARN, "Binding openvg api thread failed! %x", eglGetError());
464     return;
465   }
466
467   std::unique_lock<std::mutex> ul(renderThreadMutex, std::defer_lock);
468   int ts;
469
470   while (true)
471   {
472     ts = 1;
473
474     long long time1 = getTimeMS();
475
476     // OSDOVG-ROD-EXPERIMENT - always render, but only when signalled to
477     if (1) // ((time1 - lastrendertime) > 200)  //5 fps for OSD updates are enough, avoids tearing
478     {
479       #if DEV
480       dumpStyles();
481       #endif
482
483       InternalRendering();
484       lastrendertime = getTimeMS();
485
486       ts = 10;
487     }
488     else
489     {
490       ts = time1 - lastrendertime;
491     }
492
493     if (processOpenVGCommands()) ts = 0;
494
495     ul.lock();
496     if (renderThreadReq == 2)
497     {
498       if (eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE)
499         Log::getInstance()->log("OSD", Log::WARN, "Making egl Current out thread failed");
500       ul.unlock();
501       return;
502     }
503
504     if (ts != 0)
505     {
506       // OSDOVG-ROD-EXPERIMENT
507       renderThreadCond.wait(ul, [this]{ return renderThreadReq != 0; }); // experiment! always wait, no time limit
508       //renderThreadCond.wait_for(ul, std::chrono::milliseconds(ts), [this]{ return renderThreadReq != 0; });
509
510       if (renderThreadReq == 2)
511       {
512         if (eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE)
513           Log::getInstance()->log("OSD", Log::WARN, "Making egl Current out thread failed");
514         ul.unlock();
515         return;
516       }
517
518       if (renderThreadReq == 0) abort(); // OSDOVG-ROD-EXPERIMENT - check this never happens
519
520       // Either timeout or renderThreadReq == 1 - go around
521
522       renderThreadReq = 0;
523     }
524
525     ul.unlock();
526   }
527 }
528
529 UINT OsdOpenVG::putOpenVGCommand(OpenVGCommand& comm, bool wait)
530 {
531   if (wait) comm.response = new OpenVGResponse;
532
533   std::unique_lock<std::mutex> ul(taskmutex);     // locks
534   vgcommands.push_back(comm);
535   ul.unlock();
536
537   // signal renderLoop to go-around
538   renderThreadMutex.lock();
539   if (renderThreadReq == 0) renderThreadReq = 1;
540   renderThreadCond.notify_one();
541   renderThreadMutex.unlock();
542
543   if (!wait) return 0;
544
545   ul.lock();
546   // Wait until done is true and we are signalled. If done is already true, this returns immediately
547   comm.response->doneCond.wait(ul, [&comm]{ return comm.response->done; });
548
549   // Now response->done = true, mutex is locked, we have been signalled
550
551   ul.unlock();
552   UINT toReturn = comm.response->result;
553   delete comm.response;
554   return toReturn;
555 }
556
557 bool OsdOpenVG::processOpenVGCommands()
558 {
559   bool worked = false;
560
561   taskmutex.lock();
562   while (vgcommands.size() > 0)
563   {
564     worked = true;
565
566     OpenVGCommand comm = vgcommands.front();
567     vgcommands.pop_front();
568     taskmutex.unlock();
569
570     vgmutex.lock();
571     UINT vgResult = handleOpenVGCommand(comm);
572     vgmutex.unlock();
573
574     taskmutex.lock();
575     if (comm.response) // There is a response object, a thread is waiting
576     {
577       comm.response->result = vgResult;
578       comm.response->done = true;
579       comm.response->doneCond.notify_one();
580     }
581   }
582   taskmutex.unlock();
583
584   return worked;
585 }
586
587 void OsdOpenVG::InternalRendering()
588 {
589   vgmutex.lock();
590   Colour bg = DrawStyle::WALLPAPER;
591   float colclear[] = {1.f, 1.0f, 1.f, 1.f};
592
593   if (bg.alpha == 0) colclear[3] = 0.f;
594
595   vgSetfv(VG_CLEAR_COLOR, 4, colclear);
596   vgClear(0, 0, BACKBUFFER_WIDTH, BACKBUFFER_HEIGHT);
597   vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
598
599   drawSurfaces(); //iterate through and draws all commands
600
601   //Show it to the user!
602   eglSwapBuffers(egl_display, egl_surface);
603   vgmutex.unlock();
604 }
605
606 // --------------------------------------------------------------------------------------------------------------------------
607
608 /*
609 void OsdOpenVG::purgeAllReferences()
610 {
611         images_ref.clear();
612         drawstyleHandlesRefCounts.clear(); // remove all references
613
614
615         map<void *,ImageIndex>::iterator mitty=monobitmaps.begin();
616         while (mitty!=monobitmaps.end()) {
617                 vgDestroyImage((VGImage)(*mitty).second);
618                 mitty++;
619         }
620         monobitmaps.clear();
621
622         / *map<string,ImageIndex>::iterator jitty=jpegs.begin();
623         while (jitty!=jpegs.end()) {
624                 vgDestroyImage((VGImage)(*jitty).second);
625                 jitty++;
626         }
627         jpegs.clear();* /
628
629         map<TVMediaInfo,ImageIndex>::iterator titty=tvmedias.begin();
630         while (titty!=tvmedias.end()) {
631                 vgDestroyImage((VGImage)(*titty).second);
632                 titty++;
633         }
634         tvmedias.clear();
635
636         map<pair<Colour*,unsigned int>,unsigned int>::iterator sitty=drawstyleHandles.begin();
637         while (sitty!=drawstyleHandles.end()) {
638                 vgDestroyPaint((VGPaint)(*sitty).second);
639                 sitty++;
640         }
641         drawstyleHandles.clear();
642
643 }*/
644
645 void OsdOpenVG::updateBackgroundColor(DrawStyle bg)
646 {
647   unsigned int color[16 * 16];
648
649   if (!bcm_backres) return;
650
651   VC_RECT_T src_rect_im = {0, 0, 16, 16};
652
653   if (bg.ft != DrawStyle::GradientLinear)
654   {
655     bg.grad_col[0] = bg;
656   }
657
658   //memset(color,0xFF,sizeof(unsigned int)*4*8);
659   for (int j = 0; j < 16; j++)
660   {
661     int helpr = (((15 - j) * bg.red) + (j * bg.grad_col[0].red)) / 15;
662     int helpb = (((15 - j) * bg.blue) + (j * bg.grad_col[0].blue)) / 15;
663     int helpg = (((15 - j) * bg.green) + (j * bg.grad_col[0].green)) / 15;
664     //unsigned int cur_col=help | (help<< 8)  | (help<< 16)  | (0xFF<< 24);
665     unsigned int cur_col = helpr  | (helpg << (8))  | (helpb << (16))  | (0xFF << (24));
666
667     for (int i = 0; i < 16; i++)
668     {
669       color[i + 16 * j] = cur_col;
670     }
671   }
672
673   vc_dispmanx_resource_write_data(bcm_backres, VC_IMAGE_RGBX32, 16 * 4, color, &src_rect_im);
674 }
675
676 void OsdOpenVG::getScreenSize(int& width, int& height)
677 {
678   width = BACKBUFFER_WIDTH;
679   height = BACKBUFFER_HEIGHT;
680 }
681
682 void OsdOpenVG::getRealScreenSize(int& width, int& height)
683 {
684   width = display_width;
685   height = display_height;
686 }
687
688 bool OsdOpenVG::screenShotInternal(void* buffer, int width, int height, bool includeOSD)
689 {
690   if (!initted) return false;
691
692   if (!buffer) return false;
693
694   DISPMANX_RESOURCE_HANDLE_T res;
695   DISPMANX_DISPLAY_HANDLE_T display;
696
697   uint32_t image_ptr;
698   VC_RECT_T rect;
699   res = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, width, height, &image_ptr);
700   display = vc_dispmanx_display_open(0);
701
702   if (!includeOSD)
703   {
704     vc_dispmanx_snapshot(display, res,
705                          static_cast<DISPMANX_TRANSFORM_T>(DISPMANX_SNAPSHOT_NO_RGB | DISPMANX_SNAPSHOT_FILL/*|DISPMANX_SNAPSHOT_PACK*/));
706   }
707   else
708   {
709     vc_dispmanx_snapshot(display, res,
710                          static_cast<DISPMANX_TRANSFORM_T>(DISPMANX_SNAPSHOT_FILL));
711   }
712
713   vc_dispmanx_rect_set(&rect, 0, 0, width, height);
714   vc_dispmanx_resource_read_data(res, &rect, buffer, width * 4);
715   vc_dispmanx_resource_delete(res);
716   vc_dispmanx_display_close(display);
717   return true;
718 }
719
720 void OsdOpenVG::initPaths()
721 {
722   VGPath current;
723   current = vgCreatePath(VG_PATH_FORMAT_STANDARD,
724                          VG_PATH_DATATYPE_F, 1.f, 0.f,
725                          0, 0, VG_PATH_CAPABILITY_ALL);
726
727   vguLine(current, 0.f, 0.f, 1.f, 0.f);
728   // HorzLine
729   std_paths[PIHorzLine] = current;
730
731   current = vgCreatePath(VG_PATH_FORMAT_STANDARD,
732                          VG_PATH_DATATYPE_F, 1.f, 0.f,
733                          0, 0, VG_PATH_CAPABILITY_ALL);
734   vguLine(current, 0.f, 0.f, 0.f, 1.f);
735   // VertLine
736   std_paths[PIVertLine] = current;
737
738   current = vgCreatePath(VG_PATH_FORMAT_STANDARD,
739                          VG_PATH_DATATYPE_F, 1.f, 0.f,
740                          0, 0, VG_PATH_CAPABILITY_ALL);
741   //vguRect(current,0.f,0.f,1.f,1.f);
742   vguRect(current, 0.f, 0.f, 1.f, 1.f);
743   // Rectabgle
744   std_paths[PIRectangle] = current;
745
746   current = vgCreatePath(VG_PATH_FORMAT_STANDARD,
747                          VG_PATH_DATATYPE_F, 1.f, 0.f,
748                          0, 0, 0);
749   vguEllipse(current, 0.f, 0.f, 1.f, 1.f);
750   // Point
751   std_paths[PIPoint] = current;
752 }
753
754 void OsdOpenVG::destroyPaths()
755 {
756   vgDestroyPath(std_paths[PIHorzLine]);
757   vgDestroyPath(std_paths[PIVertLine]);
758   vgDestroyPath(std_paths[PIRectangle]);
759   vgDestroyPath(std_paths[PIPoint]);
760 }
761
762 /*font stuff*/
763
764 float OsdOpenVG::getFontHeight()
765 {
766   return font_height; //dummy
767 }
768
769 float OsdOpenVG::getCharWidth(wchar_t c)
770 {
771   if (c < 256) return byte_char_width[c];
772
773   unsigned int glyph_index = FT_Get_Char_Index(ft_face, c);
774   return font_exp_x[glyph_index];
775 }
776
777 unsigned int OsdOpenVG::loadTTchar(cTeletextChar c)
778 {
779   unsigned int glyph_index = c.getGlyphIndex();
780
781   if (tt_font_chars.find(glyph_index) != tt_font_chars.end())
782   {
783     return glyph_index;
784   }
785
786   unsigned int buffer[10];
787   const VGfloat glyphOrigin[] = { 0.f, 0.f };
788   const VGfloat escapement[] = { 12.f, 0.f };
789   unsigned int* charmap = GetFontChar(c, buffer);
790
791   if (!charmap) return 0; //invalid char
792
793   for (int i = 0; i < 10; i++)
794   {
795     buffer[i] = charmap[i] >> 4;
796   }
797
798   VGImage handle = vgCreateImage(
799                      VG_A_8,
800                      12,
801                      10,
802                      VG_IMAGE_QUALITY_NONANTIALIASED | VG_IMAGE_QUALITY_FASTER
803                      | VG_IMAGE_QUALITY_BETTER);
804   vgImageSubData(handle, buffer, 4, VG_A_1, 0, 0, 12, 10);
805   vgSetGlyphToImage(
806     vgttfont,
807     glyph_index,
808     handle, const_cast<VGfloat*>(glyphOrigin), const_cast<VGfloat*>(escapement));
809   vgDestroyImage(handle);
810   tt_font_chars[glyph_index] = 1;
811
812   return glyph_index;
813 }
814
815 int OsdOpenVG::getFontNames(const char*** names, const char*** names_keys)
816 {
817   *names = const_cast<const char**>(&(fontnames[0]));
818   *names_keys = const_cast<const char**>(&(fontnames_keys[0]));
819   return fontnames.size();
820 }
821
822 void OsdOpenVG::setFont(const char* fontname)
823 {
824   if (strcmp(fontname, cur_fontname))
825   {
826     // new font!
827     if (cur_fontname) free(cur_fontname);
828
829     cur_fontname = static_cast<char*>(malloc(strlen(fontname) + 1));
830     strcpy(cur_fontname, fontname);
831
832     struct OpenVGCommand comm;
833     comm.task = OVGreplacefont;
834     putOpenVGCommand(comm, false);
835   }
836 }
837
838 int OsdOpenVG::loadFont(bool newfont)
839 {
840   int error;
841   float font_size = 16.f;
842
843   if (!freetype_inited)
844   {
845     error = FT_Init_FreeType(&ft_library);
846
847     if (error)
848     {
849       Log::getInstance()->log("OSD", Log::WARN, "Could not load freetype %x", error);
850       return 0;
851     }
852   }
853
854   if (!freetype_inited || newfont)
855   {
856     //first get the filename algorith extracted from vdr by Klaus Schmidinger
857     FcInit();
858     FcPattern* pattern = FcNameParse(reinterpret_cast<FcChar8*>(cur_fontname));
859     FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
860     FcConfigSubstitute(NULL, pattern, FcMatchPattern);
861     FcDefaultSubstitute(pattern);
862     FcResult fres;
863     FcFontSet* fonts = FcFontSort(NULL, pattern, FcFalse, NULL, &fres);
864     FcChar8* filename = NULL;
865
866     if (fonts)
867     {
868       for (int i = 0; i < fonts->nfont; i++)
869       {
870         FcBool canscale;
871         FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &canscale);
872
873         if (canscale)
874         {
875           FcPatternGetString(fonts->fonts[i], FC_FILE, 0, &filename);
876           break;
877         }
878       }
879
880       FcFontSetDestroy(fonts);
881     }
882     else
883     {
884       Log::getInstance()->log("OSD", Log::CRIT, "Could not locate a font! Abort!");
885       return 0;
886     }
887
888     FcPatternDestroy(pattern);
889
890     Log::getInstance()->log("OSD", Log::NOTICE, "Load Font %s: %s", cur_fontname, filename);
891     // second load the font
892     FT_Face new_ft_face;
893     error = FT_New_Face(ft_library, reinterpret_cast<const char*>(filename), 0, &new_ft_face);
894
895     if (error)
896     {
897       Log::getInstance()->log("OSD", Log::WARN, "Could not load font face %x %s", error, filename);
898       return 0;
899     }
900
901     error = FT_Set_Char_Size(new_ft_face, 0, font_size * 256, 0, 0 /*dpi*/);
902
903     if (error)
904     {
905       FT_Done_Face(new_ft_face);
906       Log::getInstance()->log("OSD", Log::WARN, "Could not set  face size %x", error);
907       return 0;
908     }
909
910     FT_Face old_ft_face = ft_face; // do it thread safe
911     ft_face = new_ft_face;
912
913     if (freetype_inited) FT_Done_Face(old_ft_face);//
914
915     freetype_inited = true;
916   }
917
918   vgfont = vgCreateFont(0);
919   FT_ULong cur_char;
920   FT_UInt glyph;
921   font_height = ft_face->size->metrics.height / 256.f;
922   cur_char = FT_Get_First_Char(ft_face, &glyph);
923   std::vector<VGubyte> segments;
924   std::vector<VGfloat> coord;
925   segments.reserve(256);
926   coord.reserve(1024);
927
928   //Log::getInstance()->log("OSD", Log::DEBUG, "Create Glyph test %d %x %x %d",cur_char,font_data_end,font_data,glyph);
929   while (glyph != 0)
930   {
931     error = FT_Load_Glyph(ft_face, glyph, FT_LOAD_DEFAULT);
932
933     if (error)
934     {
935       cur_char = FT_Get_Next_Char(ft_face, cur_char, &glyph);
936       Log::getInstance()->log("OSD", Log::WARN, "Could not load glyph %x %c", error);
937       continue;
938     }
939
940     VGPath path;
941     FT_Outline ot = ft_face->glyph->outline;
942     segments.clear();
943     coord.clear();
944
945     if (ot.n_contours == 0)
946     {
947       path = VG_INVALID_HANDLE;
948     }
949     else
950     {
951       path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
952                           1.0f, 0.f, 0, 0, VG_PATH_CAPABILITY_ALL);
953
954       /* convert glyph */
955       FT_Vector* pt = ot.points;
956       const char* tags = ot.tags;
957       const short* cont = ot.contours;
958       short n_cont = ot.n_contours;
959
960       //short n_point=ot.n_points;
961       //short last_cont=0;
962       for (short point = 0; n_cont != 0; cont++, n_cont--)
963       {
964         short next_cont = *cont + 1;
965         bool first = true;
966         char last_tag = 0;
967         short first_point = point;
968
969         //Log::getInstance()->log("OSD", Log::DEBUG, "runs %d",*cont);
970         for (; point < next_cont; point++)
971         {
972           char tag = tags[point];
973           FT_Vector fpoint = pt[point];
974
975           //    Log::getInstance()->log("OSD", Log::DEBUG, "tag %d point %d %d: %d %d",tag,fpoint.x,fpoint.y,point,n_point);
976           if (first)
977           {
978             segments.push_back(VG_MOVE_TO);
979             first = false;
980           }
981           else if (tag & 0x1)    //on curve
982           {
983             if (last_tag & 0x1)
984             {
985               segments.push_back(VG_LINE_TO);
986             }
987             else if (last_tag & 0x2)
988             {
989               segments.push_back(VG_CUBIC_TO);
990             }
991             else
992             {
993               segments.push_back(VG_QUAD_TO);
994             }
995
996           }
997           else
998           {
999             if (!(tag & 0x2))
1000             {
1001               if (!(last_tag & 0x1))
1002               {
1003                 segments.push_back(VG_QUAD_TO);
1004                 int coord_size = coord.size();
1005                 VGfloat x = (coord[coord_size - 2] + ((float)fpoint.x) / 256.f) * 0.5f * aspect_correction;
1006                 VGfloat y = (coord[coord_size - 1] + (font_size - ((float)fpoint.y) / 256.f)) * 0.5f;
1007                 coord.push_back(x);
1008                 coord.push_back(y);
1009               }
1010             }
1011           }
1012
1013           last_tag = tag;
1014           coord.push_back(((float)fpoint.x)*aspect_correction / 256.);
1015           coord.push_back(font_size - ((float)fpoint.y) / 256.);
1016           //Log::getInstance()->log("OSD", Log::DEBUG, "Create APD Glyph coord %d %d %g %g",fpoint.x,fpoint.y,coord[coord.size()-2],coord[coord.size()-1]);
1017         }
1018
1019         if (!(last_tag & 0x1))
1020         {
1021           if (last_tag & 0x2)
1022           {
1023             segments.push_back(VG_CUBIC_TO);
1024           }
1025           else
1026           {
1027             segments.push_back(VG_QUAD_TO);
1028           }
1029
1030           coord.push_back(((float)pt[first_point].x)*aspect_correction / 256.);
1031           coord.push_back(font_size - ((float)pt[first_point].y) / 256.);
1032         }
1033
1034         //segments.push_back(VG_CLOSE_PATH);
1035       }
1036
1037       vgAppendPathData(path, segments.size(), &segments[0], &coord[0]);
1038       /*        for (int m=0;m<segments.size();m++) {
1039                 int n=0;
1040                 switch (segments[m])
1041                 {
1042                 case VG_MOVE_TO:
1043                         Log::getInstance()->log("OSD", Log::DEBUG, "Move To %g %g",coord[n],coord[n+1]);n+=2; break;
1044                 case VG_LINE_TO:
1045                         Log::getInstance()->log("OSD", Log::DEBUG, "Line To %g %g",coord[n],coord[n+1]);n+=2; break;
1046                 case VG_CUBIC_TO:
1047                         Log::getInstance()->log("OSD", Log::DEBUG, "Cubic To %g %g %g %g %g %g",coord[n],coord[n+1],coord[n+2],coord[n+3],coord[n+4],coord[n+5]);n+=6; break;
1048                 case VG_QUAD_TO:
1049                         Log::getInstance()->log("OSD", Log::DEBUG, "Quad To %g %g %g %g",coord[n],coord[n+1],coord[n+2],coord[n+3]);n+=4; break;
1050                 case VG_CLOSE_PATH:
1051                         Log::getInstance()->log("OSD", Log::DEBUG, "Close Path"); break;
1052                 }
1053
1054         }*/
1055       //vguRect(path,0.f,0.f,1.f,1.f);
1056       //Log::getInstance()->log("OSD", Log::DEBUG, "Create APD Glyph %d %x",segments.size(),vgGetError());
1057     }
1058
1059     VGfloat ori[] = {0.f, 0.f};
1060     VGfloat esp[] = {ft_face->glyph->advance.x / 256.f * aspect_correction, ft_face->glyph->advance.y / 256.f};
1061     font_exp_x[glyph] = ft_face->glyph->advance.x / 256.f * aspect_correction; //recalculate
1062
1063     vgSetGlyphToPath(vgfont, glyph, path, VG_FALSE, ori, esp);
1064
1065     //Log::getInstance()->log("OSD", Log::DEBUG, "Create Glyph %d %d %x",path,glyph,vgGetError());
1066     if (path != VG_INVALID_HANDLE)
1067     {
1068       vgDestroyPath(path);
1069     }
1070
1071     cur_char = FT_Get_Next_Char(ft_face, cur_char, &glyph);
1072   }
1073
1074   for (int i = 0; i < 256; i++)
1075   {
1076     unsigned int glyph_index = FT_Get_Char_Index(ft_face, i);
1077     byte_char_width[i] = font_exp_x[glyph_index];
1078   }
1079
1080   return 1;
1081 }
1082
1083 void OsdOpenVG::drawSetTrans(SurfaceInfo& sc)
1084 {
1085   vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
1086   vgLoadIdentity();
1087   vgScale(((float)BACKBUFFER_WIDTH) / 720.f, -((float)BACKBUFFER_HEIGHT) / 576.f);
1088   vgTranslate(0.f + sc.x, -576.f + sc.y);
1089
1090   vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
1091   vgLoadIdentity();
1092   vgScale(((float)BACKBUFFER_WIDTH) / 720.f, -((float)BACKBUFFER_HEIGHT) / 576.f);
1093   vgTranslate(0.f + sc.x, -576.f + sc.y);
1094
1095   vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
1096   vgLoadIdentity();
1097   vgScale(((float)BACKBUFFER_WIDTH) / 720.f, -((float)BACKBUFFER_HEIGHT) / 576.f);
1098   vgTranslate(0.f + sc.x, -576.f + sc.y);
1099   clip_shift_x = sc.x;
1100   clip_shift_y = sc.y;
1101
1102   //vgTranslate(0.f+sc.x,576.f-sc.y);
1103   //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Translate %g %g",sc.x,sc.y);
1104 }
1105
1106 void OsdOpenVG::executeDrawCommand(SVGCommand& command)
1107 {
1108   VGfloat save_matrix[9];
1109   VGfloat save_matrix2[9];
1110
1111   switch (command.instr)
1112   {
1113     case DrawPath:
1114     {
1115       vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
1116       //        VGuint rgba;
1117       //        rgba = vgGetColor((VGPaint) command.handle);
1118       //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Path %d %x %g %g %g %g",command.handle,command.target.path_index,command.x,command.y,command.w,command.h);
1119       //vgSeti(VG_FILL_RULE,);
1120
1121       vgGetMatrix(save_matrix);
1122       vgSetPaint(static_cast<VGPaint>(command.handle), VG_FILL_PATH);
1123       vgSetPaint(static_cast<VGPaint>(command.handle), VG_STROKE_PATH);
1124       vgTranslate(command.x, command.y);
1125       vgScale(command.w, command.h);
1126       vgDrawPath(std_paths[command.target.path_index], VG_FILL_PATH);
1127       vgLoadMatrix(save_matrix);
1128       break;
1129     }
1130     case DrawImage:
1131     {
1132       vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
1133       vgGetMatrix(save_matrix);
1134       VGfloat imagewidth = vgGetParameteri(static_cast<VGImage>(command.target.image), VG_IMAGE_WIDTH);
1135       VGfloat imageheight = vgGetParameteri(static_cast<VGImage>(command.target.image), VG_IMAGE_HEIGHT);
1136
1137       //vgScale(command.w,command.h);
1138
1139       if (command.handle)   //special behaviout for bw images they act as a mask on the current paint
1140       {
1141         vgTranslate(command.x, command.y);
1142         vgSetPaint(static_cast<VGPaint>(command.handle), VG_FILL_PATH);
1143         vgSetPaint(static_cast<VGPaint>(command.handle), VG_STROKE_PATH);
1144         vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL);
1145         vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
1146         vgScale(aspect_correction, 1.f);
1147         vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
1148         vgGetMatrix(save_matrix2);
1149         vgScale(imagewidth, imageheight);
1150       }
1151       else
1152       {
1153         //vgScale(720.f/((float)BACKBUFFER_WIDTH), 576.f/((float)BACKBUFFER_HEIGHT));
1154         float scalex = command.w / imagewidth;
1155         float scaley = command.h / imageheight;
1156         float tx = command.x;
1157         float ty = command.y;
1158
1159         if (command.corner == TopLeftLimited)
1160         {
1161           if (scalex != 0.f && scaley != 0.f)
1162           {
1163             float imageaspect = imagewidth / imageheight;
1164             float boxaspect = command.w / command.h / aspect_correction;
1165
1166             if (imageaspect > boxaspect)
1167             {
1168               scaley = 0.f;
1169               ty += (command.h - imageheight * scalex / aspect_correction) * 0.5f;
1170             }
1171             else
1172             {
1173               scalex = 0.f;
1174               tx += (command.w - imagewidth * scaley * aspect_correction) * 0.5f;
1175             }
1176           }
1177         }
1178
1179         if (scalex == 0.f && scaley == 0.f)
1180         {
1181           scalex = aspect_correction;
1182           scaley = 1.f;
1183         }
1184         else if (scalex == 0.f)
1185         {
1186           scalex = scaley * aspect_correction;
1187         }
1188         else if (scaley == 0.f)
1189         {
1190           scaley = scalex / aspect_correction;
1191         }
1192
1193         if (command.corner ==  BottomRight || command.corner ==  BottomLeft || command.corner == BottomMiddle)
1194         {
1195           ty -= imageheight * scaley;
1196         }
1197
1198         if (command.corner ==  BottomRight || command.corner ==  TopRight)
1199         {
1200           tx -= imagewidth * scalex;
1201         }
1202
1203         if (command.corner ==  BottomMiddle || command.corner ==  TopMiddle)
1204         {
1205           tx -= imagewidth * scalex * 0.5f;
1206         }
1207
1208         vgTranslate(tx, ty);
1209         //vgScale(command.w/imagewidth,command.h/imageheight);
1210         vgScale(scalex, scaley);
1211         vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
1212         vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
1213         //Log::getInstance()->log("OSD", Log::DEBUG, "TVMedia Draw Image Scale  %g %g %g %g %g %g",command.w,imagewidth,command.h,imageheight,scalex,scaley);
1214       }
1215
1216       //vgLoadIdentity();
1217       //vgTranslate(200.f,500.f);
1218       //vgScale(100.f,100.f);
1219
1220       vgDrawImage(static_cast<VGImage>(command.target.image));
1221
1222       //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Image %d %x %g %g %g %g %x",command.handle,command.target.image,command.x,command.y,command.w,command.h,
1223       //                vgGetError());
1224       if (command.handle)
1225       {
1226         vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
1227         vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
1228         vgLoadMatrix(save_matrix2);
1229       }
1230
1231       vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
1232
1233       vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
1234       vgLoadMatrix(save_matrix);
1235       break;
1236     }
1237     case DrawGlyph:
1238     {
1239       vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
1240       vgGetMatrix(save_matrix);
1241       vgSetPaint(static_cast<VGPaint>(command.handle), VG_FILL_PATH);
1242       vgSetPaint(static_cast<VGPaint>(command.handle), VG_STROKE_PATH);
1243       vgTranslate(command.x, command.y);
1244       vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
1245       vgGetMatrix(save_matrix2);
1246       unsigned int glyph_index = FT_Get_Char_Index(ft_face, command.target.textchar);
1247       vgScale(font_exp_x[glyph_index], font_height);
1248
1249       VGfloat gori[] = {0., 0.};
1250       vgSetfv(VG_GLYPH_ORIGIN, 2, gori);
1251
1252       vgDrawGlyph(vgfont, glyph_index, VG_FILL_PATH, VG_FALSE);
1253       //vgDrawPath(std_paths[Rectangle],VG_FILL_PATH);
1254       /*        Log::getInstance()->log("OSD", Log::DEBUG, "Draw Glyph %d %c %d %g %g %x",command.handle,command.target.textchar,glyph_index,command.x,command.y,
1255                                         vgGetError());*/
1256       vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
1257       vgLoadMatrix(save_matrix);
1258       vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
1259       vgLoadMatrix(save_matrix2);
1260
1261       vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
1262       break;
1263     }
1264     case DrawTTchar:
1265     {
1266       cTeletextChar tchar;
1267       tchar.setInternal(command.target.ttchar);
1268       vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
1269       vgGetMatrix(save_matrix);
1270       enumTeletextColor ttforegcolour = tchar.GetFGColor();
1271       enumTeletextColor ttbackgcolour = tchar.GetBGColor();
1272
1273       if (tchar.GetBoxedOut())
1274       {
1275         ttforegcolour = ttcTransparent;
1276         ttbackgcolour = ttcTransparent;
1277       }
1278
1279       vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL);
1280       vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
1281
1282       vgTranslate(command.x + command.w * 11.85f * 1.4f, command.y + command.h * 19.75f);
1283       VGfloat gori[] = {0., 0.};
1284       vgSetfv(VG_GLYPH_ORIGIN, 2, gori);
1285
1286       vgScale(-1.4f, 2.f);
1287       unsigned int color = Surface::enumTeletextColorToCoulour(ttbackgcolour).rgba();
1288       color = color << 8 | (color & 0xff000000) >> 24;
1289       vgSetColor(vgttpaint, color);
1290       vgSetPaint(static_cast<VGPaint>(vgttpaint), VG_FILL_PATH);
1291       vgSetPaint(static_cast<VGPaint>(vgttpaint), VG_STROKE_PATH);
1292       cTeletextChar filled;
1293       filled.setInternal(0x187f);
1294       unsigned int glyph_index = loadTTchar(filled);
1295       vgDrawGlyph(vgttfont, glyph_index, VG_FILL_PATH, VG_FALSE);
1296
1297       color = Surface::enumTeletextColorToCoulour(ttforegcolour).rgba();
1298       color = color << 8 | (color & 0xff000000) >> 24;
1299       vgSetColor(vgttpaint, color);
1300       vgSetPaint(static_cast<VGPaint>(vgttpaint), VG_FILL_PATH);
1301       vgSetPaint(static_cast<VGPaint>(vgttpaint), VG_STROKE_PATH);
1302       glyph_index = loadTTchar(tchar);
1303       vgDrawGlyph(vgttfont, glyph_index, VG_FILL_PATH, VG_FALSE);
1304
1305       /*        Log::getInstance()->log("OSD", Log::DEBUG, "Draw TTchar %x %x %x %x",glyph_index,ttforegcolour,Surface::enumTeletextColorToCoulour(ttforegcolour).rgba(),
1306                         vgGetColor(vgttpaint));*/
1307
1308       vgLoadMatrix(save_matrix);
1309       vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
1310       vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
1311       vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
1312       break;
1313     }
1314     case DrawClipping:
1315     {
1316       VGint coords[4] = {0, 0, 0, 0};
1317       coords[0] = ((command.x + clip_shift_x) * ((float)BACKBUFFER_WIDTH) / 720.f);
1318       coords[1] = ((576.f - command.y - clip_shift_y - command.h) * ((float)BACKBUFFER_HEIGHT) / 576.f);
1319       coords[2] = ((command.w - 1.f) * ((float)BACKBUFFER_WIDTH) / 720.f);
1320       coords[3] = ((command.h - 1.f) * ((float)BACKBUFFER_HEIGHT) / 576.f);
1321       vgSetiv(VG_SCISSOR_RECTS, 4, coords);
1322
1323       if (command.w == 0.f && command.h == 0.f)
1324         vgSeti(VG_SCISSORING, VG_FALSE);
1325       else
1326         vgSeti(VG_SCISSORING, VG_TRUE);
1327
1328       break;
1329     }
1330     case DrawImageLoading:
1331     case DrawNoop:
1332     {}
1333   }
1334 }
1335
1336 //int imcount=0;// this is debug code and should not go into release
1337 unsigned int OsdOpenVG::handleOpenVGCommand(OpenVGCommand& command)
1338 {
1339   switch (command.task)
1340   {
1341     case OVGreplacefont:
1342     {
1343       vgDestroyFont(vgfont);
1344       loadFont(true);
1345       return 0;
1346     }
1347     case OVGdestroyImageRef:  //imcount--;
1348     {
1349       //Log::getInstance()->log("OSD", Log::DEBUG, "TVMedia Draw Image Destroy %x %d",command.param1,imcount);
1350       vgDestroyImage(static_cast<VGImage>(command.param1));
1351       return 0;
1352     }
1353     case OVGdestroyPaint:
1354     {
1355       //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Paint Destroy %d ",command.param1);
1356       vgDestroyPaint(static_cast<VGPaint>(command.param1));
1357       return 0;
1358     }
1359     case OVGcreateImagePalette:  //imcount++;
1360     {
1361       VGImage input = vgCreateImage(VG_A_8, command.param1, command.param2,
1362                                     VG_IMAGE_QUALITY_NONANTIALIASED |
1363                                     VG_IMAGE_QUALITY_FASTER | VG_IMAGE_QUALITY_BETTER);
1364       //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create palette  %d %d %x %d",command.param1,command.param2,vgGetError(),input);
1365       vgImageSubData(input, command.data, command.param1,
1366                      VG_A_8, 0, 0, command.param1, command.param2); // upload palettized image data
1367       VGImage handle = vgCreateImage(VG_sRGBA_8888, command.param1, command.param2,
1368                                      VG_IMAGE_QUALITY_NONANTIALIASED |
1369                                      VG_IMAGE_QUALITY_FASTER | VG_IMAGE_QUALITY_BETTER);
1370       VGuint* palette = static_cast<VGuint*>(malloc(256 * sizeof(VGuint)));
1371       const VGuint* in_palette = static_cast<const VGuint*>(command.data2);
1372
1373       for (int i = 0; i < 256; i++)
1374       {
1375         VGuint color = in_palette[i];
1376         palette[i] = color << 8 | (color & 0xff000000) >> 24;
1377       }
1378
1379       vgLookupSingle(handle, input, palette, VG_ALPHA, VG_FALSE, VG_FALSE);
1380       free(palette);
1381       vgDestroyImage(input);
1382
1383       return handle;
1384     }
1385     case OVGcreateMonoBitmap:  //imcount++;
1386     {
1387       VGImage handle = vgCreateImage(VG_A_1, command.param1, command.param2,
1388                                      VG_IMAGE_QUALITY_FASTER);
1389       //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create mono  %d %d %x %d",command.param1,command.param2,vgGetError(),handle);
1390       unsigned int buffer_len = (command.param1 * command.param2) >> 3;
1391       unsigned char* buffer = static_cast<unsigned char*>(malloc(buffer_len));
1392       unsigned char* r_buffer1 = buffer;
1393       const unsigned char* r_buffer2 = static_cast<const unsigned char*>(command.data);
1394       unsigned char* buffer_end = buffer + buffer_len;
1395
1396       while (r_buffer1 != buffer_end)
1397       {
1398         unsigned char byte = *r_buffer2;
1399         *r_buffer1 = ((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
1400         r_buffer1++; r_buffer2++;
1401       }
1402
1403       vgImageSubData(handle, buffer, command.param1 >> 3, VG_A_1, 0, 0, command.param1, command.param2);
1404       free(buffer);
1405       //        Log::getInstance()->log("OSD", Log::DEBUG, "Draw create mono2  %d %d %x %d",command.param1,command.param2,vgGetError(),handle);
1406       return handle;
1407     }
1408     /*case OVGcreateImageFile: {
1409         VGImage handle;
1410         try{
1411                 Image *imagefile=(Image*)command.data;
1412                 Blob imageblob;
1413                 imagefile->write(&imageblob,"RGBA");
1414
1415                 handle=vgCreateImage(VG_sXBGR_8888,imagefile->columns(),imagefile->rows(),
1416                                 VG_IMAGE_QUALITY_BETTER);
1417                 //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create image details  %d %d %x mark1",imagefile->columns(),imagefile->rows(),(unsigned int*)imageblob.data());
1418                 vgImageSubData(handle,imageblob.data(),imagefile->columns()*4,
1419                                 VG_sXBGR_8888,0,0,imagefile->columns(),imagefile->rows());
1420                 //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create image details  %d %d %x mark2",imagefile->columns(),imagefile->rows(),(unsigned int*)imageblob.data());
1421                 delete imagefile;
1422         }catch( Exception &error_ )
1423         {
1424                 Log::getInstance()->log("OSD", Log::DEBUG, "Libmagick hT: %s",error_.what());
1425
1426                 return 0;
1427         }
1428
1429         //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create file  %d %d %x %d",command.param1,command.param2,vgGetError(),handle);
1430         return handle;
1431     } */
1432     case OVGcreateImageMemory:  //imcount++;
1433     {
1434       const PictureInfo* info = static_cast<const PictureInfo*>(command.data);
1435       VGImage handle;
1436       //Log::getInstance()->log("OSD", Log::DEBUG, "TVMedia OVGcreateImageMemory %d",imcount);
1437       handle = vgCreateImage(VG_sABGR_8888, info->width, info->height, VG_IMAGE_QUALITY_BETTER);
1438       vgImageSubData(handle, info->image, info->width * 4,
1439                      VG_sABGR_8888, 0, 0, info->width, info->height);
1440       info->decoder->freeReference(info->reference);
1441
1442       bool static_image = true;
1443
1444       if (info->lindex & 0xffffffff) static_image = false;
1445
1446       Message* m = new  Message();
1447       // We have a pictures! send a message to ourself, to switch to gui thread
1448       m->from = this;
1449       m->p_to = Message::CONTROL;
1450       m->data = reinterpret_cast<void*>(handle);
1451
1452       if (!static_image)
1453       {
1454         m->message = Message::NEW_PICTURE;
1455         m->tag = info->lindex;
1456       }
1457       else
1458       {
1459         m->message = Message::NEW_PICTURE_STATIC;
1460         m->tag = info->lindex >> 32LL;
1461       }
1462
1463       MessageQueue::getInstance()->postMessage(m); // inform control about new picture
1464
1465       delete info;
1466       break;
1467     }
1468     case OVGcreateEGLImage:  //imcount++;
1469     {
1470       PictureInfo* info = const_cast<PictureInfo*>(static_cast<const PictureInfo*>(command.data));
1471       VGImage handle;
1472
1473       handle = vgCreateImage(VG_sABGR_8888, info->width, info->height, VG_IMAGE_QUALITY_BETTER);
1474       //                Log::getInstance()->log("OSD", Log::DEBUG, "TVMedia OVGcreateEGLImage %d %d %x %d",info->width,info->height, handle,imcount);
1475
1476       info->handle = handle;
1477       info->reference =  eglCreateImageKHR(egl_display, egl_context, EGL_VG_PARENT_IMAGE_KHR, reinterpret_cast<EGLClientBuffer>(handle), NULL);
1478       if (info->reference) return true;
1479
1480       Log::getInstance()->log("OSD", Log::DEBUG, "TVMedia OVGcreateEGLImage %d %d %x Fail!", info->width, info->height, handle);
1481       if (handle) vgDestroyImage(handle);
1482       return false;
1483     }
1484     case OVGreadyEGLImage:
1485     {
1486       PictureInfo* info = const_cast<PictureInfo*>(static_cast<const PictureInfo*>(command.data));
1487       eglDestroyImageKHR(egl_display, info->reference);
1488       bool static_image = true;
1489
1490       if (info->lindex & 0xffffffff) static_image = false;
1491
1492       Message* m = new  Message();
1493       m->from = this;
1494       m->p_to = Message::CONTROL;
1495       m->data = reinterpret_cast<void*>(info->handle);
1496
1497       if (!static_image)
1498       {
1499         m->message = Message::NEW_PICTURE;
1500         m->tag = info->lindex;
1501       }
1502       else
1503       {
1504         m->message = Message::NEW_PICTURE_STATIC;
1505         m->tag = info->lindex >> 32LL;
1506       }
1507
1508       MessageQueue::getInstance()->postMessage(m); // inform command about new picture
1509
1510       delete info;
1511       break;
1512     }
1513     case OVGcreateColorRef:
1514     {
1515       VGPaint handle;
1516       handle = vgCreatePaint();
1517       const DrawStyle* style = static_cast<const DrawStyle*>(command.data);
1518
1519       switch (style->ft)
1520       {
1521         case DrawStyle::Color:
1522         {
1523           vgSetParameteri(handle, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
1524           vgSetColor(handle, command.param1);
1525           //VGuint rgba;
1526           //rgba = vgGetColor((VGPaint)handle);
1527           //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Paint %d %x %x",handle,command.param1,rgba);
1528           break;
1529         }
1530         case DrawStyle::GradientLinear:
1531         {
1532           vgSetParameteri(handle, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT);
1533           VGfloat params[] = {style->x1, style->y1, style->x2, style->y2, style->r};
1534           //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Gradient %d %g %g %g %g",handle,params[0],params[1],params[2],params[3]);
1535           vgSetParameterfv(handle, VG_PAINT_LINEAR_GRADIENT, 4, params);
1536           break;
1537         }
1538         case DrawStyle::GradientRadial:
1539         {
1540           vgSetParameteri(handle, VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
1541           VGfloat params[] = {style->x1, style->y1, style->x2, style->y2, style->r};
1542           vgSetParameterfv(handle, VG_PAINT_RADIAL_GRADIENT, 5, params);
1543           break;
1544         }
1545       }
1546
1547       if (style->ft == DrawStyle::GradientLinear || style->ft == DrawStyle::GradientRadial)
1548       {
1549         VGfloat colorramp[5 * 5];
1550         colorramp[0 + 0 * 5] = 0.f;
1551         colorramp[1 + 0 * 5] = static_cast<float>(style->red) / 255;
1552         colorramp[2 + 0 * 5] = static_cast<float>(style->green) / 255;
1553         colorramp[3 + 0 * 5] = static_cast<float>(style->blue) / 255;
1554         colorramp[4 + 0 * 5] = static_cast<float>(style->alpha) / 255;
1555
1556         for (int i = 0; i < (style->num_colors - 1); i++)
1557         {
1558           colorramp[0 + (i + 1) * 5] = style->grad_pos[i];
1559           colorramp[1 + (i + 1) * 5] = static_cast<float>(style->grad_col[i].red) / 255.f;
1560           colorramp[2 + (i + 1) * 5] = static_cast<float>(style->grad_col[i].green) / 255.f;
1561           colorramp[3 + (i + 1) * 5] = static_cast<float>(style->grad_col[i].blue) / 255.f;
1562           colorramp[4 + (i + 1) * 5] = static_cast<float>(style->grad_col[i].alpha) / 255.f;
1563         }
1564
1565         colorramp[0 + (style->num_colors) * 5] = 1.f;
1566         colorramp[1 + (style->num_colors) * 5] = static_cast<float>(style->grad_col[style->num_colors - 1].red) / 255.f;
1567         colorramp[2 + (style->num_colors) * 5] = static_cast<float>(style->grad_col[style->num_colors - 1].green) / 255.f;
1568         colorramp[3 + (style->num_colors) * 5] = static_cast<float>(style->grad_col[style->num_colors - 1].blue) / 255.f;
1569         colorramp[4 + (style->num_colors) * 5] = static_cast<float>(style->grad_col[style->num_colors - 1].alpha) / 255.f;
1570         vgSetParameteri(handle, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_REFLECT);
1571         vgSetParameteri(handle, VG_PAINT_COLOR_RAMP_PREMULTIPLIED, VG_FALSE);
1572         vgSetParameterfv(handle, VG_PAINT_COLOR_RAMP_STOPS, 5 + (style->num_colors) * 5, colorramp);
1573       }
1574
1575       return handle;
1576       break;
1577     }
1578     case OVGimageUploadLine:
1579     {}
1580   }
1581
1582   return 0;
1583 }
1584
1585 void OsdOpenVG::destroyImageRef(ImageIndex index)
1586 {
1587   struct OpenVGCommand comm;
1588   comm.task = OVGdestroyImageRef;
1589   comm.param1 = index;
1590   putOpenVGCommand(comm, false);
1591 }
1592
1593 bool OsdOpenVG::getStaticImageData(unsigned int static_id, UCHAR** userdata, ULONG* length)
1594 {
1595   if (sa_MAX > static_id)
1596   {
1597     *userdata = static_artwork_begin[static_id];
1598     *length = static_artwork_end[static_id] - static_artwork_begin[static_id];
1599     return true;
1600   }
1601
1602   *userdata = NULL;
1603   *length = 0;
1604   return false;
1605 }
1606
1607 void OsdOpenVG::createPicture(struct PictureInfo& pict_inf)
1608 {
1609   struct OpenVGCommand comm;
1610   Log::getInstance()->log("OsdOpenVG", Log::DEBUG, "TVMedia Create Picture %d", pict_inf.type);
1611
1612   if (pict_inf.type == PictureInfo::RGBAMemBlock)
1613   {
1614     comm.task = OVGcreateImageMemory;
1615     comm.data =  new PictureInfo(pict_inf);
1616     putOpenVGCommand(comm, false);
1617   }
1618   else if (pict_inf.type == PictureInfo::EGLImage)
1619   {
1620     comm.task = OVGreadyEGLImage;
1621     comm.data =  new PictureInfo(pict_inf);
1622     putOpenVGCommand(comm, false);
1623   }
1624   else
1625   {
1626     // unsupported
1627     pict_inf.decoder->freeReference(pict_inf.reference);
1628   }
1629 }
1630
1631 bool OsdOpenVG::getEGLPicture(struct OsdVector::PictureInfo& info, EGLDisplay* display)
1632 {
1633   struct OpenVGCommand comm;
1634   info.type = PictureInfo::EGLImage;
1635   comm.task = OVGcreateEGLImage;
1636   comm.data = &info;
1637   *display = egl_display;
1638   return  putOpenVGCommand(comm, true);
1639 }
1640
1641 ImageIndex OsdOpenVG::createMonoBitmap(void* base, int width, int height)
1642 {
1643   struct OpenVGCommand comm;
1644   comm.task = OVGcreateMonoBitmap;
1645   comm.param1 = width;
1646   comm.param2 = height;
1647   comm.data = base;
1648   return putOpenVGCommand(comm, true);
1649 }
1650
1651 ImageIndex OsdOpenVG::createImagePalette(int width, int height, const unsigned char* image_data, const unsigned int* palette_data)
1652 {
1653   struct OpenVGCommand comm;
1654   comm.task = OVGcreateImagePalette;
1655   comm.param1 = width;
1656   comm.param2 = height;
1657   comm.data = image_data;
1658   comm.data2 = palette_data;
1659   return putOpenVGCommand(comm, true);
1660 }
1661
1662 void OsdOpenVG::destroyDrawStyleHandle(VectorHandle index)
1663 {
1664   struct OpenVGCommand comm;
1665   comm.task = OVGdestroyPaint;
1666   comm.param1 = index;
1667   putOpenVGCommand(comm, false);
1668 }
1669
1670 VectorHandle OsdOpenVG::createDrawStyleHandle(const DrawStyle& c)
1671 {
1672   unsigned int col = c.rgba();
1673   struct OpenVGCommand comm;
1674   comm.task = OVGcreateColorRef;
1675   comm.param1 = col << 8 | (col & 0xff000000) >> 24;
1676   comm.data = &c;
1677   return putOpenVGCommand(comm, true);
1678 }