IStream* stream;
SHCreateStreamOnFileEx(path.toStdWString().c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, false, nullptr, &stream);
if (stream) {
const auto bitmap = GetPSDThumbnail(stream);
QtWin::fromHBITMAP(bitmap).toImage();
}
#ifndef min
#define min
#endif
#ifndef max
#define max
#endif
#include <gdiplus.h>
#include <QtWinExtras>
HBITMAP GetPSDThumbnail(IStream* stream) {
HBITMAP result = NULL;
UINT signature = ReadUInt32(stream);
USHORT version = ReadUInt16(stream);
if (signature != 0x38425053 || version != 1)
return NULL;
Seek(stream, 6, STREAM_SEEK_CUR);
USHORT channels = ReadUInt16(stream);
int height = ReadUInt32(stream);
int width = ReadUInt32(stream);
USHORT bitsPerChannel = ReadUInt16(stream);
USHORT colorMode = ReadUInt16(stream);
UINT length = ReadUInt32(stream);
if (length > 0) {
Seek(stream, length, STREAM_SEEK_CUR);
}
UINT resourcesLength = ReadUInt32(stream);
UINT reasourceOffset = Tell(stream);
UINT thumbnailOffset = 0;
UINT resourceAdvanced = 0;
while (resourcesLength > resourceAdvanced) {
Seek(stream, 4, STREAM_SEEK_CUR);
USHORT id = ReadUInt16(stream);
BYTE strl = ReadByte(stream);
Seek(stream, strl % 2 == 0 ? strl + 1 : strl, STREAM_SEEK_CUR);
length = ReadUInt32(stream);
if (id == 1036) {
thumbnailOffset = Tell(stream);
break;
}
Seek(stream, length % 2 ? length + 1 : length, STREAM_SEEK_CUR);
resourceAdvanced += 6 + 1 + (strl % 2 == 0 ? strl + 1 : strl) + 4 + (length % 2 ? length + 1 : length);
}
if (colorMode == 3 && ((width <= 256 && height <= 256) || !thumbnailOffset)) {
Seek(stream, reasourceOffset + resourcesLength, STREAM_SEEK_SET);
UINT layerAndMaskInfoLength = ReadUInt32(stream);
UINT layerAndMastInfoOffset = Tell(stream);
UINT layerInfoLength = ReadUInt32(stream);
SHORT layerCount = ReadInt16(stream);
bool globalAlpha = layerCount < 0;
Seek(stream, layerAndMastInfoOffset + layerAndMaskInfoLength, STREAM_SEEK_SET);
USHORT compression = ReadUInt16(stream);
int channelCount = 3;
int offsets[16] = { 2, 1, 0 };
if (channels && channels > 3) {
for (int i = 3; i < channels; i++) {
offsets[i] = i;
channelCount++;
}
} else if (globalAlpha) {
offsets[3] = 3;
channelCount++;
}
if (compression == 1 || compression == 0) {
int dataLength = width * height * 4;
BYTE* data = new BYTE[dataLength];
for (int i = 0; i < dataLength; i++) {
data[i] = 0xff;
}
if (compression == 1) {
USHORT* lengths = new USHORT[channelCount * height];
int step = 4;
int maxLength = 0;
for (int o = 0, li = 0; o < channelCount; o++) {
for (int y = 0; y < height; y++, li++) {
lengths[li] = ReadUInt16(stream);
maxLength = maxLength < lengths[li] ? lengths[li] : maxLength;
}
}
BYTE* buffer = new BYTE[maxLength];
for (int c = 0, li = 0; c < channelCount; c++) {
int offset = offsets[c];
int extra = c > 3 || offset > 3;
if (extra) {
for (int y = 0; y < height; y++, li++) {
Seek(stream, lengths[li], STREAM_SEEK_CUR);
}
}
else {
for (int y = 0, p = offset; y < height; y++, li++) {
int length = lengths[li];
ReadData(stream, buffer, length);
for (int i = 0; i < length; i++) {
BYTE header = buffer[i];
if (header >= 128) {
BYTE value = buffer[++i];
header = (256 - header);
for (int j = 0; j <= header; j++) {
data[p] = value;
p += step;
}
}
else {
for (int j = 0; j <= header; j++) {
data[p] = buffer[++i];
p += step;
}
}
}
}
}
}
delete lengths;
delete buffer;
} else {
int pixels = width * height;
BYTE* buffer = new BYTE[pixels];
if (channelCount > 4) channelCount = 4;
for (int c = 0; c < channelCount; c++) {
int o = offsets[c];
ReadData(stream, buffer, pixels);
for (int i = 0; i < pixels; i++) {
data[i * 4 + o] = buffer[i];
}
}
delete buffer;
}
bool allWhite = true;
for (int i = 0; i < dataLength; i++) {
if (data[i] != 0xff) {
allWhite = false;
break;
}
}
if (!allWhite) {
if (width > 256 || height > 256) {
HBITMAP fullBitmap = CreateBitmap(width, height, 1, 32, data);
int thumbWidth, thumbHeight;
if (width > height) {
thumbWidth = 256;
thumbHeight = height * thumbWidth / width;
}
else {
thumbHeight = 256;
thumbWidth = width * thumbHeight / height;
}
BLENDFUNCTION fnc;
fnc.BlendOp = AC_SRC_OVER;
fnc.BlendFlags = 0;
fnc.SourceConstantAlpha = 0xFF;
fnc.AlphaFormat = AC_SRC_ALPHA;
HDC dc = GetDC(NULL);
HDC srcDC = CreateCompatibleDC(dc);
HDC memDC = CreateCompatibleDC(dc);
result = CreateCompatibleBitmap(dc, thumbWidth, thumbHeight);
SelectObject(memDC, result);
SelectObject(srcDC, fullBitmap);
RECT rect = {};
rect.right = thumbWidth;
rect.bottom = thumbHeight;
FillRect(memDC, &rect, (HBRUSH)GetStockObject(NULL_BRUSH));
AlphaBlend(memDC, 0, 0, thumbWidth, thumbHeight, srcDC, 0, 0, width, height, fnc);
DeleteObject(fullBitmap);
DeleteDC(srcDC);
DeleteDC(memDC);
ReleaseDC(NULL, dc);
}
else {
result = CreateBitmap(width, height, 1, 32, data);
}
}
delete data;
}
}
if (!result && thumbnailOffset) {
Seek(stream, thumbnailOffset, STREAM_SEEK_SET);
ULONG_PTR token;
GdiplusStartupInput input;
if (Ok == GdiplusStartup(&token, &input, NULL)) {
Seek(stream, 4 + 4 + 4 + 4 + 4 + 4 + 2 + 2, STREAM_SEEK_CUR);
BYTE *data = new BYTE[length];
ReadData(stream, data, length);
IStream *memory = SHCreateMemStream(data, length);
if (memory) {
Seek(memory, 0, STREAM_SEEK_SET);
Bitmap *bitmap = Bitmap::FromStream(memory);
if (bitmap) {
Color color(0, 255, 255, 255);
bitmap->GetHBITMAP(color, &result);
delete bitmap;
}
memory->Release();
}
}
GdiplusShutdown(token);
}
return result;
}