-changed the coverflow cover loader, now it should not run

out of memory anymore, but therefore some coverflow views will
may take longer to load covers
-made that cover case color check easier and faster
This commit is contained in:
fix94.1 2013-01-13 15:45:05 +00:00
parent 0041618cb1
commit 0129ca9053
4 changed files with 125 additions and 110 deletions

View File

@ -190,6 +190,7 @@ CCoverFlow::CCoverFlow(void)
m_jump = 0;
m_mutex = 0;
m_dvdskin_loaded = false;
m_defcovers_loaded = false;
m_loadingCovers = false;
m_coverThrdBusy = false;
m_renderTex = false;
@ -236,6 +237,9 @@ CCoverFlow::CCoverFlow(void)
m_hoverSound = NULL;
m_selectSound = NULL;
m_cancelSound = NULL;
//
m_loadingTexture = NULL;
m_noCoverTexture = NULL;
LWP_MutexInit(&m_mutex, 0);
}
@ -1294,17 +1298,17 @@ void CCoverFlow::_drawCover(int i, bool mirror, CCoverFlow::DrawMode dm)
_drawCoverFlat(i, mirror, dm);
}
TexData &CCoverFlow::_coverTexture(int i)
const TexData *CCoverFlow::_coverTexture(int i)
{
if(m_items[i].texture.data == NULL)
return (m_items[i].state == STATE_Loading) ? m_loadingTexture : m_noCoverTexture;
return m_items[i].texture;
return &m_items[i].texture;
}
void CCoverFlow::_drawCoverFlat(int i, bool mirror, CCoverFlow::DrawMode dm)
{
GXTexObj texObj;
TexData &tex = _coverTexture(m_covers[i].index);
const TexData *tex = _coverTexture(m_covers[i].index);
bool boxTex = m_items[m_covers[i].index].boxTexture && !!m_items[m_covers[i].index].texture.data;
switch (dm)
@ -1331,12 +1335,11 @@ void CCoverFlow::_drawCoverFlat(int i, bool mirror, CCoverFlow::DrawMode dm)
}
if(dm == CCoverFlow::CFDR_NORMAL)
{
GX_InitTexObj(&texObj, tex.data, tex.width, tex.height, tex.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
if (tex.maxLOD > 0)
GX_InitTexObjLOD(&texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.f, (float)tex.maxLOD, mirror ? 1.f : m_lodBias, GX_FALSE, m_edgeLOD ? GX_TRUE : GX_FALSE, m_aniso);
GX_InitTexObj(&texObj, tex->data, tex->width, tex->height, tex->format, GX_CLAMP, GX_CLAMP, GX_FALSE);
if(tex->maxLOD > 0)
GX_InitTexObjLOD(&texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.f, (float)tex->maxLOD, mirror ? 1.f : m_lodBias, GX_FALSE, m_edgeLOD ? GX_TRUE : GX_FALSE, m_aniso);
GX_LoadTexObj(&texObj, GX_TEXMAP0);
}
GX_Begin(GX_QUADS, GX_VTXFMT0, g_flatCoverMeshSize);
for (u32 j = 0; j < g_flatCoverMeshSize; ++j)
{
@ -1352,10 +1355,9 @@ void CCoverFlow::_drawCoverFlat(int i, bool mirror, CCoverFlow::DrawMode dm)
GX_End();
}
bool CCoverFlow::_checkCoverColor(char* gameID, const char* checkID[], int len)
bool CCoverFlow::checkCoverColor(const char *gameID, const char *checkID[], u32 len)
{
int num;
for (num = 0; num < len; num++)
for(u32 num = 0; num < len; ++num)
{
if (strncmp(gameID, checkID[num], strlen(checkID[num])) == 0)
return true;
@ -1363,11 +1365,26 @@ bool CCoverFlow::_checkCoverColor(char* gameID, const char* checkID[], int len)
return false;
}
u32 CCoverFlow::InternalCoverColor(const char *ID, u32 DefCaseColor)
{
if(checkCoverColor(ID, red, red_len))
return 0xFF0000;
else if(checkCoverColor(ID, black, black_len))
return 0x000000;
else if(checkCoverColor(ID, yellow, yellow_len))
return 0xFCFF00;
else if(checkCoverColor(ID, greenOne, greenOne_len))
return 0x01A300;
else if(checkCoverColor(ID, greenTwo, greenTwo_len))
return 0x00E360;
return DefCaseColor;
}
void CCoverFlow::_drawCoverBox(int i, bool mirror, CCoverFlow::DrawMode dm)
{
GXTexObj texObj;
TexData &tex = _coverTexture(m_covers[i].index);
CColor color;
GXTexObj texObj;
const TexData *tex = _coverTexture(m_covers[i].index);
bool flatTex = !m_items[m_covers[i].index].boxTexture && !!m_items[m_covers[i].index].texture.data;
switch (dm)
@ -1390,53 +1407,31 @@ void CCoverFlow::_drawCoverBox(int i, bool mirror, CCoverFlow::DrawMode dm)
}
if(dm == CCoverFlow::CFDR_NORMAL)
{
u32 casecolor = m_items[m_covers[i].index].hdr->casecolor;
// set dvd box texture, depending on game
if(casecolor == 0xFFFFFF)
GX_InitTexObj(&texObj, m_dvdSkin.data, m_dvdSkin.width, m_dvdSkin.height, m_dvdSkin.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
else if (casecolor == 0xFF0000)
GX_InitTexObj(&texObj, m_dvdSkin_Red.data, m_dvdSkin_Red.width, m_dvdSkin_Red.height, m_dvdSkin_Red.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
else if (casecolor == 0x000000 || casecolor == 0x181919)
switch(m_items[m_covers[i].index].hdr->casecolor)
{
case 0x000000:
case 0x181919:
GX_InitTexObj(&texObj, m_dvdSkin_Black.data, m_dvdSkin_Black.width, m_dvdSkin_Black.height, m_dvdSkin_Black.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
else if (casecolor == 0xFCFF00)
GX_InitTexObj(&texObj, m_dvdSkin_Yellow.data, m_dvdSkin_Yellow.width, m_dvdSkin_Yellow.height, m_dvdSkin_Yellow.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
else if (casecolor == 0x01A300)
GX_InitTexObj(&texObj, m_dvdSkin_GreenOne.data, m_dvdSkin_GreenOne.width, m_dvdSkin_GreenOne.height, m_dvdSkin_GreenOne.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
else if (casecolor == 0x00E360)
GX_InitTexObj(&texObj, m_dvdSkin_GreenTwo.data, m_dvdSkin_GreenTwo.width, m_dvdSkin_GreenTwo.height, m_dvdSkin_GreenTwo.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
else if(_checkCoverColor(m_items[m_covers[i].index].hdr->id,red,red_len))
{
m_items[m_covers[i].index].hdr->casecolor = 0xFF0000;
break;
case 0xFF0000:
GX_InitTexObj(&texObj, m_dvdSkin_Red.data, m_dvdSkin_Red.width, m_dvdSkin_Red.height, m_dvdSkin_Red.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
}
else if(_checkCoverColor(m_items[m_covers[i].index].hdr->id,black,black_len))
{
m_items[m_covers[i].index].hdr->casecolor = 0x000000;
GX_InitTexObj(&texObj, m_dvdSkin_Black.data, m_dvdSkin_Black.width, m_dvdSkin_Black.height, m_dvdSkin_Black.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
}
else if(_checkCoverColor(m_items[m_covers[i].index].hdr->id,yellow,yellow_len))
{
m_items[m_covers[i].index].hdr->casecolor = 0xFCFF00;
break;
case 0xFCFF00:
GX_InitTexObj(&texObj, m_dvdSkin_Yellow.data, m_dvdSkin_Yellow.width, m_dvdSkin_Yellow.height, m_dvdSkin_Yellow.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
}
else if(_checkCoverColor(m_items[m_covers[i].index].hdr->id,greenOne,greenOne_len))
{
m_items[m_covers[i].index].hdr->casecolor = 0x01A300;
break;
case 0x01A300:
GX_InitTexObj(&texObj, m_dvdSkin_GreenOne.data, m_dvdSkin_GreenOne.width, m_dvdSkin_GreenOne.height, m_dvdSkin_GreenOne.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
}
else if(_checkCoverColor(m_items[m_covers[i].index].hdr->id,greenTwo,greenTwo_len))
{
m_items[m_covers[i].index].hdr->casecolor = 0x00E360;
break;
case 0x00E360:
GX_InitTexObj(&texObj, m_dvdSkin_GreenTwo.data, m_dvdSkin_GreenTwo.width, m_dvdSkin_GreenTwo.height, m_dvdSkin_GreenTwo.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
}
else
{
m_items[m_covers[i].index].hdr->casecolor = 0xFFFFFF;
break;
default:
GX_InitTexObj(&texObj, m_dvdSkin.data, m_dvdSkin.width, m_dvdSkin.height, m_dvdSkin.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
break;
}
GX_LoadTexObj(&texObj, GX_TEXMAP0);
}
GX_Begin(GX_QUADS, GX_VTXFMT0, g_boxMeshQSize);
for (u32 j = 0; j < g_boxMeshQSize; ++j)
{
@ -1455,9 +1450,9 @@ void CCoverFlow::_drawCoverBox(int i, bool mirror, CCoverFlow::DrawMode dm)
GX_End();
if (dm == CCoverFlow::CFDR_NORMAL)
{
TexData *myTex = &tex;
const TexData *myTex = tex;
if(flatTex)
myTex = &m_noCoverTexture;
myTex = m_noCoverTexture;
GX_InitTexObj(&texObj, myTex->data, myTex->width, myTex->height, myTex->format, GX_CLAMP, GX_CLAMP, GX_FALSE);
if (myTex->maxLOD > 0)
GX_InitTexObjLOD(&texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.f, (float)myTex->maxLOD, mirror ? 1.f : m_lodBias, GX_FALSE, m_edgeLOD ? GX_TRUE : GX_FALSE, m_aniso);
@ -1473,9 +1468,9 @@ void CCoverFlow::_drawCoverBox(int i, bool mirror, CCoverFlow::DrawMode dm)
GX_End();
if (dm == CCoverFlow::CFDR_NORMAL && flatTex)
{
GX_InitTexObj(&texObj, tex.data, tex.width, tex.height, tex.format, GX_CLAMP, GX_CLAMP, GX_FALSE);
if (tex.maxLOD > 0)
GX_InitTexObjLOD(&texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.f, (float)tex.maxLOD, mirror ? 1.f : m_lodBias, GX_FALSE, m_edgeLOD ? GX_TRUE : GX_FALSE, m_aniso);
GX_InitTexObj(&texObj, tex->data, tex->width, tex->height, tex->format, GX_CLAMP, GX_CLAMP, GX_FALSE);
if (tex->maxLOD > 0)
GX_InitTexObjLOD(&texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.f, (float)tex->maxLOD, mirror ? 1.f : m_lodBias, GX_FALSE, m_edgeLOD ? GX_TRUE : GX_FALSE, m_aniso);
GX_LoadTexObj(&texObj, GX_TEXMAP0);
}
GX_Begin(GX_QUADS, GX_VTXFMT0, g_boxCoverMeshSize);
@ -1881,33 +1876,33 @@ bool CCoverFlow::start()
return false;
m_dvdskin_loaded = true;
}
if(!m_defcovers_loaded)
{
if(m_pngLoadCover.empty() || TexHandle.fromImageFile(m_boxLoadingTexture, m_pngLoadCover.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromJPG(m_boxLoadingTexture, loading_jpg, loading_jpg_size, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
if(m_pngNoCover.empty() || TexHandle.fromImageFile(m_boxNoCoverTexture, m_pngNoCover.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromPNG(m_boxNoCoverTexture, nopic_png, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
if(m_pngLoadCoverFlat.empty() || TexHandle.fromImageFile(m_flatLoadingTexture, m_pngLoadCoverFlat.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromJPG(m_flatLoadingTexture, flatloading_jpg, flatloading_jpg_size, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
if(m_pngNoCoverFlat.empty() || TexHandle.fromImageFile(m_flatNoCoverTexture, m_pngNoCoverFlat.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromPNG(m_flatNoCoverTexture, flatnopic_png, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
m_defcovers_loaded = true;
}
m_loadingTexture = (m_box ? &m_boxLoadingTexture : &m_flatLoadingTexture);
m_noCoverTexture = (m_box ? &m_boxNoCoverTexture : &m_flatNoCoverTexture);
if(m_box)
{
if(m_pngLoadCover.empty() || TexHandle.fromImageFile(m_loadingTexture, m_pngLoadCover.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromJPG(m_loadingTexture, loading_jpg, loading_jpg_size, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
if(m_pngNoCover.empty() || TexHandle.fromImageFile(m_noCoverTexture, m_pngNoCover.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromPNG(m_noCoverTexture, nopic_png, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
}
else
{
if(m_pngLoadCoverFlat.empty() || TexHandle.fromImageFile(m_loadingTexture, m_pngLoadCoverFlat.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromJPG(m_loadingTexture, flatloading_jpg, flatloading_jpg_size, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
if(m_pngNoCoverFlat.empty() || TexHandle.fromImageFile(m_noCoverTexture, m_pngNoCoverFlat.c_str(), GX_TF_CMPR, 32, 512) != TE_OK)
{
if(TexHandle.fromPNG(m_noCoverTexture, flatnopic_png, GX_TF_CMPR, 32, 512) != TE_OK)
return false;
}
}
m_covers.clear();
m_covers.resize(m_range);
m_jump = 0;
@ -2805,6 +2800,11 @@ CCoverFlow::CLRet CCoverFlow::_loadCoverTex(u32 i, bool box, bool hq, bool blank
free(ptrTex);
}
}
if(!success && tex.data != NULL)
{
free(tex.data);
tex.data = NULL;
}
}
}
fclose(fp);
@ -2823,7 +2823,8 @@ int CCoverFlow::_coverLoader(CCoverFlow *cf)
{
cf->m_coverThrdBusy = true;
CLRet ret;
u32 firstItem;
int firstItem = 0;
int nextItem = 0;
bool update;
u32 i;
@ -2833,29 +2834,29 @@ int CCoverFlow::_coverLoader(CCoverFlow *cf)
{
update = cf->m_moved;
cf->m_moved = false;
for(u32 j = cf->m_items.size(); j >= bufferSize && cf->m_loadingCovers && !cf->m_moved && update; --j)
{
firstItem = cf->m_covers[cf->m_range / 2].index;
i = loopNum((j & 1) ? firstItem - (j + 1) / 2 : firstItem + j / 2, cf->m_items.size());
LWP_MutexLock(cf->m_mutex);
nextItem = cf->m_covers[cf->m_range / 2].index;
int diff = nextItem - firstItem;
if(diff < 0) diff *= (-1);
for(u32 j = bufferSize - diff; j < bufferSize && cf->m_loadingCovers && update; ++j)
{
i = loopNum((j & 1) ? firstItem - (j + 1) / 2 : firstItem + j / 2, cf->m_items.size());
TexHandle.Cleanup(cf->m_items[i].texture);
cf->m_items[i].state = STATE_Loading;
LWP_MutexUnlock(cf->m_mutex);
}
firstItem = nextItem;
LWP_MutexUnlock(cf->m_mutex);
ret = CL_OK;
for(u32 j = 0; j <= bufferSize && cf->m_loadingCovers && !cf->m_moved && update && ret != CL_NOMEM; ++j)
{
firstItem = cf->m_covers[cf->m_range / 2].index;
i = loopNum((j & 1) ? firstItem - (j + 1) / 2 : firstItem + j / 2, cf->m_items.size());
if(!cf->m_useHQcover && cf->m_items[i].state != STATE_Loading)
if(cf->m_items[i].texture.data != NULL)
continue;
else if(cf->m_useHQcover && firstItem == (u32)cf->m_hqCover && cf->m_items[i].state != STATE_Loading)
continue;
if((ret = cf->_loadCoverTex(i, cf->m_box, i == (u32)firstItem, false)) == CL_ERROR)
if((ret = cf->_loadCoverTex(i, cf->m_box, cf->m_useHQcover, false)) == CL_ERROR)
{
if ((ret = cf->_loadCoverTex(i, !cf->m_box, i == (u32)firstItem, false)) == CL_ERROR)
if ((ret = cf->_loadCoverTex(i, !cf->m_box, cf->m_useHQcover, false)) == CL_ERROR)
{
if((ret = cf->_loadCoverTex(i, cf->m_box, i == (u32)firstItem, true)) == CL_ERROR)
if((ret = cf->_loadCoverTex(i, cf->m_box, cf->m_useHQcover, true)) == CL_ERROR)
cf->m_items[i].state = STATE_NoCover;
}
}

View File

@ -136,6 +136,9 @@ public:
bool getRenderTex(void);
void setRenderTex(bool);
void RenderTex(void);
//
static u32 InternalCoverColor(const char *ID, u32 DefCaseColor);
static bool checkCoverColor(const char *ID, const char *checkID[], u32 len);
private:
enum DrawMode { CFDR_NORMAL, CFDR_STENCIL, CFDR_SHADOW };
struct SLayout
@ -250,8 +253,12 @@ private:
volatile int m_hqCover;
bool m_selected;
int m_tickCount;
TexData m_loadingTexture;
TexData m_noCoverTexture;
TexData *m_loadingTexture;
TexData *m_noCoverTexture;
TexData m_flatLoadingTexture;
TexData m_flatNoCoverTexture;
TexData m_boxLoadingTexture;
TexData m_boxNoCoverTexture;
TexData m_dvdSkin;
TexData m_dvdSkin_Red;
TexData m_dvdSkin_Black;
@ -271,6 +278,7 @@ private:
bool m_box;
bool m_useHQcover;
bool m_dvdskin_loaded;
bool m_defcovers_loaded;
u32 m_range;
u32 m_rows;
u32 m_columns;
@ -315,7 +323,6 @@ private:
void _drawCover(int i, bool mirror, CCoverFlow::DrawMode dm);
void _drawCoverFlat(int i, bool mirror, CCoverFlow::DrawMode dm);
void _drawCoverBox(int i, bool mirror, CCoverFlow::DrawMode dm);
bool _checkCoverColor(char* gameID, const char* checkID[], int len);
void _updateTarget(int i, bool instant = false);
void _updateAllTargets(bool instant = false);
void _loadCover(int i, int item);
@ -325,7 +332,7 @@ private:
Vector3D _cameraMoves(void);
Vector3D _coverMovesA(void);
Vector3D _coverMovesP(void);
TexData &_coverTexture(int i);
const TexData *_coverTexture(int i);
void _left(int repeatDelay, u32 step);
void _right(int repeatDelay, u32 step);
void _jump(void);

View File

@ -21,6 +21,7 @@
#include "channel/channels.h"
#include "devicemounter/DeviceHandler.hpp"
#include "fileOps/fileOps.h"
#include "gui/coverflow.hpp"
#include "gui/text.hpp"
ListGenerator m_gameList;
@ -77,6 +78,9 @@ static void AddISO(const char *GameID, const char *GameTitle, const char *GamePa
if(CustomTitle == NULL || CustomTitle[0] == '\0')
gameTDB.GetTitle(ListElement.id, CustomTitle);
}
if(!ValidColor(ListElement.casecolor))
ListElement.casecolor = CoverFlow.InternalCoverColor(ListElement.id, GameColor);
if(CustomTitle != NULL && CustomTitle[0] != '\0')
mbstowcs(ListElement.title, CustomTitle, 63);
else if(GameTitle != NULL)
@ -95,7 +99,7 @@ static void Create_Wii_WBFS_List(wbfs_t *handle)
s32 ret = wbfs_get_disc_info(handle, i, (u8*)&wii_hdr, sizeof(discHdr), NULL);
if(ret == 0 && wii_hdr.magic == WII_MAGIC)
AddISO((const char*)wii_hdr.id, (const char*)wii_hdr.title,
NULL, 1, TYPE_WII_GAME);
NULL, 0xFFFFFF, TYPE_WII_GAME);
}
}
@ -108,7 +112,7 @@ static void Create_Wii_EXT_List(char *FullPath)
fread((void*)&wii_hdr, 1, sizeof(discHdr), fp);
if(wii_hdr.magic == WII_MAGIC)
AddISO((const char*)wii_hdr.id, (const char*)wii_hdr.title,
FullPath, 1, TYPE_WII_GAME);
FullPath, 0xFFFFFF, TYPE_WII_GAME);
fclose(fp);
}
}
@ -131,7 +135,7 @@ static void Create_GC_List(char *FullPath)
if(gc_hdr.magic == GC_MAGIC)
{
AddISO((const char*)gc_hdr.id, (const char*)gc_hdr.title,
FullPath, 0, TYPE_GC_GAME);
FullPath, 0x000000, TYPE_GC_GAME);
/* Check for disc 2 */
fseek(fp, 6, SEEK_SET);
fread(gc_disc, 1, 1, fp);
@ -172,7 +176,7 @@ static void Create_Homebrew_List(char *FullPath)
strncpy(ListElement.id, "HB_APP", 6);
FolderTitle = strrchr(FullPath, '/') + 1;
ListElement.casecolor = CustomTitles.getColor("COVERS", FolderTitle, 1).intVal();
ListElement.casecolor = CustomTitles.getColor("COVERS", FolderTitle, 0xFFFFFF).intVal();
const string &CustomTitle = CustomTitles.getString("TITLES", FolderTitle);
if(CustomTitle.size() > 0)
mbstowcs(ListElement.title, CustomTitle.c_str(), 63);
@ -197,11 +201,11 @@ static void Create_Channel_List()
ListElement.settings[0] = TITLE_UPPER(chan->title);
ListElement.settings[1] = TITLE_LOWER(chan->title);
strncpy(ListElement.id, chan->id, 4);
ListElement.casecolor = CustomTitles.getColor("COVERS", ListElement.id, 1).intVal();
ListElement.casecolor = CustomTitles.getColor("COVERS", ListElement.id, 0xFFFFFF).intVal();
const char *CustomTitle = CustomTitles.getString("TITLES", ListElement.id).c_str();
if(gameTDB.IsLoaded())
{
if(ListElement.casecolor == 1)
if(ListElement.casecolor == 0xFFFFFF)
ListElement.casecolor = gameTDB.GetCaseColor(ListElement.id);
ListElement.wifi = gameTDB.GetWifiPlayers(ListElement.id);
ListElement.players = gameTDB.GetPlayers(ListElement.id);

View File

@ -45,6 +45,9 @@ enum
};
#define CustomIOS(x) (x != IOS_TYPE_NORMAL_IOS && x != IOS_TYPE_STUB)
#define ValidColor(x) (x == 0xFFFFFF || x == 0xFF0000 || x == 0x000000 || \
x == 0xFCFF00 || x == 0x01A300 || x == 0x00E360)
#ifdef __cplusplus
}
#endif