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