void SaveThumbnail(DisplayState& ds) { if (!ds.thumbnail) return; ScopedMem<WCHAR> bmpPath(GetThumbnailPath(ds.filePath)); if (!bmpPath) return; ScopedMem<WCHAR> thumbsPath(path::GetDir(bmpPath)); if (dir::Create(thumbsPath)) SaveRenderedBitmap(ds.thumbnail, bmpPath); }
void SaveThumbnail(DisplayState& ds) { if (!ds.thumbnail) { return; } AutoFreeW bmpPath(GetThumbnailPath(ds.filePath)); if (!bmpPath) { return; } AutoFreeW thumbsPath(path::GetDir(bmpPath)); if (dir::Create(thumbsPath)) { CrashIf(!str::EndsWithI(bmpPath, L".png")); Bitmap bmp(ds.thumbnail->GetBitmap(), nullptr); CLSID tmpClsid = GetEncoderClsid(L"image/png"); bmp.Save(bmpPath.Get(), &tmpClsid, nullptr); } }
// TODO: create in TEMP directory instead? static WCHAR *GetThumbnailPath(const WCHAR *filePath) { // create a fingerprint of a (normalized) path for the file name // I'd have liked to also include the file's last modification time // in the fingerprint (much quicker than hashing the entire file's // content), but that's too expensive for files on slow drives unsigned char digest[16]; ScopedMem<char> pathU(str::conv::ToUtf8(filePath)); if (path::HasVariableDriveLetter(filePath)) pathU[0] = '?'; // ignore the drive letter, if it might change CalcMD5Digest((unsigned char *)pathU.Get(), str::Len(pathU), digest); ScopedMem<char> fingerPrint(str::MemToHex(digest, 16)); ScopedMem<WCHAR> thumbsPath(AppGenDataFilename(THUMBNAILS_DIR_NAME)); if (!thumbsPath) return NULL; ScopedMem<WCHAR> fname(str::conv::FromAnsi(fingerPrint)); return str::Format(L"%s\\%s.png", thumbsPath, fname); }
// removes thumbnails that don't belong to any frequently used item in file history void CleanUpThumbnailCache(FileHistory& fileHistory) { ScopedMem<WCHAR> thumbsPath(AppGenDataFilename(THUMBNAILS_DIR_NAME)); if (!thumbsPath) return; ScopedMem<WCHAR> pattern(path::Join(thumbsPath, L"*.png")); WStrVec files; WIN32_FIND_DATA fdata; HANDLE hfind = FindFirstFile(pattern, &fdata); if (INVALID_HANDLE_VALUE == hfind) return; do { if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) files.Append(str::Dup(fdata.cFileName)); } while (FindNextFile(hfind, &fdata)); FindClose(hfind); Vec<DisplayState *> list; fileHistory.GetFrequencyOrder(list); for (size_t i = 0; i < list.Count() && i < FILE_HISTORY_MAX_FREQUENT * 2; i++) { ScopedMem<WCHAR> bmpPath(GetThumbnailPath(list.At(i)->filePath)); if (!bmpPath) continue; int idx = files.Find(path::GetBaseName(bmpPath)); if (idx != -1) { CrashIf(idx < 0 || files.Count() <= (size_t)idx); WCHAR *fileName = files.At(idx); files.RemoveAt(idx); free(fileName); } } for (size_t i = 0; i < files.Count(); i++) { ScopedMem<WCHAR> bmpPath(path::Join(thumbsPath, files.At(i))); file::Delete(bmpPath); } }
// TODO: create in TEMP directory instead? static WCHAR* GetThumbnailPath(const WCHAR* filePath) { // create a fingerprint of a (normalized) path for the file name // I'd have liked to also include the file's last modification time // in the fingerprint (much quicker than hashing the entire file's // content), but that's too expensive for files on slow drives unsigned char digest[16]; // TODO: why is this happening? Seen in crash reports e.g. 35043 if (!filePath) return nullptr; OwnedData pathU(str::conv::ToUtf8(filePath)); if (!pathU.Get()) return nullptr; if (path::HasVariableDriveLetter(filePath)) pathU.Get()[0] = '?'; // ignore the drive letter, if it might change CalcMD5Digest((unsigned char*)pathU.Get(), str::Len(pathU.Get()), digest); AutoFree fingerPrint(_MemToHex(&digest)); AutoFreeW thumbsPath(AppGenDataFilename(THUMBNAILS_DIR_NAME)); if (!thumbsPath) return nullptr; AutoFreeW fname(str::conv::FromAnsi(fingerPrint)); return str::Format(L"%s\\%s.png", thumbsPath.Get(), fname.Get()); }