Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Molenkamp <github@lazydodo.com>2019-08-30 17:40:08 +0300
committerRay Molenkamp <github@lazydodo.com>2019-08-30 17:40:08 +0300
commit66ec72045f8c25ec9e9710f4d70aed9fb1514df6 (patch)
tree676d5f94863224c8a0de0dec3337a73fb7707dc6 /source/blender/blendthumb/src/BlenderThumb.cpp
parent26c110f2d243eddcb5bf80c1214d00d810181f1a (diff)
Windows: Move building of blendthumb into the blender codebase.
Previously this was done in the deps builder due to the fact we needed both 32 and 64 bit versions of this dll and CMAKE does not support that in a single build folder. Now that 32 bit support has been dropped, this can be safely moved into the codebase. Reviewers: brecht Differential Revision: https://developer.blender.org/D5633
Diffstat (limited to 'source/blender/blendthumb/src/BlenderThumb.cpp')
-rw-r--r--source/blender/blendthumb/src/BlenderThumb.cpp320
1 files changed, 320 insertions, 0 deletions
diff --git a/source/blender/blendthumb/src/BlenderThumb.cpp b/source/blender/blendthumb/src/BlenderThumb.cpp
new file mode 100644
index 00000000000..553428d5b5d
--- /dev/null
+++ b/source/blender/blendthumb/src/BlenderThumb.cpp
@@ -0,0 +1,320 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <shlwapi.h>
+#include <thumbcache.h> // For IThumbnailProvider.
+#include <new>
+
+#pragma comment(lib, "shlwapi.lib")
+
+// this thumbnail provider implements IInitializeWithStream to enable being hosted
+// in an isolated process for robustness
+
+class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider
+{
+public:
+ CBlendThumb() : _cRef(1), _pStream(NULL) {}
+
+ virtual ~CBlendThumb()
+ {
+ if (_pStream)
+ {
+ _pStream->Release();
+ }
+ }
+
+ // IUnknown
+ IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
+ {
+ static const QITAB qit[] =
+ {
+ QITABENT(CBlendThumb, IInitializeWithStream),
+ QITABENT(CBlendThumb, IThumbnailProvider),
+ { 0 },
+ };
+ return QISearch(this, qit, riid, ppv);
+ }
+
+ IFACEMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&_cRef);
+ }
+
+ IFACEMETHODIMP_(ULONG) Release()
+ {
+ ULONG cRef = InterlockedDecrement(&_cRef);
+ if (!cRef)
+ {
+ delete this;
+ }
+ return cRef;
+ }
+
+ // IInitializeWithStream
+ IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);
+
+ // IThumbnailProvider
+ IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha);
+
+private:
+ long _cRef;
+ IStream *_pStream; // provided during initialization.
+};
+
+HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv)
+{
+ CBlendThumb *pNew = new (std::nothrow) CBlendThumb();
+ HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY;
+ if (SUCCEEDED(hr))
+ {
+ hr = pNew->QueryInterface(riid, ppv);
+ pNew->Release();
+ }
+ return hr;
+}
+
+// IInitializeWithStream
+IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD)
+{
+ HRESULT hr = E_UNEXPECTED; // can only be inited once
+ if (_pStream == NULL)
+ {
+ // take a reference to the stream if we have not been inited yet
+ hr = pStream->QueryInterface(&_pStream);
+ }
+ return hr;
+}
+
+#include <math.h>
+#include <zlib.h>
+#include "Wincodec.h"
+const unsigned char gzip_magic[3] = { 0x1f, 0x8b, 0x08 };
+
+// IThumbnailProvider
+IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha)
+{
+ ULONG BytesRead;
+ HRESULT hr = S_FALSE;
+ LARGE_INTEGER SeekPos;
+
+ // Compressed?
+ unsigned char in_magic[3];
+ _pStream->Read(&in_magic,3,&BytesRead);
+ bool gzipped = true;
+ for ( int i=0; i < 3; i++ )
+ if ( in_magic[i] != gzip_magic[i] )
+ {
+ gzipped = false;
+ break;
+ }
+
+ if (gzipped)
+ {
+ // Zlib inflate
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ // Get compressed file length
+ SeekPos.QuadPart = 0;
+ _pStream->Seek(SeekPos,STREAM_SEEK_END,NULL);
+
+ // Get compressed and uncompressed size
+ uLong source_size;
+ uLongf dest_size;
+ //SeekPos.QuadPart = -4; // last 4 bytes define size of uncompressed file
+ //ULARGE_INTEGER Tell;
+ //_pStream->Seek(SeekPos,STREAM_SEEK_END,&Tell);
+ //source_size = (uLong)Tell.QuadPart + 4; // src
+ //_pStream->Read(&dest_size,4,&BytesRead); // dest
+ dest_size = 1024*70; // thumbnail is currently always inside the first 65KB...if it moves or enlargens this line will have to change or go!
+ source_size = (uLong)max(SeekPos.QuadPart,dest_size); // for safety, assume no compression
+
+ // Input
+ Bytef* src = new Bytef[source_size];
+ stream.next_in = (Bytef*)src;
+ stream.avail_in = (uInt)source_size;
+
+ // Output
+ Bytef* dest = new Bytef[dest_size];
+ stream.next_out = (Bytef*)dest;
+ stream.avail_out = dest_size;
+
+ // IStream to src
+ SeekPos.QuadPart = 0;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ _pStream->Read(src,source_size,&BytesRead);
+
+ // Do the inflation
+ int err;
+ err = inflateInit2(&stream,16); // 16 means "gzip"...nice!
+ err = inflate(&stream, Z_FINISH);
+ err = inflateEnd(&stream);
+
+ // Replace the IStream, which is read-only
+ _pStream->Release();
+ _pStream = SHCreateMemStream(dest,dest_size);
+
+ delete[] src;
+ delete[] dest;
+ }
+
+ // Blender version, early out if sub 2.5
+ SeekPos.QuadPart = 9;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ char version[4];
+ version[3] = '\0';
+ _pStream->Read(&version,3,&BytesRead);
+ if ( BytesRead != 3)
+ return E_UNEXPECTED;
+ int iVersion = atoi(version);
+ if ( iVersion < 250 )
+ return S_FALSE;
+
+ // 32 or 64 bit blend?
+ SeekPos.QuadPart = 7;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+
+ char _PointerSize;
+ _pStream->Read(&_PointerSize,1,&BytesRead);
+
+ int PointerSize = _PointerSize == '_' ? 4 : 8;
+ int HeaderSize = 16 + PointerSize;
+
+ // Find and read thumbnail ("TEST") block
+ SeekPos.QuadPart = 12;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ int BlockOffset = 12;
+ while ( _pStream )
+ {
+ // Scan current block
+ char BlockName[5];
+ BlockName[4] = '\0';
+ int BlockSize = 0;
+
+ if (_pStream->Read(BlockName,4,&BytesRead) == S_OK && _pStream->Read((void*)&BlockSize,4,&BytesRead) == S_OK)
+ {
+ if ( strcmp (BlockName,"TEST") != 0 )
+ {
+ SeekPos.QuadPart = BlockOffset += HeaderSize + BlockSize;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ continue;
+ }
+ }
+ else break; // eof
+
+ // Found the block
+ SeekPos.QuadPart = BlockOffset + HeaderSize;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+
+ int width, height;
+ _pStream->Read((char*)&width,4,&BytesRead);
+ _pStream->Read((char*)&height,4,&BytesRead);
+ BlockSize -= 8;
+
+ // Isolate RGBA data
+ char* pRGBA = new char[BlockSize];
+ _pStream->Read(pRGBA,BlockSize,&BytesRead);
+
+ if (BytesRead != (ULONG)BlockSize)
+ return E_UNEXPECTED;
+
+ // Convert to BGRA for Windows
+ for (int i=0; i < BlockSize; i+=4 )
+ {
+ #define RED_BYTE pRGBA[i]
+ #define BLUE_BYTE pRGBA[i+2]
+
+ char red = RED_BYTE;
+ RED_BYTE = BLUE_BYTE;
+ BLUE_BYTE = red;
+ }
+
+ // Flip vertically (Blender stores it upside-down)
+ unsigned int LineSize = width*4;
+ char* FlippedImage = new char[BlockSize];
+ for (int i=0; i<height; i++)
+ {
+ if ( 0 != memcpy_s(&FlippedImage[ (height - i - 1)*LineSize ],LineSize,&pRGBA[ i*LineSize ],LineSize))
+ return E_UNEXPECTED;
+ }
+ delete[] pRGBA;
+ pRGBA = FlippedImage;
+
+ // Create image
+ *phbmp = CreateBitmap(width,height,1,32,pRGBA);
+ if (!*phbmp)
+ return E_FAIL;
+ *pdwAlpha = WTSAT_ARGB; // it's actually BGRA, not sure why this works
+
+ // Scale down if required
+ if ( (unsigned)width > cx || (unsigned)height > cx )
+ {
+ float scale = 1.0f / (max(width,height) / (float)cx);
+ LONG NewWidth = (LONG)(width *scale);
+ LONG NewHeight = (LONG)(height *scale);
+
+#ifdef _DEBUG
+#if 1
+ MessageBox(0,L"Attach now",L"Debugging",MB_OK);
+#endif
+#endif
+ IWICImagingFactory *pImgFac;
+ hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac));
+
+ IWICBitmap* WICBmp;
+ hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp,0,WICBitmapUseAlpha,&WICBmp);
+
+ BITMAPINFO bmi = {};
+ bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+ bmi.bmiHeader.biWidth = NewWidth;
+ bmi.bmiHeader.biHeight = -NewHeight;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ BYTE *pBits;
+ HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
+ hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY;
+ if (SUCCEEDED(hr))
+ {
+ IWICBitmapScaler* pIScaler;
+ hr = pImgFac->CreateBitmapScaler(&pIScaler);
+ hr = pIScaler->Initialize(WICBmp,NewWidth,NewHeight,WICBitmapInterpolationModeFant);
+
+ WICRect rect = {0, 0, NewWidth, NewHeight};
+ hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits);
+
+ if (SUCCEEDED(hr))
+ {
+ DeleteObject(*phbmp);
+ *phbmp = ResizedHBmp;
+ }
+ else
+ DeleteObject(ResizedHBmp);
+
+ pIScaler->Release();
+ }
+ WICBmp->Release();
+ pImgFac->Release();
+ }
+ else
+ hr = S_OK;
+ break;
+ }
+ return hr;
+}