vdr-plugin-tvguide/imagecache.c
2022-06-18 16:10:07 +02:00

721 lines
26 KiB
C

#include <string>
#include <sstream>
#include <map>
#include <vdr/channels.h>
#include "config.h"
#include "imagescaler.h"
#include "tools.h"
#include "imagecache.h"
cImageCache::cImageCache() : cImageMagickWrapper() {
tempStaticLogo = NULL;
groupsHead = NULL;
groupsBottom = NULL;
groupsLeft = NULL;
groupsRight = NULL;
imgLeft = NULL;
imgLeftActive = NULL;
imgRight = NULL;
imgRightActive = NULL;
imgHead = NULL;
imgHeadActive = NULL;
imgBottom = NULL;
imgBottomActive = NULL;
}
cImageCache::~cImageCache() {
Clear();
}
void cImageCache::CreateCache(void) {
if (config.style != eStyleGraphical)
return;
esyslog("tvguide: Creating Image Cache");
int start = cTimeMs::Now();
int startNext = cTimeMs::Now();
CreateOsdIconCache();
esyslog("tvguide: Osd Icon Cache created in %d ms", int(cTimeMs::Now()-startNext));
startNext = cTimeMs::Now();
PrepareGridIconCache();
CreateGridIconCache();
esyslog("tvguide: Grid Icon Cache created in %d ms", int(cTimeMs::Now()-startNext));
startNext = cTimeMs::Now();
CreateChannelGroupCache();
esyslog("tvguide: Channelgroup Cache created in %d ms", int(cTimeMs::Now()-startNext));
startNext = cTimeMs::Now();
CreateLogoCache();
esyslog("tvguide: Logo Cache created in %d ms", int(cTimeMs::Now()-startNext));
startNext = cTimeMs::Now();
esyslog("tvguide: Complete Image Cache created in %d ms", int(cTimeMs::Now()-start));
}
void cImageCache::CreateOsdIconCache(void) {
bool success = false;
//Status Header
std::string imgStatusHeaderContentFull = "osdElements/statusheader_content_full";
std::string imgStatusHeaderContentWindowed = "osdElements/statusheader_content_windowed";
std::string imgStatusHeaderTVFrame = "osdElements/statusheader_tvframe";
success = LoadIcon(imgStatusHeaderContentFull);
if (success)
InsertIntoOsdElementCache(oeStatusHeaderContentFull, geoManager.headerContentWidth, geoManager.statusHeaderHeight);
success = LoadIcon(imgStatusHeaderContentWindowed);
if (success)
InsertIntoOsdElementCache(oeStatusHeaderContentWindowed, geoManager.headerContentWidth, geoManager.statusHeaderHeight);
success = LoadIcon(imgStatusHeaderTVFrame);
if (success)
InsertIntoOsdElementCache(oeStatusHeaderTVFrame, geoManager.tvFrameWidth, geoManager.statusHeaderHeight);
//Color Buttons
std::string imgButtonRed = "osdElements/buttonred";
std::string imgButtonGreen = "osdElements/buttongreen";
std::string imgButtonYellow = "osdElements/buttonyellow";
std::string imgButtonBlue = "osdElements/buttonblue";
success = LoadIcon(imgButtonRed);
if (success)
InsertIntoOsdElementCache(oeButtonRed, geoManager.buttonWidth, geoManager.buttonHeight);
success = LoadIcon(imgButtonGreen);
if (success)
InsertIntoOsdElementCache(oeButtonGreen, geoManager.buttonWidth, geoManager.buttonHeight);
success = LoadIcon(imgButtonYellow);
if (success)
InsertIntoOsdElementCache(oeButtonYellow, geoManager.buttonWidth, geoManager.buttonHeight);
success = LoadIcon(imgButtonBlue);
if (success)
InsertIntoOsdElementCache(oeButtonBlue, geoManager.buttonWidth, geoManager.buttonHeight);
//Channel Logo Background
if (config.displayMode == eHorizontal) {
success = LoadIcon("osdElements/channellogoback_horizontal");
} else {
success = LoadIcon("osdElements/channellogoback_vertical");
}
if (success)
InsertIntoOsdElementCache(oeLogoBack, geoManager.channelLogoWidth, geoManager.channelLogoHeight);
//Timeline Elements
std::string imgTimeline1, imgTimeline2, imgDateViewer;
if (config.displayMode == eHorizontal) {
imgTimeline1 = "osdElements/timeline1_horizontal";
imgTimeline2 = "osdElements/timeline2_horizontal";
imgDateViewer = "osdElements/timeline2_horizontal";
} else {
imgTimeline1 = "osdElements/timeline1_vertical";
imgTimeline2 = "osdElements/timeline2_vertical";
imgDateViewer = "osdElements/date_vertical";
}
std::string imgClock = "osdElements/clock";
success = LoadIcon(imgTimeline1);
if (success)
InsertIntoOsdElementCache(oeTimeline1, geoManager.timeLineGridWidth, geoManager.timeLineGridHeight);
success = LoadIcon(imgTimeline2);
if (success)
InsertIntoOsdElementCache(oeTimeline2, geoManager.timeLineGridWidth, geoManager.timeLineGridHeight);
success = LoadIcon(imgDateViewer);
if (success)
InsertIntoOsdElementCache(oeDateViewer, geoManager.dateVieverWidth, geoManager.dateVieverHeight);
success = LoadIcon(imgClock);
if (success)
InsertIntoOsdElementCache(oeClock, geoManager.clockWidth, geoManager.clockHeight);
//Channel Jump
success = LoadIcon("osdElements/channel_jump");
if (success)
InsertIntoOsdElementCache(oeChannelJump, geoManager.channelJumpWidth, geoManager.channelJumpHeight);
}
void cImageCache::PrepareGridIconCache(void) {
bool success = false;
//Create Buffers for Background
gridsAvailable = true;
std::string grid, grid_active;
if (config.displayMode == eHorizontal) {
grid = "osdElements/grid_horizontal";
grid_active = "osdElements/grid_active_horizontal";
} else {
grid = "osdElements/grid_vertical";
grid_active = "osdElements/grid_active_vertical";
}
success = LoadIcon(grid);
if (success) {
bufferGrid = buffer;
} else {
gridsAvailable = false;
}
success = LoadIcon(grid_active);
if (success) {
bufferGridActive = buffer;
} else {
gridsAvailable = false;
}
//Create Grid Background Templates
imgLeft = NULL;
imgLeftActive = NULL;
imgRight = NULL;
imgRightActive = NULL;
imgHead = NULL;
imgHeadActive = NULL;
imgBottom = NULL;
imgBottomActive = NULL;
if (config.displayMode == eHorizontal) {
std::string left = "osdElements/grid_left";
std::string right = "osdElements/grid_right";
std::string left_active = "osdElements/grid_active_left";
std::string right_active = "osdElements/grid_active_right";
cornerWidth = 0;
cornerHeight = geoManager.rowHeight;
//first image determinates width
success = LoadIcon(left);
if (!success)
return;
int widthOriginal = 0;
int heightOriginal = 0;
widthOriginal = buffer.columns();
heightOriginal = buffer.rows();
cornerWidth = widthOriginal * cornerHeight / heightOriginal;
imgLeft = CreateImage(cornerWidth, cornerHeight, false);
success = LoadIcon(right);
if (success)
imgRight = CreateImage(cornerWidth, cornerHeight, false);
success = LoadIcon(left_active);
if (success)
imgLeftActive = CreateImage(cornerWidth, cornerHeight, false);
success = LoadIcon(right_active);
if (success)
imgRightActive = CreateImage(cornerWidth, cornerHeight, false);
} else {
std::string head = "osdElements/grid_head";
std::string bottom = "osdElements/grid_bottom";
std::string head_active = "osdElements/grid_active_head";
std::string bottom_active = "osdElements/grid_active_bottom";
cornerWidth = geoManager.colWidth;
cornerHeight = 0;
//first image determinates height
success = LoadIcon(head);
if (!success)
return;
int widthOriginal = 0;
int heightOriginal = 0;
widthOriginal = buffer.columns();
heightOriginal = buffer.rows();
cornerHeight = heightOriginal * cornerWidth / widthOriginal;
imgHead = CreateImage(cornerWidth, cornerHeight, false);
success = LoadIcon(bottom);
if (success)
imgBottom = CreateImage(cornerWidth, cornerHeight, false);
success = LoadIcon(head_active);
if (success)
imgHeadActive = CreateImage(cornerWidth, cornerHeight, false);
success = LoadIcon(bottom_active);
if (success)
imgBottomActive = CreateImage(cornerWidth, cornerHeight, false);
}
}
void cImageCache::CreateGridIconCache(void) {
if (config.displayMode == eHorizontal) {
int gridHeight = geoManager.rowHeight;
for (int minutes = 5; minutes <= 120; minutes += 5) {
GetGrid(minutes * geoManager.minutePixel, gridHeight, false);
}
} else {
int gridWidth = geoManager.colWidth;
for (int minutes = 5; minutes <= 120; minutes += 5) {
GetGrid(gridWidth, minutes * geoManager.minutePixel, false);
}
}
}
void cImageCache::CreateChannelGroupCache(void) {
bool success = false;
groupsHead = NULL;
groupsBottom = NULL;
groupsLeft = NULL;
groupsRight = NULL;
if (config.displayMode == eHorizontal) {
std::string channelGroupHead = "osdElements/channelgroup_head";
std::string channelGroupBottom = "osdElements/channelgroup_bottom";
int width = geoManager.channelGroupsWidth;
int heightHeadBottom = 0;
success = LoadIcon(channelGroupHead);
if (success) {
int widthOriginal = buffer.columns();
int heightOriginal = buffer.rows();
heightHeadBottom = heightOriginal * width / widthOriginal;
groupsHead = CreateImage(width, heightHeadBottom, false);
}
success = LoadIcon(channelGroupBottom);
if (success && heightHeadBottom) {
groupsBottom = CreateImage(width, heightHeadBottom, false);
}
for (int size = 1; size <= config.numGrids; ++size) {
InsertIntoGroupsCacheHorizontal(size);
}
} else {
std::string channelGroupLeft = "osdElements/channelgroup_left";
std::string channelGroupRight = "osdElements/channelgroup_right";
int widthHeadBottom = 0;
int height = geoManager.channelGroupsHeight;
success = LoadIcon(channelGroupLeft);
if (success) {
int widthOriginal = buffer.columns();
int heightOriginal = buffer.rows();
widthHeadBottom = widthOriginal * height / heightOriginal;
groupsLeft = CreateImage(widthHeadBottom, height, false);
}
success = LoadIcon(channelGroupRight);
if (success && widthHeadBottom) {
groupsRight = CreateImage(widthHeadBottom, height, false);
}
for (int size = 1; size <= config.numGrids; ++size) {
InsertIntoGroupsCacheVertical(size);
}
}
}
void cImageCache::CreateLogoCache(void) {
if (config.hideChannelLogos)
return;
if (config.numLogosInitial > 0) {
int channelsCached = 0;
#if VDRVERSNUM >= 20301
LOCK_CHANNELS_READ;
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel)) {
#else
for (const cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
#endif
if (channelsCached >= config.numLogosInitial)
break;
if (!channel->GroupSep()) {
bool success = LoadLogo(channel);
if (success) {
channelsCached++;
InsertIntoLogoCache(*channel->GetChannelID().ToString());
}
}
}
}
}
cImage *cImageCache::GetOsdElement(eOsdElementType type) {
std::map<eOsdElementType, cImage*>::iterator hit = osdElementCache.find(type);
if (hit != osdElementCache.end()) {
return (cImage*)hit->second;
}
return NULL;
}
cImage *cImageCache::GetGrid(int width, int height, bool active) {
if (!gridsAvailable)
return NULL;
std::stringstream gridImageName;
gridImageName << width << "x" << height;
if (active)
gridImageName << "active";
std::string gridImg = gridImageName.str();
std::map<std::string, cImage*>::iterator hit = gridCache.find(gridImg);
if (hit != gridCache.end()) {
return (cImage*)hit->second;
} else {
InsertIntoGridCache(gridImg, width, height, active);
hit = gridCache.find(gridImg);
if (hit != gridCache.end()) {
return (cImage*)hit->second;
}
}
return NULL;
}
cImage *cImageCache::GetChannelGroup(int width, int height) {
std::stringstream groupName;
groupName << width << "x" << height;
std::map<std::string, cImage*>::iterator hit = groupsCache.find(groupName.str());
if (hit != groupsCache.end()) {
return (cImage*)hit->second;
}
return NULL;
}
cImage *cImageCache::GetLogo(const cChannel *channel) {
if (!channel)
return NULL;
std::map<std::string, cImage*>::iterator hit = logoCache.find(*channel->GetChannelID().ToString());
if (hit != logoCache.end()) {
return (cImage*)hit->second;
} else {
bool success = LoadLogo(channel);
if (success) {
if ((config.limitLogoCache) && ((int)logoCache.size() >= config.numLogosMax)) {
//logo cache is full, don't cache anymore
if (tempStaticLogo) {
delete tempStaticLogo;
tempStaticLogo = NULL;
}
tempStaticLogo = CreateImage(geoManager.logoWidth * 0.8, geoManager.logoHeight * 0.8);
return tempStaticLogo;
} else {
//add requested logo to cache
InsertIntoLogoCache(*channel->GetChannelID().ToString());
hit = logoCache.find(*channel->GetChannelID().ToString());
if (hit != logoCache.end()) {
return (cImage*)hit->second;
}
}
}
}
return NULL;
}
cImage *cImageCache::GetIcon(std::string name, int width, int height) {
std::stringstream iconName;
iconName << name << width << "x" << height;
std::map<std::string, cImage*>::iterator hit = iconCache.find(iconName.str());
if (hit != iconCache.end()) {
return (cImage*)hit->second;
} else {
std::stringstream iconPath;
iconPath << "recmenuicons/" << name;
bool success = LoadIcon(iconPath.str());
if (success) {
cImage *image = CreateImage(width, height);
iconCache.insert(std::pair<std::string, cImage*>(iconName.str(), image));
hit = iconCache.find(iconName.str());
if (hit != iconCache.end()) {
return (cImage*)hit->second;
}
}
}
return NULL;
}
void cImageCache::InsertIntoOsdElementCache(eOsdElementType type, int width, int height) {
cImage *image = CreateImage(width, height, false);
if (image)
osdElementCache.insert(std::pair<eOsdElementType, cImage*>(type, image));
}
void cImageCache::InsertIntoGridCache(std::string name, int width, int height, bool active) {
cImage *image = CreateGrid(width, height, active);
if (image) {
if (config.displayMode == eHorizontal) {
AddCornersHorizontal(image, active);
} else {
AddCornersVertical(image, active);
}
gridCache.insert(std::pair<std::string, cImage*>(name, image));
}
}
cImage *cImageCache::CreateGrid(int width, int height, bool active) {
if (width > geoManager.osdWidth || width < 6 || height > geoManager.osdHeight || height < 6)
return NULL;
Image *currentGridBuffer = NULL;
if (active)
currentGridBuffer = &bufferGridActive;
else
currentGridBuffer = &bufferGrid;
int w, h;
w = currentGridBuffer->columns();
h = currentGridBuffer->rows();
const PixelPacket *pixels = currentGridBuffer->getConstPixels(0, 0, w, h);
cImage *image = NULL;
image = new cImage(cSize(width, height));
if (!image)
return NULL;
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;
}
void cImageCache::AddCornersHorizontal(cImage *img, bool active) {
int imgWidth = img->Width();
cImage *cornerLeft = NULL;
cImage *cornerRight = NULL;
if (active) {
cornerLeft = imgLeftActive;
cornerRight = imgRightActive;
} else {
cornerLeft = imgLeft;
cornerRight = imgRight;
}
if (!cornerLeft || !cornerRight)
return;
int maxX = std::min(cornerWidth, imgWidth);
for (int row = 0; row < cornerHeight; row++) {
for (int col = 0; col < maxX; ++col) {
img->SetPixel(cPoint(col, row), cornerLeft->GetPixel(cPoint(col, row)));
img->SetPixel(cPoint(imgWidth - maxX + col, row), cornerRight->GetPixel(cPoint(col, row)));
}
}
}
void cImageCache::AddCornersVertical(cImage *img, bool active) {
int imgHeight = img->Height();
cImage *cornerHead = NULL;
cImage *cornerBottom = NULL;
if (active) {
cornerHead = imgHeadActive;
cornerBottom = imgBottomActive;
} else {
cornerHead = imgHead;
cornerBottom = imgBottom;
}
if (!cornerHead || !cornerBottom)
return;
int maxY = std::min(cornerHeight, imgHeight);
for (int row = 0; row < maxY; row++) {
for (int col = 0; col < cornerWidth; ++col) {
img->SetPixel(cPoint(col, row), cornerHead->GetPixel(cPoint(col, row)));
img->SetPixel(cPoint(col, imgHeight - maxY + row), cornerBottom->GetPixel(cPoint(col, row)));
}
}
}
void cImageCache::InsertIntoGroupsCacheHorizontal(int size) {
int width = geoManager.channelGroupsWidth;
bool success = LoadIcon("osdElements/channelgroup_horizontal");
if (success) {
int height = size * geoManager.rowHeight;
std::stringstream name;
name << width << "x" << height;
cImage *image = CreateImage(width, height, false);
if (image) {
AddCornersGroupHorizontal(image);
groupsCache.insert(std::pair<std::string, cImage*>(name.str(), image));
}
}
}
void cImageCache::InsertIntoGroupsCacheVertical(int size) {
int height = geoManager.channelGroupsHeight;
bool success = LoadIcon("osdElements/channelgroup_vertical");
if (success) {
int width = size * geoManager.colWidth;
std::stringstream name;
name << width << "x" << height;
cImage *image = CreateImage(width, height, false);
if (image) {
AddCornersGroupVertical(image);
groupsCache.insert(std::pair<std::string, cImage*>(name.str(), image));
}
}
}
void cImageCache::AddCornersGroupHorizontal(cImage *img) {
if (!groupsHead || !groupsBottom || !img)
return;
int imgWidth = img->Width();
int imgHeight = img->Height();
int heightHeadBottom = groupsHead->Height();
int maxY = std::min(heightHeadBottom, imgHeight);
for (int row = 0; row < maxY; row++) {
for (int col = 0; col < imgWidth; ++col) {
img->SetPixel(cPoint(col, row), groupsHead->GetPixel(cPoint(col, row)));
img->SetPixel(cPoint(col, imgHeight - maxY + row), groupsBottom->GetPixel(cPoint(col, row)));
}
}
}
void cImageCache::AddCornersGroupVertical(cImage *img) {
if (!groupsLeft || !groupsRight || !img)
return;
int imgWidth = img->Width();
int imgHeight = img->Height();
int widthLeftRight = groupsLeft->Width();
int maxX = std::min(widthLeftRight, imgWidth);
for (int row = 0; row < imgHeight; row++) {
for (int col = 0; col < maxX; ++col) {
img->SetPixel(cPoint(col, row), groupsLeft->GetPixel(cPoint(col, row)));
img->SetPixel(cPoint(imgWidth - maxX + col, row), groupsRight->GetPixel(cPoint(col, row)));
}
}
}
bool cImageCache::LoadIcon(std::string name) {
bool success = false;
if (config.iconsPathSet) {
cString iconPathTheme = cString::sprintf("%s%s/", *config.iconPath, *config.themeName);
success = LoadImage(name, *iconPathTheme, "png");
if (success) {
return true;
} else {
success = LoadImage(name, *config.iconPath, "png");
if (success) {
return true;
}
}
}
if (!success) {
cString iconPathTheme = cString::sprintf("%s%s/", *config.iconPathDefault, *config.themeName);
success = LoadImage(name, *iconPathTheme, "png");
if (success) {
return true;
} else {
success = LoadImage(name, *config.iconPathDefault, "png");
if (success) {
return true;
}
}
}
return false;
}
bool cImageCache::LoadLogo(const cChannel *channel) {
if (!channel)
return false;
std::string channelID = StrToLowerCase(*(channel->GetChannelID().ToString()));
std::string logoLower = StrToLowerCase(channel->Name());
bool success = false;
cString extension;
if (config.logoExtension == 0) {
extension = "png";
} else if (config.logoExtension == 1) {
extension = "jpg";
}
if (config.logoPathSet) {
success = LoadImage(channelID.c_str(), *config.logoPath, *extension);
if (success)
return true;
success = LoadImage(logoLower.c_str(), *config.logoPath, *extension);
if (success)
return true;
}
success = LoadImage(channelID.c_str(), *config.logoPathDefault, *extension);
if (success)
return true;
success = LoadImage(logoLower.c_str(), *config.logoPathDefault, *extension);
return success;
}
void cImageCache::InsertIntoLogoCache(std::string channelID) {
cImage *image = CreateImage(geoManager.logoWidth * 0.8, geoManager.logoHeight * 0.8);
logoCache.insert(std::pair<std::string, cImage*>(channelID, image));
}
std::string cImageCache::GetCacheSize(eCacheType type) {
std::stringstream result;
int sizeByte = 0;
int numImages = 0;
if (type == ctOsdElement) {
for(std::map<eOsdElementType, cImage*>::const_iterator it = osdElementCache.begin(); it != osdElementCache.end(); it++) {
cImage *img = (cImage*)it->second;
if (img)
sizeByte += img->Width() * img->Height() * sizeof(tColor);
}
numImages = osdElementCache.size();
} else if ((type == ctGrid) || (type == ctLogo) || (type == ctChannelGroup) || (type == ctIcon)) {
std::map<std::string, cImage*> *cache;
if (type == ctGrid)
cache = &gridCache;
else if (type == ctLogo)
cache = &logoCache;
else if (type == ctChannelGroup)
cache = &groupsCache;
else if (type == ctIcon)
cache = &iconCache;
for(std::map<std::string, cImage*>::const_iterator it = cache->begin(); it != cache->end(); it++) {
cImage *img = (cImage*)it->second;
if (img)
sizeByte += img->Width() * img->Height() * sizeof(tColor);
}
numImages = cache->size();
}
result << numImages << " " << tr("images") << " / " << sizeByte/1024 << " KByte";
return result.str();
}
void cImageCache::Clear(void) {
for(std::map<eOsdElementType, cImage*>::const_iterator it = osdElementCache.begin(); it != osdElementCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
osdElementCache.clear();
for(std::map<std::string, cImage*>::const_iterator it = gridCache.begin(); it != gridCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
gridCache.clear();
for(std::map<std::string, cImage*>::const_iterator it = groupsCache.begin(); it != groupsCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
groupsCache.clear();
for(std::map<std::string, cImage*>::const_iterator it = logoCache.begin(); it != logoCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
logoCache.clear();
for(std::map<std::string, cImage*>::const_iterator it = iconCache.begin(); it != iconCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
iconCache.clear();
if (tempStaticLogo)
delete tempStaticLogo;
if (groupsHead)
delete groupsHead;
if (groupsBottom)
delete groupsBottom;
if (groupsLeft)
delete groupsLeft;
if (groupsRight)
delete groupsRight;
if (imgLeft)
delete imgLeft;
if (imgLeftActive)
delete imgLeftActive;
if (imgRight)
delete imgRight;
if (imgRightActive)
delete imgRightActive;
if (imgHead)
delete imgHead;
if (imgHeadActive)
delete imgHeadActive;
if (imgBottom)
delete imgBottom;
if (imgBottomActive)
delete imgBottomActive;
tempStaticLogo = NULL;
groupsHead = NULL;
groupsBottom = NULL;
groupsLeft = NULL;
groupsRight = NULL;
imgLeft = NULL;
imgLeftActive = NULL;
imgRight = NULL;
imgRightActive = NULL;
imgHead = NULL;
imgHeadActive = NULL;
imgBottom = NULL;
imgBottomActive = NULL;
}