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