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