summaryrefslogtreecommitdiff
path: root/libcore
diff options
context:
space:
mode:
authorlouis <louis.braun@gmx.de>2014-09-27 09:25:14 +0200
committerlouis <louis.braun@gmx.de>2014-09-27 09:25:14 +0200
commitb0509b5182b6e0d04f05e6b3d5676b0d21f51966 (patch)
tree22b302342f22843e0815eb5f516c85f1478cbf0b /libcore
downloadvdr-plugin-skindesigner-0.0.1.tar.gz
vdr-plugin-skindesigner-0.0.1.tar.bz2
initial commit version 0.0.10.0.1
Diffstat (limited to 'libcore')
-rw-r--r--libcore/fontmanager.c174
-rw-r--r--libcore/fontmanager.h35
-rw-r--r--libcore/helpers.c155
-rw-r--r--libcore/helpers.h35
-rw-r--r--libcore/imagecache.c389
-rw-r--r--libcore/imagecache.h53
-rw-r--r--libcore/imageloader.c56
-rw-r--r--libcore/imageloader.h23
-rw-r--r--libcore/imagemagickwrapper.c162
-rw-r--r--libcore/imagemagickwrapper.h28
-rw-r--r--libcore/imagescaler.c149
-rw-r--r--libcore/imagescaler.h97
-rw-r--r--libcore/pixmapcontainer.c477
-rw-r--r--libcore/pixmapcontainer.h73
-rw-r--r--libcore/timers.c84
-rw-r--r--libcore/timers.h20
16 files changed, 2010 insertions, 0 deletions
diff --git a/libcore/fontmanager.c b/libcore/fontmanager.c
new file mode 100644
index 0000000..1dcba44
--- /dev/null
+++ b/libcore/fontmanager.c
@@ -0,0 +1,174 @@
+#include "fontmanager.h"
+#include "../config.h"
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+using namespace std;
+
+cMutex cFontManager::mutex;
+
+cFontManager::cFontManager() {
+}
+
+cFontManager::~cFontManager() {
+ DeleteFonts();
+}
+
+void cFontManager::CacheFonts(cTemplate *tpl) {
+ cMutexLock MutexLock(&mutex);
+
+ vector< pair<string, int> > usedFonts = tpl->GetUsedFonts();
+
+ cStringList availableFonts;
+ cFont::GetAvailableFontNames(&availableFonts);
+
+ for (vector< pair<string, int> >::iterator ft = usedFonts.begin(); ft != usedFonts.end(); ft++) {
+ string fontName = ft->first;
+ int fontSize = ft->second;
+ if (fontSize < 1) {
+ continue;
+ }
+
+ int fontAvailable = availableFonts.Find(fontName.c_str());
+ if (fontAvailable == -1) {
+ esyslog("skindesigner: font %s not available, skipping", fontName.c_str());
+ continue;
+ }
+
+ InsertFont(fontName, fontSize);
+ }
+}
+
+void cFontManager::Debug(void) {
+ dsyslog("skindesigner: fontmanager fonts available:");
+ for (map < string, map< int, cFont* > >::iterator fts = fonts.begin(); fts != fonts.end(); fts++) {
+ dsyslog("skindesigner: FontName %s", fts->first.c_str());
+ for (map<int, cFont*>::iterator ftSizes = (fts->second).begin(); ftSizes != (fts->second).end(); ftSizes++) {
+ int confHeight = ftSizes->first;
+ int realHeight = (ftSizes->second)->Height();
+ dsyslog("skindesigner: fontSize %d, fontHeight %d, ratio %f", confHeight, realHeight, (double)confHeight / (double)realHeight);
+ }
+ }
+}
+
+void cFontManager::ListAvailableFonts(void) {
+ cStringList availableFonts;
+ cFont::GetAvailableFontNames(&availableFonts);
+ int numFonts = availableFonts.Size();
+ esyslog("skindesigner: %d Fonts available:", numFonts);
+ for (int i=0; i<numFonts; i++) {
+ esyslog("skindesigner: font %d: %s", i, availableFonts[i]);
+ }
+}
+
+void cFontManager::DeleteFonts() {
+ cMutexLock MutexLock(&mutex);
+ for(map<string, map<int,cFont*> >::iterator it = fonts.begin(); it != fonts.end(); it++) {
+ for(map<int,cFont*>::iterator it2 = (it->second).begin(); it2 != (it->second).end(); it2++) {
+ delete it2->second;
+ }
+ }
+ fonts.clear();
+}
+
+int cFontManager::Width(string fontName, int fontSize, const char *text) {
+ cMutexLock MutexLock(&mutex);
+ if (!text)
+ return 0;
+ cFont *font = GetFont(fontName, fontSize);
+ //if not already cached, load it new
+ if (!font)
+ InsertFont(fontName, fontSize);
+ font = GetFont(fontName, fontSize);
+ if (!font)
+ return 0;
+ int width = font->Width(text);
+ return width;
+}
+
+int cFontManager::Height(string fontName, int fontSize) {
+ cMutexLock MutexLock(&mutex);
+ cFont *font = GetFont(fontName, fontSize);
+ //if not already cached, load it new
+ if (!font)
+ InsertFont(fontName, fontSize);
+ font = GetFont(fontName, fontSize);
+ if (!font)
+ return 0;
+ return font->Height();
+}
+
+cFont *cFontManager::Font(string fontName, int fontSize) {
+ cMutexLock MutexLock(&mutex);
+ cFont *font = GetFont(fontName, fontSize);
+ //if not already cached, load it new
+ if (!font)
+ InsertFont(fontName, fontSize);
+ font = GetFont(fontName, fontSize);
+ return font;
+}
+
+/********************************************************************************
+* Private Functions
+********************************************************************************/
+
+cFont *cFontManager::CreateFont(string name, int size) {
+ cMutexLock MutexLock(&mutex);
+ cFont *fontTmp = cFont::CreateFont(name.c_str(), size);
+ if (!fontTmp)
+ fontTmp = cFont::CreateFont(Setup.FontOsd, size);
+ int realHeight = fontTmp->Height();
+ delete fontTmp;
+ cFont *font = cFont::CreateFont(name.c_str(), (double)size / (double)realHeight * (double)size);
+ if (!font)
+ font = cFont::CreateFont(Setup.FontOsd, (double)size / (double)realHeight * (double)size);
+ return font;
+}
+
+void cFontManager::InsertFont(string name, int size) {
+ cFont *newFont = CreateFont(name, size);
+ if (!newFont)
+ return;
+ map < string, map< int, cFont* > >::iterator hit = fonts.find(name);
+ if (hit != fonts.end()) {
+ (hit->second).insert(pair<int, cFont*>(size, newFont));
+ } else {
+ map<int, cFont*> fontsizes;
+ fontsizes.insert(pair<int, cFont*>(size, newFont));
+ fonts.insert(pair<string, map<int, cFont*> >(name, fontsizes));
+ }
+}
+
+cFont *cFontManager::GetFont(string name, int size) {
+ map< string, map<int,cFont*> >::iterator hitName = fonts.find(name);
+ if (hitName == fonts.end())
+ return NULL;
+ map<int,cFont*>::iterator hitSize = (hitName->second).find(size);
+ if (hitSize == (hitName->second).end())
+ return NULL;
+ return hitSize->second;
+}
+
+int cFontManager::GetFontHeight(const char *name, int height, int charWidth) {
+ FT_Library library;
+ FT_Face face;
+ cString fontFileName = cFont::GetFontFileName(name);
+
+ int descender = 0;
+ int y_ppem = 0;
+ int error = FT_Init_FreeType(&library);
+ if (error) return 0;
+ error = FT_New_Face(library, fontFileName, 0, &face);
+ if (error) return 0;
+ error = FT_Set_Char_Size(face, charWidth * 64, height * 64, 0, 0);
+ if (error) return 0;
+
+ descender = face->size->metrics.descender/64;
+ y_ppem = face->size->metrics.y_ppem;
+ int realHeight = y_ppem + descender;
+
+ FT_Done_Face(face);
+ FT_Done_FreeType(library);
+
+ return realHeight;
+}
diff --git a/libcore/fontmanager.h b/libcore/fontmanager.h
new file mode 100644
index 0000000..7067dfc
--- /dev/null
+++ b/libcore/fontmanager.h
@@ -0,0 +1,35 @@
+#ifndef __FONTMANAGER_H
+#define __FONTMANAGER_H
+
+#include <string>
+#include <map>
+#include <vector>
+#include <vdr/skins.h>
+
+#include "../libtemplate/template.h"
+
+using namespace std;
+
+class cFontManager {
+ private:
+ static cMutex mutex;
+ map < string, map< int, cFont* > > fonts;
+ cFont *CreateFont(string name, int size);
+ void InsertFont(string name, int size);
+ cFont *GetFont(string name, int size);
+ int GetFontHeight(const char *name, int height, int charWidth = 0);
+ public:
+ cFontManager();
+ ~cFontManager();
+ void Lock(void) { mutex.Lock(); };
+ void Unlock(void) { mutex.Unlock(); };
+ void CacheFonts(cTemplate *tpl);
+ void DeleteFonts(void);
+ int Width(string fontName, int fontSize, const char *text);
+ int Height(string fontName, int fontSize);
+ cFont *Font(string fontName, int fontSize);
+ void Debug(void);
+ void ListAvailableFonts(void);
+};
+
+#endif //__FONTMANAGER_H \ No newline at end of file
diff --git a/libcore/helpers.c b/libcore/helpers.c
new file mode 100644
index 0000000..81880a0
--- /dev/null
+++ b/libcore/helpers.c
@@ -0,0 +1,155 @@
+#include <string>
+#include <sstream>
+#include <vector>
+#include "helpers.h"
+#include <vdr/skins.h>
+
+cPlugin *GetScraperPlugin(void) {
+ static cPlugin *pScraper = cPluginManager::GetPlugin("scraper2vdr");
+ if( !pScraper ) // if it doesn't exit, try tvscraper
+ pScraper = cPluginManager::GetPlugin("tvscraper");
+ return pScraper;
+}
+
+cSize ScaleToFit(int widthMax, int heightMax, int widthOriginal, int heightOriginal) {
+ int width = 1;
+ int height = 1;
+
+ if ((widthMax == 0)||(heightMax==0)||(widthOriginal==0)||(heightOriginal==0))
+ return cSize(width, height);
+
+ if ((widthOriginal <= widthMax) && (heightOriginal <= heightMax)) {
+ width = widthOriginal;
+ height = heightOriginal;
+ } else if ((widthOriginal > widthMax) && (heightOriginal <= heightMax)) {
+ width = widthMax;
+ height = (double)width/(double)widthOriginal * heightOriginal;
+ } else if ((widthOriginal <= widthMax) && (heightOriginal > heightMax)) {
+ height = heightMax;
+ width = (double)height/(double)heightOriginal * widthOriginal;
+ } else {
+ width = widthMax;
+ height = (double)width/(double)widthOriginal * heightOriginal;
+ if (height > heightMax) {
+ height = heightMax;
+ width = (double)height/(double)heightOriginal * widthOriginal;
+ }
+ }
+ return cSize(width, height);
+}
+
+int Minimum(int a, int b, int c, int d, int e, int f) {
+ int min = a;
+ if (b < min) min = b;
+ if (c < min) min = c;
+ if (d < min) min = d;
+ if (e < min) min = e;
+ if (f < min) min = f;
+ return min;
+}
+
+string CutText(string &text, int width, string fontName, int fontSize) {
+ if (width <= fontManager->Font(fontName, fontSize)->Size())
+ return text.c_str();
+ cTextWrapper twText;
+ twText.Set(text.c_str(), fontManager->Font(fontName, fontSize), width);
+ string cuttedTextNative = twText.GetLine(0);
+ stringstream sstrText;
+ sstrText << cuttedTextNative << "...";
+ string cuttedText = sstrText.str();
+ int actWidth = fontManager->Width(fontName, fontSize, cuttedText.c_str());
+ if (actWidth > width) {
+ int overlap = actWidth - width;
+ int charWidth = fontManager->Width(fontName, fontSize, ".");
+ if (charWidth == 0)
+ charWidth = 1;
+ int cutChars = overlap / charWidth;
+ if (cutChars > 0) {
+ cuttedTextNative = cuttedTextNative.substr(0, cuttedTextNative.length() - cutChars);
+ stringstream sstrText2;
+ sstrText2 << cuttedTextNative << "...";
+ cuttedText = sstrText2.str();
+ }
+ }
+ return cuttedText;
+}
+
+
+string StrToLowerCase(string str) {
+ string lowerCase = str;
+ const int length = lowerCase.length();
+ for(int i=0; i < length; ++i) {
+ lowerCase[i] = std::tolower(lowerCase[i]);
+ }
+ return lowerCase;
+}
+
+bool isNumber(const string& s) {
+ string::const_iterator it = s.begin();
+ while (it != s.end() && std::isdigit(*it)) ++it;
+ return !s.empty() && it == s.end();
+}
+
+bool FileExists(const string &path, const string &name, const string &ext) {
+ stringstream fileName;
+ fileName << path << name << "." << ext;
+ struct stat buffer;
+ return (stat (fileName.str().c_str(), &buffer) == 0);
+}
+
+bool FirstFileInFolder(string &path, string &extension, string &fileName) {
+ DIR *folder = NULL;
+ struct dirent *file;
+ folder = opendir(path.c_str());
+ if (!folder)
+ return false;
+ while (file = readdir(folder)) {
+ if (endswith(file->d_name, extension.c_str())) {
+ string currentFileName = file->d_name;
+ int strlength = currentFileName.size();
+ if (strlength < 8)
+ continue;
+ fileName = currentFileName;
+ return true;
+ }
+ }
+ return false;
+}
+
+// split: receives a char delimiter; returns a vector of strings
+// By default ignores repeated delimiters, unless argument rep == 1.
+vector<string>& splitstring::split(char delim, int rep) {
+ if (!flds.empty()) flds.clear(); // empty vector if necessary
+ string work = data();
+ string buf = "";
+ int i = 0;
+ while (i < work.length()) {
+ if (work[i] != delim)
+ buf += work[i];
+ else if (rep == 1) {
+ flds.push_back(buf);
+ buf = "";
+ } else if (buf.length() > 0) {
+ flds.push_back(buf);
+ buf = "";
+ }
+ i++;
+ }
+ if (!buf.empty())
+ flds.push_back(buf);
+ return flds;
+}
+
+cStopWatch::cStopWatch(void) {
+ start = cTimeMs::Now();
+ last = start;
+}
+
+void cStopWatch::Report(const char* message) {
+ dsyslog("skindesigner: %s - needed %d ms", message, (int)(cTimeMs::Now() - last));
+ last = cTimeMs::Now();
+}
+
+void cStopWatch::Stop(const char* message) {
+ dsyslog("skindesigner: %s - needed %d ms", message, (int)(cTimeMs::Now() - start));
+}
diff --git a/libcore/helpers.h b/libcore/helpers.h
new file mode 100644
index 0000000..60f3345
--- /dev/null
+++ b/libcore/helpers.h
@@ -0,0 +1,35 @@
+#ifndef __HELPERS_H
+#define __HELPERS_H
+
+#include <vdr/osd.h>
+#include <vdr/plugin.h>
+#include "../config.h"
+
+cPlugin *GetScraperPlugin(void);
+
+cSize ScaleToFit(int widthMax, int heightMax, int widthOriginal, int heightOriginal);
+int Minimum(int a, int b, int c, int d, int e, int f);
+std::string CutText(string &text, int width, string fontName, int fontSize);
+std::string StrToLowerCase(string str);
+bool isNumber(const string& s);
+bool FileExists(const string &path, const string &name, const string &ext);
+bool FirstFileInFolder(string &path, string &extension, string &fileName);
+
+class splitstring : public std::string {
+ std::vector<std::string> flds;
+public:
+ splitstring(const char *s) : std::string(s) { };
+ std::vector<std::string>& split(char delim, int rep=0);
+};
+
+class cStopWatch {
+private:
+ uint64_t start;
+ uint64_t last;
+public:
+ cStopWatch(void);
+ ~cStopWatch(void) {};
+ void Report(const char* message);
+ void Stop(const char* message);
+};
+#endif // __HELPERS_H
diff --git a/libcore/imagecache.c b/libcore/imagecache.c
new file mode 100644
index 0000000..7347c0a
--- /dev/null
+++ b/libcore/imagecache.c
@@ -0,0 +1,389 @@
+#include <string>
+#include <sstream>
+#include <map>
+#include <fstream>
+#include <sys/stat.h>
+#include "imagecache.h"
+#include "../config.h"
+#include "helpers.h"
+
+using namespace Magick;
+
+cMutex cImageCache::mutex;
+
+string cImageCache::items[16] = { "Schedule", "Channels", "Timers", "Recordings", "Setup", "Commands",
+ "OSD", "EPG", "DVB", "LNB", "CAM", "Recording", "Replay", "Miscellaneous", "Plugins", "Restart"};
+
+cImageCache::cImageCache() : cImageMagickWrapper() {
+ tempStaticLogo = NULL;
+}
+
+cImageCache::~cImageCache() {
+ Clear();
+ if (tempStaticLogo) {
+ delete tempStaticLogo;
+ tempStaticLogo = NULL;
+ }
+}
+
+void cImageCache::CacheLogo(int width, int height) {
+ if (config.numLogosPerSizeInitial == 0)
+ return;
+ if (width == 0 || height == 0)
+ return;
+
+ int channelsCached = 0;
+
+ for (const cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
+ if (channelsCached >= config.numLogosPerSizeInitial)
+ break;
+ if (channel->GroupSep()) {
+ continue;
+ }
+ bool success = LoadLogo(channel);
+ if (success) {
+ channelsCached++;
+ cImage *image = CreateImage(width, height);
+ stringstream logoName;
+ logoName << *channel->GetChannelID().ToString() << "_" << width << "x" << height;
+ std::map<std::string, cImage*>::iterator hit = channelLogoCache.find(logoName.str());
+ if (hit != channelLogoCache.end()) {
+ delete image;
+ return;
+ }
+ channelLogoCache.insert(pair<string, cImage*>(logoName.str(), image));
+ }
+ }
+}
+
+cImage *cImageCache::GetLogo(string channelID, int width, int height) {
+ cMutexLock MutexLock(&mutex);
+
+ stringstream logoName;
+ logoName << channelID << "_" << width << "x" << height;
+
+ std::map<std::string, cImage*>::iterator hit = channelLogoCache.find(logoName.str());
+
+ if (hit != channelLogoCache.end()) {
+ return (cImage*)hit->second;
+ } else {
+ tChannelID chanID = tChannelID::FromString(channelID.c_str());
+ const cChannel *channel = Channels.GetByChannelID(chanID);
+ if (!channel)
+ return NULL;
+ bool success = LoadLogo(channel);
+ if (success) {
+ if (config.limitLogoCache && (channelLogoCache.size() >= config.numLogosMax)) {
+ //logo cache is full, don't cache anymore
+ if (tempStaticLogo) {
+ delete tempStaticLogo;
+ tempStaticLogo = NULL;
+ }
+ tempStaticLogo = CreateImage(width, height);
+ return tempStaticLogo;
+ } else {
+ //add requested logo to cache
+ cImage *image = CreateImage(width, height);
+ channelLogoCache.insert(pair<string, cImage*>(logoName.str(), image));
+ hit = channelLogoCache.find(logoName.str());
+ if (hit != channelLogoCache.end()) {
+ return (cImage*)hit->second;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+cImage *cImageCache::GetSeparatorLogo(string name, int width, int height) {
+ cMutexLock MutexLock(&mutex);
+
+ stringstream logoName;
+ logoName << name << "_" << width << "x" << height;
+
+ std::map<std::string, cImage*>::iterator hit = channelLogoCache.find(logoName.str());
+
+ if (hit != channelLogoCache.end()) {
+ return (cImage*)hit->second;
+ } else {
+ bool success = LoadSeparatorLogo(name);
+ if (success) {
+ //add requested logo to cache
+ cImage *image = CreateImage(width, height);
+ channelLogoCache.insert(pair<string, cImage*>(logoName.str(), image));
+ hit = channelLogoCache.find(logoName.str());
+ if (hit != channelLogoCache.end()) {
+ return (cImage*)hit->second;
+ }
+ }
+ }
+ return NULL;
+}
+
+bool cImageCache::LogoExists(string channelID) {
+ tChannelID chanID = tChannelID::FromString(channelID.c_str());
+ const cChannel *channel = Channels.GetByChannelID(chanID);
+ if (!channel)
+ return false;
+ string logoPath = *cString::sprintf("%s%s/logos/", *config.skinPath, Setup.OSDTheme);
+ string logoLower = StrToLowerCase(channel->Name());
+ string logoExt = *config.logoExtension;
+ bool logoExists = FileExists(logoPath, logoLower, logoExt);
+ if (logoExists) {
+ return true;
+ }
+ logoExists = FileExists(logoPath, channelID, logoExt);
+ if (logoExists) {
+ return true;
+ }
+ return false;
+}
+
+bool cImageCache::SeparatorLogoExists(string name) {
+ string separatorPath = *cString::sprintf("%s%s/logos/separatorlogos/", *config.skinPath, Setup.OSDTheme);
+ string nameLower = StrToLowerCase(name.c_str());
+ string logoExt = *config.logoExtension;
+ bool logoExists = FileExists(separatorPath, nameLower, logoExt);
+ if (logoExists) {
+ return true;
+ }
+ return false;
+}
+
+void cImageCache::CacheIcon(eImageType type, string name, int width, int height) {
+ if (width < 1 || width > 1920 || height < 1 || height > 1080)
+ return;
+ bool success = LoadIcon(type, name);
+ if (!success)
+ return;
+ stringstream iconName;
+ iconName << name << "_" << width << "x" << height;
+ cImage *image = CreateImage(width, height, true);
+ iconCache.insert(pair<string, cImage*>(iconName.str(), image));
+}
+
+cImage *cImageCache::GetIcon(eImageType type, string name, int width, int height) {
+ if (width < 1 || width > 1920 || height < 1 || height > 1080)
+ return NULL;
+ cMutexLock MutexLock(&mutex);
+ stringstream iconName;
+ iconName << name << "_" << width << "x" << height;
+ map<string, cImage*>::iterator hit = iconCache.find(iconName.str());
+ if (hit != iconCache.end()) {
+ return (cImage*)hit->second;
+ } else {
+ bool success = LoadIcon(type, name);
+ if (!success)
+ return NULL;
+ cImage *image = CreateImage(width, height, true);
+ iconCache.insert(pair<string, cImage*>(iconName.str(), image));
+ hit = iconCache.find(iconName.str());
+ if (hit != iconCache.end()) {
+ return (cImage*)hit->second;
+ }
+ }
+ return NULL;
+}
+
+string cImageCache::GetIconName(string label) {
+ //check for standard menu entries
+ for (int i=0; i<16; i++) {
+ string s = trVDR(items[i].c_str());
+ if (s == label) {
+ return *cString::sprintf("standardicons/%s", items[i].c_str());
+ }
+ }
+ //check for special main menu entries "stop recording", "stop replay"
+ string stopRecording = skipspace(trVDR(" Stop recording "));
+ string stopReplay = skipspace(trVDR(" Stop replaying"));
+ try {
+ if (label.substr(0, stopRecording.size()) == stopRecording) {
+ return "standardicons/StopRecording";
+ }
+ if (label.substr(0, stopReplay.size()) == stopReplay) {
+ return "standardicons/StopReplay";
+ }
+ } catch (...) {}
+ //check for Plugins
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
+ const char *mainMenuEntry = p->MainMenuEntry();
+ if (mainMenuEntry) {
+ string plugMainEntry = mainMenuEntry;
+ try {
+ if (label.substr(0, plugMainEntry.size()) == plugMainEntry) {
+ return *cString::sprintf("pluginicons/%s", p->Name());
+ }
+ } catch (...) {}
+ }
+ } else
+ break;
+ }
+ return *cString::sprintf("customicons/%s", label.c_str());
+}
+
+void cImageCache::CacheSkinpart(string name, int width, int height) {
+ if (width < 1 || width > 1920 || height < 1 || height > 1080)
+ return;
+ bool success = LoadSkinpart(name);
+ if (!success)
+ return;
+ stringstream iconName;
+ iconName << name << "_" << width << "x" << height;
+ cImage *image = CreateImage(width, height, false);
+ skinPartsCache.insert(pair<string, cImage*>(iconName.str(), image));
+}
+
+cImage *cImageCache::GetSkinpart(string name, int width, int height) {
+ if (width < 1 || width > 1920 || height < 1 || height > 1080)
+ return NULL;
+ cMutexLock MutexLock(&mutex);
+ stringstream iconName;
+ iconName << name << "_" << width << "x" << height;
+ map<string, cImage*>::iterator hit = skinPartsCache.find(iconName.str());
+ if (hit != skinPartsCache.end()) {
+ return (cImage*)hit->second;
+ } else {
+ bool success = LoadSkinpart(name);
+ if (!success)
+ return NULL;
+ cImage *image = CreateImage(width, height, false);
+ skinPartsCache.insert(pair<string, cImage*>(iconName.str(), image));
+ hit = skinPartsCache.find(iconName.str());
+ if (hit != skinPartsCache.end()) {
+ return (cImage*)hit->second;
+ }
+ }
+ return NULL;
+}
+
+bool cImageCache::LoadIcon(eImageType type, string name) {
+ bool success = false;
+ cString subdir("");
+ if (type == itMenuIcon)
+ subdir = "menuicons";
+ else if (type == itIcon)
+ subdir = "icons";
+ cString iconPath = cString::sprintf("%s%s/graphics/%s/", *config.skinPath, Setup.OSDTheme, *subdir);
+ success = LoadImage(name, *iconPath, "png");
+ if (success) {
+ return true;
+ }
+ return false;
+}
+
+bool cImageCache::LoadLogo(const cChannel *channel) {
+ if (!channel)
+ return false;
+ cString logoPath = cString::sprintf("%s%s/logos/", *config.skinPath, Setup.OSDTheme);
+ string channelID = StrToLowerCase(*(channel->GetChannelID().ToString()));
+ string logoLower = StrToLowerCase(channel->Name());
+ bool success = false;
+ success = LoadImage(channelID.c_str(), *logoPath, *config.logoExtension);
+ if (success)
+ return true;
+ success = LoadImage(logoLower.c_str(), *logoPath, *config.logoExtension);
+ if (success)
+ return true;
+ return false;
+}
+
+bool cImageCache::LoadSeparatorLogo(string name) {
+ cString separatorPath = cString::sprintf("%s%s/logos/separatorlogos/", *config.skinPath, Setup.OSDTheme);
+ string nameLower = StrToLowerCase(name.c_str());
+ bool success = false;
+ success = LoadImage(nameLower.c_str(), *separatorPath, *config.logoExtension);
+ if (success)
+ return true;
+ return false;
+}
+
+bool cImageCache::LoadSkinpart(string name) {
+ bool success = false;
+ cString iconPath = cString::sprintf("%s%s/graphics/skinparts/", *config.skinPath, Setup.OSDTheme);
+ success = LoadImage(name, *iconPath, "png");
+ if (success) {
+ return true;
+ }
+ return false;
+}
+
+void cImageCache::Clear(void) {
+ for(map<string, cImage*>::const_iterator it = iconCache.begin(); it != iconCache.end(); it++) {
+ cImage *img = (cImage*)it->second;
+ delete img;
+ }
+ iconCache.clear();
+
+ for(map<string, cImage*>::const_iterator it = channelLogoCache.begin(); it != channelLogoCache.end(); it++) {
+ cImage *img = (cImage*)it->second;
+ delete img;
+ }
+ channelLogoCache.clear();
+
+ for(map<std::string, cImage*>::const_iterator it = skinPartsCache.begin(); it != skinPartsCache.end(); it++) {
+ cImage *img = (cImage*)it->second;
+ delete img;
+ }
+ skinPartsCache.clear();
+}
+
+void cImageCache::Debug(bool full) {
+ int sizeIconCache = 0;
+ int numIcons = 0;
+ GetIconCacheSize(numIcons, sizeIconCache);
+ dsyslog("skindesigner: cached %d icons - size %d byte", numIcons, sizeIconCache);
+ if (full) {
+ for(std::map<std::string, cImage*>::const_iterator it = iconCache.begin(); it != iconCache.end(); it++) {
+ string name = it->first;
+ dsyslog("skindesigner: cached icon %s", name.c_str());
+ }
+ }
+
+ int sizeLogoCache = 0;
+ int numLogos = 0;
+ GetLogoCacheSize(numLogos, sizeLogoCache);
+ dsyslog("skindesigner: cached %d logos - size %d byte", numLogos, sizeLogoCache);
+ if (full) {
+ for(std::map<std::string, cImage*>::const_iterator it = channelLogoCache.begin(); it != channelLogoCache.end(); it++) {
+ string name = it->first;
+ dsyslog("skindesigner: cached logo %s", name.c_str());
+ }
+ }
+
+ int sizeSkinpartCache = 0;
+ int numSkinparts = 0;
+ GetSkinpartsCacheSize(numSkinparts, sizeSkinpartCache);
+ dsyslog("skindesigner: cached %d skinparts - size %d byte", numSkinparts, sizeSkinpartCache);
+ if (full) {
+ for(std::map<std::string, cImage*>::const_iterator it = skinPartsCache.begin(); it != skinPartsCache.end(); it++) {
+ string name = it->first;
+ dsyslog("skindesigner: cached skinpart %s", name.c_str());
+ }
+ }
+}
+
+void cImageCache::GetIconCacheSize(int &num, int &size) {
+ num = iconCache.size();
+ for (map<string, cImage*>::iterator icon = iconCache.begin(); icon != iconCache.end(); icon++) {
+ cImage* img = icon->second;
+ size += img->Width() * img->Height() * sizeof(tColor);
+ }
+}
+
+void cImageCache::GetLogoCacheSize(int &num, int &size) {
+ num = channelLogoCache.size();
+ for (map<string, cImage*>::iterator logo = channelLogoCache.begin(); logo != channelLogoCache.end(); logo++) {
+ cImage* img = logo->second;
+ size += img->Width() * img->Height() * sizeof(tColor);
+ }
+}
+
+void cImageCache::GetSkinpartsCacheSize(int &num, int &size) {
+ num = skinPartsCache.size();
+ for (map<string, cImage*>::iterator skinpart = skinPartsCache.begin(); skinpart != skinPartsCache.end(); skinpart++) {
+ cImage* img = skinpart->second;
+ size += img->Width() * img->Height() * sizeof(tColor);
+ }
+}
diff --git a/libcore/imagecache.h b/libcore/imagecache.h
new file mode 100644
index 0000000..9e700bf
--- /dev/null
+++ b/libcore/imagecache.h
@@ -0,0 +1,53 @@
+#ifndef __NOPACITY_IMAGECACHE_H
+#define __NOPACITY_IMAGECACHE_H
+
+#define X_DISPLAY_MISSING
+
+#include <vdr/osd.h>
+#include <vdr/skins.h>
+#include <Magick++.h>
+#include <vector>
+#include "imagemagickwrapper.h"
+#include "../libtemplate/templatefunction.h"
+
+using namespace Magick;
+
+class cImageCache : public cImageMagickWrapper {
+public:
+ cImageCache();
+ ~cImageCache();
+ void Lock(void) { mutex.Lock(); }
+ void Unlock(void) { mutex.Unlock(); }
+ //channel logos
+ void CacheLogo(int width, int height);
+ cImage *GetLogo(string channelID, int width, int height);
+ bool LogoExists(string channelID);
+ cImage *GetSeparatorLogo(string name, int width, int height);
+ bool SeparatorLogoExists(string name);
+ //icons
+ void CacheIcon(eImageType type, string path, int width, int height);
+ cImage *GetIcon(eImageType type, string name, int width, int height);
+ string GetIconName(string label);
+ //skinparts
+ void CacheSkinpart(string path, int width, int height);
+ cImage *GetSkinpart(string name, int width, int height);
+ //helpers
+ void Clear(void);
+ void Debug(bool full);
+ void GetIconCacheSize(int &num, int &size);
+ void GetLogoCacheSize(int &num, int &size);
+ void GetSkinpartsCacheSize(int &num, int &size);
+private:
+ static cMutex mutex;
+ static string items[16];
+ cImage *tempStaticLogo;
+ map<string, cImage*> iconCache;
+ map<string, cImage*> channelLogoCache;
+ map<string, cImage*> skinPartsCache;
+ bool LoadIcon(eImageType type, string name);
+ bool LoadLogo(const cChannel *channel);
+ bool LoadSeparatorLogo(string name);
+ bool LoadSkinpart(string name);
+};
+
+#endif //__NOPACITY_IMAGECACHE_H
diff --git a/libcore/imageloader.c b/libcore/imageloader.c
new file mode 100644
index 0000000..1b220a6
--- /dev/null
+++ b/libcore/imageloader.c
@@ -0,0 +1,56 @@
+#include "../config.h"
+#include "helpers.h"
+#include "imageloader.h"
+#include <math.h>
+#include <string>
+#include <dirent.h>
+#include <iostream>
+
+using namespace Magick;
+
+cImageLoader::cImageLoader() : cImageMagickWrapper() {
+}
+
+cImageLoader::~cImageLoader() {
+}
+
+cImage cImageLoader::GetImage() {
+ return CreateImageCopy();
+}
+
+bool cImageLoader::LoadImage(const char *path, int width, int height) {
+ if (cImageMagickWrapper::LoadImage(path)) {
+ buffer.sample(Geometry(width, height));
+ return true;
+ }
+ return false;
+}
+
+void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) {
+ cString logoPath = cString::sprintf("%s%s/logos/", *config.skinPath, Setup.OSDTheme);
+ cString logoExt = config.logoExtension;
+ DIR *folder = NULL;
+ struct dirent *file;
+ folder = opendir(logoPath);
+ if (!folder) {
+ return;
+ }
+ while (file = readdir(folder)) {
+ if (endswith(file->d_name, *logoExt)) {
+ std::stringstream filePath;
+ filePath << *logoPath << file->d_name;
+ Image logo;
+ try {
+ logo.read(filePath.str().c_str());
+ Geometry g = logo.size();
+ int logoWidth = g.width();
+ int logoHeight = g.height();
+ if (logoWidth > 0 && logoHeight > 0) {
+ width = logoWidth;
+ height = logoHeight;
+ return;
+ }
+ } catch( ... ) { }
+ }
+ }
+}
diff --git a/libcore/imageloader.h b/libcore/imageloader.h
new file mode 100644
index 0000000..2a148be
--- /dev/null
+++ b/libcore/imageloader.h
@@ -0,0 +1,23 @@
+#ifndef __NOPACITY_IMAGELOADER_H
+#define __NOPACITY_IMAGELOADER_H
+
+#define X_DISPLAY_MISSING
+
+#include <vdr/osd.h>
+#include <vdr/skins.h>
+#include <Magick++.h>
+#include "imagemagickwrapper.h"
+
+using namespace Magick;
+
+class cImageLoader : public cImageMagickWrapper {
+public:
+ cImageLoader();
+ ~cImageLoader();
+ cImage GetImage();
+ bool LoadImage(const char *path, int width, int height);
+ void DeterminateChannelLogoSize(int &width, int &height);
+private:
+};
+
+#endif //__NOPACITY_IMAGELOADER_H
diff --git a/libcore/imagemagickwrapper.c b/libcore/imagemagickwrapper.c
new file mode 100644
index 0000000..ab1bcba
--- /dev/null
+++ b/libcore/imagemagickwrapper.c
@@ -0,0 +1,162 @@
+#include <string>
+#include <sstream>
+#include "imagemagickwrapper.h"
+#include "../config.h"
+#include "imagescaler.h"
+
+cImageMagickWrapper::cImageMagickWrapper() {
+ InitializeMagick(NULL);
+}
+
+cImageMagickWrapper::~cImageMagickWrapper() {
+}
+
+cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAspect) {
+ int w, h;
+ w = buffer.columns();
+ h = buffer.rows();
+ if (width == 0)
+ width = w;
+ if (height == 0)
+ height = h;
+ if (preserveAspect) {
+ unsigned scale_w = 1000 * width / w;
+ unsigned scale_h = 1000 * height / h;
+ if (scale_w > scale_h)
+ width = w * height / h;
+ else
+ height = h * width / w;
+ }
+ const PixelPacket *pixels = buffer.getConstPixels(0, 0, w, h);
+ cImage *image = new cImage(cSize(width, height));
+ tColor *imgData = (tColor *)image->Data();
+ if (w != width || h != height) {
+ ImageScaler scaler;
+ scaler.SetImageParameters(imgData, width, width, height, w, h);
+ for (const void *pixels_end = &pixels[w*h]; pixels < pixels_end; ++pixels)
+ scaler.PutSourcePixel(pixels->blue / ((MaxRGB + 1) / 256),
+ pixels->green / ((MaxRGB + 1) / 256),
+ pixels->red / ((MaxRGB + 1) / 256),
+ ~((unsigned char)(pixels->opacity / ((MaxRGB + 1) / 256))));
+ return image;
+ }
+ for (const void *pixels_end = &pixels[width*height]; pixels < pixels_end; ++pixels)
+ *imgData++ = ((~int(pixels->opacity / ((MaxRGB + 1) / 256)) << 24) |
+ (int(pixels->green / ((MaxRGB + 1) / 256)) << 8) |
+ (int(pixels->red / ((MaxRGB + 1) / 256)) << 16) |
+ (int(pixels->blue / ((MaxRGB + 1) / 256)) ));
+ return image;
+}
+
+cImage cImageMagickWrapper::CreateImageCopy() {
+ int w, h;
+ w = buffer.columns();
+ h = buffer.rows();
+ cImage image (cSize(w, h));
+ const PixelPacket *pixels = buffer.getConstPixels(0, 0, w, h);
+ for (int iy = 0; iy < h; ++iy) {
+ for (int ix = 0; ix < w; ++ix) {
+ tColor col = (~int(pixels->opacity * 255 / MaxRGB) << 24)
+ | (int(pixels->green * 255 / MaxRGB) << 8)
+ | (int(pixels->red * 255 / MaxRGB) << 16)
+ | (int(pixels->blue * 255 / MaxRGB) );
+ image.SetPixel(cPoint(ix, iy), col);
+ ++pixels;
+ }
+ }
+ return image;
+}
+
+bool cImageMagickWrapper::LoadImage(std::string FileName, std::string Path, std::string Extension) {
+ try {
+ std::stringstream sstrImgFile;
+ sstrImgFile << Path << FileName << "." << Extension;
+ std::string imgFile = sstrImgFile.str();
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: trying to load: %s", imgFile.c_str());
+ buffer.read(imgFile.c_str());
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: %s sucessfully loaded", imgFile.c_str());
+ } catch( Magick::Warning &warning ) {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: Magick Warning: %s", warning.what());
+ return true;
+ } catch( Magick::Error &error ) {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: Magick Error: %s", error.what());
+ return false;
+ } catch(...) {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: an unknown Magick error occured during image loading");
+ return false;
+ }
+ return true;
+}
+
+bool cImageMagickWrapper::LoadImage(const char *fullpath) {
+ if ((fullpath == NULL) || (strlen(fullpath) < 5))
+ return false;
+ try {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: trying to load: %s", fullpath);
+ buffer.read(fullpath);
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: %s sucessfully loaded", fullpath);
+ } catch( Magick::Warning &warning ) {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: Magick Warning: %s", warning.what());
+ return true;
+ } catch( Magick::Error &error ) {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: Magick Error: %s", error.what());
+ return false;
+ } catch(...) {
+ if (config.debugImageLoading)
+ dsyslog("skindesigner: an unknown Magick error occured during image loading");
+ return false;
+ }
+ return true;
+}
+
+Color cImageMagickWrapper::Argb2Color(tColor col) {
+ tIndex alpha = (col & 0xFF000000) >> 24;
+ tIndex red = (col & 0x00FF0000) >> 16;
+ tIndex green = (col & 0x0000FF00) >> 8;
+ tIndex blue = (col & 0x000000FF);
+ Color color(MaxRGB*red/255, MaxRGB*green/255, MaxRGB*blue/255, MaxRGB*(0xFF-alpha)/255);
+ return color;
+}
+
+void cImageMagickWrapper::CreateGradient(tColor back, tColor blend, int width, int height, double wfactor, double hfactor) {
+ Color Back = Argb2Color(back);
+ Color Blend = Argb2Color(blend);
+ int maxw = MaxRGB * wfactor;
+ int maxh = MaxRGB * hfactor;
+
+ Image imgblend(Geometry(width, height), Blend);
+ imgblend.modifyImage();
+ imgblend.type(TrueColorMatteType);
+ PixelPacket *pixels = imgblend.getPixels(0, 0, width, height);
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ PixelPacket *pixel = pixels + y * width + x;
+ int opacity = (maxw / width * x + maxh - maxh / height * y) / 2;
+ pixel->opacity = (opacity <= MaxRGB) ? opacity : MaxRGB;
+ }
+ }
+ imgblend.syncPixels();
+
+ Image imgback(Geometry(width, height), Back);
+ imgback.composite(imgblend, 0, 0, OverCompositeOp);
+
+ buffer = imgback;
+}
+
+void cImageMagickWrapper::CreateBackground(tColor back, tColor blend, int width, int height, bool mirror) {
+ CreateGradient(back, blend, width, height, 0.8, 0.8);
+ if (mirror)
+ buffer.flop();
+}
+void cImageMagickWrapper::CreateBackgroundReverse(tColor back, tColor blend, int width, int height) {
+ CreateGradient(back, blend, width, height, 1.3, 0.7);
+}
diff --git a/libcore/imagemagickwrapper.h b/libcore/imagemagickwrapper.h
new file mode 100644
index 0000000..5f9901e
--- /dev/null
+++ b/libcore/imagemagickwrapper.h
@@ -0,0 +1,28 @@
+#ifndef __NOPACITY_IMAGEMAGICKWRAPPER_H
+#define __NOPACITY_IMAGEMAGICKWRAPPER_H
+
+#define X_DISPLAY_MISSING
+
+#include <Magick++.h>
+#include <vdr/osd.h>
+
+using namespace Magick;
+
+class cImageMagickWrapper {
+private:
+ void CreateGradient(tColor back, tColor blend, int width, int height, double wfactor, double hfactor);
+public:
+ cImageMagickWrapper();
+ ~cImageMagickWrapper();
+protected:
+ Image buffer;
+ Color Argb2Color(tColor col);
+ cImage *CreateImage(int width, int height, bool preserveAspect = true);
+ cImage CreateImageCopy(void);
+ bool LoadImage(std::string FileName, std::string Path, std::string Extension);
+ bool LoadImage(const char *fullpath);
+ void CreateBackground(tColor back, tColor blend, int width, int height, bool mirror = false);
+ void CreateBackgroundReverse(tColor back, tColor blend, int width, int height);
+};
+
+#endif //__NOPACITY_IMAGEMAGICKWRAPPER_H
diff --git a/libcore/imagescaler.c b/libcore/imagescaler.c
new file mode 100644
index 0000000..64fe3dc
--- /dev/null
+++ b/libcore/imagescaler.c
@@ -0,0 +1,149 @@
+
+#include "imagescaler.h"
+
+#include <cstdlib>
+#include <cmath>
+
+ImageScaler::ImageScaler() :
+ m_memory(NULL),
+ m_hor_filters(NULL),
+ m_ver_filters(NULL),
+ m_buffer(NULL),
+ m_dst_image(NULL),
+ m_dst_stride(0),
+ m_dst_width(0),
+ m_dst_height(0),
+ m_src_width(0),
+ m_src_height(0),
+ m_src_x(0),
+ m_src_y(0),
+ m_dst_x(0),
+ m_dst_y(0) {
+}
+
+ImageScaler::~ImageScaler() {
+ if ( m_memory ) free( m_memory );
+}
+
+// sin(x)/(x)
+static float sincf( float x ) {
+ if ( fabsf(x) < 0.05f ) return 1.0f - (1.0f/6.0f)*x*x; // taylor series approximation to avoid 0/0
+ return sin(x)/x;
+}
+
+static void CalculateFilters( ImageScaler::Filter *filters, int dst_size, int src_size ) {
+ const float fc = dst_size >= src_size ? 1.0f : ((float) dst_size)/((float) src_size);
+
+ for (int i = 0; i < dst_size; i++) {
+ const int d = 2*dst_size; // sample position denominator
+ const int e = (2*i+1) * src_size - dst_size; // sample position enumerator
+ int offset = e / d; // truncated sample position
+ const float sub_offset = ((float) (e - offset*d)) / ((float) d); // exact sample position is (float) e/d = offset + sub_offset
+
+ // calculate filter coefficients
+ float h[4];
+ for (int j=0; j<4; j++) {
+ const float t = 3.14159265359f * (sub_offset+(1-j));
+ h[j] = sincf( fc * t ) * cosf( 0.25f * t ); // sinc-lowpass and cos-window
+ }
+
+ // ensure that filter does not reach out off image bounds:
+ while ( offset < 1 ) {
+ h[0] += h[1];
+ h[1] = h[2];
+ h[2] = h[3];
+ h[3] = 0.0f;
+ offset++;
+ }
+
+ while ( offset+3 > src_size ) {
+ h[3] += h[2];
+ h[2] = h[1];
+ h[1] = h[0];
+ h[0] = 0.0f;
+ offset--;
+ }
+
+ // coefficients are normalized to sum up to 2048
+ const float norm = 2048.0f / ( h[0] + h[1] + h[2] + h[3] );
+
+ offset--; // offset of fist used pixel
+
+ filters[i].m_offset = offset + 4; // store offset of first unused pixel
+
+ for (int j=0; j<4; j++) {
+ const float t = norm * h[j];
+ filters[i].m_coeff[(offset+j) & 3] = (int) ((t > 0.0f) ? (t+0.5f) : (t-0.5f)); // consider ring buffer index permutations
+ }
+ }
+
+ // set end marker
+ filters[dst_size].m_offset = (unsigned) -1;
+
+}
+
+void ImageScaler::SetImageParameters( unsigned *dst_image, unsigned dst_stride, unsigned dst_width, unsigned dst_height, unsigned src_width, unsigned src_height ) {
+ m_src_x = 0;
+ m_src_y = 0;
+ m_dst_x = 0;
+ m_dst_y = 0;
+
+ m_dst_image = dst_image;
+ m_dst_stride = dst_stride;
+
+ // if image dimensions do not change we can keep the old filter coefficients
+ if ( (src_width == m_src_width) && (src_height == m_src_height) && (dst_width == m_dst_width) && (dst_height == m_dst_height) ) return;
+
+ m_dst_width = dst_width;
+ m_dst_height = dst_height;
+ m_src_width = src_width;
+ m_src_height = src_height;
+
+ if ( m_memory ) free( m_memory );
+
+ const unsigned hor_filters_size = (m_dst_width + 1) * sizeof(Filter); // reserve one extra position for end marker
+ const unsigned ver_filters_size = (m_dst_height + 1) * sizeof(Filter);
+ const unsigned buffer_size = 4 * m_dst_width * sizeof(TmpPixel);
+
+ char *p = (char *) malloc( hor_filters_size + ver_filters_size + buffer_size );
+
+ m_memory = p;
+
+ m_hor_filters = (Filter *) p; p += hor_filters_size;
+ m_ver_filters = (Filter *) p; p += ver_filters_size;
+ m_buffer = (TmpPixel *) p;
+
+ CalculateFilters( m_hor_filters, m_dst_width , m_src_width );
+ CalculateFilters( m_ver_filters, m_dst_height, m_src_height );
+}
+
+// shift range to 0..255 and clamp overflows
+static unsigned shift_clamp( int x ) {
+ x = ( x + (1<<21) ) >> 22;
+ if ( x < 0 ) return 0;
+ if ( x > 255 ) return 255;
+ return x;
+}
+
+void ImageScaler::NextSourceLine() {
+ m_dst_x = 0;
+ m_src_x = 0;
+ m_src_y++;
+
+ while ( m_ver_filters[m_dst_y].m_offset == m_src_y ) {
+ const int h0 = m_ver_filters[m_dst_y].m_coeff[0];
+ const int h1 = m_ver_filters[m_dst_y].m_coeff[1];
+ const int h2 = m_ver_filters[m_dst_y].m_coeff[2];
+ const int h3 = m_ver_filters[m_dst_y].m_coeff[3];
+ const TmpPixel *src = m_buffer;
+ unsigned *dst = m_dst_image + m_dst_stride * m_dst_y;
+
+ for (unsigned i=0; i<m_dst_width; i++) {
+ const ImageScaler::TmpPixel t( src[0]*h0 + src[1]*h1 + src[2]*h2 + src[3]*h3 );
+ src += 4;
+ dst[i] = shift_clamp(t[0]) | (shift_clamp(t[1])<<8) | (shift_clamp(t[2])<<16) | (shift_clamp(t[3])<<24);
+ }
+
+ m_dst_y++;
+ }
+}
diff --git a/libcore/imagescaler.h b/libcore/imagescaler.h
new file mode 100644
index 0000000..1182811
--- /dev/null
+++ b/libcore/imagescaler.h
@@ -0,0 +1,97 @@
+#ifndef _ImageScaler_h
+#define _ImageScaler_h
+
+/*!
+ * this class scales images consisting of 4 components (RGBA)
+ * to an arbitrary size using a 4-tap filter
+ */
+class ImageScaler {
+public:
+
+ struct Filter {
+ unsigned m_offset;
+ short m_coeff[4];
+ };
+
+ ImageScaler();
+ ~ImageScaler();
+
+ //! set destination image and source image size
+ void SetImageParameters( unsigned *dst_image, unsigned dst_stride, unsigned dst_width, unsigned dst_height, unsigned src_width, unsigned src_height );
+
+ /*! process one pixel of source image; destination image is written while input is processed
+ * SetImageParameters() must be called first
+ */
+ void PutSourcePixel( unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3 ) {
+ m_hbuf[ (m_src_x++) & 3 ].Set( c0, c1, c2, c3 );
+
+ TmpPixel *bp = m_buffer + 4 * m_dst_x + (m_src_y & 3);
+ const Filter *fh;
+
+ while ( (fh=m_hor_filters+m_dst_x)->m_offset == m_src_x ) {
+ *bp = m_hbuf[0]*fh->m_coeff[0] + m_hbuf[1]*fh->m_coeff[1] + m_hbuf[2]*fh->m_coeff[2] + m_hbuf[3]*fh->m_coeff[3];
+ m_dst_x++;
+ bp += 4;
+ }
+
+ if ( m_src_x == m_src_width ) NextSourceLine();
+ }
+
+private:
+
+ //! temporary image pixel class - a 4-element integer vector
+ class TmpPixel {
+ public:
+ TmpPixel() {
+ }
+
+ TmpPixel( int c0, int c1, int c2, int c3 ) {
+ Set(c0,c1,c2,c3);
+ }
+
+ void Set( int c0, int c1, int c2, int c3 ) {
+ m_comp[0] = c0;
+ m_comp[1] = c1;
+ m_comp[2] = c2;
+ m_comp[3] = c3;
+ }
+
+ TmpPixel operator*( int s ) const {
+ return TmpPixel( m_comp[0]*s, m_comp[1]*s, m_comp[2]*s, m_comp[3]*s );
+ }
+
+ TmpPixel operator+( const TmpPixel &x ) const {
+ return TmpPixel( m_comp[0] + x[0], m_comp[1] + x[1], m_comp[2] + x[2], m_comp[3] + x[3] );
+ }
+
+ // return component i=[0..3] - No range check!
+ int operator[](unsigned i) const {
+ return m_comp[i];
+ }
+
+ private:
+ int m_comp[4];
+ };
+
+ //! this is called whenever one input line is processed completely
+ void NextSourceLine();
+
+ TmpPixel m_hbuf[4]; //! ring buffer for 4 input pixels
+ char *m_memory; //! buffer container
+ Filter *m_hor_filters; //! buffer for horizontal filters (one for each output image column)
+ Filter *m_ver_filters; //! buffer for vertical filters (one for each output image row)
+ TmpPixel *m_buffer; //! buffer contains 4 horizontally filtered input lines, multiplexed
+ unsigned *m_dst_image; //! pointer to destination image
+ unsigned m_dst_stride; //! destination image stride
+ unsigned m_dst_width; //! destination image width
+ unsigned m_dst_height; //! destination image height
+ unsigned m_src_width; //! source image width
+ unsigned m_src_height; //! source image height
+ unsigned m_src_x; //! x position of next source image pixel
+ unsigned m_src_y; //! y position of source image line currently beeing processed
+ unsigned m_dst_x; //! x position of next destination image pixel
+ unsigned m_dst_y; //! x position of next destination image line
+};
+
+#endif // _ImageScaler_h
+
diff --git a/libcore/pixmapcontainer.c b/libcore/pixmapcontainer.c
new file mode 100644
index 0000000..6329638
--- /dev/null
+++ b/libcore/pixmapcontainer.c
@@ -0,0 +1,477 @@
+#define __STL_CONFIG_H
+#include "pixmapcontainer.h"
+#include "../config.h"
+
+cMutex cPixmapContainer::mutex;
+cOsd *cPixmapContainer::osd = NULL;
+eFlushState cPixmapContainer::flushState = fsOpen;
+
+cPixmapContainer::cPixmapContainer(int numPixmaps) {
+ this->numPixmaps = numPixmaps;
+ pixContainerInit = true;
+ mutex.Lock();
+ pixmaps = new cPixmap*[numPixmaps];
+ pixmapsTransparency = new int[numPixmaps];
+ for(int i=0; i < numPixmaps; i++) {
+ pixmaps[i] = NULL;
+ pixmapsTransparency[i] = 0;
+ }
+ mutex.Unlock();
+ checkRunning = false;
+ fadeTime = 0;
+ deleteOsdOnExit = false;
+}
+
+cPixmapContainer::~cPixmapContainer(void) {
+ for (int i=0; i < numPixmaps; i++) {
+ mutex.Lock();
+ if (pixmaps[i] && osd) {
+ osd->DestroyPixmap(pixmaps[i]);
+ pixmaps[i] = NULL;
+ }
+ mutex.Unlock();
+ }
+ delete[] pixmaps;
+ delete[] pixmapsTransparency;
+ if (deleteOsdOnExit && osd) {
+ mutex.Lock();
+ delete osd;
+ osd = NULL;
+ mutex.Unlock();
+ }
+}
+
+bool cPixmapContainer::CreateOsd(int Left, int Top, int Width, int Height) {
+ if (osd) {
+ return true;
+ }
+ cOsd *newOsd = cOsdProvider::NewOsd(Left, Top);
+ if (newOsd) {
+ tArea Area = { 0, 0, Width, Height, 32 };
+ if (newOsd->SetAreas(&Area, 1) == oeOk) {
+ osd = newOsd;
+ return true;
+ }
+ }
+ return false;
+}
+
+void cPixmapContainer::LockFlush(void) {
+ flushState = fsLock;
+}
+
+void cPixmapContainer::OpenFlush(void) {
+ flushState = fsOpen;
+}
+
+bool cPixmapContainer::PixmapExists(int num) {
+ cMutexLock MutexLock(&mutex);
+ if (pixmaps[num])
+ return true;
+ return false;
+}
+
+void cPixmapContainer::DoFlush(void) {
+ cMutexLock MutexLock(&mutex);
+ if (!osd || (checkRunning && !Running()))
+ return;
+ if (flushState == fsOpen) {
+ osd->Flush();
+ }
+}
+
+void cPixmapContainer::CreatePixmap(int num, int Layer, const cRect &ViewPort, const cRect &DrawPort) {
+ cMutexLock MutexLock(&mutex);
+ if (!osd || (checkRunning && !Running()))
+ return;
+ pixmaps[num] = osd->CreatePixmap(Layer, ViewPort, DrawPort);
+ pixmaps[num]->Fill(clrTransparent);
+ if (pixContainerInit && fadeTime) {
+ pixmaps[num]->SetAlpha(0);
+ } else if (pixmapsTransparency[num] > 0) {
+ int alpha = (100 - pixmapsTransparency[num])*255/100;
+ pixmaps[num]->SetAlpha(alpha);
+ }
+}
+
+bool cPixmapContainer::DestroyPixmap(int num) {
+ cMutexLock MutexLock(&mutex);
+ if (pixmaps[num] && osd) {
+ osd->DestroyPixmap(pixmaps[num]);
+ pixmaps[num] = NULL;
+ return true;
+ }
+ return false;
+}
+
+void cPixmapContainer::DrawText(int num, const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, std::string fontName, int fontSize) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ fontManager->Lock();
+ cFont *font = fontManager->Font(fontName, fontSize);
+ if (font)
+ pixmaps[num]->DrawText(Point, s, ColorFg, ColorBg, font);
+ fontManager->Unlock();
+}
+
+
+void cPixmapContainer::DrawRectangle(int num, const cRect &Rect, tColor Color) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->DrawRectangle(Rect, Color);
+}
+
+void cPixmapContainer::DrawEllipse(int num, const cRect &Rect, tColor Color, int Quadrants) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->DrawEllipse(Rect, Color, Quadrants);
+}
+
+void cPixmapContainer::DrawImage(int num, const cPoint &Point, const cImage &Image) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->DrawImage(Point, Image);
+}
+
+void cPixmapContainer::DrawBitmap(int num, const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->DrawBitmap(Point, Bitmap, ColorFg, ColorBg, Overlay);
+}
+
+void cPixmapContainer::Fill(int num, tColor Color) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->Fill(Color);
+}
+
+void cPixmapContainer::SetAlpha(int num, int Alpha) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->SetAlpha(Alpha);
+}
+
+void cPixmapContainer::SetTransparency(int num, int Transparency) {
+ if (Transparency < 0 && Transparency > 100)
+ return;
+ pixmapsTransparency[num] = Transparency;
+}
+
+void cPixmapContainer::SetLayer(int num, int Layer) {
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->SetLayer(Layer);
+}
+
+int cPixmapContainer::Width(int num) {
+ if (checkRunning && !Running())
+ return 0;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return 0;
+ int width = pixmaps[num]->ViewPort().Width();
+ return width;
+}
+
+int cPixmapContainer::Height(int num) {
+ if (checkRunning && !Running())
+ return 0;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return 0;
+ int height = pixmaps[num]->ViewPort().Height();
+ return height;
+}
+
+int cPixmapContainer::DrawportWidth(int num) {
+ if (checkRunning && !Running())
+ return 0;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return 0;
+ int width = pixmaps[num]->DrawPort().Width();
+ return width;
+}
+
+int cPixmapContainer::DrawportHeight(int num) {
+ if (checkRunning && !Running())
+ return 0;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return 0;
+ int height = pixmaps[num]->DrawPort().Height();
+ return height;
+}
+
+int cPixmapContainer::DrawportX(int num) {
+ if (checkRunning && !Running())
+ return 0;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return 0;
+ int x = pixmaps[num]->DrawPort().X();
+ return x;
+}
+
+int cPixmapContainer::DrawportY(int num) {
+ if (checkRunning && !Running())
+ return 0;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return 0;
+ int y = pixmaps[num]->DrawPort().Y();
+ return y;
+}
+
+void cPixmapContainer::SetDrawPortPoint(int num, const cPoint &Point) {
+ if (checkRunning && !Running())
+ return;
+ cMutexLock MutexLock(&mutex);
+ if (!pixmaps[num])
+ return;
+ pixmaps[num]->SetDrawPortPoint(Point);
+}
+
+/***************************************************************************
+* HELPERS -- do not access the pixmaps array directly, use wrapper functions
+* to ensure that a proper lock is set before accessing pixmaps
+****************************************************************************/
+
+void cPixmapContainer::FadeIn(void) {
+ if (!fadeTime)
+ return;
+ uint64_t Start = cTimeMs::Now();
+ int FadeFrameTime = fadeTime / 10;
+ while (Running()) {
+ uint64_t Now = cTimeMs::Now();
+ double t = min(double(Now - Start) / fadeTime, 1.0);
+ int Alpha = t * ALPHA_OPAQUE;
+ for (int i = 0; i < numPixmaps; i++) {
+ if (!PixmapExists(i))
+ continue;
+ if (pixmapsTransparency[i] > 0) {
+ int alpha = (100 - pixmapsTransparency[i])*Alpha/100;
+ SetAlpha(i, alpha);
+ } else {
+ SetAlpha(i, Alpha);
+ }
+ }
+ DoFlush();
+ int Delta = cTimeMs::Now() - Now;
+ if (Running() && (Delta < FadeFrameTime))
+ cCondWait::SleepMs(FadeFrameTime - Delta);
+ if ((int)(Now - Start) > fadeTime)
+ break;
+ }
+}
+
+void cPixmapContainer::FadeOut(void) {
+ if (!fadeTime)
+ return;
+ uint64_t Start = cTimeMs::Now();
+ int FadeFrameTime = fadeTime / 10;
+ while (true) {
+ uint64_t Now = cTimeMs::Now();
+ double t = min(double(Now - Start) / fadeTime, 1.0);
+ int Alpha = (1 - t) * ALPHA_OPAQUE;
+ for (int i = 0; i < numPixmaps; i++) {
+ if (!PixmapExists(i))
+ continue;
+ if (pixmapsTransparency[i] > 0) {
+ int alpha = (100 - pixmapsTransparency[i])*Alpha/100;
+ SetAlpha(i, alpha);
+ } else {
+ SetAlpha(i, Alpha);
+ }
+ }
+ DoFlush();
+ int Delta = cTimeMs::Now() - Now;
+ if (Running() && (Delta < FadeFrameTime))
+ cCondWait::SleepMs(FadeFrameTime - Delta);
+ if ((int)(Now - Start) > fadeTime)
+ break;
+ }
+}
+
+/*****************************************
+* scrollSpeed: 1 slow
+* 2 medium
+* 3 fast
+******************************************/
+void cPixmapContainer::ScrollHorizontal(int num, int scrollDelay, int scrollSpeed, int scrollMode) {
+ bool carriageReturn = (scrollMode == 1) ? true : false;
+
+ int scrollDelta = 1;
+ int drawPortX;
+
+ int FrameTime = 0;
+ if (scrollSpeed == 1)
+ FrameTime = 50;
+ else if (scrollSpeed == 2)
+ FrameTime = 30;
+ else
+ FrameTime = 15;
+ if (!Running())
+ return;
+ int maxX = DrawportWidth(num) - Width(num);
+ bool doSleep = false;
+ while (Running()) {
+ if (doSleep) {
+ DoSleep(scrollDelay);
+ doSleep = false;
+ }
+ if (!Running())
+ return;
+ uint64_t Now = cTimeMs::Now();
+ drawPortX = DrawportX(num);
+ drawPortX -= scrollDelta;
+
+ if (abs(drawPortX) > maxX) {
+ DoSleep(scrollDelay);
+ if (carriageReturn)
+ drawPortX = 0;
+ else {
+ scrollDelta *= -1;
+ drawPortX -= scrollDelta;
+ }
+ doSleep = true;
+ }
+ if (!carriageReturn && (drawPortX == 0)) {
+ scrollDelta *= -1;
+ doSleep = true;
+ }
+ SetDrawPortPoint(num, cPoint(drawPortX, 0));
+ int Delta = cTimeMs::Now() - Now;
+ DoFlush();
+ if (Running() && (Delta < FrameTime))
+ cCondWait::SleepMs(FrameTime - Delta);
+ }
+}
+
+/*****************************************
+* scrollSpeed: 1 slow
+* 2 medium
+* 3 fast
+******************************************/
+void cPixmapContainer::ScrollVertical(int num, int scrollDelay, int scrollSpeed) {
+ if (!scrollSpeed)
+ return;
+ DoSleep(scrollDelay);
+ int drawPortY;
+ int FrameTime = 0;
+ if (scrollSpeed == 1)
+ FrameTime = 50;
+ else if (scrollSpeed == 2)
+ FrameTime = 30;
+ else
+ FrameTime = 15;
+ int maxY = DrawportHeight(num) - Height(num);
+ bool doSleep = false;
+ while (Running()) {
+ if (doSleep) {
+ doSleep = false;
+ DoSleep(scrollDelay);
+ }
+ uint64_t Now = cTimeMs::Now();
+ drawPortY = DrawportY(num);
+ drawPortY -= 1;
+ if (abs(drawPortY) > maxY) {
+ doSleep = true;
+ DoSleep(scrollDelay);
+ drawPortY = 0;
+ }
+ SetDrawPortPoint(num, cPoint(0, drawPortY));
+ if (doSleep) {
+ DoSleep(scrollDelay);
+ }
+ int Delta = cTimeMs::Now() - Now;
+ DoFlush();
+ if (Running() && (Delta < FrameTime))
+ cCondWait::SleepMs(FrameTime - Delta);
+ }
+}
+
+void cPixmapContainer::CancelSave(void) {
+ Cancel(-1);
+ while (Active())
+ cCondWait::SleepMs(10);
+}
+
+void cPixmapContainer::DoSleep(int duration) {
+ int sleepSlice = 10;
+ for (int i = 0; Running() && (i*sleepSlice < duration); i++)
+ cCondWait::SleepMs(sleepSlice);
+}
+
+void cPixmapContainer::DrawBlendedBackground(int num, int xStart, int width, tColor color, tColor colorBlending, bool fromTop) {
+ int height = Height(num);
+ int numSteps = 16;
+ int alphaStep = 0x0F;
+ int alpha = 0x00;
+ int step, begin, end;
+ if (fromTop) {
+ step = 1;
+ begin = 0;
+ end = numSteps;
+ } else {
+ step = -1;
+ begin = height;
+ end = height - numSteps;
+ }
+ tColor clr;
+ bool cont = true;
+ for (int i = begin; cont; i = i + step) {
+ clr = AlphaBlend(color, colorBlending, alpha);
+ DrawRectangle(num, cRect(xStart,i,width,1), clr);
+ alpha += alphaStep;
+ if (i == end)
+ cont = false;
+ }
+}
+
+void cPixmapContainer::DrawRoundedCorners(int num, int radius, int x, int y, int width, int height) {
+ if (radius > 2) {
+ DrawEllipse(num, cRect(x, y, radius, radius), clrTransparent, -2);
+ DrawEllipse(num, cRect(x + width - radius, y , radius, radius), clrTransparent, -1);
+ DrawEllipse(num, cRect(x, y + height - radius, radius, radius), clrTransparent, -3);
+ DrawEllipse(num, cRect(x + width - radius, y + height - radius, radius, radius), clrTransparent, -4);
+ }
+}
+
+void cPixmapContainer::DrawRoundedCornersWithBorder(int num, tColor borderColor, int radius, int width, int height) {
+ if (radius < 3)
+ return;
+ DrawEllipse(num, cRect(0,0,radius,radius), borderColor, -2);
+ DrawEllipse(num, cRect(-1,-1,radius,radius), clrTransparent, -2);
+
+ DrawEllipse(num, cRect(width-radius,0,radius,radius), borderColor, -1);
+ DrawEllipse(num, cRect(width-radius+1,-1,radius,radius), clrTransparent, -1);
+
+ DrawEllipse(num, cRect(0,height-radius,radius,radius), borderColor, -3);
+ DrawEllipse(num, cRect(-1,height-radius+1,radius,radius), clrTransparent, -3);
+
+ DrawEllipse(num, cRect(width-radius,height-radius,radius,radius), borderColor, -4);
+ DrawEllipse(num, cRect(width-radius+1,height-radius+1,radius,radius), clrTransparent, -4);
+}
diff --git a/libcore/pixmapcontainer.h b/libcore/pixmapcontainer.h
new file mode 100644
index 0000000..06f9104
--- /dev/null
+++ b/libcore/pixmapcontainer.h
@@ -0,0 +1,73 @@
+#ifndef __PIXMAP_CONTAINER_H
+#define __PIXMAP_CONTAINER_H
+
+#include <string>
+#include <vdr/plugin.h>
+#include "fontmanager.h"
+
+enum eFlushState {
+ fsOpen,
+ fsLock,
+ fsCount,
+};
+
+class cPixmapContainer : public cThread {
+private:
+ static cMutex mutex;
+ static cOsd *osd;
+ static eFlushState flushState;
+ bool pixContainerInit;
+ int numPixmaps;
+ cPixmap **pixmaps;
+ int *pixmapsTransparency;
+ bool checkRunning;
+ int fadeTime;
+ bool deleteOsdOnExit;
+protected:
+ void SetInitFinished(void) { pixContainerInit = false; };
+ bool CreateOsd(int Left, int Top, int Width, int Height);
+ void DeleteOsdOnExit(void) { deleteOsdOnExit = true; };
+ void LockFlush(void);
+ void OpenFlush(void);
+ //Wrappers for access to pixmaps
+ bool PixmapExists(int num);
+ int NumPixmaps(void) { return numPixmaps; };
+ void CreatePixmap(int num, int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null);
+ bool DestroyPixmap(int num);
+ void DrawText(int num, const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, std::string fontName, int fontSize);
+ void DrawRectangle(int num, const cRect &Rect, tColor Color);
+ void DrawEllipse(int num, const cRect &Rect, tColor Color, int Quadrants = 0);
+ void DrawImage(int num, const cPoint &Point, const cImage &Image);
+ void DrawBitmap(int num, const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false);
+ void Fill(int num, tColor Color);
+ void SetAlpha(int num, int Alpha);
+ void SetTransparency(int num, int Transparency);
+ void SetLayer(int num, int Layer);
+ int Width(int num);
+ int Height(int num);
+ int DrawportWidth(int num);
+ int DrawportHeight(int num);
+ int DrawportX(int num);
+ int DrawportY(int num);
+ void SetDrawPortPoint(int num, const cPoint &Point);
+ void SetCheckRunning(void) { checkRunning = true; };
+ void UnsetCheckRunning(void) { checkRunning = false; };
+ //HELPERS -- do not access the pixmaps array directly, use wrapper functions
+ void SetFadeTime(int fade) { fadeTime = fade; };
+ void FadeIn(void);
+ void FadeOut(void);
+ void ScrollVertical(int num, int scrollDelay, int scrollSpeed);
+ void ScrollHorizontal(int num, int scrollDelay, int scrollSpeed, int scrollMode);
+ void CancelSave(void);
+ void DoSleep(int duration);
+ void DrawBlendedBackground(int num, int xStart, int width, tColor color, tColor colorBlending, bool fromTop);
+ void DrawRoundedCorners(int num, int radius, int x, int y, int width, int height);
+ void DrawRoundedCornersWithBorder(int num, tColor borderColor, int radius, int width, int height);
+public:
+ cPixmapContainer(int numPixmaps);
+ virtual ~cPixmapContainer(void);
+ void DoFlush(void);
+ virtual void Action(void) {};
+};
+
+#endif //__PIXMAP_CONTAINER_H \ No newline at end of file
diff --git a/libcore/timers.c b/libcore/timers.c
new file mode 100644
index 0000000..09af69b
--- /dev/null
+++ b/libcore/timers.c
@@ -0,0 +1,84 @@
+#include "timers.h"
+#include "../services/epgsearch.h"
+#include "../services/remotetimers.h"
+
+static int CompareTimers(const void *a, const void *b) {
+ return (*(const cTimer **)a)->Compare(**(const cTimer **)b);
+}
+
+cGlobalSortedTimers::cGlobalSortedTimers(bool forceRefresh) : cVector<const cTimer *>(Timers.Count()) {
+ static bool initial = true;
+ static cRemoteTimerRefresh *remoteTimerRefresh = NULL;
+
+ if (forceRefresh)
+ initial = true;
+ //check if remotetimers plugin is available
+ static cPlugin* pRemoteTimers = cPluginManager::GetPlugin("remotetimers");
+
+ cSchedulesLock SchedulesLock;
+ const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
+
+ if (pRemoteTimers && initial) {
+ cString errorMsg;
+ pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg);
+ initial = false;
+ }
+
+ for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer))
+ Append(Timer);
+
+ //if remotetimers plugin is available, take timers also from him
+ if (pRemoteTimers) {
+ cTimer* remoteTimer = NULL;
+ while (pRemoteTimers->Service("RemoteTimers::ForEach-v1.0", &remoteTimer) && remoteTimer != NULL) {
+ remoteTimer->SetEventFromSchedule(Schedules); // make sure the event is current
+ Append(remoteTimer);
+ }
+ }
+
+ Sort(CompareTimers);
+
+ if (pRemoteTimers && (remoteTimerRefresh == NULL))
+ remoteTimerRefresh = new cRemoteTimerRefresh();
+}
+
+int cGlobalSortedTimers::NumTimerConfilicts(void) {
+ int numConflicts = 0;
+ cPlugin *p = cPluginManager::GetPlugin("epgsearch");
+ if (p) {
+ Epgsearch_lastconflictinfo_v1_0 *serviceData = new Epgsearch_lastconflictinfo_v1_0;
+ if (serviceData) {
+ serviceData->nextConflict = 0;
+ serviceData->relevantConflicts = 0;
+ serviceData->totalConflicts = 0;
+ p->Service("Epgsearch-lastconflictinfo-v1.0", serviceData);
+ if (serviceData->relevantConflicts > 0) {
+ numConflicts = serviceData->relevantConflicts;
+ }
+ delete serviceData;
+ }
+ }
+ return numConflicts;
+}
+
+cRemoteTimerRefresh::cRemoteTimerRefresh(): cThread("skindesigner: RemoteTimers::RefreshTimers") {
+ Start();
+}
+
+cRemoteTimerRefresh::~cRemoteTimerRefresh(void) {
+ Cancel(-1);
+ while (Active())
+ cCondWait::SleepMs(10);
+}
+
+void cRemoteTimerRefresh::Action(void) {
+ #define REFESH_INTERVALL_MS 30000
+ while (Running()) {
+ cCondWait::SleepMs(REFESH_INTERVALL_MS);
+ if (!cOsd::IsOpen()) {//make sure that no timer is currently being edited
+ cGlobalSortedTimers(true);
+ Timers.SetModified();
+ }
+ }
+}
+ \ No newline at end of file
diff --git a/libcore/timers.h b/libcore/timers.h
new file mode 100644
index 0000000..81d988a
--- /dev/null
+++ b/libcore/timers.h
@@ -0,0 +1,20 @@
+#ifndef __NOPACITY_TIMERS_H
+#define __NOPACITY_TIMERS_H
+
+#include <vdr/timers.h>
+#include <vdr/plugin.h>
+
+class cGlobalSortedTimers : public cVector<const cTimer *> {
+ public:
+ cGlobalSortedTimers(bool forceRefresh = false);
+ int NumTimerConfilicts(void);
+};
+
+class cRemoteTimerRefresh: public cThread {
+ protected:
+ virtual void Action(void);
+ public:
+ cRemoteTimerRefresh(void);
+ virtual ~cRemoteTimerRefresh(void);
+};
+#endif //__NOPACITY_TIMERS_H