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