diff options
-rw-r--r-- | HISTORY | 6 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | baserender.c | 17 | ||||
-rw-r--r-- | displaychannel.c | 59 | ||||
-rw-r--r-- | displaymenu.c | 73 | ||||
-rw-r--r-- | displayreplay.c | 51 | ||||
-rw-r--r-- | displayvolume.c | 5 | ||||
-rw-r--r-- | flat.c | 1 | ||||
-rw-r--r-- | flat.h | 3 | ||||
-rw-r--r-- | imagecache.c | 52 | ||||
-rw-r--r-- | imagecache.h | 25 | ||||
-rw-r--r-- | imageloader.c | 114 | ||||
-rw-r--r-- | imageloader.h | 25 | ||||
-rw-r--r-- | imagemagickwrapper.c | 87 | ||||
-rw-r--r-- | imagemagickwrapper.h | 23 | ||||
-rw-r--r-- | imagescaler.c | 149 | ||||
-rw-r--r-- | imagescaler.h | 97 | ||||
-rw-r--r-- | po/de_DE.po | 2 | ||||
-rw-r--r-- | skinflatplus.c | 5 |
19 files changed, 635 insertions, 161 deletions
@@ -9,11 +9,13 @@ VDR Plugin 'skinflatplus' Revision History - [fix] remove button border if button is not shown - [fix] menuitem progressbar overlay itemtext - [fix] fixes in TopBar while icon set -- [add] add support for menu SetItemChannel -- [add] add support for menu SetItemTimer - [update] add Patch from MegaV0lt, support for simple aspect & format, new icons - [update] only log not loaded images - [update] imageloader logging +- [add] add support for menu SetItemChannel +- [add] add support for menu SetItemTimer +- [add] imagescaler from skin nopacity (thank you!) +- [add] imagecache for faster image loading 2013-11-24: Version 0.1.0 - MegaV0lt Version Special thanks to MegaV0lt@VDR-Portal for @@ -56,7 +56,7 @@ LIBS += $(shell pkg-config --libs Magick++) ### The object files (add further files here): -OBJS = config.o setup.o imageloader.o baserender.o displaychannel.o displaymenu.o displaymessage.o \ +OBJS = config.o setup.o imagecache.o imagescaler.o imagemagickwrapper.o imageloader.o baserender.o displaychannel.o displaymenu.o displaymessage.o \ displayreplay.o displaytracks.o displayvolume.o flat.o $(PLUGIN).o ### The main target: diff --git a/baserender.c b/baserender.c index 2d9cbcfd..0ab832a4 100644 --- a/baserender.c +++ b/baserender.c @@ -170,9 +170,11 @@ void cFlatBaseRender::TopBarUpdate(void) { topBarIconPixmap->Fill(clrTransparent); if( topBarExtraIconSet ) { int extraIconLeft = extraLeft + extraMaxWidth + marginItem; - if (imgLoader.LoadIcon(*topBarExtraIcon, 999, topBarHeight)) { - int iconTop = topBarHeight / 2 - imgLoader.Height()/2; - topBarIconPixmap->DrawImage(cPoint(extraIconLeft, iconTop), imgLoader.GetImage()); +// if (imgLoader.LoadIcon(*topBarExtraIcon, 999, topBarHeight)) { + cImage *img = imgLoader.LoadIcon(*topBarExtraIcon, 999, topBarHeight); + if( img ) { + int iconTop = 0; + topBarIconPixmap->DrawImage(cPoint(extraIconLeft, iconTop), *img); RecLeft += topBarHeight + marginItem; } @@ -180,10 +182,11 @@ void cFlatBaseRender::TopBarUpdate(void) { if( topBarMenuIconSet && Config.TopBarMenuIconShow ) { int IconLeft = marginItem; - if (imgLoader.LoadIcon(*topBarMenuIcon, 999, topBarHeight - marginItem*2)) { - int iconTop = (topBarHeight / 2 - imgLoader.Height()/2); - topBarIconPixmap->DrawImage(cPoint(IconLeft, iconTop), imgLoader.GetImage()); - MenuIconWidth = imgLoader.Width()+marginItem*2; + cImage *img = imgLoader.LoadIcon(*topBarMenuIcon, 999, topBarHeight - marginItem*2); + if( img ) { + int iconTop = (topBarHeight / 2 - img->Height()/2); + topBarIconPixmap->DrawImage(cPoint(IconLeft, iconTop), *img); + MenuIconWidth = img->Width()+marginItem*2; TitleWidthLeft -= MenuIconWidth + marginItem*3; } } diff --git a/displaychannel.c b/displaychannel.c index af6567d8..5bf02c55 100644 --- a/displaychannel.c +++ b/displaychannel.c @@ -112,20 +112,23 @@ void cFlatDisplayChannel::SetChannel(const cChannel *Channel, int Number) { chanInfoTopPixmap->Fill(Theme.Color(clrChannelBg)); chanInfoTopPixmap->DrawText(cPoint(50, 0), channelString, Theme.Color(clrChannelFontTitle), Theme.Color(clrChannelBg), font); + chanLogoPixmap->Fill(clrTransparent); int imageHeight = heightImageLogo - marginItem*2; int imageLeft = marginItem*2; int imageTop = marginItem; - if( imgLoader.LoadLogo(*channelName, imageHeight, imageHeight) ) { - - chanLogoPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + cImage *img = imgLoader.LoadLogo(*channelName, imageHeight, imageHeight); + if( img ) { + chanLogoPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); } else if( !isGroup ) { // draw default logo if( isRadioChannel ) { - if( imgLoader.LoadIcon("radio", imageHeight, imageHeight) ) { - chanLogoPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("radio", imageHeight, imageHeight); + if( img ) { + chanLogoPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); } } else { - if( imgLoader.LoadIcon("tv", imageHeight, imageHeight) ) { - chanLogoPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("tv", imageHeight, imageHeight); + if( img ) { + chanLogoPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); } } } @@ -140,6 +143,7 @@ void cFlatDisplayChannel::ChannelIconsDraw(const cChannel *Channel, bool Resolut int top = fontHeight*2 + fontSmlHeight*2 + marginItem; int height = fontSmlHeight; int imageTop = 0; + cImage *img = NULL; if( Config.SignalQualityShow ) { top += max(fontSmlHeight, Config.decorProgressSignalSize) / 2 - fontSmlHeight / 2; @@ -151,15 +155,17 @@ void cFlatDisplayChannel::ChannelIconsDraw(const cChannel *Channel, bool Resolut if( Channel ) { if (Channel->Ca()) { - if (imgLoader.LoadIcon("crypted", 999, height)) { - imageTop = top + (height - imgLoader.Height())/2; - chanIconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon("crypted", 999, height); + if( img ) { + imageTop = top + (height - img->Height())/2; + chanIconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } else { - if (imgLoader.LoadIcon("uncrypted", 999, height)) { - imageTop = top + (height - imgLoader.Height())/2; - chanIconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon("uncrypted", 999, height); + if( img ) { + imageTop = top + (height - img->Height())/2; + chanIconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } @@ -178,10 +184,11 @@ void cFlatDisplayChannel::ChannelIconsDraw(const cChannel *Channel, bool Resolut else if( screenAspect == 2.21 ) asp = "221"; } - if (imgLoader.LoadIcon(*asp, 999, height)) { - imageTop = top + (height - imgLoader.Height())/2; - left -= imgLoader.Width(); - chanIconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon(*asp, 999, height); + if( img ) { + imageTop = top + (height - img->Height())/2; + left -= img->Width(); + chanIconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } @@ -223,10 +230,11 @@ void cFlatDisplayChannel::ChannelIconsDraw(const cChannel *Channel, bool Resolut break; } - if (imgLoader.LoadIcon(*res, 999, height)) { - imageTop = top + (height - imgLoader.Height())/2; - left -= imgLoader.Width(); - chanIconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon(*res, 999, height); + if( img ) { + imageTop = top + (height - img->Height())/2; + left -= img->Width(); + chanIconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } @@ -246,10 +254,11 @@ void cFlatDisplayChannel::ChannelIconsDraw(const cChannel *Channel, bool Resolut break; } - if (imgLoader.LoadIcon(*iconName, 999, height)) { - imageTop = top + (height - imgLoader.Height())/2; - left -= imgLoader.Width(); - chanIconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon(*iconName, 999, height); + if( img ) { + imageTop = top + (height - img->Height())/2; + left -= img->Width(); + chanIconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } diff --git a/displaymenu.c b/displaymenu.c index 840851f5..a2358515 100644 --- a/displaymenu.c +++ b/displaymenu.c @@ -396,11 +396,13 @@ void cFlatDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool S if( (menuCategory == mcMain || menuCategory == mcSetup) && Config.MenuItemIconsShow) { cString cIcon = GetIconName( MainMenuText(s) ); cImageLoader imgLoader; - if (imgLoader.LoadIcon(*cIcon, fontHeight - marginItem*2)) { - menuIconsPixmap->DrawImage(cPoint(xt + Config.decorBorderMenuItemSize + marginItem, y + marginItem), imgLoader.GetImage()); + cImage *img = imgLoader.LoadIcon(*cIcon, fontHeight - marginItem*2, fontHeight - marginItem*2); + if( img ) { + menuIconsPixmap->DrawImage(cPoint(xt + Config.decorBorderMenuItemSize + marginItem, y + marginItem), *img); } else { - if (imgLoader.LoadIcon("menuIcons/blank", fontHeight)) { - menuIconsPixmap->DrawImage(cPoint(xt + Config.decorBorderMenuItemSize + marginItem, y + marginItem), imgLoader.GetImage()); + img = imgLoader.LoadIcon("menuIcons/blank", fontHeight - marginItem*2, fontHeight - marginItem*2); + if( img ) { + menuIconsPixmap->DrawImage(cPoint(xt + Config.decorBorderMenuItemSize + marginItem, y + marginItem), *img); } } menuPixmap->DrawText(cPoint(fontHeight + marginItem*2 + xt + Config.decorBorderMenuItemSize, y), s, ColorFg, ColorBg, font, @@ -627,29 +629,33 @@ bool cFlatDisplayMenu::SetItemChannel(const cChannel *Channel, int Index, bool C int imageHeight = fontHeight; int imageLeft = Left; int imageTop = Top; - if( imgLoader.LoadLogo(Channel->Name(), imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + cImage *img = imgLoader.LoadLogo(Channel->Name(), imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } else { bool isRadioChannel = ((!Channel->Vpid())&&(Channel->Apid(0))) ? true : false; if( isRadioChannel ) { - if( imgLoader.LoadIcon("radio", imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("radio", imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } } else if( Channel->GroupSep() ) { - if( imgLoader.LoadIcon("changroup", imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("changroup", imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } } else { - if( imgLoader.LoadIcon("tv", imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("tv", imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } } @@ -926,9 +932,10 @@ bool cFlatDisplayMenu::SetItemTimer(const cTimer *Timer, int Index, bool Current } else TimerIconName = "timerActive"; - if( imgLoader.LoadIcon(TimerIconName, imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + cImage *img = imgLoader.LoadIcon(TimerIconName, imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } @@ -943,29 +950,33 @@ bool cFlatDisplayMenu::SetItemTimer(const cTimer *Timer, int Index, bool Current imageLeft = Left; - if( imgLoader.LoadLogo(Channel->Name(), imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadLogo(Channel->Name(), imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } else { bool isRadioChannel = ((!Channel->Vpid())&&(Channel->Apid(0))) ? true : false; if( isRadioChannel ) { - if( imgLoader.LoadIcon("radio", imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("radio", imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } } else if( Channel->GroupSep() ) { - if( imgLoader.LoadIcon("changroup", imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("changroup", imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } } else { - if( imgLoader.LoadIcon("tv", imageHeight, imageHeight) ) { - imageTop = Top + (fontHeight - imgLoader.Height()) / 2; - menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), imgLoader.GetImage() ); + img = imgLoader.LoadIcon("tv", imageHeight, imageHeight); + if( img ) { + imageTop = Top + (fontHeight - img->Height()) / 2; + menuIconsPixmap->DrawImage( cPoint(imageLeft, imageTop), *img ); Left += imageHeight + marginItem * 2; } } diff --git a/displayreplay.c b/displayreplay.c index 6d9a8b0b..c6ec4225 100644 --- a/displayreplay.c +++ b/displayreplay.c @@ -93,15 +93,21 @@ void cFlatDisplayReplay::SetMode(bool Play, bool Forward, int Speed) { labelPixmap->DrawText(cPoint(left - font->Width(speed) - marginItem, 0), speed, Theme.Color(clrReplayFontSpeed), Theme.Color(clrReplayBg), font); } } + cImage *img = imgLoader.LoadIcon(*rewind, fontHeight, fontHeight); + if( img ) + iconsPixmap->DrawImage( cPoint(left, 0), *img ); - if( imgLoader.LoadIcon(*rewind, fontHeight, fontHeight) ) - iconsPixmap->DrawImage( cPoint(left, 0), imgLoader.GetImage() ); - if( imgLoader.LoadIcon(*pause, fontHeight, fontHeight) ) - iconsPixmap->DrawImage( cPoint(left + fontHeight + marginItem, 0), imgLoader.GetImage() ); - if( imgLoader.LoadIcon(*play, fontHeight, fontHeight) ) - iconsPixmap->DrawImage( cPoint(left + fontHeight*2 + marginItem*2, 0), imgLoader.GetImage() ); - if( imgLoader.LoadIcon(*forward, fontHeight, fontHeight) ) - iconsPixmap->DrawImage( cPoint(left + fontHeight*3 + marginItem*3, 0), imgLoader.GetImage() ); + img = imgLoader.LoadIcon(*pause, fontHeight, fontHeight); + if( img ) + iconsPixmap->DrawImage( cPoint(left + fontHeight + marginItem, 0), *img ); + + img = imgLoader.LoadIcon(*play, fontHeight, fontHeight); + if( img ) + iconsPixmap->DrawImage( cPoint(left + fontHeight*2 + marginItem*2, 0), *img ); + + img = imgLoader.LoadIcon(*forward, fontHeight, fontHeight); + if( img ) + iconsPixmap->DrawImage( cPoint(left + fontHeight*3 + marginItem*3, 0), *img ); } @@ -160,6 +166,8 @@ void cFlatDisplayReplay::SetJump(const char *Jump) { void cFlatDisplayReplay::ResolutionAspectDraw(void) { int left = osdWidth - Config.decorBorderReplaySize*2; int imageTop = 0; + cImage *img = NULL; + if( screenWidth > 0 ) { if( Config.RecordingResolutionAspectShow ) { // Show Aspect cString asp = "unknown_asp"; // ??? @@ -173,10 +181,11 @@ void cFlatDisplayReplay::ResolutionAspectDraw(void) { else if( screenAspect == 2.21 ) asp = "221"; } - if (imgLoader.LoadIcon(*asp, 999, fontSmlHeight)) { - imageTop = fontHeight + (fontSmlHeight - imgLoader.Height())/2; - left -= imgLoader.Width(); - iconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon(*asp, 999, fontSmlHeight); + if( img ) { + imageTop = fontHeight + (fontSmlHeight - img->Height())/2; + left -= img->Width(); + iconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } @@ -219,10 +228,11 @@ void cFlatDisplayReplay::ResolutionAspectDraw(void) { //printf("Width: %d Height: %d Aspect: %.2f\n", screenWidth, screenHeight, screenAspect); - if (imgLoader.LoadIcon(*res, 999, fontSmlHeight)) { - imageTop = fontHeight + (fontSmlHeight - imgLoader.Height())/2; - left -= imgLoader.Width(); - iconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon(*res, 999, fontSmlHeight); + if (img) { + imageTop = fontHeight + (fontSmlHeight - img->Height())/2; + left -= img->Width(); + iconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2; } } @@ -242,10 +252,11 @@ void cFlatDisplayReplay::ResolutionAspectDraw(void) { break; } - if (imgLoader.LoadIcon(*iconName, 999, fontSmlHeight)) { - imageTop = fontHeight + (fontSmlHeight - imgLoader.Height())/2; - left -= imgLoader.Width(); - iconsPixmap->DrawImage(cPoint(left, imageTop), imgLoader.GetImage()); + img = imgLoader.LoadIcon(*iconName, 999, fontSmlHeight); + if( img ) { + imageTop = fontHeight + (fontSmlHeight - img->Height())/2; + left -= img->Width(); + iconsPixmap->DrawImage(cPoint(left, imageTop), *img); left -= marginItem*2 ; } } diff --git a/displayvolume.c b/displayvolume.c index cc2aee73..f9daaf1d 100644 --- a/displayvolume.c +++ b/displayvolume.c @@ -44,8 +44,9 @@ void cFlatDisplayVolume::SetVolume(int Current, int Total, bool Mute) { if (Mute) { labelPixmap->DrawText(cPoint(left, marginItem), *label, Theme.Color(clrVolumeFont), Theme.Color(clrVolumeBg), font, maxlabelWidth + marginItem + labelHeight, fontHeight, taLeft); - if( imgLoader.LoadIcon("mute", fontHeight, fontHeight) ) { - muteLogoPixmap->DrawImage( cPoint(left + maxlabelWidth + marginItem, marginItem), imgLoader.GetImage() ); + cImage *img = imgLoader.LoadIcon("mute", fontHeight, fontHeight); + if( img ) { + muteLogoPixmap->DrawImage( cPoint(left + maxlabelWidth + marginItem, marginItem), *img ); } DecorBorderDraw(left - marginItem, DecorTop, maxlabelWidth + marginItem*4 + fontHeight, fontHeight, Config.decorBorderVolumeSize, Config.decorBorderVolumeType, Config.decorBorderVolumeFg, Config.decorBorderVolumeBg); @@ -11,6 +11,7 @@ #include "displayvolume.h" class cFlatConfig Config; +class cImageCache imgCache; cTheme Theme; static bool menuActive = false; @@ -4,7 +4,10 @@ #include <vdr/videodir.h> #include "config.h" +#include "imagecache.h" + extern class cFlatConfig Config; +extern class cImageCache imgCache; extern bool firstDisplay; class cFlatDisplayMenu; diff --git a/imagecache.c b/imagecache.c new file mode 100644 index 00000000..b220f6e2 --- /dev/null +++ b/imagecache.c @@ -0,0 +1,52 @@ +#include "imagecache.h" + +cImageCache::cImageCache() { + +} + +cImageCache::~cImageCache() { + +} + +void cImageCache::Create(void) { + for(int i = 0; i < MAX_IMAGE_CACHE; i++) { + CacheImage[i] = NULL; + CacheName[i] = ""; + CacheWidth[i] = -1; + CacheHeight[i] = -1; + } + + InsertIndex = 0; +} + +void cImageCache::Clear(void) { + for(int i = 0; i < MAX_IMAGE_CACHE; i++) { + if( CacheImage[i] != NULL ) + delete CacheImage[i]; + } + + InsertIndex = 0; +} + +cImage* cImageCache::GetImage(std::string Name, int Width, int Height) { + for(int index = 0; index < MAX_IMAGE_CACHE; index++ ) { + if( CacheName[index] == Name && CacheWidth[index] == Width && CacheHeight[index] == Height ) + return CacheImage[index]; + } + return NULL; +} + +void cImageCache::InsertImage(cImage *Image, std::string Name, int Width, int Height) { + CacheImage[InsertIndex] = Image; + CacheName[InsertIndex] = Name; + CacheWidth[InsertIndex] = Width; + CacheHeight[InsertIndex] = Height; + + dsyslog("imagecache InsertImage"); + + InsertIndex++; + if( InsertIndex >= MAX_IMAGE_CACHE ) { + InsertIndex = 0; + dsyslog("imagecache overflow"); + } +} diff --git a/imagecache.h b/imagecache.h new file mode 100644 index 00000000..edeb8f41 --- /dev/null +++ b/imagecache.h @@ -0,0 +1,25 @@ +#pragma once +#include <vdr/osd.h> +#include <vdr/skins.h> +#include <string> + +#define MAX_IMAGE_CACHE 999 + +class cImageCache { +private: + cImage *CacheImage[MAX_IMAGE_CACHE]; + std::string CacheName[MAX_IMAGE_CACHE]; + int CacheWidth[MAX_IMAGE_CACHE]; + int CacheHeight[MAX_IMAGE_CACHE]; + + int InsertIndex; +public: + cImageCache(); + ~cImageCache(); + + void Create(void); + void Clear(void); + + cImage *GetImage(std::string Name, int Width, int Height); + void InsertImage(cImage *Image, std::string Name, int Width, int Height); +}; diff --git a/imageloader.c b/imageloader.c index fd8e6a35..e989b84d 100644 --- a/imageloader.c +++ b/imageloader.c @@ -6,6 +6,7 @@ using namespace Magick; + cImageLoader::cImageLoader() { InitializeMagick(NULL); logoExtension = "png"; @@ -14,39 +15,64 @@ cImageLoader::cImageLoader() { cImageLoader::~cImageLoader() { } -bool cImageLoader::LoadLogo(const char *logo, int width = -1, int height = -1) { - if( width == -1 ) - width = logoWidth; - if( height == -1 ) - height = logoHeight; - +cImage* cImageLoader::LoadLogo(const char *logo, int width, int height) { if( (width == 0) || (height==0) ) - return false; + return NULL; + dsyslog("imageloader LoadLogo: %s", logo); std::string logoLower = logo; toLowerCase(logoLower); cString File = cString::sprintf("%s/%s.%s", *Config.logoPath, logoLower.c_str(), *logoExtension); + + cImage *img; + img = imgCache.GetImage( *File, width, height ); + if( img != NULL ) + return img; + bool success = LoadImage(File); if( !success ) { dsyslog("imageloader LoadLogo: %s could not be loaded", *File); - return false; + return NULL; } - if( height != 0 || width != 0 ) { - buffer.sample( Geometry(width, height) ); - } - return true; + img = CreateImage(width, height); + if( img == NULL ) + return NULL; + imgCache.InsertImage(img, logoLower, width, height); + return img; } -int cImageLoader::Height(void) { - Geometry geo = buffer.size(); - return geo.height(); -} +cImage* cImageLoader::LoadIcon(const char *cIcon, int width, int height, bool preserveAspect) { + if ((width == 0)||(height==0)) + return NULL; + + cString File = cString::sprintf("%s%s/%s.%s", *Config.iconPath, Setup.OSDTheme, cIcon, *logoExtension); + + cImage *img; + img = imgCache.GetImage( *File, width, height ); + if( img != NULL ) + return img; + + bool success = LoadImage(File); + if( !success ) { + File = cString::sprintf("%s%s/%s.%s", *Config.iconPath, "default", cIcon, *logoExtension); + img = imgCache.GetImage( *File, width, height ); + if( img != NULL ) + return img; -int cImageLoader::Width(void) { - Geometry geo = buffer.size(); - return geo.width(); + success = LoadImage(File); + if( !success ) { + dsyslog("imageloader LoadIcon: %s could not be loaded", *File); + return NULL; + } + } + img = CreateImage(width, height); + if( img == NULL ) + return NULL; + imgCache.InsertImage(img, cIcon, width, height); + return img; } +/* bool cImageLoader::LoadIcon(const char *cIcon, int size) { if (size==0) return false; @@ -64,11 +90,11 @@ bool cImageLoader::LoadIcon(const char *cIcon, int size) { buffer.sample(Geometry(size, size)); return true; } +*/ -bool cImageLoader::LoadIcon(const char *cIcon, int width, int height, bool preserveAspect) { +/* +bool cImageLoader::LoadIcon2(const char *cIcon) { try { - if ((width == 0)||(height==0)) - return false; cString File = cString::sprintf("%s%s/%s.%s", *Config.iconPath, Setup.OSDTheme, cIcon, *logoExtension); bool success = LoadImage(File); @@ -80,12 +106,6 @@ bool cImageLoader::LoadIcon(const char *cIcon, int width, int height, bool prese return false; } } - if (preserveAspect) { - buffer.sample(Geometry(width, height)); - } else { - cString geometry = cString::sprintf("%dx%d!", width, height); - buffer.scale(Geometry(*geometry)); - } return true; } catch (...) { @@ -94,32 +114,15 @@ bool cImageLoader::LoadIcon(const char *cIcon, int width, int height, bool prese } cImage cImageLoader::GetImage() { - 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; + return CreateImageCopy(); } -Color cImageLoader::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; +cImage cImageLoader::GetImage(int width, int height, bool preserveAspect) { + cImage *img; + img = CreateImage(width, height, preserveAspect); + return *img; } +*/ void cImageLoader::toLowerCase(std::string &str) { const int length = str.length(); @@ -127,12 +130,3 @@ void cImageLoader::toLowerCase(std::string &str) { str[i] = std::tolower(str[i]); } } - -bool cImageLoader::LoadImage(cString File) { - try { - buffer.read(*File); - } catch (...) { - return false; - } - return true; -} diff --git a/imageloader.h b/imageloader.h index fc9a3186..b7ffb243 100644 --- a/imageloader.h +++ b/imageloader.h @@ -2,34 +2,35 @@ #define X_DISPLAY_MISSING + #include <vdr/osd.h> #include <vdr/skins.h> #include <Magick++.h> +#include "imagemagickwrapper.h" #include "flat.h" using namespace Magick; - -class cImageLoader { + +class cImageLoader : public cImageMagickWrapper { public: cImageLoader(); ~cImageLoader(); - cImage GetImage(); - bool LoadLogo(const char *logo, int width, int height); - bool LoadIcon(const char *cIcon, int size = -1); - bool LoadIcon(const char *cIcon, int width, int height, bool preserveAspect = true); + + cImage* LoadLogo(const char *logo, int width, int height); + cImage* LoadIcon(const char *cIcon, int width, int height, bool preserveAspect = true); + + //cImage GetImage(); + //cImage GetImage(int width, int height, bool preserveAspect = true); + + //bool LoadIcon(const char *cIcon, int size = -1); + //bool LoadIcon2(const char *cIcon); - int Height(void); - int Width(void); private: int epgImageWidthLarge, epgImageHeightLarge; int epgImageWidth, epgImageHeight; - int logoWidth, logoHeight; cString logoExtension; - Image buffer; - Color Argb2Color(tColor col); void toLowerCase(std::string &str); - bool LoadImage(cString File); }; diff --git a/imagemagickwrapper.c b/imagemagickwrapper.c new file mode 100644 index 00000000..22532b3b --- /dev/null +++ b/imagemagickwrapper.c @@ -0,0 +1,87 @@ +#include <string> +#include <sstream> +#include "imagemagickwrapper.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(const char *fullpath) { + if ((fullpath == NULL) || (strlen(fullpath) < 5)) + return false; + try { + buffer.read(fullpath); + } catch(...) { + 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; +} diff --git a/imagemagickwrapper.h b/imagemagickwrapper.h new file mode 100644 index 00000000..9750a888 --- /dev/null +++ b/imagemagickwrapper.h @@ -0,0 +1,23 @@ +#ifndef __NOPACITY_IMAGEMAGICKWRAPPER_H +#define __NOPACITY_IMAGEMAGICKWRAPPER_H + +#define X_DISPLAY_MISSING + +#include <Magick++.h> +#include <vdr/osd.h> + +using namespace Magick; + +class cImageMagickWrapper { +public: + cImageMagickWrapper(); + ~cImageMagickWrapper(); +protected: + Image buffer; + Color Argb2Color(tColor col); + cImage *CreateImage(int width, int height, bool preserveAspect = true); + cImage CreateImageCopy(void); + bool LoadImage(const char *fullpath); +}; + +#endif diff --git a/imagescaler.c b/imagescaler.c new file mode 100644 index 00000000..cebe9129 --- /dev/null +++ b/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/imagescaler.h b/imagescaler.h new file mode 100644 index 00000000..f2de6ba4 --- /dev/null +++ b/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/po/de_DE.po b/po/de_DE.po index eb4b0b56..b584980e 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -34,7 +34,7 @@ msgid "Subtitle" msgstr "Untertitel" msgid "timer not enabled" -msgstr "" +msgstr "Timer nicht aktiv" msgid "Length" msgstr "Länge" diff --git a/skinflatplus.c b/skinflatplus.c index 33c26262..fb6dbc63 100644 --- a/skinflatplus.c +++ b/skinflatplus.c @@ -14,6 +14,7 @@ #include "flat.h" #include "setup.h" +#include "imageloader.h" static const char *VERSION = "0.1.0"; static const char *DESCRIPTION = "skin flatplus"; @@ -86,11 +87,15 @@ bool cPluginFlat::Start(void) { return false; } else dsyslog("skinflatplus: TrueColor OSD found"); + + imgCache.Create(); + flat = new cFlat; return flat; } void cPluginFlat::Stop(void) { + imgCache.Clear(); } void cPluginFlat::Housekeeping(void) { |