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