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