diff options
author | louis <louis.braun@gmx.de> | 2014-09-27 09:25:14 +0200 |
---|---|---|
committer | louis <louis.braun@gmx.de> | 2014-09-27 09:25:14 +0200 |
commit | b0509b5182b6e0d04f05e6b3d5676b0d21f51966 (patch) | |
tree | 22b302342f22843e0815eb5f516c85f1478cbf0b /libcore | |
download | vdr-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.c | 174 | ||||
-rw-r--r-- | libcore/fontmanager.h | 35 | ||||
-rw-r--r-- | libcore/helpers.c | 155 | ||||
-rw-r--r-- | libcore/helpers.h | 35 | ||||
-rw-r--r-- | libcore/imagecache.c | 389 | ||||
-rw-r--r-- | libcore/imagecache.h | 53 | ||||
-rw-r--r-- | libcore/imageloader.c | 56 | ||||
-rw-r--r-- | libcore/imageloader.h | 23 | ||||
-rw-r--r-- | libcore/imagemagickwrapper.c | 162 | ||||
-rw-r--r-- | libcore/imagemagickwrapper.h | 28 | ||||
-rw-r--r-- | libcore/imagescaler.c | 149 | ||||
-rw-r--r-- | libcore/imagescaler.h | 97 | ||||
-rw-r--r-- | libcore/pixmapcontainer.c | 477 | ||||
-rw-r--r-- | libcore/pixmapcontainer.h | 73 | ||||
-rw-r--r-- | libcore/timers.c | 84 | ||||
-rw-r--r-- | libcore/timers.h | 20 |
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 |