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