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