2 Copyright 2012 Marten Richter, 2021 Chris Tallon
4 This file is part of VOMP.
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.
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.
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/>.
21 #include "surfacevector.h"
22 #include "vdrresponsepacket.h"
24 #include "imageloader.h"
26 #include "osdvector.h"
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
34 using namespace Magick;
36 static const char* TAG = "OsdVector";
38 class MagickDecoder: public PictureDecoder
43 unsigned char* decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem);
45 bool getDecodedPicture(struct PictureInfo& pict_inf);
47 void freeReference(void* ref);
54 unsigned char* MagickDecoder::decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem)
56 if (pictInfValid) return buffer; // does support only one image at a Time;
58 Magick::Image magicimage;
59 Blob* imageblob = new Blob();
64 LogNT::getInstance()->debug("MagickDecoder", "decodePicture");
66 if (freemem) myblob.updateNoCopy(buffer, length, Blob::MallocAllocator);
67 else myblob.update(buffer, length);
69 magicimage.read(myblob);
71 magicimage.write(imageblob, "RGBA");
74 catch ( Exception& error_ )
76 LogNT::getInstance()->debug("MagickDecoder", "Libmagick: {}", error_.what());
78 LogNT::getInstance()->debug("MagickDecoder", "Libmagick: error mark2");
79 unsigned char* newbuffer = (unsigned char*) malloc(length);
80 memcpy(newbuffer, myblob.data(), length);
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;
93 // I can handle everything, so the return value is always true
96 void MagickDecoder::freeReference(void* ref)
98 Blob* todelete = (Blob*) ref;
102 bool MagickDecoder::getDecodedPicture(struct PictureInfo& pict_inf)
104 if (!pictInfValid) return false;
107 pictInfValid = false;
115 OsdVector::OsdVector()
117 logger = LogNT::getInstance();
119 setlocale(LC_CTYPE, "C.UTF-8");
122 int OsdVector::init()
124 #ifdef PICTURE_DECODER_MAGICK
125 ImageLoader::getInstance()->addDecoder(new MagickDecoder());
130 OsdVector::~OsdVector()
134 bool OsdVector::screenShot(const char* fileName)
136 //Do nothing, if no libmagick is there
137 #ifdef PICTURE_DECODER_MAGICK
139 getRealScreenSize(width, height);
140 size_t length = width * height * 4;
141 void* mem = malloc(length);
147 if (!screenShotInternal(mem, width, height, true))
149 logger->debug(TAG, "Screenshot failed!");
154 myblob.updateNoCopy(mem, length, Blob::MallocAllocator);
155 Magick::Image image(myblob, Geometry(width, height), 8, "RGBA");
156 image.write(fileName);
158 catch ( Exception& error_ )
160 logger->debug("MagickEncoder", "Libmagick: {}", error_.what());
169 Surface* OsdVector::createNewSurface()
171 return new SurfaceVector(this);
174 void OsdVector::Blank()
176 // FIXME do nothing? remove this one?
179 int OsdVector::restore()
181 // First clear the contents of all registered surfaces
182 surfaces_mutex.lock();
184 //Now go through all surfaces and clear them
185 for (auto& surface : surfaces)
187 surface.commands.clear();
188 surface.commands.reserve(2048);
191 //also clear all handles, they are now invalid, no need to release them
192 vhi_refcounts.clear();
195 drawstyleHandles.clear();
196 drawstyleHandlesRefCounts.clear();
197 drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
200 surfaces_mutex.unlock();
204 void OsdVector::drawSurfaces()
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();
210 while (itty1 != surfaces.end())
212 std::list<SurfaceInfo>::iterator itty2 = itty1;
216 while (itty2 != surfaces.end())
218 SurfaceInfo& ref1 = *itty1;
219 SurfaceInfo& ref2 = *itty2;
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) )
232 if (!hidden) // we are not hidden, perfect
234 todraw.push_back(&(*itty1));
241 getScreenSize(swidth, sheight);
242 //Now go through all surfaces and draw them
243 std::list<SurfaceInfo*>::iterator curdraw = todraw.begin();
245 while (curdraw != todraw.end())
247 drawSetTrans(*(*curdraw));
248 std::vector<SVGCommand>::iterator commands = (*(*curdraw)).commands.begin();
249 std::vector<SVGCommand>::iterator end = (*(*curdraw)).commands.end();
251 while (commands != end)
253 // update any images loaded in the mean time
254 if (commands->instr == DrawImageLoading)
256 if (commands->image->isReady())
258 commands->instr = DrawImage;
262 // Now check if the command is on screen!
263 if (!(*commands).Outside(0, 0, swidth, sheight))
265 executeDrawCommand(*commands);
274 surfaces_mutex.unlock();
277 void OsdVector::updateOrAddSurface(const SurfaceVector* surf, float x, float y, float height, float width, std::vector<SVGCommand>& commands)
279 std::lock_guard<std::mutex> lg(surfaces_mutex);
283 //logger->trace(TAG, "updateOrAddSurface, surfaces.length {}", surfaces.size());
287 // First determine it is already in our system
288 for(si = surfaces.begin(); si != surfaces.end(); si++)
290 if (si->surface == surf)
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();
299 // if not found, make a new SurfaceInfo
300 if (si == surfaces.end())
303 new_sc.surface = surf;
308 si = surfaces.insert(si, new_sc);
311 // update any images loaded in the mean time
312 for (SVGCommand& command : commands)
314 if (command.instr == DrawImageLoading)
316 if (command.image->isReady())
318 command.instr = DrawImage;
323 si->commands = commands; // Copy surf->commands to our SurfaceInfo->commands
324 incrementAllRefCounts(si->commands);
326 cleanupOrphanedRefs();
329 // logger->trace(TAG, "After UOAS:");
333 // OSDOVG-ROD-EXPERIMENT
334 logger->debug(TAG, "EXPERIMENT - call doRender");
338 void OsdVector::removeSurface(const SurfaceVector* surf)
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++)
344 if (i->surface == surf)
346 decrementAllRefCounts(i->commands);
354 void OsdVector::decrementAllRefCounts(std::vector<SVGCommand>& commands)
356 for (SVGCommand& command : commands)
358 VectorHandle handle = command.getHandle();
359 if (handle != 0) // command might not have a handle
360 decrementDrawStyleHandleRefCount(handle);
362 VectorHandleImage imageHandle = command.getImageHandle();
363 if (imageHandle) removeImageRef(imageHandle);
367 void OsdVector::incrementAllRefCounts(std::vector<SVGCommand>& commands)
369 for (SVGCommand& command : commands)
371 VectorHandle handle = command.getHandle();
372 if (handle != 0) // command might not have a handle
373 incrementDrawStyleHandleRefCount(handle);
375 VectorHandleImage imageHandle = command.getImageHandle();
376 if (imageHandle) incImageRef(imageHandle);
380 void OsdVector::incImageRef(VectorHandleImage handle)
382 if (vhi_refcounts.find(handle) == vhi_refcounts.end())
384 vhi_refcounts[handle] = 1;
388 vhi_refcounts[handle]++;
392 void OsdVector::removeImageRef(const VectorHandleImage handle)
394 vhi_refcounts[handle]--;
397 void OsdVector::dumpImages()
399 // surfaces_mutex.lock();
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)
406 // printf("ImageIndex: %u int: %i\n", a.first, a.second);
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)
414 // printf("LoadingIndex: %llu (%llu) int: %i\n", a.first, a.first >> 32, a.second);
417 // surfaces_mutex.unlock();
420 ImageLoader::getInstance()->dumpImages();
424 void OsdVector::cleanupOrphanedRefs()
426 // Do some garbage collection
428 std::map<void*, VectorHandleImage>::iterator mitty = monobitmaps.begin();
430 while (mitty != monobitmaps.end())
432 std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*mitty).second);
433 int count = (*curitty).second;
437 VectorHandleImage handle = (*curitty).first;
438 monobitmaps.erase(mitty++);
439 vhi_refcounts.erase(curitty++);
440 destroyImageRef(handle);
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;
450 VectorHandleImage handle=(*curitty).first;
451 jpegs.erase(jitty++);
452 vhi_refcounts.erase(curitty++);
453 destroyImageRef(handle);
457 std::list<VectorHandleImage>::iterator pitty = palettepics.begin();
459 while (pitty != palettepics.end())
461 std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*pitty));
462 int count = (*curitty).second;
466 VectorHandleImage handle = (*curitty).first;
467 palettepics.erase(pitty++);
468 vhi_refcounts.erase(curitty++);
469 destroyImageRef(handle);
474 std::map<VectorHandleImage, int>::iterator citty = vhi_refcounts.begin();
476 while (citty != vhi_refcounts.end())
478 int count = (*citty).second;
482 VectorHandleImage handle = (*citty).first;
483 vhi_refcounts.erase(citty++);
484 destroyImageRef(handle);
489 std::map<DrawStyle, VectorHandle>::iterator sitty = drawstyleHandles.begin();
491 while (sitty != drawstyleHandles.end())
493 std::map<VectorHandle, int>::iterator curitty = drawstyleHandlesRefCounts.find((*sitty).second);
494 int count = (*curitty).second;
498 VectorHandle ref = (*curitty).first;
499 drawstyleHandles.erase(sitty++);
500 drawstyleHandlesRefCounts.erase(curitty++);
501 drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
502 destroyDrawStyleHandle(ref);
509 ImageLoader::getInstance()->garbageCollect();
510 garbageCollectOsdImages(); // Call this on self - derived object knows how to destroy them
513 void OsdVector::incrementDrawStyleHandleRefCount(VectorHandle index)
515 if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
517 drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
518 if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
520 drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.insert(std::pair<VectorHandle, int>(index, 0)).first;
524 drawstyleHandlesRefCounts_lastit->second++;
525 drawstyleHandlesRefCounts_lastit_valid = true;
528 void OsdVector::decrementDrawStyleHandleRefCount(VectorHandle index)
530 if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
532 drawstyleHandlesRefCounts_lastit_valid = false;
533 drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
536 if (drawstyleHandlesRefCounts_lastit != drawstyleHandlesRefCounts.end())
538 drawstyleHandlesRefCounts_lastit_valid = true;
539 drawstyleHandlesRefCounts_lastit->second--;
543 VectorHandle OsdVector::getDrawStyleHandle(const DrawStyle& c)
545 surfaces_mutex.lock();
546 VectorHandle style_handle = 0;
552 if (!drawstyleHandles_lastit_valid || (drawstyleHandles_lastit->first != c))
554 drawstyleHandles_lastit_valid = false;
555 drawstyleHandles_lastit = drawstyleHandles.find(c);
558 if (drawstyleHandles_lastit == drawstyleHandles.end())
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;
567 style_handle = drawstyleHandles_lastit->second;
569 //Now check if the handle is valid
570 if (!drawstyleHandlesRefCounts_lastit_valid || (*drawstyleHandlesRefCounts_lastit).first != style_handle)
572 drawstyleHandlesRefCounts_lastit_valid = false;
573 drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(style_handle);
576 if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
578 //invalid handle recreate
579 surfaces_mutex.unlock();
580 style_handle = createDrawStyleHandle(c);
581 surfaces_mutex.lock();
582 drawstyleHandles_lastit->second = style_handle;
584 else drawstyleHandlesRefCounts_lastit_valid = true;
587 drawstyleHandles_lastit_valid = true;
588 incrementDrawStyleHandleRefCount(style_handle);
589 surfaces_mutex.unlock();
594 void OsdVector::dumpStyles()
598 std::map<DrawStyle, VectorHandle>::iterator i;
599 for(i = drawstyleHandles.begin(); i != drawstyleHandles.end(); i++)
601 const DrawStyle* test = &(i->first);
602 logger->debug(TAG, "DumpStyles: {} {}", (void*)test , i->second);
605 std::map<VectorHandle, int>::iterator ib;
606 for (ib = drawstyleHandlesRefCounts.begin(); ib != drawstyleHandlesRefCounts.end(); ib++)
608 logger->debug(TAG, "DumpStylesRef: {} {}", ib->first, ib->second);
614 VectorHandleImage OsdVector::getJpegRef(const char* fileName, int *width,int *height)
616 VectorHandleImage image_handle=0;
617 if (jpegs.find(fileName)==jpegs.end())
619 image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
621 image_handle=jpegs[fileName];
624 if (vhi_refcounts.find(image_handle)==vhi_refcounts.end()) {
625 //invalid handle recreate
626 image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
629 incImageRef(image_handle);
634 VectorHandleImage OsdVector::getMonoBitmapRef(void* base, int width, int height)
636 VectorHandleImage image_handle;
637 surfaces_mutex.lock();
639 if (monobitmaps.find(base) == monobitmaps.end())
641 surfaces_mutex.unlock();
642 image_handle = createMonoBitmap(base, width, height);
643 surfaces_mutex.lock();
644 monobitmaps[base] = image_handle;
648 image_handle = monobitmaps[base];
650 if (vhi_refcounts.find(image_handle) == vhi_refcounts.end())
652 //invalid handle recreate
653 surfaces_mutex.unlock();
654 image_handle = createMonoBitmap(base, width, height);
655 surfaces_mutex.lock();
656 monobitmaps[base] = image_handle;
660 incImageRef(image_handle);
661 surfaces_mutex.unlock();
665 VectorHandleImage OsdVector::getImagePalette(int width, int height, const unsigned char* image_data, const unsigned int* palette_data)
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();