-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebp_decoder.cpp
More file actions
140 lines (123 loc) · 4.51 KB
/
webp_decoder.cpp
File metadata and controls
140 lines (123 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "webp_decoder.h"
#include <vector>
#include <wincodec.h>
#include <shlwapi.h>
#pragma comment(lib, "windowscodecs.lib")
bool is_webp_signature(const unsigned char* data, size_t size) {
if (size < 12) return false;
return data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F' &&
data[8] == 'W' && data[9] == 'E' && data[10] == 'B' && data[11] == 'P';
}
Gdiplus::Bitmap* decode_webp_via_wic(const unsigned char* data, size_t size) {
if (!data || size < 12 || !is_webp_signature(data, size)) return nullptr;
IWICImagingFactory* factory = nullptr;
IWICBitmapDecoder* decoder = nullptr;
IWICBitmapFrameDecode* frame = nullptr;
IWICFormatConverter* converter = nullptr;
IStream* stream = nullptr;
Gdiplus::Bitmap* result = nullptr;
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory, (void**)&factory);
if (FAILED(hr) || !factory) return nullptr;
// Create IStream from memory
stream = SHCreateMemStream(data, (UINT)size);
if (!stream) {
factory->Release();
return nullptr;
}
// Create decoder from stream — this is where it fails on pre-1809 Windows (no WebP codec)
hr = factory->CreateDecoderFromStream(stream, nullptr, WICDecodeMetadataCacheOnDemand, &decoder);
if (FAILED(hr) || !decoder) {
stream->Release();
factory->Release();
return nullptr;
}
// Get first frame
hr = decoder->GetFrame(0, &frame);
if (FAILED(hr) || !frame) {
decoder->Release();
stream->Release();
factory->Release();
return nullptr;
}
// Convert to 32bpp BGRA
hr = factory->CreateFormatConverter(&converter);
if (FAILED(hr) || !converter) {
frame->Release();
decoder->Release();
stream->Release();
factory->Release();
return nullptr;
}
hr = converter->Initialize(frame, GUID_WICPixelFormat32bppBGRA,
WICBitmapDitherTypeNone, nullptr, 0.0,
WICBitmapPaletteTypeMedianCut);
if (FAILED(hr)) {
converter->Release();
frame->Release();
decoder->Release();
stream->Release();
factory->Release();
return nullptr;
}
// Get dimensions
UINT width = 0, height = 0;
hr = converter->GetSize(&width, &height);
if (FAILED(hr) || width == 0 || height == 0 || width > 16384 || height > 16384) {
converter->Release();
frame->Release();
decoder->Release();
stream->Release();
factory->Release();
return nullptr;
}
// Create GDI+ Bitmap and copy pixels
try {
result = new Gdiplus::Bitmap(width, height, PixelFormat32bppRGB);
} catch (...) {
result = nullptr;
}
if (!result || result->GetLastStatus() != Gdiplus::Ok) {
delete result;
converter->Release();
frame->Release();
decoder->Release();
stream->Release();
factory->Release();
return nullptr;
}
Gdiplus::BitmapData bmpData;
Gdiplus::Rect lockRect(0, 0, width, height);
if (result->LockBits(&lockRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppRGB, &bmpData) == Gdiplus::Ok) {
// WIC outputs BGRA, GDI+ PixelFormat32bppRGB is also BGRA in memory
UINT stride = bmpData.Stride;
UINT wicStride = width * 4;
if ((UINT)stride == wicStride) {
// Strides match — single copy
hr = converter->CopyPixels(nullptr, wicStride, wicStride * height, (BYTE*)bmpData.Scan0);
} else {
// Strides differ — copy row by row
std::vector<BYTE> buffer(wicStride * height);
hr = converter->CopyPixels(nullptr, wicStride, wicStride * height, buffer.data());
if (SUCCEEDED(hr)) {
for (UINT y = 0; y < height; y++) {
memcpy((BYTE*)bmpData.Scan0 + y * stride, buffer.data() + y * wicStride, wicStride);
}
}
}
result->UnlockBits(&bmpData);
if (FAILED(hr)) {
delete result;
result = nullptr;
}
} else {
delete result;
result = nullptr;
}
converter->Release();
frame->Release();
decoder->Release();
stream->Release();
factory->Release();
return result;
}