Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
9.5 KiB

  1. #include "precomp.h"
  2. #include "prevwnd.h"
  3. #pragma hdrstop
  4. #include "strsafe.h"
  5. // class which implements IRecompress
  6. class CImgRecompress : public IImageRecompress, public NonATLObject
  7. {
  8. public:
  9. CImgRecompress();
  10. ~CImgRecompress();
  11. // IUnknown
  12. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  13. STDMETHOD_(ULONG, AddRef)();
  14. STDMETHOD_(ULONG, Release)();
  15. // IImageRecompress
  16. STDMETHODIMP RecompressImage(IShellItem *psi, int cx, int cy, int iQuality, IStorage *pstg, IStream **ppstrmOut);
  17. protected:
  18. LONG _cRef; // object lifetime
  19. IShellItem *_psi; // current shell item
  20. IShellImageDataFactory *_psidf;
  21. HRESULT _FindEncoder(IShellItem *psi, IShellImageData *psid, IStorage *pstg, IStream **ppstrmOut, BOOL *pfChangeFmt, GUID *pDataFormat);
  22. HRESULT _InitRecompress(IShellItem *psi, IStream **ppstrm, STATSTG *pstatIn);
  23. HRESULT _SaveImage(IShellImageData *psid, int cx, int cy, int iQuality, GUID *pRawDataFmt, IStream *pstrm);
  24. };
  25. // Recompress interface
  26. CImgRecompress::CImgRecompress() :
  27. _cRef(1), _psidf(NULL)
  28. {
  29. _Module.Lock();
  30. }
  31. CImgRecompress::~CImgRecompress()
  32. {
  33. ATOMICRELEASE(_psidf);
  34. _Module.Unlock();
  35. }
  36. STDMETHODIMP CImgRecompress::QueryInterface(REFIID riid, void **ppv)
  37. {
  38. static const QITAB qit[] =
  39. {
  40. QITABENT(CImgRecompress, IImageRecompress),
  41. { 0 },
  42. };
  43. return QISearch(this, qit, riid, ppv);
  44. }
  45. STDMETHODIMP_(ULONG) CImgRecompress::AddRef()
  46. {
  47. return InterlockedIncrement(&_cRef);
  48. }
  49. STDMETHODIMP_(ULONG) CImgRecompress::Release()
  50. {
  51. ASSERT( 0 != _cRef );
  52. ULONG cRef = InterlockedDecrement(&_cRef);
  53. if ( 0 == cRef )
  54. {
  55. delete this;
  56. }
  57. return cRef;
  58. }
  59. HRESULT CImgRecompress::_InitRecompress(IShellItem *psi, IStream **ppstrm, STATSTG *pstatIn)
  60. {
  61. HRESULT hr = S_OK;
  62. if (!_psidf)
  63. hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellImageDataFactory, &_psidf));
  64. if (SUCCEEDED(hr))
  65. {
  66. IBindCtx *pbc;
  67. hr = BindCtx_CreateWithMode(STGM_READ | STGM_SHARE_DENY_NONE, &pbc);
  68. if (SUCCEEDED(hr))
  69. {
  70. IStream *pstrm;
  71. hr = psi->BindToHandler(pbc, BHID_Stream, IID_PPV_ARG(IStream, &pstrm));
  72. if (SUCCEEDED(hr))
  73. {
  74. hr = pstrm->Stat(pstatIn, STATFLAG_NONAME);
  75. if (SUCCEEDED(hr))
  76. {
  77. hr = pstrm->QueryInterface(IID_PPV_ARG(IStream, ppstrm));
  78. }
  79. pstrm->Release();
  80. }
  81. pbc->Release();
  82. }
  83. }
  84. return hr;
  85. }
  86. HRESULT CImgRecompress::RecompressImage(IShellItem *psi, int cx, int cy, int iQuality, IStorage *pstg, IStream **ppstrmOut)
  87. {
  88. STATSTG statIn;
  89. IStream *pstrm;
  90. HRESULT hr = S_FALSE;
  91. if (SUCCEEDED(_InitRecompress(psi, &pstrm, &statIn)))
  92. {
  93. IShellImageData * psid;
  94. if (SUCCEEDED(_psidf->CreateImageFromStream(pstrm, &psid)))
  95. {
  96. // we need to decode the image before we can read its header - unfortunately
  97. if (SUCCEEDED(psid->Decode(SHIMGDEC_DEFAULT, 0, 0)))
  98. {
  99. BOOL fRecompress = FALSE;
  100. GUID guidDataFormat;
  101. if (S_OK == _FindEncoder(psi, psid, pstg, ppstrmOut, &fRecompress, &guidDataFormat))
  102. {
  103. int cxOut = 0, cyOut = 0;
  104. // lets compute to see if we need to recompress the image, we do this by
  105. // looking at its size compared ot the size the caller has given us,
  106. // we also compare based on the larger axis to ensure we keep aspect ratio.
  107. SIZE szImage;
  108. if (SUCCEEDED(psid->GetSize(&szImage)))
  109. {
  110. // If the image is too big scale it down to screen size (use large axis for threshold check)
  111. if (szImage.cx > szImage.cy)
  112. {
  113. cxOut = min(szImage.cx, cx);
  114. fRecompress |= szImage.cx > cx;
  115. }
  116. else
  117. {
  118. cyOut = min(szImage.cy, cy);
  119. fRecompress |= szImage.cy > cy;
  120. }
  121. }
  122. // if fRecompress then we generate the new stream, if the new stream is not
  123. // smaller than the current image that we started with then lets
  124. // ignore it (always better to send the smaller of the two).
  125. //
  126. if (fRecompress)
  127. {
  128. hr = _SaveImage(psid, cxOut, cyOut, iQuality, &guidDataFormat, *ppstrmOut);
  129. }
  130. if (hr == S_OK)
  131. {
  132. (*ppstrmOut)->Commit(0); // commit our changes to the stream
  133. LARGE_INTEGER li0 = {0}; // seek to the head of the file so reading gives us bits
  134. (*ppstrmOut)->Seek(li0, 0, NULL);
  135. }
  136. else if (*ppstrmOut)
  137. {
  138. (*ppstrmOut)->Release();
  139. *ppstrmOut = NULL;
  140. }
  141. }
  142. }
  143. psid->Release();
  144. }
  145. pstrm->Release();
  146. }
  147. return hr;
  148. }
  149. HRESULT CImgRecompress::_SaveImage(IShellImageData *psid, int cx, int cy, int iQuality, GUID *pRawDataFmt, IStream *pstrm)
  150. {
  151. HRESULT hr = S_OK;
  152. // Scale the image
  153. if (cx || cy)
  154. {
  155. hr = psid->Scale(cx, cy, InterpolationModeHighQuality);
  156. }
  157. // Make a property bag containing the encoder parameters and set it (if we are changing format)
  158. if (SUCCEEDED(hr) && pRawDataFmt)
  159. {
  160. IPropertyBag *pbagEnc;
  161. hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &pbagEnc));
  162. if (SUCCEEDED(hr))
  163. {
  164. // write the encoder CLSID into the property bag
  165. VARIANT var;
  166. hr = InitVariantFromGUID(&var, *pRawDataFmt);
  167. if (SUCCEEDED(hr))
  168. {
  169. hr = pbagEnc->Write(SHIMGKEY_RAWFORMAT, &var);
  170. VariantClear(&var);
  171. }
  172. // write the quality value for the recompression into the property bag
  173. if (SUCCEEDED(hr))
  174. hr = SHPropertyBag_WriteInt(pbagEnc, SHIMGKEY_QUALITY, iQuality);
  175. // pass the parameters over to the encoder
  176. if (SUCCEEDED(hr))
  177. hr = psid->SetEncoderParams(pbagEnc);
  178. pbagEnc->Release();
  179. }
  180. }
  181. // Now persist the file away
  182. if (SUCCEEDED(hr))
  183. {
  184. IPersistStream *ppsImg;
  185. hr = psid->QueryInterface(IID_PPV_ARG(IPersistStream, &ppsImg));
  186. if (SUCCEEDED(hr))
  187. {
  188. hr = ppsImg->Save(pstrm, TRUE);
  189. ppsImg->Release();
  190. }
  191. }
  192. return hr;
  193. }
  194. HRESULT CImgRecompress::_FindEncoder(IShellItem *psi, IShellImageData *psid, IStorage *pstg, IStream **ppstrmOut, BOOL *pfChangeFmt, GUID *pDataFormat)
  195. {
  196. GUID guidDataFormat;
  197. BOOL fChangeExt = FALSE;
  198. // read the relative name from the stream so that we can create a temporary one which maps
  199. LPWSTR pwszName;
  200. HRESULT hr = psi->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &pwszName);
  201. if (SUCCEEDED(hr))
  202. {
  203. // get the data format from the image we are decompressing
  204. hr = psid->GetRawDataFormat(&guidDataFormat);
  205. if (SUCCEEDED(hr))
  206. {
  207. if (!IsEqualGUID(guidDataFormat, ImageFormatJPEG))
  208. {
  209. // ask the image about it's properties
  210. if ((S_FALSE == psid->IsMultipage()) &&
  211. (S_FALSE == psid->IsVector()) &&
  212. (S_FALSE == psid->IsTransparent()) &&
  213. (S_FALSE == psid->IsAnimated()))
  214. {
  215. guidDataFormat = ImageFormatJPEG;
  216. fChangeExt = TRUE;
  217. }
  218. else
  219. {
  220. hr = S_FALSE; // can't be translated
  221. }
  222. }
  223. // update the name accordingly before making a stream
  224. WCHAR szOutName[MAX_PATH];
  225. hr = StringCchCopyW(szOutName, ARRAYSIZE(szOutName), pwszName);
  226. if (SUCCEEDED(hr))
  227. {
  228. if (fChangeExt)
  229. {
  230. PathRenameExtension(szOutName, TEXT(".jpg"));
  231. }
  232. // TODO: need to get FILE_FLAG_DELETE_ON_CLOSE to happen on CreateFile
  233. hr = StgMakeUniqueName(pstg, szOutName, IID_PPV_ARG(IStream, ppstrmOut));
  234. }
  235. }
  236. CoTaskMemFree(pwszName);
  237. }
  238. if (pfChangeFmt)
  239. *pfChangeFmt = fChangeExt;
  240. *pDataFormat = guidDataFormat;
  241. return hr;
  242. }
  243. STDAPI CImgRecompress_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  244. {
  245. CImgRecompress *pr = new CImgRecompress();
  246. if (!pr)
  247. {
  248. *ppunk = NULL; // incase of failure
  249. return E_OUTOFMEMORY;
  250. }
  251. HRESULT hr = pr->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  252. pr->Release();
  253. return hr;
  254. }