Blender  V2.93
BlenderThumb.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #include <new>
18 #include <shlwapi.h>
19 #include <thumbcache.h> // For IThumbnailProvider.
20 
21 #pragma comment(lib, "shlwapi.lib")
22 
23 // this thumbnail provider implements IInitializeWithStream to enable being hosted
24 // in an isolated process for robustness
25 
26 class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider {
27  public:
28  CBlendThumb() : _cRef(1), _pStream(NULL)
29  {
30  }
31 
32  virtual ~CBlendThumb()
33  {
34  if (_pStream) {
35  _pStream->Release();
36  }
37  }
38 
39  // IUnknown
40  IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
41  {
42  static const QITAB qit[] = {
43  QITABENT(CBlendThumb, IInitializeWithStream),
44  QITABENT(CBlendThumb, IThumbnailProvider),
45  {0},
46  };
47  return QISearch(this, qit, riid, ppv);
48  }
49 
50  IFACEMETHODIMP_(ULONG) AddRef()
51  {
52  return InterlockedIncrement(&_cRef);
53  }
54 
55  IFACEMETHODIMP_(ULONG) Release()
56  {
57  ULONG cRef = InterlockedDecrement(&_cRef);
58  if (!cRef) {
59  delete this;
60  }
61  return cRef;
62  }
63 
64  // IInitializeWithStream
65  IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);
66 
67  // IThumbnailProvider
68  IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha);
69 
70  private:
71  long _cRef;
72  IStream *_pStream; // provided during initialization.
73 };
74 
75 HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv)
76 {
77  CBlendThumb *pNew = new (std::nothrow) CBlendThumb();
78  HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY;
79  if (SUCCEEDED(hr)) {
80  hr = pNew->QueryInterface(riid, ppv);
81  pNew->Release();
82  }
83  return hr;
84 }
85 
86 // IInitializeWithStream
87 IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD)
88 {
89  HRESULT hr = E_UNEXPECTED; // can only be inited once
90  if (_pStream == NULL) {
91  // take a reference to the stream if we have not been inited yet
92  hr = pStream->QueryInterface(&_pStream);
93  }
94  return hr;
95 }
96 
97 #include "Wincodec.h"
98 #include <math.h>
99 #include <zlib.h>
100 const unsigned char gzip_magic[3] = {0x1f, 0x8b, 0x08};
101 
102 // IThumbnailProvider
103 IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha)
104 {
105  ULONG BytesRead;
106  HRESULT hr = S_FALSE;
107  LARGE_INTEGER SeekPos;
108 
109  // Compressed?
110  unsigned char in_magic[3];
111  _pStream->Read(&in_magic, 3, &BytesRead);
112  bool gzipped = true;
113  for (int i = 0; i < 3; i++)
114  if (in_magic[i] != gzip_magic[i]) {
115  gzipped = false;
116  break;
117  }
118 
119  if (gzipped) {
120  // Zlib inflate
121  z_stream stream;
122  stream.zalloc = Z_NULL;
123  stream.zfree = Z_NULL;
124  stream.opaque = Z_NULL;
125 
126  // Get compressed file length
127  SeekPos.QuadPart = 0;
128  _pStream->Seek(SeekPos, STREAM_SEEK_END, NULL);
129 
130  // Get compressed and uncompressed size
131  uLong source_size;
132  uLongf dest_size;
133  // SeekPos.QuadPart = -4; // last 4 bytes define size of uncompressed file
134  // ULARGE_INTEGER Tell;
135  //_pStream->Seek(SeekPos,STREAM_SEEK_END,&Tell);
136  // source_size = (uLong)Tell.QuadPart + 4; // src
137  //_pStream->Read(&dest_size,4,&BytesRead); // dest
138  dest_size = 1024 * 70; // thumbnail is currently always inside the first 65KB...if it moves or
139  // enlargens this line will have to change or go!
140  source_size = (uLong)max(SeekPos.QuadPart, dest_size); // for safety, assume no compression
141 
142  // Input
143  Bytef *src = new Bytef[source_size];
144  stream.next_in = (Bytef *)src;
145  stream.avail_in = (uInt)source_size;
146 
147  // Output
148  Bytef *dest = new Bytef[dest_size];
149  stream.next_out = (Bytef *)dest;
150  stream.avail_out = dest_size;
151 
152  // IStream to src
153  SeekPos.QuadPart = 0;
154  _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL);
155  _pStream->Read(src, source_size, &BytesRead);
156 
157  // Do the inflation
158  int err;
159  err = inflateInit2(&stream, 16); // 16 means "gzip"...nice!
160  err = inflate(&stream, Z_FINISH);
161  err = inflateEnd(&stream);
162 
163  // Replace the IStream, which is read-only
164  _pStream->Release();
165  _pStream = SHCreateMemStream(dest, dest_size);
166 
167  delete[] src;
168  delete[] dest;
169  }
170 
171  // Blender version, early out if sub 2.5
172  SeekPos.QuadPart = 9;
173  _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL);
174  char version[4];
175  version[3] = '\0';
176  _pStream->Read(&version, 3, &BytesRead);
177  if (BytesRead != 3) {
178  return E_UNEXPECTED;
179  }
180  int iVersion = atoi(version);
181  if (iVersion < 250) {
182  return S_FALSE;
183  }
184 
185  // 32 or 64 bit blend?
186  SeekPos.QuadPart = 7;
187  _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL);
188 
189  char _PointerSize;
190  _pStream->Read(&_PointerSize, 1, &BytesRead);
191 
192  int PointerSize = _PointerSize == '_' ? 4 : 8;
193  int HeaderSize = 16 + PointerSize;
194 
195  // Find and read thumbnail ("TEST") block
196  SeekPos.QuadPart = 12;
197  _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL);
198  int BlockOffset = 12;
199  while (_pStream) {
200  // Scan current block
201  char BlockName[5];
202  BlockName[4] = '\0';
203  int BlockSize = 0;
204 
205  if (_pStream->Read(BlockName, 4, &BytesRead) == S_OK &&
206  _pStream->Read((void *)&BlockSize, 4, &BytesRead) == S_OK) {
207  if (strcmp(BlockName, "TEST") != 0) {
208  SeekPos.QuadPart = BlockOffset += HeaderSize + BlockSize;
209  _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL);
210  continue;
211  }
212  }
213  else {
214  break; // eof
215  }
216 
217  // Found the block
218  SeekPos.QuadPart = BlockOffset + HeaderSize;
219  _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL);
220 
221  int width, height;
222  _pStream->Read((char *)&width, 4, &BytesRead);
223  _pStream->Read((char *)&height, 4, &BytesRead);
224  BlockSize -= 8;
225 
226  // Isolate RGBA data
227  char *pRGBA = new char[BlockSize];
228  _pStream->Read(pRGBA, BlockSize, &BytesRead);
229 
230  if (BytesRead != (ULONG)BlockSize) {
231  return E_UNEXPECTED;
232  }
233 
234  // Convert to BGRA for Windows
235  for (int i = 0; i < BlockSize; i += 4) {
236 #define RED_BYTE pRGBA[i]
237 #define BLUE_BYTE pRGBA[i + 2]
238 
239  char red = RED_BYTE;
241  BLUE_BYTE = red;
242  }
243 
244  // Flip vertically (Blender stores it upside-down)
245  unsigned int LineSize = width * 4;
246  char *FlippedImage = new char[BlockSize];
247  for (int i = 0; i < height; i++) {
248  if (0 != memcpy_s(&FlippedImage[(height - i - 1) * LineSize],
249  LineSize,
250  &pRGBA[i * LineSize],
251  LineSize)) {
252  return E_UNEXPECTED;
253  }
254  }
255  delete[] pRGBA;
256  pRGBA = FlippedImage;
257 
258  // Create image
259  *phbmp = CreateBitmap(width, height, 1, 32, pRGBA);
260  if (!*phbmp) {
261  return E_FAIL;
262  }
263  *pdwAlpha = WTSAT_ARGB; // it's actually BGRA, not sure why this works
264 
265  // Scale down if required
266  if ((unsigned)width > cx || (unsigned)height > cx) {
267  float scale = 1.0f / (max(width, height) / (float)cx);
268  LONG NewWidth = (LONG)(width * scale);
269  LONG NewHeight = (LONG)(height * scale);
270 
271 #ifdef _DEBUG
272 # if 0
273  MessageBox(0, "Attach now", "Debugging", MB_OK);
274 # endif
275 #endif
276  IWICImagingFactory *pImgFac;
277  hr = CoCreateInstance(
278  CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac));
279 
280  IWICBitmap *WICBmp;
281  hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp, 0, WICBitmapUseAlpha, &WICBmp);
282 
283  BITMAPINFO bmi = {};
284  bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
285  bmi.bmiHeader.biWidth = NewWidth;
286  bmi.bmiHeader.biHeight = -NewHeight;
287  bmi.bmiHeader.biPlanes = 1;
288  bmi.bmiHeader.biBitCount = 32;
289  bmi.bmiHeader.biCompression = BI_RGB;
290 
291  BYTE *pBits;
292  HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0);
293  hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY;
294  if (SUCCEEDED(hr)) {
295  IWICBitmapScaler *pIScaler;
296  hr = pImgFac->CreateBitmapScaler(&pIScaler);
297  hr = pIScaler->Initialize(WICBmp, NewWidth, NewHeight, WICBitmapInterpolationModeFant);
298 
299  WICRect rect = {0, 0, NewWidth, NewHeight};
300  hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits);
301 
302  if (SUCCEEDED(hr)) {
303  DeleteObject(*phbmp);
304  *phbmp = ResizedHBmp;
305  }
306  else {
307  DeleteObject(ResizedHBmp);
308  }
309 
310  pIScaler->Release();
311  }
312  WICBmp->Release();
313  pImgFac->Release();
314  }
315  else {
316  hr = S_OK;
317  }
318  break;
319  }
320  return hr;
321 }
typedef float(TangentPoint)[2]
#define BLUE_BYTE
HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv)
#define RED_BYTE
const unsigned char gzip_magic[3]
typedef UINT
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode)
IFACEMETHODIMP_(ULONG) AddRef()
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha)
IFACEMETHODIMP_(ULONG) Release()
virtual ~CBlendThumb()
static FT_Error err
Definition: freetypefont.c:52
float max