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