]> git.vomp.tv Git - vompclient.git/blob - src/osdvector.cc
CWFs
[vompclient.git] / src / osdvector.cc
1 /*
2     Copyright 2012 Marten Richter, 2021 Chris Tallon
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 "log.h"
21 #include "surfacevector.h"
22 #include "vdrresponsepacket.h"
23 #include "control.h"
24 #include "imageloader.h"
25
26 #include "osdvector.h"
27
28 // The next section is activated, if the magick++ PictureReader is provided, it should be available for many POSIX platforms
29 #ifdef PICTURE_DECODER_MAGICK
30 #define MAGICKCORE_QUANTUM_DEPTH 16
31 #define MAGICKCORE_HDRI_ENABLE 0
32 #include <Magick++.h>
33
34 using namespace Magick;
35
36 static const char* TAG = "OsdVector";
37
38 class MagickDecoder: public PictureDecoder
39 {
40   public:
41     MagickDecoder() {}
42
43     unsigned char* decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem);
44
45     bool getDecodedPicture(struct PictureInfo& pict_inf);
46
47     void freeReference(void* ref);
48
49   protected:
50     PictureInfo pictInf;
51     bool pictInfValid{};
52 };
53
54 unsigned char* MagickDecoder::decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem)
55 {
56   if (pictInfValid) return buffer; // does support only one image at a Time;
57
58   Magick::Image magicimage;
59   Blob* imageblob = new Blob();
60   Blob myblob;
61
62   try
63   {
64     LogNT::getInstance()->debug("MagickDecoder", "decodePicture");
65
66     if (freemem) myblob.updateNoCopy(buffer, length, Blob::MallocAllocator);
67     else myblob.update(buffer, length);
68
69     magicimage.read(myblob);
70
71     magicimage.write(imageblob, "RGBA");
72
73   }
74   catch ( Exception& error_ )
75   {
76     LogNT::getInstance()->debug("MagickDecoder", "Libmagick: {}", error_.what());
77     delete imageblob;
78     LogNT::getInstance()->debug("MagickDecoder", "Libmagick: error mark2");
79     unsigned char* newbuffer = (unsigned char*) malloc(length);
80     memcpy(newbuffer, myblob.data(), length);
81     return newbuffer;
82   }
83
84   pictInf.reference = (void*) imageblob;
85   pictInf.width = magicimage.columns();
86   pictInf.height = magicimage.rows();
87   pictInf.image = imageblob->data();
88   pictInf.decoder = this;
89   pictInf.type = PictureInfo::RGBAMemBlock;
90   pictInf.lindex = index;
91   pictInfValid = true;
92
93   // I can handle everything, so the return value is always true
94   return NULL;
95 }
96 void MagickDecoder::freeReference(void* ref)
97 {
98   Blob* todelete = (Blob*) ref;
99   delete todelete;
100 }
101
102 bool MagickDecoder::getDecodedPicture(struct PictureInfo& pict_inf)
103 {
104   if (!pictInfValid) return false;
105
106   pict_inf = pictInf;
107   pictInfValid = false;
108   return true;
109 }
110
111 #endif
112
113
114
115 OsdVector::OsdVector()
116 {
117   logger = LogNT::getInstance();
118
119   setlocale(LC_CTYPE, "C.UTF-8");
120 }
121
122 int OsdVector::init()
123 {
124 #ifdef PICTURE_DECODER_MAGICK
125   ImageLoader::getInstance()->addDecoder(new MagickDecoder());
126 #endif
127   return 1;
128 }
129
130 OsdVector::~OsdVector()
131 {
132 }
133
134 bool OsdVector::screenShot(const char* fileName)
135 {
136   //Do nothing, if no libmagick is there
137 #ifdef PICTURE_DECODER_MAGICK
138   int width, height;
139   getRealScreenSize(width, height);
140   size_t length = width * height * 4;
141   void* mem = malloc(length);
142
143   try
144   {
145     Blob myblob;
146
147     if (!screenShotInternal(mem, width, height, true))
148     {
149       logger->debug(TAG, "Screenshot failed!");
150       free(mem);
151       return false;
152     }
153
154     myblob.updateNoCopy(mem, length, Blob::MallocAllocator);
155     Magick::Image image(myblob, Geometry(width, height), 8, "RGBA");
156     image.write(fileName);
157   }
158   catch ( Exception& error_ )
159   {
160     logger->debug("MagickEncoder", "Libmagick: {}", error_.what());
161     return false;
162   }
163
164   return true;
165 #endif
166   return false;
167 }
168
169 Surface* OsdVector::createNewSurface()
170 {
171   return new SurfaceVector(this);
172 }
173
174 void OsdVector::Blank()
175 {
176   // FIXME do nothing? remove this one?
177 }
178
179 int OsdVector::restore()
180 {
181   // First clear the contents of all registered surfaces
182   surfaces_mutex.lock();
183
184   //Now go through all surfaces and clear them
185   for (auto& surface : surfaces)
186   {
187     surface.commands.clear();
188     surface.commands.reserve(2048);
189   }
190
191   //also clear all handles, they are now invalid, no need to release them
192   vhi_refcounts.clear();
193   monobitmaps.clear();
194   //jpegs.clear();
195   drawstyleHandles.clear();
196   drawstyleHandlesRefCounts.clear();
197   drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
198   palettepics.clear();
199
200   surfaces_mutex.unlock();
201   return 1;
202 }
203
204 void OsdVector::drawSurfaces()
205 {
206   surfaces_mutex.lock();
207   std::list<SurfaceInfo*> todraw; //First figure out if a surfaces is below another surface
208   std::list<SurfaceInfo>::iterator itty1 = surfaces.begin();
209
210   while (itty1 != surfaces.end())
211   {
212     std::list<SurfaceInfo>::iterator itty2 = itty1;
213     itty2++;
214     bool hidden = false;
215
216     while (itty2 != surfaces.end())
217     {
218       SurfaceInfo& ref1 = *itty1;
219       SurfaceInfo& ref2 = *itty2;
220
221       if (ref1.x >= ref2.x && ref1.y >= ref2.y
222           && (ref1.x + ref1.w) <= (ref2.x + ref2.w)
223           && (ref1.y + ref1.h) <= (ref2.y + ref2.h) )
224       {
225         hidden = true;
226         break;
227       }
228
229       itty2++;
230     }
231
232     if (!hidden)   // we are not hidden, perfect
233     {
234       todraw.push_back(&(*itty1));
235     }
236
237     itty1++;
238   }
239
240   int swidth, sheight;
241   getScreenSize(swidth, sheight);
242   //Now go through all surfaces and draw them
243   std::list<SurfaceInfo*>::iterator curdraw = todraw.begin();
244
245   while (curdraw != todraw.end())
246   {
247     drawSetTrans(*(*curdraw));
248     std::vector<SVGCommand>::iterator commands = (*(*curdraw)).commands.begin();
249     std::vector<SVGCommand>::iterator end = (*(*curdraw)).commands.end();
250
251     while (commands != end)
252     {
253       // update any images loaded in the mean time
254       if (commands->instr == DrawImageLoading)
255       {
256         if (commands->image->isReady())
257         {
258           commands->instr = DrawImage;
259         }
260       }
261
262       // Now check if the command is on screen!
263       if  (!(*commands).Outside(0, 0, swidth, sheight))
264       {
265         executeDrawCommand(*commands);
266       }
267
268       commands++;
269     }
270
271     curdraw++;
272   }
273
274   surfaces_mutex.unlock();
275 }
276
277 void OsdVector::updateOrAddSurface(const SurfaceVector* surf, float x, float y, float height, float width, std::vector<SVGCommand>& commands)
278 {
279   std::lock_guard<std::mutex> lg(surfaces_mutex);
280   SurfacesIterator si;
281
282   #if DEV
283   //logger->trace(TAG, "updateOrAddSurface, surfaces.length {}", surfaces.size());
284   //dumpStyles();
285   #endif
286
287   // First determine it is already in our system
288   for(si = surfaces.begin(); si != surfaces.end(); si++)
289   {
290     if (si->surface == surf)
291     {
292       // If the surface given (surf) is already in our surfaces vector, reset our SurfaceInfo->commands vector
293       decrementAllRefCounts(si->commands);
294       si->commands.clear();
295       break;
296     }
297   }
298
299   // if not found, make a new SurfaceInfo
300   if (si == surfaces.end())
301   {
302     SurfaceInfo new_sc;
303     new_sc.surface = surf;
304     new_sc.x = x;
305     new_sc.y = y;
306     new_sc.w = width;
307     new_sc.h = height;
308     si = surfaces.insert(si, new_sc);
309   }
310
311   // update any images loaded in the mean time
312   for (SVGCommand& command : commands)
313   {
314     if (command.instr == DrawImageLoading)
315     {
316       if (command.image->isReady())
317       {
318         command.instr = DrawImage;
319       }
320     }
321   }
322
323   si->commands = commands; // Copy surf->commands to our SurfaceInfo->commands
324   incrementAllRefCounts(si->commands);
325
326   cleanupOrphanedRefs();
327
328   #if DEV
329 //  logger->trace(TAG, "After UOAS:");
330 //  dumpStyles();
331   #endif
332
333   // OSDOVG-ROD-EXPERIMENT
334   logger->debug(TAG, "EXPERIMENT - call doRender");
335   doRender();
336 }
337
338 void OsdVector::removeSurface(const SurfaceVector* surf)
339 {
340   std::lock_guard<std::mutex> lg(surfaces_mutex);                      // FIXME - Can block here on shutdown if a timer is fired just as the wrong time
341         logger->trace("OsdVector-348", "EXPERIMENT - removeSurface");
342   for (auto i = surfaces.begin(); i != surfaces.end(); i++)
343   {
344     if (i->surface == surf)
345     {
346       decrementAllRefCounts(i->commands);
347       i->commands.clear();
348       surfaces.erase(i);
349       return;
350     }
351   }
352 }
353
354 void OsdVector::decrementAllRefCounts(std::vector<SVGCommand>& commands)
355 {
356   for (SVGCommand& command : commands)
357   {
358     VectorHandle handle = command.getHandle();
359     if (handle != 0) // command might not have a handle
360       decrementDrawStyleHandleRefCount(handle);
361
362    VectorHandleImage imageHandle = command.getImageHandle();
363    if (imageHandle) removeImageRef(imageHandle);
364   }
365 }
366
367 void OsdVector::incrementAllRefCounts(std::vector<SVGCommand>& commands)
368 {
369   for (SVGCommand& command : commands)
370   {
371     VectorHandle handle = command.getHandle();
372     if (handle != 0) // command might not have a handle
373       incrementDrawStyleHandleRefCount(handle);
374
375    VectorHandleImage imageHandle = command.getImageHandle();
376    if (imageHandle) incImageRef(imageHandle);
377   }
378 }
379
380 void OsdVector::incImageRef(VectorHandleImage handle)
381 {
382   if (vhi_refcounts.find(handle) == vhi_refcounts.end())
383   {
384     vhi_refcounts[handle] = 1;
385   }
386   else
387   {
388     vhi_refcounts[handle]++;
389   }
390 }
391
392 void OsdVector::removeImageRef(const VectorHandleImage handle)
393 {
394   vhi_refcounts[handle]--;
395 }
396
397 void OsdVector::dumpImages()
398 {
399    // surfaces_mutex.lock();
400
401 //   printf("vhi_refcounts\n");
402 //   // std::map<ImageIndex, int> vhi_refcounts;
403 //   // ImageIndex is a VectorHandle. VectorHandle is an unsigned int
404 //   for(auto& a : vhi_refcounts)
405 //   {
406 //     printf("ImageIndex: %u int: %i\n", a.first, a.second);
407 //   }
408
409 //   printf("loadingindex_ref\n");
410 //   // std::map<LoadingIndex, int> loadindex_ref;
411 //   // LoadingIndex is a unsigned long long
412 //   for(auto& a : loadindex_ref)
413 //   {
414 //     printf("LoadingIndex: %llu (%llu) int: %i\n", a.first, a.first >> 32, a.second);
415 //   }
416
417   // surfaces_mutex.unlock();
418
419
420   ImageLoader::getInstance()->dumpImages();
421   Osd::dumpImages();
422 }
423
424 void OsdVector::cleanupOrphanedRefs()
425 {
426   // Do some garbage collection
427
428   std::map<void*, VectorHandleImage>::iterator mitty = monobitmaps.begin();
429
430   while (mitty != monobitmaps.end())
431   {
432     std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*mitty).second);
433     int count = (*curitty).second;
434
435     if (count == 0)
436     {
437       VectorHandleImage handle = (*curitty).first;
438       monobitmaps.erase(mitty++);
439       vhi_refcounts.erase(curitty++);
440       destroyImageRef(handle);
441     }
442     else ++mitty;
443   }
444
445   /*map<string,VectorHandleImage>::iterator jitty=jpegs.begin();
446   while (jitty!=jpegs.end()) {
447         map<VectorHandleImage,int>::iterator curitty=vhi_refcounts.find((*jitty).second);
448         int count=(*curitty).second;
449         if (count==0) {
450                 VectorHandleImage handle=(*curitty).first;
451                 jpegs.erase(jitty++);
452                 vhi_refcounts.erase(curitty++);
453                 destroyImageRef(handle);
454         } else ++jitty;
455   }*/
456
457   std::list<VectorHandleImage>::iterator pitty = palettepics.begin();
458
459   while (pitty != palettepics.end())
460   {
461     std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*pitty));
462     int count = (*curitty).second;
463
464     if (count == 0)
465     {
466       VectorHandleImage handle = (*curitty).first;
467       palettepics.erase(pitty++);
468       vhi_refcounts.erase(curitty++);
469       destroyImageRef(handle);
470     }
471     else ++pitty;
472   }
473
474   std::map<VectorHandleImage, int>::iterator citty = vhi_refcounts.begin();
475
476   while (citty != vhi_refcounts.end())
477   {
478     int count = (*citty).second;
479
480     if (count == 0)
481     {
482       VectorHandleImage handle = (*citty).first;
483       vhi_refcounts.erase(citty++);
484       destroyImageRef(handle);
485     }
486     else ++citty;
487   }
488
489   std::map<DrawStyle, VectorHandle>::iterator sitty = drawstyleHandles.begin();
490
491   while (sitty != drawstyleHandles.end())
492   {
493     std::map<VectorHandle, int>::iterator curitty = drawstyleHandlesRefCounts.find((*sitty).second);
494     int count = (*curitty).second;
495
496     if (count == 0)
497     {
498       VectorHandle ref = (*curitty).first;
499       drawstyleHandles.erase(sitty++);
500       drawstyleHandlesRefCounts.erase(curitty++);
501       drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
502       destroyDrawStyleHandle(ref);
503
504     }
505     else ++sitty;
506
507   }
508
509   ImageLoader::getInstance()->garbageCollect();
510   garbageCollectOsdImages(); // Call this on self - derived object knows how to destroy them
511 }
512
513 void OsdVector::incrementDrawStyleHandleRefCount(VectorHandle index)
514 {
515   if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
516   {
517     drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
518     if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
519     {
520       drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.insert(std::pair<VectorHandle, int>(index, 0)).first;
521     }
522   }
523
524   drawstyleHandlesRefCounts_lastit->second++;
525   drawstyleHandlesRefCounts_lastit_valid = true;
526 }
527
528 void OsdVector::decrementDrawStyleHandleRefCount(VectorHandle index)
529 {
530   if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
531   {
532     drawstyleHandlesRefCounts_lastit_valid = false;
533     drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
534   }
535
536   if (drawstyleHandlesRefCounts_lastit != drawstyleHandlesRefCounts.end())
537   {
538     drawstyleHandlesRefCounts_lastit_valid = true;
539     drawstyleHandlesRefCounts_lastit->second--;
540   }
541 }
542
543 VectorHandle OsdVector::getDrawStyleHandle(const DrawStyle& c)
544 {
545   surfaces_mutex.lock();
546   VectorHandle style_handle = 0;
547
548   #if DEV
549   dumpStyles();
550   #endif
551
552   if (!drawstyleHandles_lastit_valid || (drawstyleHandles_lastit->first != c))
553   {
554     drawstyleHandles_lastit_valid = false;
555     drawstyleHandles_lastit = drawstyleHandles.find(c);
556   }
557
558   if (drawstyleHandles_lastit == drawstyleHandles.end())
559   {
560     surfaces_mutex.unlock();
561     style_handle = createDrawStyleHandle(c);
562     surfaces_mutex.lock();
563     drawstyleHandles_lastit = drawstyleHandles.insert(std::pair<DrawStyle, VectorHandle>(c, style_handle)).first;
564   }
565   else
566   {
567     style_handle = drawstyleHandles_lastit->second;
568
569     //Now check if the handle is valid
570     if (!drawstyleHandlesRefCounts_lastit_valid || (*drawstyleHandlesRefCounts_lastit).first != style_handle)
571     {
572       drawstyleHandlesRefCounts_lastit_valid = false;
573       drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(style_handle);
574     }
575
576     if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
577     {
578       //invalid handle recreate
579       surfaces_mutex.unlock();
580       style_handle = createDrawStyleHandle(c);
581       surfaces_mutex.lock();
582       drawstyleHandles_lastit->second = style_handle;
583     }
584     else drawstyleHandlesRefCounts_lastit_valid = true;
585   }
586
587   drawstyleHandles_lastit_valid = true;
588   incrementDrawStyleHandleRefCount(style_handle);
589   surfaces_mutex.unlock();
590   return style_handle;
591 }
592
593 #if DEV
594 void OsdVector::dumpStyles()
595 {
596   return;
597
598   std::map<DrawStyle, VectorHandle>::iterator i;
599   for(i = drawstyleHandles.begin(); i != drawstyleHandles.end(); i++)
600   {
601     const DrawStyle* test = &(i->first);
602     logger->debug(TAG, "DumpStyles: {} {}", (void*)test , i->second);
603   }
604
605   std::map<VectorHandle, int>::iterator i2;
606   for (i2 = drawstyleHandlesRefCounts.begin(); i2 != drawstyleHandlesRefCounts.end(); i2++)
607   {
608     logger->debug(TAG, "DumpStylesRef: {} {}", i2->first, i2->second);
609   }
610 }
611 #endif
612
613 /*
614 VectorHandleImage OsdVector::getJpegRef(const char* fileName, int *width,int *height)
615 {
616         VectorHandleImage image_handle=0;
617         if (jpegs.find(fileName)==jpegs.end())
618         {
619                 image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
620         } else {
621                 image_handle=jpegs[fileName];
622                 *width=0;
623                 *height=0;
624                 if (vhi_refcounts.find(image_handle)==vhi_refcounts.end()) {
625                         //invalid handle recreate
626                         image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
627                 }
628         }
629         incImageRef(image_handle);
630         return image_handle;
631 }
632 */
633
634 VectorHandleImage OsdVector::getMonoBitmapRef(void* base, int width, int height)
635 {
636   VectorHandleImage image_handle;
637   surfaces_mutex.lock();
638
639   if (monobitmaps.find(base) == monobitmaps.end())
640   {
641     surfaces_mutex.unlock();
642     image_handle = createMonoBitmap(base, width, height);
643     surfaces_mutex.lock();
644     monobitmaps[base] = image_handle;
645   }
646   else
647   {
648     image_handle = monobitmaps[base];
649
650     if (vhi_refcounts.find(image_handle) == vhi_refcounts.end())
651     {
652       //invalid handle recreate
653       surfaces_mutex.unlock();
654       image_handle = createMonoBitmap(base, width, height);
655       surfaces_mutex.lock();
656       monobitmaps[base] = image_handle;
657     }
658   }
659
660   incImageRef(image_handle);
661   surfaces_mutex.unlock();
662   return image_handle;
663 }
664
665 VectorHandleImage OsdVector::getImagePalette(int width, int height, const unsigned char* image_data, const unsigned int* palette_data)
666 {
667   VectorHandleImage image_handle;
668   image_handle = createImagePalette(width, height, image_data, palette_data);
669   surfaces_mutex.lock();
670   palettepics.push_back(image_handle);
671   incImageRef(image_handle);
672   surfaces_mutex.unlock();
673   return image_handle;
674 }