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