d19e219feab76a5f215e1a144457a2eb0f9fb117
[reactos.git] / 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 #include "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
28
29 #include "wincodecs_private.h"
30
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
34
35 typedef struct BitmapScaler {
36 IWICBitmapScaler IWICBitmapScaler_iface;
37 LONG ref;
38 IMILBitmapScaler IMILBitmapScaler_iface;
39 IWICBitmapSource *source;
40 UINT width, height;
41 UINT src_width, src_height;
42 WICBitmapInterpolationMode mode;
43 UINT bpp;
44 void (*fn_get_required_source_rect)(struct BitmapScaler*,UINT,UINT,WICRect*);
45 void (*fn_copy_scanline)(struct BitmapScaler*,UINT,UINT,UINT,BYTE**,UINT,UINT,BYTE*);
46 CRITICAL_SECTION lock; /* must be held when initialized */
47 } BitmapScaler;
48
49 static inline BitmapScaler *impl_from_IWICBitmapScaler(IWICBitmapScaler *iface)
50 {
51 return CONTAINING_RECORD(iface, BitmapScaler, IWICBitmapScaler_iface);
52 }
53
54 static inline BitmapScaler *impl_from_IMILBitmapScaler(IMILBitmapScaler *iface)
55 {
56 return CONTAINING_RECORD(iface, BitmapScaler, IMILBitmapScaler_iface);
57 }
58
59 static HRESULT WINAPI BitmapScaler_QueryInterface(IWICBitmapScaler *iface, REFIID iid,
60 void **ppv)
61 {
62 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
63 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
64
65 if (!ppv) return E_INVALIDARG;
66
67 if (IsEqualIID(&IID_IUnknown, iid) ||
68 IsEqualIID(&IID_IWICBitmapSource, iid) ||
69 IsEqualIID(&IID_IWICBitmapScaler, iid))
70 {
71 *ppv = &This->IWICBitmapScaler_iface;
72 }
73 else if (IsEqualIID(&IID_IMILBitmapScaler, iid))
74 {
75 *ppv = &This->IMILBitmapScaler_iface;
76 }
77 else
78 {
79 FIXME("unknown interface %s\n", debugstr_guid(iid));
80 *ppv = NULL;
81 return E_NOINTERFACE;
82 }
83
84 IUnknown_AddRef((IUnknown*)*ppv);
85 return S_OK;
86 }
87
88 static ULONG WINAPI BitmapScaler_AddRef(IWICBitmapScaler *iface)
89 {
90 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
91 ULONG ref = InterlockedIncrement(&This->ref);
92
93 TRACE("(%p) refcount=%u\n", iface, ref);
94
95 return ref;
96 }
97
98 static ULONG WINAPI BitmapScaler_Release(IWICBitmapScaler *iface)
99 {
100 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
101 ULONG ref = InterlockedDecrement(&This->ref);
102
103 TRACE("(%p) refcount=%u\n", iface, ref);
104
105 if (ref == 0)
106 {
107 This->lock.DebugInfo->Spare[0] = 0;
108 DeleteCriticalSection(&This->lock);
109 if (This->source) IWICBitmapSource_Release(This->source);
110 HeapFree(GetProcessHeap(), 0, This);
111 }
112
113 return ref;
114 }
115
116 static HRESULT WINAPI BitmapScaler_GetSize(IWICBitmapScaler *iface,
117 UINT *puiWidth, UINT *puiHeight)
118 {
119 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
120 TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
121
122 if (!This->source)
123 return WINCODEC_ERR_NOTINITIALIZED;
124
125 if (!puiWidth || !puiHeight)
126 return E_INVALIDARG;
127
128 *puiWidth = This->width;
129 *puiHeight = This->height;
130
131 return S_OK;
132 }
133
134 static HRESULT WINAPI BitmapScaler_GetPixelFormat(IWICBitmapScaler *iface,
135 WICPixelFormatGUID *pPixelFormat)
136 {
137 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
138 TRACE("(%p,%p)\n", iface, pPixelFormat);
139
140 if (!pPixelFormat)
141 return E_INVALIDARG;
142
143 if (!This->source)
144 {
145 memcpy(pPixelFormat, &GUID_WICPixelFormatDontCare, sizeof(*pPixelFormat));
146 return S_OK;
147 }
148
149 return IWICBitmapSource_GetPixelFormat(This->source, pPixelFormat);
150 }
151
152 static HRESULT WINAPI BitmapScaler_GetResolution(IWICBitmapScaler *iface,
153 double *pDpiX, double *pDpiY)
154 {
155 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
156 TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
157
158 if (!This->source)
159 return WINCODEC_ERR_NOTINITIALIZED;
160
161 if (!pDpiX || !pDpiY)
162 return E_INVALIDARG;
163
164 return IWICBitmapSource_GetResolution(This->source, pDpiX, pDpiY);
165 }
166
167 static HRESULT WINAPI BitmapScaler_CopyPalette(IWICBitmapScaler *iface,
168 IWICPalette *pIPalette)
169 {
170 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
171 TRACE("(%p,%p)\n", iface, pIPalette);
172
173 if (!pIPalette)
174 return E_INVALIDARG;
175
176 if (!This->source)
177 return WINCODEC_ERR_PALETTEUNAVAILABLE;
178
179 return IWICBitmapSource_CopyPalette(This->source, pIPalette);
180 }
181
182 static void NearestNeighbor_GetRequiredSourceRect(BitmapScaler *This,
183 UINT x, UINT y, WICRect *src_rect)
184 {
185 src_rect->X = x * This->src_width / This->width;
186 src_rect->Y = y * This->src_height / This->height;
187 src_rect->Width = src_rect->Height = 1;
188 }
189
190 static void NearestNeighbor_CopyScanline(BitmapScaler *This,
191 UINT dst_x, UINT dst_y, UINT dst_width,
192 BYTE **src_data, UINT src_data_x, UINT src_data_y, BYTE *pbBuffer)
193 {
194 UINT i;
195 UINT bytesperpixel = This->bpp/8;
196 UINT src_x, src_y;
197
198 src_y = dst_y * This->src_height / This->height - src_data_y;
199
200 for (i=0; i<dst_width; i++)
201 {
202 src_x = (dst_x + i) * This->src_width / This->width - src_data_x;
203 memcpy(pbBuffer + bytesperpixel * i, src_data[src_y] + bytesperpixel * src_x, bytesperpixel);
204 }
205 }
206
207 static HRESULT WINAPI BitmapScaler_CopyPixels(IWICBitmapScaler *iface,
208 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
209 {
210 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
211 HRESULT hr;
212 WICRect dest_rect;
213 WICRect src_rect_ul, src_rect_br, src_rect;
214 BYTE **src_rows;
215 BYTE *src_bits;
216 ULONG bytesperrow;
217 ULONG src_bytesperrow;
218 ULONG buffer_size;
219 UINT y;
220
221 TRACE("(%p,%s,%u,%u,%p)\n", iface, debug_wic_rect(prc), cbStride, cbBufferSize, pbBuffer);
222
223 EnterCriticalSection(&This->lock);
224
225 if (!This->source)
226 {
227 hr = WINCODEC_ERR_WRONGSTATE;
228 goto end;
229 }
230
231 if (prc)
232 dest_rect = *prc;
233 else
234 {
235 dest_rect.X = dest_rect.Y = 0;
236 dest_rect.Width = This->width;
237 dest_rect.Height = This->height;
238 }
239
240 if (dest_rect.X < 0 || dest_rect.Y < 0 ||
241 dest_rect.X+dest_rect.Width > This->width|| dest_rect.Y+dest_rect.Height > This->height)
242 {
243 hr = E_INVALIDARG;
244 goto end;
245 }
246
247 bytesperrow = ((This->bpp * dest_rect.Width)+7)/8;
248
249 if (cbStride < bytesperrow)
250 {
251 hr = E_INVALIDARG;
252 goto end;
253 }
254
255 if ((cbStride * dest_rect.Height) > cbBufferSize)
256 {
257 hr = E_INVALIDARG;
258 goto end;
259 }
260
261 /* MSDN recommends calling CopyPixels once for each scanline from top to
262 * bottom, and claims codecs optimize for this. Ideally, when called in this
263 * way, we should avoid requesting a scanline from the source more than
264 * once, by saving the data that will be useful for the next scanline after
265 * the call returns. The GetRequiredSourceRect/CopyScanline functions are
266 * designed to make it possible to do this in a generic way, but for now we
267 * just grab all the data we need in each call. */
268
269 This->fn_get_required_source_rect(This, dest_rect.X, dest_rect.Y, &src_rect_ul);
270 This->fn_get_required_source_rect(This, dest_rect.X+dest_rect.Width-1,
271 dest_rect.Y+dest_rect.Height-1, &src_rect_br);
272
273 src_rect.X = src_rect_ul.X;
274 src_rect.Y = src_rect_ul.Y;
275 src_rect.Width = src_rect_br.Width + src_rect_br.X - src_rect_ul.X;
276 src_rect.Height = src_rect_br.Height + src_rect_br.Y - src_rect_ul.Y;
277
278 src_bytesperrow = (src_rect.Width * This->bpp + 7)/8;
279 buffer_size = src_bytesperrow * src_rect.Height;
280
281 src_rows = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE*) * src_rect.Height);
282 src_bits = HeapAlloc(GetProcessHeap(), 0, buffer_size);
283
284 if (!src_rows || !src_bits)
285 {
286 HeapFree(GetProcessHeap(), 0, src_rows);
287 HeapFree(GetProcessHeap(), 0, src_bits);
288 hr = E_OUTOFMEMORY;
289 goto end;
290 }
291
292 for (y=0; y<src_rect.Height; y++)
293 src_rows[y] = src_bits + y * src_bytesperrow;
294
295 hr = IWICBitmapSource_CopyPixels(This->source, &src_rect, src_bytesperrow,
296 buffer_size, src_bits);
297
298 if (SUCCEEDED(hr))
299 {
300 for (y=0; y < dest_rect.Height; y++)
301 {
302 This->fn_copy_scanline(This, dest_rect.X, dest_rect.Y+y, dest_rect.Width,
303 src_rows, src_rect.X, src_rect.Y, pbBuffer + cbStride * y);
304 }
305 }
306
307 HeapFree(GetProcessHeap(), 0, src_rows);
308 HeapFree(GetProcessHeap(), 0, src_bits);
309
310 end:
311 LeaveCriticalSection(&This->lock);
312
313 return hr;
314 }
315
316 static HRESULT WINAPI BitmapScaler_Initialize(IWICBitmapScaler *iface,
317 IWICBitmapSource *pISource, UINT uiWidth, UINT uiHeight,
318 WICBitmapInterpolationMode mode)
319 {
320 BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
321 HRESULT hr;
322 GUID src_pixelformat;
323
324 TRACE("(%p,%p,%u,%u,%u)\n", iface, pISource, uiWidth, uiHeight, mode);
325
326 if (!pISource || !uiWidth || !uiHeight)
327 return E_INVALIDARG;
328
329 EnterCriticalSection(&This->lock);
330
331 if (This->source)
332 {
333 hr = WINCODEC_ERR_WRONGSTATE;
334 goto end;
335 }
336
337 This->width = uiWidth;
338 This->height = uiHeight;
339 This->mode = mode;
340
341 hr = IWICBitmapSource_GetSize(pISource, &This->src_width, &This->src_height);
342
343 if (SUCCEEDED(hr))
344 hr = IWICBitmapSource_GetPixelFormat(pISource, &src_pixelformat);
345
346 if (SUCCEEDED(hr))
347 {
348 hr = get_pixelformat_bpp(&src_pixelformat, &This->bpp);
349 }
350
351 if (SUCCEEDED(hr))
352 {
353 switch (mode)
354 {
355 default:
356 FIXME("unsupported mode %i\n", mode);
357 /* fall-through */
358 case WICBitmapInterpolationModeNearestNeighbor:
359 if ((This->bpp % 8) == 0)
360 {
361 IWICBitmapSource_AddRef(pISource);
362 This->source = pISource;
363 }
364 else
365 {
366 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA,
367 pISource, &This->source);
368 This->bpp = 32;
369 }
370 This->fn_get_required_source_rect = NearestNeighbor_GetRequiredSourceRect;
371 This->fn_copy_scanline = NearestNeighbor_CopyScanline;
372 break;
373 }
374 }
375
376 end:
377 LeaveCriticalSection(&This->lock);
378
379 return hr;
380 }
381
382 static const IWICBitmapScalerVtbl BitmapScaler_Vtbl = {
383 BitmapScaler_QueryInterface,
384 BitmapScaler_AddRef,
385 BitmapScaler_Release,
386 BitmapScaler_GetSize,
387 BitmapScaler_GetPixelFormat,
388 BitmapScaler_GetResolution,
389 BitmapScaler_CopyPalette,
390 BitmapScaler_CopyPixels,
391 BitmapScaler_Initialize
392 };
393
394 static HRESULT WINAPI IMILBitmapScaler_QueryInterface(IMILBitmapScaler *iface, REFIID iid,
395 void **ppv)
396 {
397 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
398
399 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
400
401 if (!ppv) return E_INVALIDARG;
402
403 if (IsEqualIID(&IID_IUnknown, iid) ||
404 IsEqualIID(&IID_IMILBitmapScaler, iid) ||
405 IsEqualIID(&IID_IMILBitmapSource, iid))
406 {
407 IUnknown_AddRef(&This->IMILBitmapScaler_iface);
408 *ppv = &This->IMILBitmapScaler_iface;
409 return S_OK;
410 }
411 else if (IsEqualIID(&IID_IWICBitmapScaler, iid) ||
412 IsEqualIID(&IID_IWICBitmapSource, iid))
413 {
414 IUnknown_AddRef(&This->IWICBitmapScaler_iface);
415 *ppv = &This->IWICBitmapScaler_iface;
416 return S_OK;
417 }
418
419 FIXME("unknown interface %s\n", debugstr_guid(iid));
420 *ppv = NULL;
421 return E_NOINTERFACE;
422 }
423
424 static ULONG WINAPI IMILBitmapScaler_AddRef(IMILBitmapScaler *iface)
425 {
426 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
427 return IWICBitmapScaler_AddRef(&This->IWICBitmapScaler_iface);
428 }
429
430 static ULONG WINAPI IMILBitmapScaler_Release(IMILBitmapScaler *iface)
431 {
432 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
433 return IWICBitmapScaler_Release(&This->IWICBitmapScaler_iface);
434 }
435
436 static HRESULT WINAPI IMILBitmapScaler_GetSize(IMILBitmapScaler *iface,
437 UINT *width, UINT *height)
438 {
439 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
440
441 TRACE("(%p,%p,%p)\n", iface, width, height);
442
443 if (!This->source)
444 return WINCODEC_ERR_NOTINITIALIZED;
445
446 return IWICBitmapScaler_GetSize(&This->IWICBitmapScaler_iface, width, height);
447 }
448
449 static HRESULT WINAPI IMILBitmapScaler_GetPixelFormat(IMILBitmapScaler *iface,
450 int *format)
451 {
452 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
453 IMILBitmapSource *source;
454 HRESULT hr;
455
456 TRACE("(%p,%p)\n", iface, format);
457
458 if (!format) return E_INVALIDARG;
459
460 if (!This->source)
461 return WINCODEC_ERR_NOTINITIALIZED;
462
463 hr = IWICBitmapSource_QueryInterface(This->source, &IID_IMILBitmapSource, (void **)&source);
464 if (hr == S_OK)
465 {
466 hr = source->lpVtbl->GetPixelFormat(source, format);
467 source->lpVtbl->Release(source);
468 }
469 return hr;
470 }
471
472 static HRESULT WINAPI IMILBitmapScaler_GetResolution(IMILBitmapScaler *iface,
473 double *dpix, double *dpiy)
474 {
475 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
476
477 TRACE("(%p,%p,%p)\n", iface, dpix, dpiy);
478
479 if (!This->source)
480 return WINCODEC_ERR_NOTINITIALIZED;
481
482 return IWICBitmapScaler_GetResolution(&This->IWICBitmapScaler_iface, dpix, dpiy);
483 }
484
485 static HRESULT WINAPI IMILBitmapScaler_CopyPalette(IMILBitmapScaler *iface,
486 IWICPalette *palette)
487 {
488 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
489
490 TRACE("(%p,%p)\n", iface, palette);
491
492 if (!This->source)
493 return WINCODEC_ERR_NOTINITIALIZED;
494
495 return IWICBitmapScaler_CopyPalette(&This->IWICBitmapScaler_iface, palette);
496 }
497
498 static HRESULT WINAPI IMILBitmapScaler_CopyPixels(IMILBitmapScaler *iface,
499 const WICRect *rc, UINT stride, UINT size, BYTE *buffer)
500 {
501 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
502
503 TRACE("(%p,%p,%u,%u,%p)\n", iface, rc, stride, size, buffer);
504
505 if (!This->source)
506 return WINCODEC_ERR_NOTINITIALIZED;
507
508 return IWICBitmapScaler_CopyPixels(&This->IWICBitmapScaler_iface, rc, stride, size, buffer);
509 }
510
511 static HRESULT WINAPI IMILBitmapScaler_unknown1(IMILBitmapScaler *iface, void **ppv)
512 {
513 TRACE("(%p,%p)\n", iface, ppv);
514 return E_NOINTERFACE;
515 }
516
517 static HRESULT WINAPI IMILBitmapScaler_Initialize(IMILBitmapScaler *iface,
518 IMILBitmapSource *mil_source, UINT width, UINT height,
519 WICBitmapInterpolationMode mode)
520 {
521 BitmapScaler *This = impl_from_IMILBitmapScaler(iface);
522 IWICBitmapSource *wic_source;
523 HRESULT hr;
524
525 TRACE("(%p,%p,%u,%u,%u)\n", iface, mil_source, width, height, mode);
526
527 if (!mil_source) return E_INVALIDARG;
528
529 hr = mil_source->lpVtbl->QueryInterface(mil_source, &IID_IWICBitmapSource, (void **)&wic_source);
530 if (hr == S_OK)
531 {
532 hr = IWICBitmapScaler_Initialize(&This->IWICBitmapScaler_iface, wic_source, width, height, mode);
533 IWICBitmapSource_Release(wic_source);
534 }
535 return hr;
536 }
537
538 static const IMILBitmapScalerVtbl IMILBitmapScaler_Vtbl = {
539 IMILBitmapScaler_QueryInterface,
540 IMILBitmapScaler_AddRef,
541 IMILBitmapScaler_Release,
542 IMILBitmapScaler_GetSize,
543 IMILBitmapScaler_GetPixelFormat,
544 IMILBitmapScaler_GetResolution,
545 IMILBitmapScaler_CopyPalette,
546 IMILBitmapScaler_CopyPixels,
547 IMILBitmapScaler_unknown1,
548 IMILBitmapScaler_Initialize
549 };
550
551 HRESULT BitmapScaler_Create(IWICBitmapScaler **scaler)
552 {
553 BitmapScaler *This;
554
555 This = HeapAlloc(GetProcessHeap(), 0, sizeof(BitmapScaler));
556 if (!This) return E_OUTOFMEMORY;
557
558 This->IWICBitmapScaler_iface.lpVtbl = &BitmapScaler_Vtbl;
559 This->IMILBitmapScaler_iface.lpVtbl = &IMILBitmapScaler_Vtbl;
560 This->ref = 1;
561 This->source = NULL;
562 This->width = 0;
563 This->height = 0;
564 This->src_width = 0;
565 This->src_height = 0;
566 This->mode = 0;
567 This->bpp = 0;
568 InitializeCriticalSection(&This->lock);
569 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BitmapScaler.lock");
570
571 *scaler = &This->IWICBitmapScaler_iface;
572
573 return S_OK;
574 }