123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- #include "main.h"
- #include <nde/nde_c.h>
- #include "../nu/threadname.h"
- #include "AlbumArtContainer.h"
- #include <tataki/bitmap/bitmap.h>
- #include "MD5.h"
- #include "../replicant/nu/AutoLock.h"
- #include <strsafe.h>
- #include <map>
- #include <deque>
- static bool GetArtFromCache(const wchar_t *filename, int size, ARGB32 **bits);
- static bool SetArtCache(const wchar_t *filename, int size, const ARGB32 *bits, uint8_t hash[16]);
- static void CloseArtCache();
- static HANDLE artWake=0;
- static HANDLE mainThread=0;
- struct CreateCacheParameters
- {
- AlbumArtContainer *container;
- int w;
- int h;
- SkinBitmap *cache;
- AlbumArtContainer::CacheStatus status;
- };
- static std::deque<CreateCacheParameters*> artQueue;
- static nu::LockGuard queueGuard;
- static void Adjust(int bmpw, int bmph, int &x, int &y, int &w, int &h)
- {
- // maintain 'square' stretching
- double aspX = (double)(w)/(double)bmpw;
- double aspY = (double)(h)/(double)bmph;
- double asp = min(aspX, aspY);
- int newW = (int)(bmpw*asp);
- int newH = (int)(bmph*asp);
- x = (w - newW)/2;
- y = (h - newH)/2;
- w = newW;
- h = newH;
- }
- static void CALLBACK CreateCacheCallbackAPC(ULONG_PTR parameter)
- {
- CreateCacheParameters *parameters = (CreateCacheParameters *)parameter;
- parameters->container->SetCache(parameters->cache, parameters->status);
- parameters->container->Release();
- WASABI_API_MEMMGR->sysFree(parameters);
- }
- static void CreateCache(CreateCacheParameters *parameters)
- {
- // let's hope this doesn't overflow the stack
- const wchar_t *filename = parameters->container->filename;
- if ((unsigned int)(ULONG_PTR)filename < 65536)
- {
- parameters->cache = 0;
- parameters->status = AlbumArtContainer::CACHE_NOTFOUND;
- QueueUserAPC(CreateCacheCallbackAPC, mainThread, (ULONG_PTR)parameters);
- return;
- }
- int w = parameters->w, h = parameters->h;
- ARGB32 *cacheBits = 0;
- if (GetArtFromCache(filename, w, &cacheBits))
- {
- SkinBitmap *cache = new SkinBitmap(cacheBits, w, h, true);
- parameters->cache = cache;
- parameters->status = AlbumArtContainer::CACHE_CACHED;
- }
- else
- {
- int artsize = w;
- int bmp_w = 0, bmp_h = 0;
- ARGB32 *bits = 0;
- if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArt(filename, L"Cover", &bmp_w, &bmp_h, &bits) == ALBUMART_SUCCESS)
- {
- uint8_t hash[16] = {0};
- MD5_CTX hashCtx;
- MD5Init(&hashCtx);
- MD5Update(&hashCtx, (uint8_t *)bits, bmp_w*bmp_h*sizeof(ARGB32));
- MD5Final(hash, &hashCtx);
- BltCanvas canvas(w,h);
- HQSkinBitmap temp(bits, bmp_w,bmp_h); // wrap into a SkinBitmap (no copying involved)
- int x = 0, y = 0;
- Adjust(bmp_w, bmp_h, x,y,w,h);
- temp.stretch(&canvas,x,y,w,h);
- SkinBitmap *cache = new SkinBitmap(&canvas);
- parameters->cache = cache;
- SetArtCache(filename, artsize, (ARGB32 *)canvas.getBits(), hash);
- WASABI_API_MEMMGR->sysFree(bits);
- parameters->status = AlbumArtContainer::CACHE_CACHED;
- }
- else
- {
- parameters->cache = 0;
- parameters->status = AlbumArtContainer::CACHE_NOTFOUND;
- }
- }
- QueueUserAPC(CreateCacheCallbackAPC, mainThread, (ULONG_PTR)parameters);
- return;
- }
- static int ArtLoadThread(HANDLE handle, void *user_data, intptr_t id)
- {
- // TODO? SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
- queueGuard.Lock();
- if (artQueue.empty())
- {
- queueGuard.Unlock();
- return 0;
- }
- CreateCacheParameters *parameters = artQueue.front();
- artQueue.pop_front();
- queueGuard.Unlock();
- CreateCache(parameters);
- return 0;
- }
- enum
- {
- ARTHASH_FILENAME = 0,
- ARTHASH_HASH = 1,
- ARTTABLE_HASH = 0,
- ARTTABLE_ARGB32 = 1,
- };
- typedef std::map<int, nde_table_t> ArtCache;
- static ArtCache artcache;
- static nde_database_t art_db = 0;
- static nde_table_t artHashes = 0;
- static ThreadID *artThread=0;
- static void InitArtThread()
- {
- if (!artThread)
- {
- artWake = CreateSemaphore(0, 0, 65536, 0);
- mainThread = WASABI_API_APP->main_getMainThreadHandle();
- artThread = WASABI_API_THREADPOOL->ReserveThread(0);
- WASABI_API_THREADPOOL->AddHandle(artThread, artWake, ArtLoadThread, 0, 0, 0);
- }
- }
- static int ArtKillThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
- {
- WASABI_API_THREADPOOL->RemoveHandle(artThread, artWake);
- CloseArtCache();
- SetEvent((HANDLE)user_data);
- return 0;
- }
- void KillArtThread()
- {
- if (artThread)
- {
- HANDLE artKill = CreateEvent(0, FALSE, FALSE, 0);
- WASABI_API_THREADPOOL->RunFunction(artThread, ArtKillThreadPoolFunc, (void *)artKill, 0, 0);
- WaitForSingleObject(artKill, INFINITE);
- CloseHandle(mainThread); mainThread = 0;
- CloseHandle(artKill); artKill = 0;
- CloseHandle(artWake); artWake = 0;
- WASABI_API_THREADPOOL->ReleaseThread(artThread);
- }
- }
- void MigrateArtCache()
- {
- int size[] = {0, 60, 90, 120, 180};
- for (int i = 0; i < sizeof(size)/sizeof(size[0]); i++)
- {
- if (!size[i])
- {
- const wchar_t *inidir = WASABI_API_APP->path_getUserSettingsPath();
- art_db = NDE_CreateDatabase(plugin.hDllInstance);
- wchar_t tableName[MAX_PATH] = {0}, oldTableName[MAX_PATH] = {0}, indexName[MAX_PATH] = {0}, oldIndexName[MAX_PATH] = {0};
- PathCombineW(oldIndexName, inidir, L"Plugins\\ml");
- PathCombineW(indexName, inidir, L"Plugins\\ml\\art");
- CreateDirectoryW(indexName, NULL);
- PathCombineW(tableName, indexName, L"art.dat");
- PathCombineW(oldTableName, oldIndexName, L"art.dat");
- PathAppendW(indexName, L"art.idx");
- PathAppendW(oldIndexName, L"art.idx");
- // migrate files to their own 'art' sub-folder
- if (PathFileExistsW(oldIndexName) && !PathFileExistsW(indexName))
- {
- MoveFileW(oldIndexName, indexName);
- MoveFileW(oldTableName, tableName);
- }
- }
- else if (size[i])
- {
- const wchar_t *inidir = WASABI_API_APP->path_getUserSettingsPath();
- wchar_t temp[MAX_PATH] = {0}, tableName[MAX_PATH] = {0}, oldTableName[MAX_PATH] = {0}, indexName[MAX_PATH] = {0}, oldIndexName[MAX_PATH] = {0};
- PathCombineW(temp, inidir, L"Plugins\\ml");
- StringCchPrintfW(tableName, MAX_PATH, L"%s\\art\\art_%d.dat", temp, size[i]);
- StringCchPrintfW(oldTableName, MAX_PATH, L"%s\\art_%d.dat", temp, size[i]);
- StringCchPrintfW(indexName, MAX_PATH, L"%s\\art\\art_%d.idx", temp, size[i]);
- StringCchPrintfW(oldIndexName, MAX_PATH, L"%s\\art_%d.idx", temp, size[i]);
- // migrate files to their own 'art' sub-folder
- if (PathFileExistsW(oldIndexName) && !PathFileExistsW(indexName))
- {
- MoveFileW(oldIndexName, indexName);
- MoveFileW(oldTableName, tableName);
- }
- }
- }
- }
- static bool InitArtCache(int size)
- {
- if (!art_db)
- {
- const wchar_t *inidir = WASABI_API_APP->path_getUserSettingsPath();
- art_db = NDE_CreateDatabase(plugin.hDllInstance);
- wchar_t tableName[MAX_PATH] = {0}, indexName[MAX_PATH] = {0};
- PathCombineW(indexName, inidir, L"Plugins\\ml\\art");
- PathCombineW(tableName, indexName, L"art.dat");
- PathAppendW(indexName, L"art.idx");
- artHashes = NDE_Database_OpenTable(art_db, tableName, indexName, NDE_OPEN_ALWAYS, NDE_CACHE);
- NDE_Table_NewColumnW(artHashes, ARTHASH_FILENAME, DB_FIELDNAME_filename, FIELD_FILENAME);
- NDE_Table_NewColumnW(artHashes, ARTHASH_HASH, L"hash", FIELD_INT128);
- NDE_Table_PostColumns(artHashes);
- NDE_Table_AddIndexByIDW( artHashes, ARTHASH_FILENAME, DB_FIELDNAME_filename );
- }
- if (size && !artcache[size])
- {
- const wchar_t *inidir = WASABI_API_APP->path_getUserSettingsPath();
- wchar_t temp[MAX_PATH] = {0}, tableName[MAX_PATH] = {0}, indexName[MAX_PATH] = {0};
- PathCombineW(temp, inidir, L"Plugins\\ml");
- StringCchPrintfW(tableName, MAX_PATH, L"%s\\art\\art_%d.dat", temp, size);
- StringCchPrintfW(indexName, MAX_PATH, L"%s\\art\\art_%d.idx", temp, size);
- nde_table_t artTable = NDE_Database_OpenTable(art_db, tableName, indexName, NDE_OPEN_ALWAYS, NDE_NOCACHE);
- NDE_Table_NewColumnW(artTable, ARTTABLE_HASH, L"hash", FIELD_INT128);
- NDE_Table_NewColumnW(artTable, ARTTABLE_ARGB32, L"art", FIELD_BINARY32);
- NDE_Table_PostColumns(artTable);
- NDE_Table_AddIndexByIDW(artTable, ARTTABLE_HASH, L"hash");
- artcache[size] = artTable;
- }
- else
- return artHashes != 0;
- return artcache[size] != 0;
- }
- static void CloseArtHashes()
- {
- __try
- {
- if (artHashes)
- {
- NDE_Database_CloseTable(art_db, artHashes);
- }
- artHashes = 0;
- NDE_DestroyDatabase(art_db);
- art_db = 0;
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- artHashes = 0;
- art_db = 0;
- }
- }
- static void CloseArtCache()
- {
- ArtCache::iterator itr;
- for (itr = artcache.begin(); itr != artcache.end(); itr++)
- {
- if (itr->second)
- NDE_Database_CloseTable(art_db, itr->second);
- itr->second = 0;
- }
- artcache.clear();
- CloseArtHashes();
- }
- /*
- @param size = art dimensions. e.g. size==120 is for 120x120 album art
- @param bits better be allocated to size*size*sizeof(ARGB32) or you're in for a world of hurt
- */
- static bool GetArtFromCache(const wchar_t *filename, int size, ARGB32 **bits)
- {
- if (InitArtCache(size))
- {
- nde_scanner_t hashscanner = NDE_Table_CreateScanner(artHashes);
- if (NDE_Scanner_LocateFilename(hashscanner, ARTHASH_FILENAME, FIRST_RECORD, filename))
- {
- nde_field_t field = NDE_Scanner_GetFieldByID(hashscanner, ARTHASH_HASH);
- if (field)
- {
- nde_scanner_t artscanner = NDE_Table_CreateScanner(artcache[size]);
- if (NDE_Scanner_LocateField(artscanner, ARTTABLE_HASH, FIRST_RECORD, field))
- {
- nde_field_t field = NDE_Scanner_GetFieldByID(artscanner, ARTTABLE_ARGB32);
- if (field)
- {
- size_t len = 0;
- void *data = NDE_BinaryField_GetData(field, &len);
- if (data && len == size*size*sizeof(ARGB32))
- {
- *bits = (ARGB32 *)WASABI_API_MEMMGR->sysMalloc(len);
- memcpy(*bits, data, len);
- NDE_Table_DestroyScanner(artcache[size], artscanner);
- NDE_Table_DestroyScanner(artHashes, hashscanner);
- return true;
- }
- }
- }
- NDE_Table_DestroyScanner(artcache[size], artscanner);
- }
- }
- NDE_Table_DestroyScanner(artHashes, hashscanner);
- }
- return false;
- }
- static bool SetArtCache(const wchar_t *filename, int size, const ARGB32 *bits, uint8_t hash[16])
- {
- if (InitArtCache(size))
- {
- nde_scanner_t hashscanner = NDE_Table_CreateScanner(artHashes);
- if (!NDE_Scanner_LocateFilename(hashscanner, ARTHASH_FILENAME, FIRST_RECORD, filename))
- {
- NDE_Scanner_New(hashscanner);
- db_setFieldStringW(hashscanner, ARTHASH_FILENAME, filename);
- }
- else
- NDE_Scanner_Edit(hashscanner);
- nde_field_t field = NDE_Scanner_GetFieldByID(hashscanner, ARTHASH_HASH);
- if (!field)
- field = NDE_Scanner_NewFieldByID(hashscanner, ARTHASH_HASH);
- NDE_Int128Field_SetValue(field, hash);
- NDE_Scanner_Post(hashscanner);
- nde_scanner_t artscanner = NDE_Table_CreateScanner(artcache[size]);
- if (!NDE_Scanner_LocateField(artscanner, ARTTABLE_HASH, FIRST_RECORD, field))
- {
- NDE_Scanner_New(artscanner);
- field = NDE_Scanner_NewFieldByID(artscanner, ARTTABLE_HASH);
- NDE_Int128Field_SetValue(field, hash);
- field = NDE_Scanner_NewFieldByID(artscanner, ARTTABLE_ARGB32);
- // TODO when size is possibly zero, this is causing a crash in nde.dll
- if (size > 0) NDE_BinaryField_SetData(field, bits, size*size*sizeof(ARGB32));
- NDE_Scanner_Post(artscanner);
- NDE_Table_DestroyScanner(artcache[size], artscanner);
- NDE_Table_DestroyScanner(artHashes, hashscanner);
- NDE_Table_Sync(artHashes);
- return true;
- }
- NDE_Table_DestroyScanner(artcache[size], artscanner);
- NDE_Table_DestroyScanner(artHashes, hashscanner);
- NDE_Table_Sync(artHashes);
- }
- return false;
- }
- size_t maxCache = 65536/*100*/;
- void HintCacheSize(int _cachesize)
- {
- maxCache = _cachesize;
- }
- void CreateCache(AlbumArtContainer *container, int w, int h)
- {
- InitArtThread();
- assert(artThread);
- container->AddRef();
- CreateCacheParameters *parameters = (CreateCacheParameters *)WASABI_API_MEMMGR->sysMalloc(sizeof(CreateCacheParameters));
- parameters->container = container;
- parameters->w = w;
- parameters->h = h;
- parameters->status = AlbumArtContainer::CACHE_UNKNOWN;
- nu::AutoLock lock(queueGuard);
- if (artQueue.size() > maxCache)
- {
- CreateCacheParameters *kill = artQueue.back();
- artQueue.pop_back();
- kill->cache = 0;
- kill->status = AlbumArtContainer::CACHE_UNKNOWN;
- QueueUserAPC(CreateCacheCallbackAPC, mainThread, (ULONG_PTR)kill);
- }
- artQueue.push_front(parameters);
- ReleaseSemaphore(artWake, 1, 0);
- }
- void FlushCache()
- {
- nu::AutoLock lock(queueGuard);
- while (!artQueue.empty())
- {
- CreateCacheParameters *kill = artQueue.front();
- kill->container->SetCache(0, AlbumArtContainer::CACHE_UNKNOWN);
- artQueue.pop_front();
- WASABI_API_MEMMGR->sysFree(kill);
- }
- }
- void ResumeCache()
- {
- }
- static int ClearFilenameCacheAPC(HANDLE handle, void *param, intptr_t id)
- {
- wchar_t *filename = (wchar_t *)param;
- if (InitArtCache(0))
- {
- nde_scanner_t hashscanner = NDE_Table_CreateScanner(artHashes);
- if (NDE_Scanner_LocateNDEFilename(hashscanner, ARTHASH_FILENAME, FIRST_RECORD, filename))
- {
- nde_field_t field = NDE_Scanner_GetFieldByID(hashscanner, ARTHASH_HASH);
- if (field)
- {
- nde_scanner_t deleteAllScanner = NDE_Table_CreateScanner(artHashes);
- while (NDE_Scanner_LocateField(deleteAllScanner, ARTHASH_HASH, FIRST_RECORD, field))
- {
- NDE_Scanner_Delete(deleteAllScanner);
- NDE_Scanner_Post(deleteAllScanner);
- }
- NDE_Table_DestroyScanner(artHashes, deleteAllScanner);
- NDE_Table_Sync(artHashes);
- /* delete it from the art table as well, but we'll just
- use the already-opened ones */
- for (ArtCache::iterator itr=artcache.begin();itr!=artcache.end();itr++)
- {
- nde_table_t table = itr->second;
- if (table)
- {
- nde_scanner_t s= NDE_Table_CreateScanner(table);
- while (NDE_Scanner_LocateField(s, ARTTABLE_HASH, FIRST_RECORD, field))
- {
- NDE_Scanner_Delete(s);
- NDE_Scanner_Post(s);
- }
- NDE_Table_DestroyScanner(table, s);
- NDE_Table_Sync(table);
- }
- }
- }
- }
- NDE_Table_DestroyScanner(artHashes, hashscanner);
- }
- ndestring_release(filename);
- return 0;
- }
- static int DeleteDatabaseAPC(HANDLE handle, void *user_data, intptr_t id)
- {
- CloseArtCache();
- wchar_t search_mask[MAX_PATH] = {0};
- StringCchPrintfW(search_mask, MAX_PATH, L"%s\\art_*.*", g_tableDir);
- wchar_t fn[MAX_PATH] = {0};
- StringCchPrintfW(fn, MAX_PATH, L"%s\\art.idx", g_tableDir);
- DeleteFileW(fn);
- StringCchPrintfW(fn, MAX_PATH, L"%s\\art.dat", g_tableDir);
- DeleteFileW(fn);
- WIN32_FIND_DATAW findData;
- HANDLE hFind = FindFirstFileW(search_mask, &findData);
- if (hFind != INVALID_HANDLE_VALUE)
- {
- do
- {
- if (!_wcsnicmp(findData.cFileName, L"art_", 4))
- {
- StringCchPrintfW(fn, MAX_PATH, L"%s\\%s", g_tableDir, findData.cFileName);
- DeleteFileW(fn);
- }
- }
- while (FindNextFileW(hFind, &findData));
- FindClose(hFind);
- }
- return 0;
- }
- void DumpArtCache()
- {
- InitArtThread();
- assert(artThread);
- WASABI_API_THREADPOOL->RunFunction(artThread, DeleteDatabaseAPC, 0, 0, 0);
- }
- void ClearCache(const wchar_t *filename)
- {
- InitArtThread();
- assert(artThread);
- WASABI_API_THREADPOOL->RunFunction(artThread, ClearFilenameCacheAPC, ndestring_wcsdup(filename), 0, 0);
- }
|