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