windows-nt/Source/XPSP1/NT/shell/osshell/control/scrnsave/museum/pictures.cpp
2020-09-26 16:20:57 +08:00

486 lines
13 KiB
C++

/*****************************************************************************\
FILE: pictures.cpp
DESCRIPTION:
Manage the pictures in the user's directories. Convert them when needed.
Handle caching and making sure we don't use too much diskspace. Also add
picture frame when needed.
BryanSt 12/24/2000
Copyright (C) Microsoft Corp 2000-2001. All rights reserved.
\*****************************************************************************/
#include "stdafx.h"
#include <shlobj.h>
#include "pictures.h"
#include "util.h"
#undef __IShellFolder2_FWD_DEFINED__
#include <ccstock.h>
CPictureManager * g_pPictureMgr = NULL;
int CALLBACK DSACallback_FreePainting(LPVOID p, LPVOID pData)
{
SSPICTURE_INFO * pPInfo = (SSPICTURE_INFO *) p;
if (pPInfo)
{
Str_SetPtr(&pPInfo->pszPath, NULL);
}
return 1;
}
CPictureManager::CPictureManager(CMSLogoDXScreenSaver * pMain)
{
m_nCurrent = 0;
m_hdsaPictures = NULL;
m_pMain = pMain;
m_hdsaBatches = 0;
m_nCurrentBatch = 0;
_EnumPaintings();
}
CPictureManager::~CPictureManager()
{
if (m_hdsaBatches)
{
DSA_Destroy(m_hdsaBatches);
m_hdsaBatches = NULL;
}
if (m_hdsaPictures)
{
DSA_DestroyCallback(m_hdsaPictures, DSACallback_FreePainting, NULL);
m_hdsaPictures = NULL;
}
m_pMain = NULL;
}
HRESULT CPictureManager::_PInfoCreate(int nIndex, LPCTSTR pszPath)
{
HRESULT hr = S_OK;
SSPICTURE_INFO pInfo = {0};
pInfo.fInABatch = FALSE;
pInfo.pszPath = NULL;
Str_SetPtr(&pInfo.pszPath, pszPath);
// We add them in a random order so the pattern doesn't get boring.
if (-1 == DSA_InsertItem(m_hdsaPictures, nIndex, &pInfo))
{
// We failed so free the memory
Str_SetPtr(&pInfo.pszPath, NULL);
hr = E_OUTOFMEMORY;
}
return S_OK;
}
HRESULT CPictureManager::_AddPaintingsFromDir(LPCTSTR pszPath)
{
HRESULT hr = E_OUTOFMEMORY;
if (!m_hdsaPictures)
{
m_hdsaPictures = DSA_Create(sizeof(SSPICTURE_INFO), 20);
}
if (m_hdsaPictures)
{
TCHAR szSearch[MAX_PATH];
WIN32_FIND_DATA findFileData;
hr = S_OK;
StrCpyN(szSearch, pszPath, ARRAYSIZE(szSearch));
PathAppend(szSearch, TEXT("*.*"));
HANDLE hFindFiles = FindFirstFile(szSearch, &findFileData);
if (INVALID_HANDLE_VALUE != hFindFiles)
{
while ((INVALID_HANDLE_VALUE != hFindFiles))
{
if (!PathIsDotOrDotDot(findFileData.cFileName))
{
if (!(FILE_ATTRIBUTE_DIRECTORY & findFileData.dwFileAttributes))
{
LPCTSTR pszExt = PathFindExtension(findFileData.cFileName);
if (pszExt && pszExt[0] &&
(!StrCmpI(pszExt, TEXT(".bmp"))
|| !StrCmpI(pszExt, TEXT(".jpg"))
|| !StrCmpI(pszExt, TEXT(".jpeg"))
|| !StrCmpI(pszExt, TEXT(".png"))
// || !StrCmpI(pszExt, TEXT(".gif"))
|| !StrCmpI(pszExt, TEXT(".tiff"))
))
{
int nInsertLoc = GetRandomInt(0, max(0, DSA_GetItemCount(m_hdsaPictures) - 1));
StrCpyN(szSearch, pszPath, ARRAYSIZE(szSearch));
PathAppend(szSearch, findFileData.cFileName);
hr = _PInfoCreate(nInsertLoc, szSearch);
}
}
else
{
StrCpyN(szSearch, pszPath, ARRAYSIZE(szSearch));
PathAppend(szSearch, findFileData.cFileName);
hr = _AddPaintingsFromDir(szSearch);
}
}
if (!FindNextFile(hFindFiles, &findFileData))
{
break;
}
}
FindClose(hFindFiles);
}
}
return hr;
}
HRESULT CPictureManager::_EnumPaintings(void)
{
HRESULT hr = E_FAIL;
TCHAR szDir[MAX_PATH];
if (g_pConfig)
{
hr = S_OK;
if (g_pConfig->GetFolderOn(CONFIG_FOLDER_MYPICTS) &&
SHGetSpecialFolderPath(NULL, szDir, CSIDL_MYPICTURES, TRUE))
{
hr = _AddPaintingsFromDir(szDir);
}
if (g_pConfig->GetFolderOn(CONFIG_FOLDER_COMMONPICTS))
{
// TODO: When Common Pictures are added.
}
if (g_pConfig->GetFolderOn(CONFIG_FOLDER_OTHER) &&
SUCCEEDED(g_pConfig->GetOtherDir(szDir, ARRAYSIZE(szDir))))
{
hr = _AddPaintingsFromDir(szDir);
}
// If we have less than 10 paintings, then we force add the
// Windows wallpapers.
if ((g_pConfig->GetFolderOn(CONFIG_FOLDER_WINPICTS) ||
(10 > DSA_GetItemCount(m_hdsaPictures))) &&
SHGetSpecialFolderPath(NULL, szDir, CSIDL_WINDOWS, TRUE))
{
PathAppend(szDir, TEXT("Web\\Wallpaper"));
hr = _AddPaintingsFromDir(szDir);
}
if (m_hdsaPictures)
{
m_nCurrent = GetRandomInt(0, max(0, DSA_GetItemCount(m_hdsaPictures) - 1));
}
}
return hr;
}
HRESULT CPictureManager::_LoadTexture(SSPICTURE_INFO * pInfo, BOOL fFaultInTexture)
{
// We keep trying until we hit the end. We give up after that.
HRESULT hr = E_INVALIDARG;
if (pInfo)
{
if (!pInfo->pTexture)
{
pInfo->pTexture = new CTexture(m_pMain, pInfo->pszPath, NULL, 1.0f);
}
hr = (pInfo->pTexture ? S_OK : E_OUTOFMEMORY);
if (pInfo->pTexture && fFaultInTexture)
{
pInfo->pTexture->GetTexture(NULL); // Force the image to be paged in.
}
}
return hr;
}
#define GNPF_NONE 0x00000000
#define GNPF_ALREADYLOADED 0x00000001
#define GNPF_FAULTINTEXTURE 0x00000002
#define GNPF_ALLOWALREADYINBATCH 0x00000004
HRESULT CPictureManager::_TryGetNextPainting(SSPICTURE_INFO ** ppInfo, DWORD dwFlags)
{
// We keep trying until we hit the end. We give up after that.
HRESULT hr = E_FAIL;
while (m_hdsaPictures &&
FAILED(hr) && (m_nCurrent < DSA_GetItemCount(m_hdsaPictures)))
{
SSPICTURE_INFO * pPInfo = (SSPICTURE_INFO *) DSA_GetItemPtr(m_hdsaPictures, m_nCurrent);
if (pPInfo && pPInfo->pszPath)
{
if (!pPInfo->fInABatch || (dwFlags & GNPF_ALLOWALREADYINBATCH))
{
if (dwFlags & GNPF_ALREADYLOADED)
{
if (pPInfo->pTexture && pPInfo->pTexture->IsLoadedInAnyDevice())
{
// The caller only wants an object that's already pre-fetched, and we just found one.
*ppInfo = pPInfo;
pPInfo->fInABatch = TRUE;
hr = S_OK;
}
}
else
{
// The caller is happen to this now. We only get this far if we failed to load it.
hr = _LoadTexture(pPInfo, (dwFlags & GNPF_FAULTINTEXTURE));
if (SUCCEEDED(hr))
{
*ppInfo = pPInfo;
pPInfo->fInABatch = TRUE;
}
}
}
}
m_nCurrent++;
}
return hr;
}
HRESULT CPictureManager::_GetNextWithWrap(SSPICTURE_INFO ** ppInfo, BOOL fAlreadyLoaded, BOOL fFaultInTexture)
{
DWORD dwFlags = ((fAlreadyLoaded ? GNPF_ALREADYLOADED : GNPF_NONE) | (fFaultInTexture ? GNPF_FAULTINTEXTURE : GNPF_NONE));
*ppInfo = NULL;
HRESULT hr = _TryGetNextPainting(ppInfo, dwFlags);
if (m_nCurrent >= DSA_GetItemCount(m_hdsaPictures))
{
m_nCurrent = 0;
if (FAILED(hr))
{
// Maybe it was necessary to wrap. We don't loop to prevent infinite recursion in corner cases.
hr = _TryGetNextPainting(ppInfo, dwFlags);
if (FAILED(hr))
{
// Maybe we have so few paintings available that we need to reuse.
m_nCurrent = 0;
hr = _TryGetNextPainting(ppInfo, (dwFlags | GNPF_ALLOWALREADYINBATCH));
}
}
}
if (SUCCEEDED(hr) && (10 < DSA_GetItemCount(m_hdsaPictures)) &&
(1 == GetRandomInt(0, 4)))
{
// There is a one in for chance we want to skip pictures. This will keep the order somewhat random
// while still not always going in the same order. We only do this if the user has more than 10
// pictures or it may lap and the user will have the same picture twice in the same room.
m_nCurrent += GetRandomInt(1, 5);
if (m_nCurrent >= DSA_GetItemCount(m_hdsaPictures))
{
m_nCurrent = 0;
}
}
return hr;
}
HRESULT CPictureManager::_CreateNewBatch(int nBatch, BOOL fFaultInTexture)
{
HRESULT hr = S_OK;
SSPICTURES_BATCH batch = {0};
int nIndex;
for (nIndex = 0; (nIndex < ARRAYSIZE(batch.pInfo)) && SUCCEEDED(hr); nIndex++)
{
// First try and not get any dups
hr = _GetNextWithWrap(&(batch.pInfo[nIndex]), FALSE, fFaultInTexture);
}
if (SUCCEEDED(hr))
{
if (-1 == DSA_AppendItem(m_hdsaBatches, &batch))
{
// We failed so free the memory
hr = E_OUTOFMEMORY;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////
// FUNCTION: GetPainting
///////////////////////////////////////////////////////////////////////
HRESULT CPictureManager::GetPainting(int nBatch, int nIndex, DWORD dwFlags, CTexture ** ppTexture)
{
HRESULT hr = E_FAIL;
if (!m_hdsaBatches)
{
m_hdsaBatches = DSA_Create(sizeof(SSPICTURES_BATCH), 20);
}
m_nCurrentBatch = max(m_nCurrentBatch, nBatch);
*ppTexture = NULL;
if (m_hdsaPictures && m_hdsaBatches)
{
hr = S_OK;
while ((nBatch >= DSA_GetItemCount(m_hdsaBatches)) && SUCCEEDED(hr))
{
hr = _CreateNewBatch(nBatch, TRUE);
}
if (SUCCEEDED(hr))
{
SSPICTURES_BATCH * pBatch = (SSPICTURES_BATCH *) DSA_GetItemPtr(m_hdsaBatches, nBatch);
if (pBatch && ((void *)-1 != pBatch) && pBatch->pInfo[nIndex] && pBatch->pInfo[nIndex]->pTexture)
{
IUnknown_Set((IUnknown **) ppTexture, (IUnknown *) pBatch->pInfo[nIndex]->pTexture);
}
else
{
hr = E_FAIL;
}
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////
// FUNCTION: PreFetch
///////////////////////////////////////////////////////////////////////
HRESULT CPictureManager::PreFetch(int nBatch, int nToFetch)
{
HRESULT hr = S_OK;
while ((nBatch >= DSA_GetItemCount(m_hdsaBatches)) && SUCCEEDED(hr))
{
hr = _CreateNewBatch(nBatch, FALSE);
if (FAILED(hr))
{
DXUtil_Trace(TEXT("ERROR: PreFetch() _CreateNewBatch failed. nBatch=%d\n"), nBatch);
}
}
SSPICTURES_BATCH * pBatch = (SSPICTURES_BATCH *) DSA_GetItemPtr(m_hdsaBatches, nBatch);
if (pBatch && (nToFetch < ARRAYSIZE(pBatch->pInfo)))
{
int nIndex;
for (nIndex = 0; (nIndex < ARRAYSIZE(pBatch->pInfo)) && nToFetch; nIndex++)
{
if (pBatch->pInfo[nIndex] &&
(!pBatch->pInfo[nIndex]->pTexture ||
!pBatch->pInfo[nIndex]->pTexture->IsLoadedForThisDevice()))
{
hr = _LoadTexture(pBatch->pInfo[nIndex], TRUE);
nToFetch--;
}
}
}
else
{
hr = E_FAIL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////
// FUNCTION: ReleaseBatch
///////////////////////////////////////////////////////////////////////
HRESULT CPictureManager::ReleaseBatch(int nBatch)
{
HRESULT hr = E_INVALIDARG;
for (int nIndex = 0; nIndex < nBatch; nIndex++)
{
SSPICTURES_BATCH * pBatch = (SSPICTURES_BATCH *) DSA_GetItemPtr(m_hdsaBatches, nIndex);
for (int nIndex2 = 0; nIndex2 < ARRAYSIZE(pBatch->pInfo); nIndex2++)
{
if (pBatch && pBatch->pInfo[nIndex2] && (pBatch->pInfo[nIndex2]->fInABatch || pBatch->pInfo[nIndex2]->pTexture))
{
ReleaseBatch(nIndex);
DXUtil_Trace(TEXT("ERROR: ReleaseBatch() and previous batch aren't released. nBatch=%d, nIndex=%d\n"), nBatch, nIndex);
break;
}
}
}
if (nBatch < m_nCurrentBatch)
{
SSPICTURES_BATCH * pBatch = (SSPICTURES_BATCH *) DSA_GetItemPtr(m_hdsaBatches, nBatch);
if (pBatch && ((void *)-1 != pBatch))
{
hr = S_OK;
for (int nIndex = 0; (nIndex < ARRAYSIZE(pBatch->pInfo)); nIndex++)
{
if (pBatch->pInfo[nIndex])
{
SAFE_RELEASE(pBatch->pInfo[nIndex]->pTexture);
pBatch->pInfo[nIndex]->fInABatch = FALSE;
pBatch->pInfo[nIndex] = NULL;
}
}
}
}
else
{
DXUtil_Trace(TEXT("ERROR: ReleaseBatch() and batch is bad. nBatch=%d, m_nCurrentBatch=%d\n"), nBatch, m_nCurrentBatch);
hr = E_UNEXPECTED;
}
return hr;
}