68502f602f0258cef24e561a31671515faa1bb0b
[reactos.git] / reactos / dll / win32 / windowscodecs / scaler.c
1 /*
2 * Copyright 2010 Vincent Povirk for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #define WIN32_NO_STATUS
20 #define _INC_WINDOWS
21 #define COM_NO_WINDOWS_H
22
23 #include <config.h>
24
25 #include <stdarg.h>
26
27 #define COBJMACROS
28
29 #include <windef.h>
30 #include <winbase.h>
31 #include <objbase.h>
32 #include <wincodec.h>
33
34 #include "wincodecs_private.h"
35
36 #include <wine/debug.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
39
40 typedef struct BitmapScaler {
41 IWICBitmapScaler IWICBitmapScaler_iface;
42 LONG ref;
43 IWICBitmapSource *source;
44 UINT width, height;
45 UINT src_width, src_height;
46 WICBitmapInterpolationMode mode;
47 UINT bpp;
48 void (*fn_get_required_source_rect)(struct BitmapScaler*,UINT,UINT,WICRect*);
49 void (*fn_copy_scanline)(struct BitmapScaler*,UINT,UINT,UINT,BYTE**,UINT,UINT,BYTE*);
50 CRITICAL_SECTION lock; /* must be held when initialized */
51 } BitmapScaler;
52
53 static inline BitmapScaler *impl_from_IWICBitmapScaler(IWICBitmapScaler *iface)
54 {
55 return CONTAINING_RECORD(iface, BitmapScaler, IWICBitmapScaler_iface);
56 }
57
58 static HRESULT WINAPI BitmapScaler_QueryInterface(IWICBitmapScaler *iface, REFIID iid,
59 void **ppv)
60 {
61 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
62 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
63
64 if (!ppv) return E_INVALIDARG;
65
66 if (IsEqualIID(&IID_IUnknown, iid) ||
67 IsEqualIID(&IID_IWICBitmapSource, iid) ||
68 IsEqualIID(&IID_IWICBitmapScaler, iid))
69 {
70 *ppv = &This->IWICBitmapScaler_iface;
71 }
72 else
73 {
74 *ppv = NULL;
75 return E_NOINTERFACE;
76 }
77
78 IUnknown_AddRef((IUnknown*)*ppv);
79 return S_OK;
80 }
81
82 static ULONG WINAPI BitmapScaler_AddRef(IWICBitmapScaler *iface)
83 {
84 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
85 ULONG ref = InterlockedIncrement(&This->ref);
86
87 TRACE("(%p) refcount=%u\n", iface, ref);
88
89 return ref;
90 }
91
92 static ULONG WINAPI BitmapScaler_Release(IWICBitmapScaler *iface)
93 {
94 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
95 ULONG ref = InterlockedDecrement(&This->ref);
96
97 TRACE("(%p) refcount=%u\n", iface, ref);
98
99 if (ref == 0)
100 {
101 This->lock.DebugInfo->Spare[0] = 0;
102 DeleteCriticalSection(&This->lock);
103 if (This->source) IWICBitmapSource_Release(This->source);
104 HeapFree(GetProcessHeap(), 0, This);
105 }
106
107 return ref;
108 }
109
110 static HRESULT WINAPI BitmapScaler_GetSize(IWICBitmapScaler *iface,
111 UINT *puiWidth, UINT *puiHeight)
112 {
113 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
114 TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
115
116 if (!puiWidth || !puiHeight)
117 return E_INVALIDARG;
118
119 if (!This->source)
120 return WINCODEC_ERR_WRONGSTATE;
121
122 *puiWidth = This->width;
123 *puiHeight = This->height;
124
125 return S_OK;
126 }
127
128 static HRESULT WINAPI BitmapScaler_GetPixelFormat(IWICBitmapScaler *iface,
129 WICPixelFormatGUID *pPixelFormat)
130 {
131 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
132 TRACE("(%p,%p)\n", iface, pPixelFormat);
133
134 if (!pPixelFormat)
135 return E_INVALIDARG;
136
137 if (!This->source)
138 return WINCODEC_ERR_WRONGSTATE;
139
140 return IWICBitmapSource_GetPixelFormat(This->source, pPixelFormat);
141 }
142
143 static HRESULT WINAPI BitmapScaler_GetResolution(IWICBitmapScaler *iface,
144 double *pDpiX, double *pDpiY)
145 {
146 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
147 TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
148
149 if (!pDpiX || !pDpiY)
150 return E_INVALIDARG;
151
152 if (!This->source)
153 return WINCODEC_ERR_WRONGSTATE;
154
155 return IWICBitmapSource_GetResolution(This->source, pDpiX, pDpiY);
156 }
157
158 static HRESULT WINAPI BitmapScaler_CopyPalette(IWICBitmapScaler *iface,
159 IWICPalette *pIPalette)
160 {
161 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
162 TRACE("(%p,%p)\n", iface, pIPalette);
163
164 if (!pIPalette)
165 return E_INVALIDARG;
166
167 if (!This->source)
168 return WINCODEC_ERR_WRONGSTATE;
169
170 return IWICBitmapSource_CopyPalette(This->source, pIPalette);
171 }
172
173 static void NearestNeighbor_GetRequiredSourceRect(BitmapScaler *This,
174 UINT x, UINT y, WICRect *src_rect)
175 {
176 src_rect->X = x * This->src_width / This->width;
177 src_rect->Y = y * This->src_height / This->height;
178 src_rect->Width = src_rect->Height = 1;
179 }
180
181 static void NearestNeighbor_CopyScanline(BitmapScaler *This,
182 UINT dst_x, UINT dst_y, UINT dst_width,
183 BYTE **src_data, UINT src_data_x, UINT src_data_y, BYTE *pbBuffer)
184 {
185 UINT i;
186 UINT bytesperpixel = This->bpp/8;
187 UINT src_x, src_y;
188
189 src_y = dst_y * This->src_height / This->height - src_data_y;
190
191 for (i=0; i<dst_width; i++)
192 {
193 src_x = (dst_x + i) * This->src_width / This->width - src_data_x;
194 memcpy(pbBuffer + bytesperpixel * i, src_data[src_y] + bytesperpixel * src_x, bytesperpixel);
195 }
196 }
197
198 static HRESULT WINAPI BitmapScaler_CopyPixels(IWICBitmapScaler *iface,
199 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
200 {
201 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
202 HRESULT hr;
203 WICRect dest_rect;
204 WICRect src_rect_ul, src_rect_br, src_rect;
205 BYTE **src_rows;
206 BYTE *src_bits;
207 ULONG bytesperrow;
208 ULONG src_bytesperrow;
209 ULONG buffer_size;
210 UINT y;
211
212 TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
213
214 EnterCriticalSection(&This->lock);
215
216 if (!This->source)
217 {
218 hr = WINCODEC_ERR_WRONGSTATE;
219 goto end;
220 }
221
222 if (prc)
223 dest_rect = *prc;
224 else
225 {
226 dest_rect.X = dest_rect.Y = 0;
227 dest_rect.Width = This->width;
228 dest_rect.Height = This->height;
229 }
230
231 if (dest_rect.X < 0 || dest_rect.Y < 0 ||
232 dest_rect.X+dest_rect.Width > This->width|| dest_rect.Y+dest_rect.Height > This->height)
233 {
234 hr = E_INVALIDARG;
235 goto end;
236 }
237
238 bytesperrow = ((This->bpp * dest_rect.Width)+7)/8;
239
240 if (cbStride < bytesperrow)
241 {
242 hr = E_INVALIDARG;
243 goto end;
244 }
245
246 if ((cbStride * dest_rect.Height) > cbBufferSize)
247 {
248 hr = E_INVALIDARG;
249 goto end;
250 }
251
252 /* MSDN recommends calling CopyPixels once for each scanline from top to
253 * bottom, and claims codecs optimize for this. Ideally, when called in this
254 * way, we should avoid requesting a scanline from the source more than
255 * once, by saving the data that will be useful for the next scanline after
256 * the call returns. The GetRequiredSourceRect/CopyScanline functions are
257 * designed to make it possible to do this in a generic way, but for now we
258 * just grab all the data we need in each call. */
259
260 This->fn_get_required_source_rect(This, dest_rect.X, dest_rect.Y, &src_rect_ul);
261 This->fn_get_required_source_rect(This, dest_rect.X+dest_rect.Width-1,
262 dest_rect.Y+dest_rect.Height-1, &src_rect_br);
263
264 src_rect.X = src_rect_ul.X;
265 src_rect.Y = src_rect_ul.Y;
266 src_rect.Width = src_rect_br.Width + src_rect_br.X - src_rect_ul.X;
267 src_rect.Height = src_rect_br.Height + src_rect_br.Y - src_rect_ul.Y;
268
269 src_bytesperrow = (src_rect.Width * This->bpp + 7)/8;
270 buffer_size = src_bytesperrow * src_rect.Height;
271
272 src_rows = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE*) * src_rect.Height);
273 src_bits = HeapAlloc(GetProcessHeap(), 0, buffer_size);
274
275 if (!src_rows || !src_bits)
276 {
277 HeapFree(GetProcessHeap(), 0, src_rows);
278 HeapFree(GetProcessHeap(), 0, src_bits);
279 hr = E_OUTOFMEMORY;
280 goto end;
281 }
282
283 for (y=0; y<src_rect.Height; y++)
284 src_rows[y] = src_bits + y * src_bytesperrow;
285
286 hr = IWICBitmapSource_CopyPixels(This->source, &src_rect, src_bytesperrow,
287 buffer_size, src_bits);
288
289 if (SUCCEEDED(hr))
290 {
291 for (y=0; y < dest_rect.Height; y++)
292 {
293 This->fn_copy_scanline(This, dest_rect.X, dest_rect.Y+y, dest_rect.Width,
294 src_rows, src_rect.X, src_rect.Y, pbBuffer + cbStride * y);
295 }
296 }
297
298 HeapFree(GetProcessHeap(), 0, src_rows);
299 HeapFree(GetProcessHeap(), 0, src_bits);
300
301 end:
302 LeaveCriticalSection(&This->lock);
303
304 return hr;
305 }
306
307 static HRESULT WINAPI BitmapScaler_Initialize(IWICBitmapScaler *iface,
308 IWICBitmapSource *pISource, UINT uiWidth, UINT uiHeight,
309 WICBitmapInterpolationMode mode)
310 {
311 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
312 HRESULT hr;
313 GUID src_pixelformat;
314
315 TRACE("(%p,%p,%u,%u,%u)\n", iface, pISource, uiWidth, uiHeight, mode);
316
317 EnterCriticalSection(&This->lock);
318
319 if (This->source)
320 {
321 hr = WINCODEC_ERR_WRONGSTATE;
322 goto end;
323 }
324
325 This->width = uiWidth;
326 This->height = uiHeight;
327 This->mode = mode;
328
329 hr = IWICBitmapSource_GetSize(pISource, &This->src_width, &This->src_height);
330
331 if (SUCCEEDED(hr))
332 hr = IWICBitmapSource_GetPixelFormat(pISource, &src_pixelformat);
333
334 if (SUCCEEDED(hr))
335 {
336 hr = get_pixelformat_bpp(&src_pixelformat, &This->bpp);
337 }
338
339 if (SUCCEEDED(hr))
340 {
341 switch (mode)
342 {
343 default:
344 FIXME("unsupported mode %i\n", mode);
345 /* fall-through */
346 case WICBitmapInterpolationModeNearestNeighbor:
347 if ((This->bpp % 8) == 0)
348 {
349 IWICBitmapSource_AddRef(pISource);
350 This->source = pISource;
351 }
352 else
353 {
354 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA,
355 pISource, &This->source);
356 This->bpp = 32;
357 }
358 This->fn_get_required_source_rect = NearestNeighbor_GetRequiredSourceRect;
359 This->fn_copy_scanline = NearestNeighbor_CopyScanline;
360 break;
361 }
362 }
363
364 end:
365 LeaveCriticalSection(&This->lock);
366
367 return hr;
368 }
369
370 static const IWICBitmapScalerVtbl BitmapScaler_Vtbl = {
371 BitmapScaler_QueryInterface,
372 BitmapScaler_AddRef,
373 BitmapScaler_Release,
374 BitmapScaler_GetSize,
375 BitmapScaler_GetPixelFormat,
376 BitmapScaler_GetResolution,
377 BitmapScaler_CopyPalette,
378 BitmapScaler_CopyPixels,
379 BitmapScaler_Initialize
380 };
381
382 HRESULT BitmapScaler_Create(IWICBitmapScaler **scaler)
383 {
384 BitmapScaler *This;
385
386 This = HeapAlloc(GetProcessHeap(), 0, sizeof(BitmapScaler));
387 if (!This) return E_OUTOFMEMORY;
388
389 This->IWICBitmapScaler_iface.lpVtbl = &BitmapScaler_Vtbl;
390 This->ref = 1;
391 This->source = NULL;
392 This->width = 0;
393 This->height = 0;
394 This->src_width = 0;
395 This->src_height = 0;
396 This->mode = 0;
397 This->bpp = 0;
398 InitializeCriticalSection(&This->lock);
399 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BitmapScaler.lock");
400
401 *scaler = &This->IWICBitmapScaler_iface;
402
403 return S_OK;
404 }