wined3d: Get rid of the surface_type parameter to wined3d_surface_create().
[wine] / dlls / 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 #include "wincodec.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     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 HRESULT WINAPI BitmapScaler_QueryInterface(IWICBitmapScaler *iface, REFIID iid,
55     void **ppv)
56 {
57     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
58     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
59
60     if (!ppv) return E_INVALIDARG;
61
62     if (IsEqualIID(&IID_IUnknown, iid) ||
63         IsEqualIID(&IID_IWICBitmapSource, iid) ||
64         IsEqualIID(&IID_IWICBitmapScaler, iid))
65     {
66         *ppv = &This->IWICBitmapScaler_iface;
67     }
68     else
69     {
70         *ppv = NULL;
71         return E_NOINTERFACE;
72     }
73
74     IUnknown_AddRef((IUnknown*)*ppv);
75     return S_OK;
76 }
77
78 static ULONG WINAPI BitmapScaler_AddRef(IWICBitmapScaler *iface)
79 {
80     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
81     ULONG ref = InterlockedIncrement(&This->ref);
82
83     TRACE("(%p) refcount=%u\n", iface, ref);
84
85     return ref;
86 }
87
88 static ULONG WINAPI BitmapScaler_Release(IWICBitmapScaler *iface)
89 {
90     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
91     ULONG ref = InterlockedDecrement(&This->ref);
92
93     TRACE("(%p) refcount=%u\n", iface, ref);
94
95     if (ref == 0)
96     {
97         This->lock.DebugInfo->Spare[0] = 0;
98         DeleteCriticalSection(&This->lock);
99         if (This->source) IWICBitmapSource_Release(This->source);
100         HeapFree(GetProcessHeap(), 0, This);
101     }
102
103     return ref;
104 }
105
106 static HRESULT WINAPI BitmapScaler_GetSize(IWICBitmapScaler *iface,
107     UINT *puiWidth, UINT *puiHeight)
108 {
109     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
110     TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
111
112     if (!puiWidth || !puiHeight)
113         return E_INVALIDARG;
114
115     if (!This->source)
116         return WINCODEC_ERR_WRONGSTATE;
117
118     *puiWidth = This->width;
119     *puiHeight = This->height;
120
121     return S_OK;
122 }
123
124 static HRESULT WINAPI BitmapScaler_GetPixelFormat(IWICBitmapScaler *iface,
125     WICPixelFormatGUID *pPixelFormat)
126 {
127     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
128     TRACE("(%p,%p)\n", iface, pPixelFormat);
129
130     if (!pPixelFormat)
131         return E_INVALIDARG;
132
133     if (!This->source)
134         return WINCODEC_ERR_WRONGSTATE;
135
136     return IWICBitmapSource_GetPixelFormat(This->source, pPixelFormat);
137 }
138
139 static HRESULT WINAPI BitmapScaler_GetResolution(IWICBitmapScaler *iface,
140     double *pDpiX, double *pDpiY)
141 {
142     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
143     TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
144
145     if (!pDpiX || !pDpiY)
146         return E_INVALIDARG;
147
148     if (!This->source)
149         return WINCODEC_ERR_WRONGSTATE;
150
151     return IWICBitmapSource_GetResolution(This->source, pDpiX, pDpiY);
152 }
153
154 static HRESULT WINAPI BitmapScaler_CopyPalette(IWICBitmapScaler *iface,
155     IWICPalette *pIPalette)
156 {
157     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
158     TRACE("(%p,%p)\n", iface, pIPalette);
159
160     if (!pIPalette)
161         return E_INVALIDARG;
162
163     if (!This->source)
164         return WINCODEC_ERR_WRONGSTATE;
165
166     return IWICBitmapSource_CopyPalette(This->source, pIPalette);
167 }
168
169 static void NearestNeighbor_GetRequiredSourceRect(BitmapScaler *This,
170     UINT x, UINT y, WICRect *src_rect)
171 {
172     src_rect->X = x * This->src_width / This->width;
173     src_rect->Y = y * This->src_height / This->height;
174     src_rect->Width = src_rect->Height = 1;
175 }
176
177 static void NearestNeighbor_CopyScanline(BitmapScaler *This,
178     UINT dst_x, UINT dst_y, UINT dst_width,
179     BYTE **src_data, UINT src_data_x, UINT src_data_y, BYTE *pbBuffer)
180 {
181     int i;
182     UINT bytesperpixel = This->bpp/8;
183     UINT src_x, src_y;
184
185     src_y = dst_y * This->src_height / This->height - src_data_y;
186
187     for (i=0; i<dst_width; i++)
188     {
189         src_x = (dst_x + i) * This->src_width / This->width - src_data_x;
190         memcpy(pbBuffer + bytesperpixel * i, src_data[src_y] + bytesperpixel * src_x, bytesperpixel);
191     }
192 }
193
194 static HRESULT WINAPI BitmapScaler_CopyPixels(IWICBitmapScaler *iface,
195     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
196 {
197     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
198     HRESULT hr;
199     WICRect dest_rect;
200     WICRect src_rect_ul, src_rect_br, src_rect;
201     BYTE **src_rows;
202     BYTE *src_bits;
203     ULONG bytesperrow;
204     ULONG src_bytesperrow;
205     ULONG buffer_size;
206     UINT y;
207
208     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
209
210     EnterCriticalSection(&This->lock);
211
212     if (!This->source)
213     {
214         hr = WINCODEC_ERR_WRONGSTATE;
215         goto end;
216     }
217
218     if (prc)
219         dest_rect = *prc;
220     else
221     {
222         dest_rect.X = dest_rect.Y = 0;
223         dest_rect.Width = This->width;
224         dest_rect.Height = This->height;
225     }
226
227     if (dest_rect.X < 0 || dest_rect.Y < 0 ||
228         dest_rect.X+dest_rect.Width > This->width|| dest_rect.Y+dest_rect.Height > This->height)
229     {
230         hr = E_INVALIDARG;
231         goto end;
232     }
233
234     bytesperrow = ((This->bpp * dest_rect.Width)+7)/8;
235
236     if (cbStride < bytesperrow)
237     {
238         hr = E_INVALIDARG;
239         goto end;
240     }
241
242     if ((cbStride * dest_rect.Height) > cbBufferSize)
243     {
244         hr = E_INVALIDARG;
245         goto end;
246     }
247
248     /* MSDN recommends calling CopyPixels once for each scanline from top to
249      * bottom, and claims codecs optimize for this. Ideally, when called in this
250      * way, we should avoid requesting a scanline from the source more than
251      * once, by saving the data that will be useful for the next scanline after
252      * the call returns. The GetRequiredSourceRect/CopyScanline functions are
253      * designed to make it possible to do this in a generic way, but for now we
254      * just grab all the data we need in each call. */
255
256     This->fn_get_required_source_rect(This, dest_rect.X, dest_rect.Y, &src_rect_ul);
257     This->fn_get_required_source_rect(This, dest_rect.X+dest_rect.Width-1,
258         dest_rect.Y+dest_rect.Height-1, &src_rect_br);
259
260     src_rect.X = src_rect_ul.X;
261     src_rect.Y = src_rect_ul.Y;
262     src_rect.Width = src_rect_br.Width + src_rect_br.X - src_rect_ul.X;
263     src_rect.Height = src_rect_br.Height + src_rect_br.Y - src_rect_ul.Y;
264
265     src_bytesperrow = (src_rect.Width * This->bpp + 7)/8;
266     buffer_size = src_bytesperrow * src_rect.Height;
267
268     src_rows = HeapAlloc(GetProcessHeap(), 0, sizeof(BYTE*) * src_rect.Height);
269     src_bits = HeapAlloc(GetProcessHeap(), 0, buffer_size);
270
271     if (!src_rows || !src_bits)
272     {
273         HeapFree(GetProcessHeap(), 0, src_rows);
274         HeapFree(GetProcessHeap(), 0, src_bits);
275         hr = E_OUTOFMEMORY;
276         goto end;
277     }
278
279     for (y=0; y<src_rect.Height; y++)
280         src_rows[y] = src_bits + y * src_bytesperrow;
281
282     hr = IWICBitmapSource_CopyPixels(This->source, &src_rect, src_bytesperrow,
283         buffer_size, src_bits);
284
285     if (SUCCEEDED(hr))
286     {
287         for (y=0; y < dest_rect.Height; y++)
288         {
289             This->fn_copy_scanline(This, dest_rect.X, dest_rect.Y+y, dest_rect.Width,
290                 src_rows, src_rect.X, src_rect.Y, pbBuffer + cbStride * y);
291         }
292     }
293
294     HeapFree(GetProcessHeap(), 0, src_rows);
295     HeapFree(GetProcessHeap(), 0, src_bits);
296
297 end:
298     LeaveCriticalSection(&This->lock);
299
300     return hr;
301 }
302
303 static HRESULT WINAPI BitmapScaler_Initialize(IWICBitmapScaler *iface,
304     IWICBitmapSource *pISource, UINT uiWidth, UINT uiHeight,
305     WICBitmapInterpolationMode mode)
306 {
307     BitmapScaler *This = impl_from_IWICBitmapScaler(iface);
308     HRESULT hr;
309     GUID src_pixelformat;
310
311     TRACE("(%p,%p,%u,%u,%u)\n", iface, pISource, uiWidth, uiHeight, mode);
312
313     EnterCriticalSection(&This->lock);
314
315     if (This->source)
316     {
317         hr = WINCODEC_ERR_WRONGSTATE;
318         goto end;
319     }
320
321     This->width = uiWidth;
322     This->height = uiHeight;
323     This->mode = mode;
324
325     hr = IWICBitmapSource_GetSize(pISource, &This->src_width, &This->src_height);
326
327     if (SUCCEEDED(hr))
328         hr = IWICBitmapSource_GetPixelFormat(pISource, &src_pixelformat);
329
330     if (SUCCEEDED(hr))
331     {
332         hr = get_pixelformat_bpp(&src_pixelformat, &This->bpp);
333     }
334
335     if (SUCCEEDED(hr))
336     {
337         switch (mode)
338         {
339         default:
340             FIXME("unsupported mode %i\n", mode);
341             /* fall-through */
342         case WICBitmapInterpolationModeNearestNeighbor:
343             if ((This->bpp % 8) == 0)
344             {
345                 IWICBitmapSource_AddRef(pISource);
346                 This->source = pISource;
347             }
348             else
349             {
350                 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA,
351                     pISource, &This->source);
352                 This->bpp = 32;
353             }
354             This->fn_get_required_source_rect = NearestNeighbor_GetRequiredSourceRect;
355             This->fn_copy_scanline = NearestNeighbor_CopyScanline;
356             break;
357         }
358     }
359
360 end:
361     LeaveCriticalSection(&This->lock);
362
363     return hr;
364 }
365
366 static const IWICBitmapScalerVtbl BitmapScaler_Vtbl = {
367     BitmapScaler_QueryInterface,
368     BitmapScaler_AddRef,
369     BitmapScaler_Release,
370     BitmapScaler_GetSize,
371     BitmapScaler_GetPixelFormat,
372     BitmapScaler_GetResolution,
373     BitmapScaler_CopyPalette,
374     BitmapScaler_CopyPixels,
375     BitmapScaler_Initialize
376 };
377
378 HRESULT BitmapScaler_Create(IWICBitmapScaler **scaler)
379 {
380     BitmapScaler *This;
381
382     This = HeapAlloc(GetProcessHeap(), 0, sizeof(BitmapScaler));
383     if (!This) return E_OUTOFMEMORY;
384
385     This->IWICBitmapScaler_iface.lpVtbl = &BitmapScaler_Vtbl;
386     This->ref = 1;
387     This->source = NULL;
388     This->width = 0;
389     This->height = 0;
390     This->src_width = 0;
391     This->src_height = 0;
392     This->mode = 0;
393     This->bpp = 0;
394     InitializeCriticalSection(&This->lock);
395     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BitmapScaler.lock");
396
397     *scaler = &This->IWICBitmapScaler_iface;
398
399     return S_OK;
400 }