wined3d: Convert and load U8V8 surfaces as rgb.
[wine/wine-gecko.git] / dlls / wined3d / surface.c
blobdabee6d627caa10cc96554ed7e7618b51b291ea7
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006 Stefan Dösinger for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h"
28 #include "wine/port.h"
29 #include "wined3d_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
32 #define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
34 typedef enum {
35 NO_CONVERSION,
36 CONVERT_PALETTED,
37 CONVERT_PALETTED_CK,
38 CONVERT_CK_565,
39 CONVERT_CK_5551,
40 CONVERT_CK_4444,
41 CONVERT_CK_4444_ARGB,
42 CONVERT_CK_1555,
43 CONVERT_555,
44 CONVERT_CK_RGB24,
45 CONVERT_CK_8888,
46 CONVERT_CK_8888_ARGB,
47 CONVERT_RGB32_888,
48 CONVERT_V8U8
49 } CONVERT_TYPES;
51 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
53 static void surface_download_data(IWineD3DSurfaceImpl *This) {
54 if (This->resource.format == WINED3DFMT_DXT1 ||
55 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
56 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
57 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
58 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
59 } else {
60 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
61 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
63 ENTER_GL();
65 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
66 checkGLcall("glGetCompressedTexImageARB()");
68 LEAVE_GL();
70 } else {
71 void *mem;
72 int src_pitch = 0;
73 int dst_pitch = 0;
75 if(This->Flags & SFLAG_CONVERTED) {
76 FIXME("Read back converted textures unsupported\n");
77 return;
80 if (This->Flags & SFLAG_NONPOW2) {
81 src_pitch = This->bytesPerPixel * This->pow2Width;
82 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
83 src_pitch = (src_pitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
84 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
85 } else {
86 mem = This->resource.allocatedMemory;
89 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
90 This->glDescription.glFormat, This->glDescription.glType, mem);
92 ENTER_GL();
94 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
95 This->glDescription.glType, mem);
96 checkGLcall("glGetTexImage()");
98 LEAVE_GL();
100 if (This->Flags & SFLAG_NONPOW2) {
101 LPBYTE src_data, dst_data;
102 int y;
104 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
105 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
106 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
108 * We're doing this...
110 * instead of boxing the texture :
111 * |<-texture width ->| -->pow2width| /\
112 * |111111111111111111| | |
113 * |222 Texture 222222| boxed empty | texture height
114 * |3333 Data 33333333| | |
115 * |444444444444444444| | \/
116 * ----------------------------------- |
117 * | boxed empty | boxed empty | pow2height
118 * | | | \/
119 * -----------------------------------
122 * we're repacking the data to the expected texture width
124 * |<-texture width ->| -->pow2width| /\
125 * |111111111111111111222222222222222| |
126 * |222333333333333333333444444444444| texture height
127 * |444444 | |
128 * | | \/
129 * | | |
130 * | empty | pow2height
131 * | | \/
132 * -----------------------------------
134 * == is the same as
136 * |<-texture width ->| /\
137 * |111111111111111111|
138 * |222222222222222222|texture height
139 * |333333333333333333|
140 * |444444444444444444| \/
141 * --------------------
143 * this also means that any references to allocatedMemory should work with the data as if were a
144 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
146 * internally the texture is still stored in a boxed format so any references to textureName will
147 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
149 * Performance should not be an issue, because applications normally do not lock the surfaces when
150 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
151 * and doesn't have to be re-read.
153 src_data = mem;
154 dst_data = This->resource.allocatedMemory;
155 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
156 for (y = 1 ; y < This->currentDesc.Height; y++) {
157 /* skip the first row */
158 src_data += src_pitch;
159 dst_data += dst_pitch;
160 memcpy(dst_data, src_data, dst_pitch);
163 HeapFree(GetProcessHeap(), 0, mem);
168 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
169 if (This->resource.format == WINED3DFMT_DXT1 ||
170 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
171 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
172 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
173 FIXME("Using DXT1/3/5 without advertized support\n");
174 } else {
175 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
176 ENTER_GL();
177 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
178 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
179 * function uses glCompressedTexImage2D instead of the SubImage call
181 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, This->glDescription.glFormatInternal,
182 width, height, 0 /* border */, This->resource.size, data));
183 checkGLcall("glCompressedTexSubImage2D");
184 LEAVE_GL();
186 } else {
187 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
188 ENTER_GL();
189 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
190 checkGLcall("glTexSubImage2D");
191 LEAVE_GL();
195 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
196 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
197 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
199 if (This->resource.format == WINED3DFMT_DXT1 ||
200 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
201 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
202 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
203 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
204 return;
207 ENTER_GL();
209 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, NULL);
210 checkGLcall("glTexImage2D");
212 LEAVE_GL();
215 /* *******************************************
216 IWineD3DSurface IUnknown parts follow
217 ******************************************* */
218 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
220 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
221 /* Warn ,but be nice about things */
222 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
224 if (IsEqualGUID(riid, &IID_IUnknown)
225 || IsEqualGUID(riid, &IID_IWineD3DBase)
226 || IsEqualGUID(riid, &IID_IWineD3DResource)
227 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
228 IUnknown_AddRef((IUnknown*)iface);
229 *ppobj = This;
230 return S_OK;
232 *ppobj = NULL;
233 return E_NOINTERFACE;
236 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
237 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
238 ULONG ref = InterlockedIncrement(&This->resource.ref);
239 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
240 return ref;
243 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
245 ULONG ref = InterlockedDecrement(&This->resource.ref);
246 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
247 if (ref == 0) {
248 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
249 TRACE("(%p) : cleaning up\n", This);
250 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
251 ENTER_GL();
252 TRACE("Deleting texture %d\n", This->glDescription.textureName);
253 glDeleteTextures(1, &This->glDescription.textureName);
254 LEAVE_GL();
257 if(This->Flags & SFLAG_DIBSECTION) {
258 /* Release the DC */
259 SelectObject(This->hDC, This->dib.holdbitmap);
260 DeleteDC(This->hDC);
261 /* Release the DIB section */
262 DeleteObject(This->dib.DIBsection);
263 This->dib.bitmap_data = NULL;
264 This->resource.allocatedMemory = NULL;
266 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
268 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
269 if(iface == device->ddraw_primary)
270 device->ddraw_primary = NULL;
272 if(iface == device->lastActiveRenderTarget) {
273 device->lastActiveRenderTarget = NULL;
276 TRACE("(%p) Released\n", This);
277 HeapFree(GetProcessHeap(), 0, This);
280 return ref;
283 /* ****************************************************
284 IWineD3DSurface IWineD3DResource parts follow
285 **************************************************** */
286 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
287 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
290 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
291 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
294 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
295 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
298 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
299 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
302 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
303 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
306 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
307 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
310 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
311 /* TODO: re-write the way textures and managed,
312 * use a 'opengl context manager' to manage RenderTarget surfaces
313 ** *********************************************************/
315 /* TODO: check for locks */
316 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
317 IWineD3DBaseTexture *baseTexture = NULL;
318 TRACE("(%p)Checking to see if the container is a base texture\n", This);
319 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
320 TRACE("Passing to conatiner\n");
321 IWineD3DBaseTexture_PreLoad(baseTexture);
322 IWineD3DBaseTexture_Release(baseTexture);
323 } else {
324 TRACE("(%p) : About to load surface\n", This);
325 ENTER_GL();
326 #if 0 /* TODO: context manager support */
327 IWineD3DContextManager_PushState(This->contextManager, GL_TEXTURE_2D, ENABLED, NOW /* make sure the state is applied now */);
328 #endif
329 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
330 if (!This->glDescription.level) {
331 if (!This->glDescription.textureName) {
332 glGenTextures(1, &This->glDescription.textureName);
333 checkGLcall("glGenTextures");
334 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
336 glBindTexture(This->glDescription.target, This->glDescription.textureName);
337 checkGLcall("glBindTexture");
338 IWineD3DSurface_LoadTexture(iface);
339 /* This is where we should be reducing the amount of GLMemoryUsed */
340 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
341 /* assume this is a coding error not a real error for now */
342 FIXME("Mipmap surface has a glTexture bound to it!\n");
344 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
345 /* Tell opengl to try and keep this texture in video ram (well mostly) */
346 GLclampf tmp;
347 tmp = 0.9f;
348 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
350 /* TODO: disable texture support, if it wastn't enabled when we entered. */
351 #if 0 /* TODO: context manager support */
352 IWineD3DContextManager_PopState(This->contextManager, GL_TEXTURE_2D, DISABLED,DELAYED
353 /* we don't care when the state is disabled(if atall) */);
354 #endif
355 LEAVE_GL();
357 return;
360 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
361 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
362 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
365 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
366 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
367 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
370 /* ******************************************************
371 IWineD3DSurface IWineD3DSurface parts follow
372 ****************************************************** */
374 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
375 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
376 IWineD3DBase *container = 0;
378 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
380 if (!ppContainer) {
381 ERR("Called without a valid ppContainer.\n");
384 /** From MSDN:
385 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
386 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
387 * GetContainer will return the Direct3D device used to create the surface.
389 if (This->container) {
390 container = This->container;
391 } else {
392 container = (IWineD3DBase *)This->resource.wineD3DDevice;
395 TRACE("Relaying to QueryInterface\n");
396 return IUnknown_QueryInterface(container, riid, ppContainer);
399 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
400 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
402 TRACE("(%p) : copying into %p\n", This, pDesc);
403 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
404 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
405 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
406 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
407 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
408 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
409 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
410 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
411 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
412 return WINED3D_OK;
415 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
416 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
417 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
418 if (This->glDescription.textureName == 0 && textureName != 0) {
419 This->Flags |= SFLAG_DIRTY;
420 IWineD3DSurface_AddDirtyRect(iface, NULL);
422 This->glDescription.textureName = textureName;
423 This->glDescription.target = target;
426 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
427 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
428 TRACE("(%p) : returning %p\n", This, &This->glDescription);
429 *glDescription = &This->glDescription;
432 /* TODO: think about moving this down to resource? */
433 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
434 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
435 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the futture */
436 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
437 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
439 return (CONST void*)(This->resource.allocatedMemory);
442 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
443 BYTE *mem;
444 GLint fmt;
445 GLint type;
446 BYTE *row, *top, *bottom;
447 int i;
448 BOOL bpp;
450 switch(This->resource.format)
452 case WINED3DFMT_P8:
454 /* GL can't return palettized data, so read ARGB pixels into a
455 * separate block of memory and convert them into palettized format
456 * in software. Slow, but if the app means to use palettized render
457 * targets and locks it...
459 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
460 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
461 * for the color channels when palettizing the colors.
463 fmt = GL_RGB;
464 type = GL_UNSIGNED_BYTE;
465 pitch *= 3;
466 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
467 if(!mem) {
468 ERR("Out of memory\n");
469 return;
471 bpp = This->bytesPerPixel * 3;
473 break;
475 default:
476 mem = dest;
477 fmt = This->glDescription.glFormat;
478 type = This->glDescription.glType;
479 bpp = This->bytesPerPixel;
482 glReadPixels(rect->left, rect->top,
483 rect->right - rect->left,
484 rect->bottom - rect->top,
485 fmt, type, mem);
486 vcheckGLcall("glReadPixels");
488 /* TODO: Merge this with the palettization loop below for P8 targets */
490 if(!srcUpsideDown) {
491 UINT len, off;
492 /* glReadPixels returns the image upside down, and there is no way to prevent this.
493 Flip the lines in software */
494 len = (rect->right - rect->left) * bpp;
495 off = rect->left * bpp;
497 row = HeapAlloc(GetProcessHeap(), 0, len);
498 if(!row) {
499 ERR("Out of memory\n");
500 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
501 return;
504 top = mem + pitch * rect->top;
505 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
506 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
507 memcpy(row, top + off, len);
508 memcpy(top + off, bottom + off, len);
509 memcpy(bottom + off, row, len);
510 top += pitch;
511 bottom -= pitch;
513 HeapFree(GetProcessHeap(), 0, row);
516 if(This->resource.format == WINED3DFMT_P8) {
517 PALETTEENTRY *pal;
518 DWORD width = pitch / 3;
519 int x, y, c;
520 if(This->palette) {
521 pal = This->palette->palents;
522 } else {
523 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
526 for(y = rect->top; y < rect->bottom; y++) {
527 for(x = rect->left; x < rect->right; x++) {
528 /* start lines pixels */
529 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
530 BYTE *green = blue + 1;
531 BYTE *red = green + 1;
533 for(c = 0; c < 256; c++) {
534 if(*red == pal[c].peRed &&
535 *green == pal[c].peGreen &&
536 *blue == pal[c].peBlue)
538 *((BYTE *) dest + y * width + x) = c;
539 break;
544 HeapFree(GetProcessHeap(), 0, mem);
548 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
549 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
550 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
551 IWineD3DSwapChainImpl *swapchain = NULL;
553 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
555 if (!(This->Flags & SFLAG_LOCKABLE)) {
556 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
557 texture regions, and since the destination is an unlockable region we need
558 to tolerate this */
559 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
560 /*return WINED3DERR_INVALIDCALL; */
563 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
565 /* Mark the surface locked */
566 This->Flags |= SFLAG_LOCKED;
568 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
569 if(!This->resource.allocatedMemory) {
570 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
571 This->Flags |= SFLAG_GLDIRTY; /* This is the marker that surface data has to be downloaded */
574 /* Calculate the correct start address to report */
575 if (NULL == pRect) {
576 pLockedRect->pBits = This->resource.allocatedMemory;
577 This->lockedRect.left = 0;
578 This->lockedRect.top = 0;
579 This->lockedRect.right = This->currentDesc.Width;
580 This->lockedRect.bottom = This->currentDesc.Height;
581 TRACE("Locked Rect (%p) = l %d, t %d, r %d, b %d\n", &This->lockedRect, This->lockedRect.left, This->lockedRect.top, This->lockedRect.right, This->lockedRect.bottom);
582 } else {
583 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
585 if ((pRect->top < 0) ||
586 (pRect->left < 0) ||
587 (pRect->left >= pRect->right) ||
588 (pRect->top >= pRect->bottom) ||
589 (pRect->right > This->currentDesc.Width) ||
590 (pRect->bottom > This->currentDesc.Height))
592 WARN(" Invalid values in pRect !!!\n");
593 return D3DERR_INVALIDCALL;
596 if (This->resource.format == WINED3DFMT_DXT1) { /* DXT1 is half byte per pixel */
597 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + ((pRect->left * This->bytesPerPixel / 2));
598 } else {
599 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
601 This->lockedRect.left = pRect->left;
602 This->lockedRect.top = pRect->top;
603 This->lockedRect.right = pRect->right;
604 This->lockedRect.bottom = pRect->bottom;
607 if (This->Flags & SFLAG_NONPOW2) {
608 TRACE("Locking non-power 2 texture\n");
611 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
612 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
613 * changed
615 if(!(This->Flags & SFLAG_DYNLOCK)) {
616 This->lockCount++;
617 /* MAXLOCKCOUNT is defined in wined3d_private.h */
618 if(This->lockCount > MAXLOCKCOUNT) {
619 TRACE("Surface is locked regularily, not freeing the system memory copy any more\n");
620 This->Flags |= SFLAG_DYNLOCK;
624 if((Flags & WINED3DLOCK_DISCARD) || !(This->Flags & SFLAG_GLDIRTY) ) {
625 TRACE("WINED3DLOCK_DISCARD flag passed, or local copy is up to date, not downloading data\n");
626 goto lock_end;
629 /* Now download the surface content from opengl
630 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
631 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
633 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
634 if(swapchain || iface == myDevice->render_targets[0]) {
635 BOOL srcIsUpsideDown;
637 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
638 static BOOL warned = FALSE;
639 if(!warned) {
640 ERR("The application tries to lock the render target, but render target locking is disabled\n");
641 warned = TRUE;
643 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
644 return WINED3D_OK;
647 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
648 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
649 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
650 * context->last_was_blit set on the unlock.
652 ENTER_GL();
653 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
655 /* Select the correct read buffer, and give some debug output.
656 * There is no need to keep track of the current read buffer or reset it, every part of the code
657 * that reads sets the read buffer as desired.
659 if(!swapchain) {
660 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
661 * Read from the back buffer
663 TRACE("Locking offscreen render target\n");
664 glReadBuffer(GL_BACK);
665 srcIsUpsideDown = TRUE;
666 } else {
667 if(iface == swapchain->frontBuffer) {
668 TRACE("Locking the front buffer\n");
669 glReadBuffer(GL_FRONT);
670 } else if(swapchain->backBuffer && iface == swapchain->backBuffer[0]) {
671 TRACE("Locking the back buffer\n");
672 glReadBuffer(GL_BACK);
673 } else {
674 /* Ok, there is an issue: OpenGL does not guarant any back buffer number, so all we can do is to read GL_BACK
675 * and hope it gives what the app wants
677 FIXME("Application is locking a 2nd or higher back buffer\n");
678 glReadBuffer(GL_BACK);
680 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
681 srcIsUpsideDown = FALSE;
684 switch(wined3d_settings.rendertargetlock_mode) {
685 case RTL_AUTO:
686 case RTL_READDRAW:
687 case RTL_READTEX:
688 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
689 break;
691 case RTL_TEXDRAW:
692 case RTL_TEXTEX:
693 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
694 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
695 break;
697 LEAVE_GL();
699 /* Mark the local copy up to date if a full download was done */
700 if(This->lockedRect.left == 0 &&
701 This->lockedRect.top == 0 &&
702 This->lockedRect.right == This->currentDesc.Width &&
703 This->lockedRect.bottom == This->currentDesc.Height) {
704 This->Flags &= ~SFLAG_GLDIRTY;
706 } else if(iface == myDevice->stencilBufferTarget) {
707 /** the depth stencil in openGL has a format of GL_FLOAT
708 * which should be good for WINED3DFMT_D16_LOCKABLE
709 * and WINED3DFMT_D16
710 * it is unclear what format the stencil buffer is in except.
711 * 'Each index is converted to fixed point...
712 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
713 * mappings in the table GL_PIXEL_MAP_S_TO_S.
714 * glReadPixels(This->lockedRect.left,
715 * This->lockedRect.bottom - j - 1,
716 * This->lockedRect.right - This->lockedRect.left,
717 * 1,
718 * GL_DEPTH_COMPONENT,
719 * type,
720 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
722 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
723 * gl texture(next path), or in local memory(early return because of missing SFLAG_GLDIRTY above). If
724 * none of that is the case the problem is not in this function :-)
725 ********************************************/
726 FIXME("Depth stencil locking not supported yet\n");
727 } else {
728 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
729 TRACE("locking an ordinarary surface\n");
731 /* TODO: Make sure that *any* context is active for this thread. It is not important which context that is,
732 * nor that is has any special setup(CTXUSAGE_LOADRESOURCE is fine), but the code below needs a context.
733 * A context is guaranted to be there in a single threaded environment, but not with multithreading
735 if (0 != This->glDescription.textureName) {
736 /* Now I have to copy thing bits back */
738 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
739 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
740 ENTER_GL();
741 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
742 checkGLcall("glActiveTextureARB");
743 LEAVE_GL();
745 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
746 IWineD3DSurface_PreLoad(iface);
748 surface_download_data(This);
751 /* The local copy is now up to date to the opengl one because a full download was done */
752 This->Flags &= ~SFLAG_GLDIRTY;
755 lock_end:
756 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
757 /* Don't dirtify */
758 } else {
759 IWineD3DBaseTexture *pBaseTexture;
761 * Dirtify on lock
762 * as seen in msdn docs
764 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
766 /** Dirtify Container if needed */
767 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
768 TRACE("Making container dirty\n");
769 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
770 IWineD3DBaseTexture_Release(pBaseTexture);
771 } else {
772 TRACE("Surface is standalone, no need to dirty the container\n");
776 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch, This->Flags & SFLAG_DIRTY ? 0 : 1);
777 return WINED3D_OK;
780 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
781 GLint prev_store;
782 GLint prev_rasterpos[4];
783 GLint skipBytes = 0;
784 BOOL storechanged = FALSE, memory_allocated = FALSE;
785 GLint fmt, type;
786 BYTE *mem;
787 UINT bpp;
788 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
790 glDisable(GL_TEXTURE_2D);
791 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
793 glFlush();
794 vcheckGLcall("glFlush");
795 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
796 vcheckGLcall("glIntegerv");
797 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
798 vcheckGLcall("glIntegerv");
799 glPixelZoom(1.0, -1.0);
800 vcheckGLcall("glPixelZoom");
802 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
803 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
804 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
806 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
807 vcheckGLcall("glRasterPos2f");
809 /* Some drivers(radeon dri, others?) don't like exceptions during
810 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
811 * after ReleaseDC. Reading it will cause an exception, which x11drv will
812 * catch to put the dib section in InSync mode, which leads to a crash
813 * and a blocked x server on my radeon card.
815 * The following lines read the dib section so it is put in inSync mode
816 * before glDrawPixels is called and the crash is prevented. There won't
817 * be any interfering gdi accesses, because UnlockRect is called from
818 * ReleaseDC, and the app won't use the dc any more afterwards.
820 if(This->Flags & SFLAG_DIBSECTION) {
821 volatile BYTE read;
822 read = This->resource.allocatedMemory[0];
825 switch (This->resource.format) {
826 /* No special care needed */
827 case WINED3DFMT_A4R4G4B4:
828 case WINED3DFMT_R5G6B5:
829 case WINED3DFMT_A1R5G5B5:
830 case WINED3DFMT_R8G8B8:
831 type = This->glDescription.glType;
832 fmt = This->glDescription.glFormat;
833 mem = This->resource.allocatedMemory;
834 bpp = This->bytesPerPixel;
835 break;
837 case WINED3DFMT_X4R4G4B4:
839 int size;
840 unsigned short *data;
841 data = (unsigned short *)This->resource.allocatedMemory;
842 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
843 while(size > 0) {
844 *data |= 0xF000;
845 data++;
846 size--;
848 type = This->glDescription.glType;
849 fmt = This->glDescription.glFormat;
850 mem = This->resource.allocatedMemory;
851 bpp = This->bytesPerPixel;
853 break;
855 case WINED3DFMT_X1R5G5B5:
857 int size;
858 unsigned short *data;
859 data = (unsigned short *)This->resource.allocatedMemory;
860 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
861 while(size > 0) {
862 *data |= 0x8000;
863 data++;
864 size--;
866 type = This->glDescription.glType;
867 fmt = This->glDescription.glFormat;
868 mem = This->resource.allocatedMemory;
869 bpp = This->bytesPerPixel;
871 break;
873 case WINED3DFMT_X8R8G8B8:
875 /* make sure the X byte is set to alpha on, since it
876 could be any random value. This fixes the intro movie in Pirates! */
877 int size;
878 unsigned int *data;
879 data = (unsigned int *)This->resource.allocatedMemory;
880 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
881 while(size > 0) {
882 *data |= 0xFF000000;
883 data++;
884 size--;
887 /* Fall through */
889 case WINED3DFMT_A8R8G8B8:
891 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
892 vcheckGLcall("glPixelStorei");
893 storechanged = TRUE;
894 type = This->glDescription.glType;
895 fmt = This->glDescription.glFormat;
896 mem = This->resource.allocatedMemory;
897 bpp = This->bytesPerPixel;
899 break;
901 case WINED3DFMT_A2R10G10B10:
903 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
904 vcheckGLcall("glPixelStorei");
905 storechanged = TRUE;
906 type = This->glDescription.glType;
907 fmt = This->glDescription.glFormat;
908 mem = This->resource.allocatedMemory;
909 bpp = This->bytesPerPixel;
911 break;
913 case WINED3DFMT_P8:
915 int height = This->glRect.bottom - This->glRect.top;
916 type = GL_UNSIGNED_BYTE;
917 fmt = GL_RGBA;
919 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
920 if(!mem) {
921 ERR("Out of memory\n");
922 return;
924 memory_allocated = TRUE;
925 d3dfmt_convert_surface(This->resource.allocatedMemory,
926 mem,
927 pitch,
928 pitch,
929 height,
930 pitch * 4,
931 CONVERT_PALETTED,
932 This);
933 bpp = This->bytesPerPixel * 4;
934 pitch *= 4;
936 break;
938 default:
939 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
941 /* Give it a try */
942 type = This->glDescription.glType;
943 fmt = This->glDescription.glFormat;
944 mem = This->resource.allocatedMemory;
945 bpp = This->bytesPerPixel;
948 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
949 (This->lockedRect.bottom - This->lockedRect.top)-1,
950 fmt, type,
951 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
952 checkGLcall("glDrawPixels");
953 glPixelZoom(1.0,1.0);
954 vcheckGLcall("glPixelZoom");
956 glRasterPos3iv(&prev_rasterpos[0]);
957 vcheckGLcall("glRasterPos3iv");
959 /* Reset to previous pack row length */
960 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
961 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
962 if(storechanged) {
963 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
964 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
967 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
968 return;
971 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
972 float glTexCoord[4];
974 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
975 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
976 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
977 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
979 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
981 ENTER_GL();
983 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
984 checkGLcall("glEnable glBindTexture");
986 /* No filtering for blts */
987 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
988 checkGLcall("glTexParameteri");
989 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
990 checkGLcall("glTexParameteri");
992 /* Start drawing a quad */
993 glBegin(GL_QUADS);
995 glColor3d(1.0f, 1.0f, 1.0f);
996 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
997 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
999 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1000 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1002 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1003 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1005 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1006 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1008 glEnd();
1009 checkGLcall("glEnd");
1011 /* Unbind the texture */
1012 glBindTexture(GL_TEXTURE_2D, 0);
1013 checkGLcall("glEnable glBindTexture");
1015 LEAVE_GL();
1018 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1019 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1020 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1021 IWineD3DSwapChainImpl *swapchain = NULL;
1023 if (!(This->Flags & SFLAG_LOCKED)) {
1024 WARN("trying to Unlock an unlocked surf@%p\n", This);
1025 return WINED3DERR_INVALIDCALL;
1028 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & SFLAG_DIRTY ? 1 : 0);
1030 if (!(This->Flags & SFLAG_DIRTY)) {
1031 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1032 goto unlock_end;
1035 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1036 if(swapchain || iface == myDevice->render_targets[0]) {
1037 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1038 static BOOL warned = FALSE;
1039 if(!warned) {
1040 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1041 warned = TRUE;
1043 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1044 goto unlock_end;
1047 /* Activate the correct context for the render target */
1048 ENTER_GL();
1049 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1051 if(!swapchain) {
1052 /* Primary offscreen render target */
1053 TRACE("Offscreen render target\n");
1054 glDrawBuffer(GL_BACK);
1055 checkGLcall("glDrawBuffer(GL_BACK)");
1056 } else {
1057 if(iface == swapchain->frontBuffer) {
1058 TRACE("Onscreen front buffer\n");
1059 glDrawBuffer(GL_FRONT);
1060 checkGLcall("glDrawBuffer(GL_FRONT)");
1061 } else if(iface == swapchain->backBuffer[0]) {
1062 TRACE("Onscreen back buffer\n");
1063 glDrawBuffer(GL_BACK);
1064 checkGLcall("glDrawBuffer(GL_BACK)");
1065 } else {
1066 FIXME("Unlocking a higher back buffer\n");
1067 glDrawBuffer(GL_BACK);
1068 checkGLcall("glDrawBuffer(GL_BACK)");
1070 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1073 switch(wined3d_settings.rendertargetlock_mode) {
1074 case RTL_AUTO:
1075 case RTL_READDRAW:
1076 case RTL_TEXDRAW:
1077 flush_to_framebuffer_drawpixels(This);
1078 break;
1080 case RTL_READTEX:
1081 case RTL_TEXTEX:
1082 flush_to_framebuffer_texture(This);
1083 break;
1085 if(!swapchain || swapchain->backBuffer) {
1086 glDrawBuffer(GL_BACK);
1087 checkGLcall("glDrawBuffer(GL_BACK)");
1088 } else {
1089 glDrawBuffer(GL_FRONT);
1090 checkGLcall("glDrawBuffer(GL_FRONT)");
1092 LEAVE_GL();
1094 /** restore clean dirty state */
1095 IWineD3DSurface_CleanDirtyRect(iface);
1096 } else if(iface == myDevice->stencilBufferTarget) {
1097 FIXME("Depth Stencil buffer locking is not implemented\n");
1098 } else {
1099 /* The rest should be a normal texture */
1100 IWineD3DBaseTextureImpl *impl;
1101 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1102 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1103 * states need resetting
1105 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1106 if(impl->baseTexture.bindCount) {
1107 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1109 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1113 unlock_end:
1114 This->Flags &= ~SFLAG_LOCKED;
1115 memset(&This->lockedRect, 0, sizeof(RECT));
1116 return WINED3D_OK;
1119 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1120 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1121 WINED3DLOCKED_RECT lock;
1122 UINT usage;
1123 BITMAPINFO* b_info;
1124 HDC ddc;
1125 DWORD *masks;
1126 HRESULT hr;
1127 RGBQUAD col[256];
1128 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1130 TRACE("(%p)->(%p)\n",This,pHDC);
1132 if(This->Flags & SFLAG_USERPTR) {
1133 ERR("Not supported on surfaces with an application-provided surfaces\n");
1134 return DDERR_NODC;
1137 /* Give more detailed info for ddraw */
1138 if (This->Flags & SFLAG_DCINUSE)
1139 return DDERR_DCALREADYCREATED;
1141 /* Can't GetDC if the surface is locked */
1142 if (This->Flags & SFLAG_LOCKED)
1143 return WINED3DERR_INVALIDCALL;
1145 memset(&lock, 0, sizeof(lock)); /* To be sure */
1147 /* Create a DIB section if there isn't a hdc yet */
1148 if(!This->hDC) {
1149 int extraline = 0;
1150 SYSTEM_INFO sysInfo;
1152 switch (This->bytesPerPixel) {
1153 case 2:
1154 case 4:
1155 /* Allocate extra space to store the RGB bit masks. */
1156 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1157 break;
1159 case 3:
1160 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1161 break;
1163 default:
1164 /* Allocate extra space for a palette. */
1165 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1166 sizeof(BITMAPINFOHEADER)
1167 + sizeof(RGBQUAD)
1168 * (1 << (This->bytesPerPixel * 8)));
1169 break;
1172 if (!b_info)
1173 return E_OUTOFMEMORY;
1175 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1176 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1177 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1178 * add an extra line to the dib section
1180 GetSystemInfo(&sysInfo);
1181 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1182 extraline = 1;
1183 TRACE("Adding an extra line to the dib section\n");
1186 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1187 b_info->bmiHeader.biWidth = This->currentDesc.Width;
1188 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1189 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1190 b_info->bmiHeader.biPlanes = 1;
1191 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1193 b_info->bmiHeader.biXPelsPerMeter = 0;
1194 b_info->bmiHeader.biYPelsPerMeter = 0;
1195 b_info->bmiHeader.biClrUsed = 0;
1196 b_info->bmiHeader.biClrImportant = 0;
1198 /* Get the bit masks */
1199 masks = (DWORD *) &(b_info->bmiColors);
1200 switch (This->resource.format) {
1201 case WINED3DFMT_R8G8B8:
1202 usage = DIB_RGB_COLORS;
1203 b_info->bmiHeader.biCompression = BI_RGB;
1204 break;
1206 case WINED3DFMT_X1R5G5B5:
1207 case WINED3DFMT_A1R5G5B5:
1208 case WINED3DFMT_A4R4G4B4:
1209 case WINED3DFMT_X4R4G4B4:
1210 case WINED3DFMT_R3G3B2:
1211 case WINED3DFMT_A8R3G3B2:
1212 case WINED3DFMT_A2B10G10R10:
1213 case WINED3DFMT_A8B8G8R8:
1214 case WINED3DFMT_X8B8G8R8:
1215 case WINED3DFMT_A2R10G10B10:
1216 case WINED3DFMT_R5G6B5:
1217 case WINED3DFMT_A16B16G16R16:
1218 usage = 0;
1219 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1220 masks[0] = formatEntry->redMask;
1221 masks[1] = formatEntry->greenMask;
1222 masks[2] = formatEntry->blueMask;
1223 break;
1225 default:
1226 /* Don't know palette */
1227 b_info->bmiHeader.biCompression = BI_RGB;
1228 usage = 0;
1229 break;
1232 ddc = GetDC(0);
1233 if (ddc == 0) {
1234 HeapFree(GetProcessHeap(), 0, b_info);
1235 return HRESULT_FROM_WIN32(GetLastError());
1238 TRACE("Creating a DIB section with size %dx%dx%d, size=%d\n", b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight, b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
1239 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1240 ReleaseDC(0, ddc);
1242 if (!This->dib.DIBsection) {
1243 ERR("CreateDIBSection failed!\n");
1244 HeapFree(GetProcessHeap(), 0, b_info);
1245 return HRESULT_FROM_WIN32(GetLastError());
1248 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1250 /* copy the existing surface to the dib section */
1251 if(This->resource.allocatedMemory) {
1252 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1253 /* We won't need that any more */
1254 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1255 } else {
1256 /* This is to make LockRect read the gl Texture although memory is allocated */
1257 This->Flags |= SFLAG_GLDIRTY;
1260 HeapFree(GetProcessHeap(), 0, b_info);
1262 /* Use the dib section from now on */
1263 This->resource.allocatedMemory = This->dib.bitmap_data;
1265 /* Now allocate a HDC */
1266 This->hDC = CreateCompatibleDC(0);
1267 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1268 TRACE("using wined3d palette %p\n", This->palette);
1269 SelectPalette(This->hDC,
1270 This->palette ? This->palette->hpal : 0,
1271 FALSE);
1273 This->Flags |= SFLAG_DIBSECTION;
1276 /* Lock the surface */
1277 hr = IWineD3DSurface_LockRect(iface,
1278 &lock,
1279 NULL,
1281 if(FAILED(hr)) {
1282 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1283 /* keep the dib section */
1284 return hr;
1287 if(This->resource.format == WINED3DFMT_P8 ||
1288 This->resource.format == WINED3DFMT_A8P8) {
1289 unsigned int n;
1290 if(This->palette) {
1291 PALETTEENTRY ent[256];
1293 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1294 for (n=0; n<256; n++) {
1295 col[n].rgbRed = ent[n].peRed;
1296 col[n].rgbGreen = ent[n].peGreen;
1297 col[n].rgbBlue = ent[n].peBlue;
1298 col[n].rgbReserved = 0;
1300 } else {
1301 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1303 for (n=0; n<256; n++) {
1304 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1305 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1306 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1307 col[n].rgbReserved = 0;
1311 SetDIBColorTable(This->hDC, 0, 256, col);
1314 *pHDC = This->hDC;
1315 TRACE("returning %p\n",*pHDC);
1316 This->Flags |= SFLAG_DCINUSE;
1318 return WINED3D_OK;
1321 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1322 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1324 TRACE("(%p)->(%p)\n",This,hDC);
1326 if (!(This->Flags & SFLAG_DCINUSE))
1327 return D3DERR_INVALIDCALL;
1329 /* we locked first, so unlock now */
1330 IWineD3DSurface_UnlockRect(iface);
1332 This->Flags &= ~SFLAG_DCINUSE;
1334 return WINED3D_OK;
1337 /* ******************************************************
1338 IWineD3DSurface Internal (No mapping to directx api) parts follow
1339 ****************************************************** */
1341 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, GLenum *format, GLenum *internal, GLenum *type, CONVERT_TYPES *convert, int *target_bpp) {
1342 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & DDSD_CKSRCBLT);
1343 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1345 /* Default values: From the surface */
1346 *format = formatEntry->glFormat;
1347 *internal = formatEntry->glInternal;
1348 *type = formatEntry->glType;
1349 *convert = NO_CONVERSION;
1350 *target_bpp = This->bytesPerPixel;
1352 /* Ok, now look if we have to do any conversion */
1353 switch(This->resource.format) {
1354 case WINED3DFMT_P8:
1355 /* ****************
1356 Paletted Texture
1357 **************** */
1358 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1359 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1361 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1362 *format = GL_RGBA;
1363 *internal = GL_RGBA;
1364 *type = GL_UNSIGNED_BYTE;
1365 *target_bpp = 4;
1366 if(colorkey_active) {
1367 *convert = CONVERT_PALETTED_CK;
1368 } else {
1369 *convert = CONVERT_PALETTED;
1373 break;
1375 case WINED3DFMT_R3G3B2:
1376 /* **********************
1377 GL_UNSIGNED_BYTE_3_3_2
1378 ********************** */
1379 if (colorkey_active) {
1380 /* This texture format will never be used.. So do not care about color keying
1381 up until the point in time it will be needed :-) */
1382 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1384 break;
1386 case WINED3DFMT_R5G6B5:
1387 if (colorkey_active) {
1388 *convert = CONVERT_CK_565;
1389 *format = GL_RGBA;
1390 *internal = GL_RGBA;
1391 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1393 break;
1395 case WINED3DFMT_R8G8B8:
1396 if (colorkey_active) {
1397 *convert = CONVERT_CK_RGB24;
1398 *format = GL_RGBA;
1399 *internal = GL_RGBA;
1400 *type = GL_UNSIGNED_INT_8_8_8_8;
1401 *target_bpp = 4;
1403 break;
1405 case WINED3DFMT_X8R8G8B8:
1406 if (colorkey_active) {
1407 *convert = CONVERT_RGB32_888;
1408 *format = GL_RGBA;
1409 *internal = GL_RGBA;
1410 *type = GL_UNSIGNED_INT_8_8_8_8;
1412 break;
1414 case WINED3DFMT_V8U8:
1415 *convert = CONVERT_V8U8;
1416 *format = GL_BGR;
1417 *internal = GL_RGB8;
1418 *type = GL_BYTE;
1419 *target_bpp = 3;
1420 break;
1422 default:
1423 break;
1426 return WINED3D_OK;
1429 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1430 BYTE *source, *dest;
1431 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1433 switch (convert) {
1434 case NO_CONVERSION:
1436 memcpy(dst, src, pitch * height);
1437 break;
1439 case CONVERT_PALETTED:
1440 case CONVERT_PALETTED_CK:
1442 IWineD3DPaletteImpl* pal = surf->palette;
1443 BYTE table[256][4];
1444 unsigned int i;
1445 unsigned int x, y;
1447 if( pal == NULL) {
1448 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1451 if (pal == NULL) {
1452 /* Still no palette? Use the device's palette */
1453 /* Get the surface's palette */
1454 for (i = 0; i < 256; i++) {
1455 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1457 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1458 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1459 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1460 if ((convert == CONVERT_PALETTED_CK) &&
1461 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1462 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1463 /* We should maybe here put a more 'neutral' color than the standard bright purple
1464 one often used by application to prevent the nice purple borders when bi-linear
1465 filtering is on */
1466 table[i][3] = 0x00;
1467 } else {
1468 table[i][3] = 0xFF;
1471 } else {
1472 TRACE("Using surface palette %p\n", pal);
1473 /* Get the surface's palette */
1474 for (i = 0; i < 256; i++) {
1475 table[i][0] = pal->palents[i].peRed;
1476 table[i][1] = pal->palents[i].peGreen;
1477 table[i][2] = pal->palents[i].peBlue;
1478 if ((convert == CONVERT_PALETTED_CK) &&
1479 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1480 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1481 /* We should maybe here put a more 'neutral' color than the standard bright purple
1482 one often used by application to prevent the nice purple borders when bi-linear
1483 filtering is on */
1484 table[i][3] = 0x00;
1485 } else {
1486 table[i][3] = 0xFF;
1491 for (y = 0; y < height; y++)
1493 source = src + pitch * y;
1494 dest = dst + outpitch * y;
1495 /* This is an 1 bpp format, using the width here is fine */
1496 for (x = 0; x < width; x++) {
1497 BYTE color = *source++;
1498 *dest++ = table[color][0];
1499 *dest++ = table[color][1];
1500 *dest++ = table[color][2];
1501 *dest++ = table[color][3];
1505 break;
1507 case CONVERT_CK_565:
1509 /* Converting the 565 format in 5551 packed to emulate color-keying.
1511 Note : in all these conversion, it would be best to average the averaging
1512 pixels to get the color of the pixel that will be color-keyed to
1513 prevent 'color bleeding'. This will be done later on if ever it is
1514 too visible.
1516 Note2: Nvidia documents say that their driver does not support alpha + color keying
1517 on the same surface and disables color keying in such a case
1519 unsigned int x, y;
1520 WORD *Source;
1521 WORD *Dest;
1523 TRACE("Color keyed 565\n");
1525 for (y = 0; y < height; y++) {
1526 Source = (WORD *) (src + y * pitch);
1527 Dest = (WORD *) (dst + y * outpitch);
1528 for (x = 0; x < width; x++ ) {
1529 WORD color = *Source++;
1530 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1531 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1532 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1533 *Dest |= 0x0001;
1535 Dest++;
1539 break;
1541 case CONVERT_V8U8:
1543 unsigned int x, y;
1544 short *Source;
1545 char *Dest;
1546 for(y = 0; y < height; y++) {
1547 Source = (short *) (src + y * pitch);
1548 Dest = (char *) (dst + y * outpitch);
1549 for (x = 0; x < width; x++ ) {
1550 long color = (*Source++);
1551 Dest[0] = color >> 8;
1552 Dest[1] = color;
1553 Dest[2] = 0xff;
1554 Dest += 3;
1557 break;
1560 default:
1561 ERR("Unsupported conversation type %d\n", convert);
1563 return WINED3D_OK;
1566 /* This function is used in case of 8bit paletted textures to upload the palette.
1567 For now it only supports GL_EXT_paletted_texture extension but support for other
1568 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1570 void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1571 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1572 IWineD3DPaletteImpl* pal = This->palette;
1573 BYTE table[256][4];
1574 int i;
1576 if (pal == NULL) {
1577 /* Still no palette? Use the device's palette */
1578 /* Get the surface's palette */
1579 for (i = 0; i < 256; i++) {
1580 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1582 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1583 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1584 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1585 if ((convert == CONVERT_PALETTED_CK) &&
1586 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1587 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1588 /* We should maybe here put a more 'neutral' color than the standard bright purple
1589 one often used by application to prevent the nice purple borders when bi-linear
1590 filtering is on */
1591 table[i][3] = 0x00;
1592 } else {
1593 table[i][3] = 0xFF;
1596 } else {
1597 TRACE("Using surface palette %p\n", pal);
1598 /* Get the surface's palette */
1599 for (i = 0; i < 256; i++) {
1600 table[i][0] = pal->palents[i].peRed;
1601 table[i][1] = pal->palents[i].peGreen;
1602 table[i][2] = pal->palents[i].peBlue;
1603 if ((convert == CONVERT_PALETTED_CK) &&
1604 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1605 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1606 /* We should maybe here put a more 'neutral' color than the standard bright purple
1607 one often used by application to prevent the nice purple borders when bi-linear
1608 filtering is on */
1609 table[i][3] = 0x00;
1610 } else {
1611 table[i][3] = 0xFF;
1615 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1618 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface) {
1619 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1620 GLenum format, internal, type;
1621 CONVERT_TYPES convert;
1622 int bpp;
1623 int width, pitch, outpitch;
1624 BYTE *mem;
1626 if (This->Flags & SFLAG_INTEXTURE) {
1627 TRACE("Surface already in texture\n");
1628 return WINED3D_OK;
1630 if (This->Flags & SFLAG_DIRTY) {
1631 TRACE("Reloading because surface is dirty\n");
1632 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1633 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & DDSD_CKSRCBLT))) ||
1634 /* Reload: vice versa OR */
1635 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & DDSD_CKSRCBLT)) ||
1636 /* Also reload: Color key is active AND the color key has changed */
1637 ((This->CKeyFlags & DDSD_CKSRCBLT) && (
1638 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1639 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1640 TRACE("Reloading because of color keying\n");
1641 } else {
1642 TRACE("surface isn't dirty\n");
1643 return WINED3D_OK;
1646 This->Flags &= ~SFLAG_DIRTY;
1648 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1649 * These resources are not bound by device size or format restrictions. Because of this,
1650 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1651 * However, these resources can always be created, locked, and copied.
1653 if (This->resource.pool == WINED3DPOOL_SCRATCH && !(This->Flags & SFLAG_FORCELOAD) )
1655 FIXME("(%p) Operation not supported for scratch textures\n",This);
1656 return WINED3DERR_INVALIDCALL;
1659 if (This->Flags & SFLAG_INPBUFFER) {
1660 if (This->glDescription.level != 0)
1661 FIXME("Surface in texture is only supported for level 0\n");
1662 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
1663 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
1664 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
1665 This->resource.format == WINED3DFMT_DXT5)
1666 FIXME("Format %d not supported\n", This->resource.format);
1667 else {
1668 GLint prevRead;
1670 ENTER_GL();
1672 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1673 vcheckGLcall("glGetIntegerv");
1674 glReadBuffer(GL_BACK);
1675 vcheckGLcall("glReadBuffer");
1677 glCopyTexImage2D(This->glDescription.target,
1678 This->glDescription.level,
1679 This->glDescription.glFormatInternal,
1682 This->currentDesc.Width,
1683 This->currentDesc.Height,
1686 checkGLcall("glCopyTexImage2D");
1687 glReadBuffer(prevRead);
1688 vcheckGLcall("glReadBuffer");
1690 LEAVE_GL();
1692 TRACE("Updating target %d\n", This->glDescription.target);
1693 This->Flags |= SFLAG_INTEXTURE;
1695 return WINED3D_OK;
1698 if(This->CKeyFlags & DDSD_CKSRCBLT) {
1699 This->Flags |= SFLAG_GLCKEY;
1700 This->glCKey = This->SrcBltCKey;
1702 else This->Flags &= ~SFLAG_GLCKEY;
1704 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp);
1706 /* The width is in 'length' not in bytes */
1707 width = This->currentDesc.Width;
1708 pitch = IWineD3DSurface_GetPitch(iface);
1710 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
1711 int height = This->glRect.bottom - This->glRect.top;
1713 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
1714 outpitch = width * bpp;
1715 outpitch = (outpitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
1717 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
1718 if(!mem) {
1719 ERR("Out of memory %d, %d!\n", outpitch, height);
1720 return WINED3DERR_OUTOFVIDEOMEMORY;
1722 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
1724 This->Flags |= SFLAG_CONVERTED;
1725 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
1726 d3dfmt_p8_upload_palette(iface, convert);
1727 This->Flags &= ~SFLAG_CONVERTED;
1728 mem = This->resource.allocatedMemory;
1729 } else {
1730 This->Flags &= ~SFLAG_CONVERTED;
1731 mem = This->resource.allocatedMemory;
1734 /* Make sure the correct pitch is used */
1735 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
1737 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
1738 TRACE("non power of two support\n");
1739 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
1740 if (mem) {
1741 surface_upload_data(This, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
1743 } else {
1744 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
1745 if (mem) {
1746 surface_upload_data(This, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
1750 /* Restore the default pitch */
1751 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1753 if (mem != This->resource.allocatedMemory)
1754 HeapFree(GetProcessHeap(), 0, mem);
1756 #if 0
1758 static unsigned int gen = 0;
1759 char buffer[4096];
1760 ++gen;
1761 if ((gen % 10) == 0) {
1762 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
1763 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
1766 * debugging crash code
1767 if (gen == 250) {
1768 void** test = NULL;
1769 *test = 0;
1773 #endif
1775 if (!(This->Flags & SFLAG_DONOTFREE)) {
1776 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1777 This->resource.allocatedMemory = NULL;
1780 return WINED3D_OK;
1783 #include <errno.h>
1784 #include <stdio.h>
1785 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
1786 FILE* f = NULL;
1787 UINT i, y;
1788 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1789 char *allocatedMemory;
1790 char *textureRow;
1791 IWineD3DSwapChain *swapChain = NULL;
1792 int width, height;
1793 GLuint tmpTexture = 0;
1794 DWORD color;
1795 /*FIXME:
1796 Textures my not be stored in ->allocatedgMemory and a GlTexture
1797 so we should lock the surface before saving a snapshot, or at least check that
1799 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
1800 by calling GetTexImage and in compressed form by calling
1801 GetCompressedTexImageARB. Queried compressed images can be saved and
1802 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
1803 texture images do not need to be processed by the GL and should
1804 significantly improve texture loading performance relative to uncompressed
1805 images. */
1807 /* Setup the width and height to be the internal texture width and height. */
1808 width = This->pow2Width;
1809 height = This->pow2Height;
1810 /* check to see if were a 'virtual' texture e.g. were not a pbuffer of texture were a back buffer*/
1811 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
1813 if (swapChain || (This->Flags & SFLAG_INPBUFFER)) { /* if were not a real texture then read the back buffer into a real texture*/
1814 /* we don't want to interfere with the back buffer so read the data into a temporary texture and then save the data out of the temporary texture */
1815 GLint prevRead;
1816 ENTER_GL();
1817 FIXME("(%p) This surface needs to be locked before a snapshot can be taken\n", This);
1818 glEnable(GL_TEXTURE_2D);
1820 glGenTextures(1, &tmpTexture);
1821 glBindTexture(GL_TEXTURE_2D, tmpTexture);
1823 glTexImage2D(GL_TEXTURE_2D,
1825 GL_RGBA,
1826 width,
1827 height,
1828 0/*border*/,
1829 GL_RGBA,
1830 GL_UNSIGNED_INT_8_8_8_8_REV,
1831 NULL);
1833 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1834 vcheckGLcall("glGetIntegerv");
1835 glReadBuffer(GL_BACK);
1836 vcheckGLcall("glReadBuffer");
1837 glCopyTexImage2D(GL_TEXTURE_2D,
1839 GL_RGBA,
1842 width,
1843 height,
1846 checkGLcall("glCopyTexImage2D");
1847 glReadBuffer(prevRead);
1848 LEAVE_GL();
1850 } else { /* bind the real texture */
1851 IWineD3DSurface_PreLoad(iface);
1853 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
1854 ENTER_GL();
1855 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
1856 glGetTexImage(GL_TEXTURE_2D,
1857 This->glDescription.level,
1858 GL_RGBA,
1859 GL_UNSIGNED_INT_8_8_8_8_REV,
1860 allocatedMemory);
1861 checkGLcall("glTexImage2D");
1862 if (tmpTexture) {
1863 glBindTexture(GL_TEXTURE_2D, 0);
1864 glDeleteTextures(1, &tmpTexture);
1866 LEAVE_GL();
1868 f = fopen(filename, "w+");
1869 if (NULL == f) {
1870 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
1871 return WINED3DERR_INVALIDCALL;
1873 /* Save the dat out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha chanel*/
1874 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
1875 /* TGA header */
1876 fputc(0,f);
1877 fputc(0,f);
1878 fputc(2,f);
1879 fputc(0,f);
1880 fputc(0,f);
1881 fputc(0,f);
1882 fputc(0,f);
1883 fputc(0,f);
1884 fputc(0,f);
1885 fputc(0,f);
1886 fputc(0,f);
1887 fputc(0,f);
1888 /* short width*/
1889 fwrite(&width,2,1,f);
1890 /* short height */
1891 fwrite(&height,2,1,f);
1892 /* format rgba */
1893 fputc(0x20,f);
1894 fputc(0x28,f);
1895 /* raw data */
1896 /* if the data is upside down if we've fetched it from a back buffer, so it needs flipping again to make it the correct way up*/
1897 if(swapChain)
1898 textureRow = allocatedMemory + (width * (height - 1) *4);
1899 else
1900 textureRow = allocatedMemory;
1901 for (y = 0 ; y < height; y++) {
1902 for (i = 0; i < width; i++) {
1903 color = *((DWORD*)textureRow);
1904 fputc((color >> 16) & 0xFF, f); /* B */
1905 fputc((color >> 8) & 0xFF, f); /* G */
1906 fputc((color >> 0) & 0xFF, f); /* R */
1907 fputc((color >> 24) & 0xFF, f); /* A */
1908 textureRow += 4;
1910 /* take two rows of the pointer to the texture memory */
1911 if(swapChain)
1912 (textureRow-= width << 3);
1915 TRACE("Closing file\n");
1916 fclose(f);
1918 if(swapChain) {
1919 IWineD3DSwapChain_Release(swapChain);
1921 HeapFree(GetProcessHeap(), 0, allocatedMemory);
1922 return WINED3D_OK;
1925 HRESULT WINAPI IWineD3DSurfaceImpl_CleanDirtyRect(IWineD3DSurface *iface) {
1926 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1927 This->Flags &= ~SFLAG_DIRTY;
1928 This->dirtyRect.left = This->currentDesc.Width;
1929 This->dirtyRect.top = This->currentDesc.Height;
1930 This->dirtyRect.right = 0;
1931 This->dirtyRect.bottom = 0;
1932 TRACE("(%p) : Dirty?%d, Rect:(%d,%d,%d,%d)\n", This, This->Flags & SFLAG_DIRTY ? 1 : 0, This->dirtyRect.left,
1933 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
1934 return WINED3D_OK;
1938 * Slightly inefficient way to handle multiple dirty rects but it works :)
1940 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
1941 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1942 IWineD3DBaseTexture *baseTexture = NULL;
1943 This->Flags |= SFLAG_DIRTY;
1944 if (NULL != pDirtyRect) {
1945 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
1946 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
1947 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
1948 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
1949 } else {
1950 This->dirtyRect.left = 0;
1951 This->dirtyRect.top = 0;
1952 This->dirtyRect.right = This->currentDesc.Width;
1953 This->dirtyRect.bottom = This->currentDesc.Height;
1955 TRACE("(%p) : Dirty?%d, Rect:(%d,%d,%d,%d)\n", This, This->Flags & SFLAG_DIRTY, This->dirtyRect.left,
1956 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
1957 /* if the container is a basetexture then mark it dirty. */
1958 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
1959 TRACE("Passing to conatiner\n");
1960 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
1961 IWineD3DBaseTexture_Release(baseTexture);
1963 return WINED3D_OK;
1966 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
1967 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1969 TRACE("This %p, container %p\n", This, container);
1971 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
1973 TRACE("Setting container to %p from %p\n", container, This->container);
1974 This->container = container;
1976 return WINED3D_OK;
1979 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
1980 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1981 const PixelFormatDesc *formatEntry = getFormatDescEntry(format);
1983 if (This->resource.format != WINED3DFMT_UNKNOWN) {
1984 FIXME("(%p) : The foramt of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
1985 return WINED3DERR_INVALIDCALL;
1988 TRACE("(%p) : Setting texture foramt to (%d,%s)\n", This, format, debug_d3dformat(format));
1989 if (format == WINED3DFMT_UNKNOWN) {
1990 This->resource.size = 0;
1991 } else if (format == WINED3DFMT_DXT1) {
1992 /* DXT1 is half byte per pixel */
1993 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
1995 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
1996 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
1997 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
1998 } else {
1999 This->resource.size = ((This->pow2Width * formatEntry->bpp) + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
2000 This->resource.size *= This->pow2Height;
2004 /* Setup some glformat defaults */
2005 This->glDescription.glFormat = formatEntry->glFormat;
2006 This->glDescription.glFormatInternal = formatEntry->glInternal;
2007 This->glDescription.glType = formatEntry->glType;
2009 if (format != WINED3DFMT_UNKNOWN) {
2010 This->bytesPerPixel = formatEntry->bpp;
2011 } else {
2012 This->bytesPerPixel = 0;
2015 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2017 This->resource.format = format;
2019 TRACE("(%p) : Size %d, bytesPerPixel %d, glFormat %d, glFotmatInternal %d, glType %d\n", This, This->resource.size, This->bytesPerPixel, This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2021 return WINED3D_OK;
2024 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2025 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2027 /* Render targets depend on their hdc, and we can't create a hdc on a user pointer */
2028 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2029 ERR("Not supported on render targets\n");
2030 return WINED3DERR_INVALIDCALL;
2033 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2034 WARN("Surface is locked or the HDC is in use\n");
2035 return WINED3DERR_INVALIDCALL;
2038 if(Mem && Mem != This->resource.allocatedMemory) {
2040 /* Do I have to copy the old surface content? */
2041 if(This->Flags & SFLAG_DIBSECTION) {
2042 /* Release the DC. No need to hold the critical section for the update
2043 * Thread because this thread runs only on front buffers, but this method
2044 * fails for render targets in the check above.
2046 SelectObject(This->hDC, This->dib.holdbitmap);
2047 DeleteDC(This->hDC);
2048 /* Release the DIB section */
2049 DeleteObject(This->dib.DIBsection);
2050 This->dib.bitmap_data = NULL;
2051 This->resource.allocatedMemory = NULL;
2052 This->hDC = NULL;
2053 This->Flags &= ~SFLAG_DIBSECTION;
2054 } else if(!(This->Flags & SFLAG_USERPTR)) {
2055 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2057 This->resource.allocatedMemory = Mem;
2058 This->Flags |= SFLAG_USERPTR;
2059 } else if(This->Flags & SFLAG_USERPTR) {
2060 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2061 This->resource.allocatedMemory = NULL;
2062 This->Flags &= ~SFLAG_USERPTR;
2064 return WINED3D_OK;
2067 /* TODO: replace this function with context management routines */
2068 HRESULT WINAPI IWineD3DSurfaceImpl_SetPBufferState(IWineD3DSurface *iface, BOOL inPBuffer, BOOL inTexture) {
2069 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2071 if(inPBuffer) {
2072 This->Flags |= SFLAG_INPBUFFER;
2073 } else {
2074 This->Flags &= ~SFLAG_INPBUFFER;
2077 if(inTexture) {
2078 This->Flags |= SFLAG_INTEXTURE;
2079 } else {
2080 This->Flags &= ~SFLAG_INTEXTURE;
2083 return WINED3D_OK;
2086 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2087 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2088 IWineD3DDevice *D3D = (IWineD3DDevice *) This->resource.wineD3DDevice;
2089 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2091 /* Flipping is only supported on RenderTargets */
2092 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return DDERR_NOTFLIPPABLE;
2094 if(override) {
2095 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2096 * FIXME("(%p) Target override is not supported by now\n", This);
2097 * Additionally, it isn't really possible to support triple-buffering
2098 * properly on opengl at all
2102 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2103 return IWineD3DDevice_Present(D3D, NULL, NULL, 0, NULL);
2106 /* Does a direct frame buffer -> texture copy. Stretching is done
2107 * with single pixel copy calls
2109 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2110 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2111 float xrel, yrel;
2112 UINT row;
2113 BOOL warned = FALSE; /* deliberately not static */
2114 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2116 ENTER_GL();
2118 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2120 /* Bind the target texture */
2121 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2122 checkGLcall("glBindTexture");
2123 if(swapchain->backBuffer && SrcSurface == swapchain->backBuffer[0]) {
2124 glReadBuffer(GL_BACK);
2125 } else {
2126 glReadBuffer(GL_FRONT);
2128 checkGLcall("glReadBuffer");
2130 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2131 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2133 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2134 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2137 if(upsidedown &&
2138 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2139 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2140 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2142 glCopyTexSubImage2D(This->glDescription.target,
2143 This->glDescription.level,
2144 drect->x1, drect->y1, /* xoffset, yoffset */
2145 srect->x1, Src->currentDesc.Height - srect->y2,
2146 drect->x2 - drect->x1, drect->y2 - drect->y1);
2147 } else {
2148 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2149 /* I have to process this row by row to swap the image,
2150 * otherwise it would be upside down, so streching in y direction
2151 * doesn't cost extra time
2153 * However, streching in x direction can be avoided if not necessary
2155 for(row = drect->y1; row < drect->y2; row++) {
2156 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2157 /* Well, that stuff works, but it's very slow.
2158 * find a better way instead
2160 UINT col;
2162 if(!warned) {
2163 warned = TRUE;
2164 FIXME("Doing a pixel by pixel render target -> texture copy, expect performance issues\n");
2167 for(col = drect->x1; col < drect->x2; col++) {
2168 glCopyTexSubImage2D(This->glDescription.target,
2169 This->glDescription.level,
2170 drect->x1 + col, row, /* xoffset, yoffset */
2171 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2172 1, 1);
2174 } else {
2175 glCopyTexSubImage2D(This->glDescription.target,
2176 This->glDescription.level,
2177 drect->x1, row, /* xoffset, yoffset */
2178 srect->x1, yoffset - (int) (row * yrel),
2179 drect->x2-drect->x1, 1);
2184 vcheckGLcall("glCopyTexSubImage2D");
2185 LEAVE_GL();
2188 /* Uses the hardware to stretch and flip the image */
2189 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2190 GLuint backup, src;
2191 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2192 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2193 float left, right, top, bottom; /* Texture coordinates */
2194 UINT fbwidth = Src->currentDesc.Width;
2195 UINT fbheight = Src->currentDesc.Height;
2197 TRACE("Using hwstretch blit\n");
2198 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2199 ENTER_GL();
2200 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2202 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2203 * we are reading from the back buffer, the backup can be used as source texture
2205 glGenTextures(1, &backup);
2206 checkGLcall("glGenTextures(1, &backup)");
2207 glBindTexture(GL_TEXTURE_2D, backup);
2208 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2210 glReadBuffer(GL_BACK);
2211 checkGLcall("glReadBuffer(GL_BACK)");
2213 /* TODO: Only back up the part that will be overwritten */
2214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2215 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2216 checkGLcall("glTexImage2D");
2218 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2219 0, 0 /* read offsets */,
2220 0, 0,
2221 fbwidth,
2222 fbheight);
2224 checkGLcall("glCopyTexSubImage2D");
2226 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2227 checkGLcall("glTexParameteri");
2228 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2229 checkGLcall("glTexParameteri");
2231 if((IWineD3DSurface *) This == swapchain->backBuffer[0] || TRUE) {
2232 src = backup;
2233 } else {
2234 glReadBuffer(GL_FRONT);
2235 checkGLcall("glReadBuffer(GL_FRONT)");
2237 glGenTextures(1, &src);
2238 checkGLcall("glGenTextures(1, &src)");
2239 glBindTexture(GL_TEXTURE_2D, src);
2240 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2242 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2243 * out for power of 2 sizes
2245 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2246 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2247 checkGLcall("glTexImage2D");
2248 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2249 0, 0 /* read offsets */,
2250 0, 0,
2251 fbwidth,
2252 fbheight);
2254 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2255 checkGLcall("glTexParameteri");
2256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2257 checkGLcall("glTexParameteri");
2259 glReadBuffer(GL_BACK);
2260 checkGLcall("glReadBuffer(GL_BACK)");
2262 checkGLcall("glEnd and previous");
2264 left = (float) srect->x1 / (float) Src->pow2Width;
2265 right = (float) srect->x2 / (float) Src->pow2Width;
2267 if(upsidedown) {
2268 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2269 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2270 } else {
2271 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2272 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2275 /* draw the source texture stretched and upside down. The correct surface is bound already */
2276 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2277 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2279 glBegin(GL_QUADS);
2280 /* bottom left */
2281 glTexCoord2f(left, bottom);
2282 glVertex2i(0, fbheight);
2284 /* top left */
2285 glTexCoord2f(left, top);
2286 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2288 /* top right */
2289 glTexCoord2f(right, top);
2290 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2292 /* bottom right */
2293 glTexCoord2f(right, bottom);
2294 glVertex2i(drect->x2 - drect->x1, fbheight);
2295 glEnd();
2296 checkGLcall("glEnd and previous");
2298 /* Now read the stretched and upside down image into the destination texture */
2299 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2300 checkGLcall("glBindTexture");
2301 glCopyTexSubImage2D(This->glDescription.target,
2303 drect->x1, drect->y1, /* xoffset, yoffset */
2304 0, 0, /* We blitted the image to the origin */
2305 drect->x2 - drect->x1, drect->y2 - drect->y1);
2306 checkGLcall("glCopyTexSubImage2D");
2308 /* Write the back buffer backup back */
2309 glBindTexture(GL_TEXTURE_2D, backup);
2310 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2312 glBegin(GL_QUADS);
2313 /* top left */
2314 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2315 glVertex2i(0, 0);
2317 /* bottom left */
2318 glTexCoord2f(0.0, 0.0);
2319 glVertex2i(0, fbheight);
2321 /* bottom right */
2322 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2323 glVertex2i(fbwidth, Src->currentDesc.Height);
2325 /* top right */
2326 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2327 glVertex2i(fbwidth, 0);
2328 glEnd();
2330 /* Cleanup */
2331 if(src != backup) {
2332 glDeleteTextures(1, &src);
2333 checkGLcall("glDeleteTextures(1, &src)");
2335 glDeleteTextures(1, &backup);
2336 checkGLcall("glDeleteTextures(1, &backup)");
2337 LEAVE_GL();
2340 /* Not called from the VTable */
2341 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2342 WINED3DRECT rect;
2343 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2344 IWineD3DSwapChainImpl *swapchain = NULL;
2345 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2346 BOOL SrcOK = TRUE;
2348 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2350 /* Get the swapchain. One of the surfaces has to be a primary surface */
2351 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
2352 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2353 else if(Src) {
2354 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&swapchain);
2355 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2356 else return WINED3DERR_INVALIDCALL;
2357 } else {
2358 swapchain = NULL;
2361 if (DestRect) {
2362 rect.x1 = DestRect->left;
2363 rect.y1 = DestRect->top;
2364 rect.x2 = DestRect->right;
2365 rect.y2 = DestRect->bottom;
2366 } else {
2367 rect.x1 = 0;
2368 rect.y1 = 0;
2369 rect.x2 = This->currentDesc.Width;
2370 rect.y2 = This->currentDesc.Height;
2373 /* Half-life does a Blt from the back buffer to the front buffer,
2374 * Full surface size, no flags... Use present instead
2376 if(Src)
2378 /* First, check if we can do a Flip */
2380 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2381 if( SrcRect ) {
2382 if( (SrcRect->left == 0) && (SrcRect->top == 0) &&
2383 (SrcRect->right == Src->currentDesc.Width) && (SrcRect->bottom == Src->currentDesc.Height) ) {
2384 SrcOK = TRUE;
2386 } else {
2387 SrcOK = TRUE;
2390 /* Check the Destination rect and the surface sizes */
2391 if(SrcOK &&
2392 (rect.x1 == 0) && (rect.y1 == 0) &&
2393 (rect.x2 == This->currentDesc.Width) && (rect.y2 == This->currentDesc.Height) &&
2394 (This->currentDesc.Width == Src->currentDesc.Width) &&
2395 (This->currentDesc.Height == Src->currentDesc.Height)) {
2396 /* These flags are unimportant for the flag check, remove them */
2398 if((Flags & ~(DDBLT_DONOTWAIT | DDBLT_WAIT)) == 0) {
2399 if( swapchain->backBuffer && ((IWineD3DSurface *) This == swapchain->frontBuffer) && ((IWineD3DSurface *) Src == swapchain->backBuffer[0]) ) {
2401 D3DSWAPEFFECT orig_swap = swapchain->presentParms.SwapEffect;
2403 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2404 * take very long, while a flip is fast.
2405 * This applies to Half-Life, which does such Blts every time it finished
2406 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2407 * menu. This is also used by all apps when they do windowed rendering
2409 * The problem is that flipping is not really the same as copying. After a
2410 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2411 * untouched. Therefore it's necessary to override the swap effect
2412 * and to set it back after the flip.
2415 swapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2417 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2418 IWineD3DDevice_Present((IWineD3DDevice *) This->resource.wineD3DDevice,
2419 NULL, NULL, 0, NULL);
2421 swapchain->presentParms.SwapEffect = orig_swap;
2423 return WINED3D_OK;
2428 /* Blt from texture to rendertarget? */
2429 if( ( ( (IWineD3DSurface *) This == swapchain->frontBuffer) ||
2430 ( swapchain->backBuffer && (IWineD3DSurface *) This == swapchain->backBuffer[0]) )
2432 ( ( (IWineD3DSurface *) Src != swapchain->frontBuffer) &&
2433 ( swapchain->backBuffer && (IWineD3DSurface *) Src != swapchain->backBuffer[0]) ) ) {
2434 float glTexCoord[4];
2435 DWORD oldCKey;
2436 DDCOLORKEY oldBltCKey = {0,0};
2437 RECT SourceRectangle;
2438 GLint oldDraw;
2440 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2442 if(SrcRect) {
2443 SourceRectangle.left = SrcRect->left;
2444 SourceRectangle.right = SrcRect->right;
2445 SourceRectangle.top = SrcRect->top;
2446 SourceRectangle.bottom = SrcRect->bottom;
2447 } else {
2448 SourceRectangle.left = 0;
2449 SourceRectangle.right = Src->currentDesc.Width;
2450 SourceRectangle.top = 0;
2451 SourceRectangle.bottom = Src->currentDesc.Height;
2454 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2455 /* Fall back to software */
2456 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2457 SourceRectangle.left, SourceRectangle.top,
2458 SourceRectangle.right, SourceRectangle.bottom);
2459 return WINED3DERR_INVALIDCALL;
2462 /* Color keying: Check if we have to do a color keyed blt,
2463 * and if not check if a color key is activated.
2465 oldCKey = Src->CKeyFlags;
2466 if(!(Flags & DDBLT_KEYSRC) &&
2467 Src->CKeyFlags & DDSD_CKSRCBLT) {
2468 /* Ok, the surface has a color key, but we shall not use it -
2469 * Deactivate it for now, LoadTexture will catch this
2471 Src->CKeyFlags &= ~DDSD_CKSRCBLT;
2474 /* Color keying */
2475 if(Flags & DDBLT_KEYDEST) {
2476 oldBltCKey = This->SrcBltCKey;
2477 /* Temporary replace the source color key with the destination one. We do this because the color conversion code which
2478 * is in the end called from LoadTexture works with the source color. At the end of this function we restore the color key.
2480 This->SrcBltCKey = This->DestBltCKey;
2481 } else if (Flags & DDBLT_KEYSRC)
2482 oldBltCKey = This->SrcBltCKey;
2484 /* Now load the surface */
2485 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
2487 ENTER_GL();
2489 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
2491 glGetIntegerv(GL_DRAW_BUFFER, &oldDraw);
2492 if(This == (IWineD3DSurfaceImpl *) swapchain->frontBuffer) {
2493 TRACE("Drawing to front buffer\n");
2494 glDrawBuffer(GL_FRONT);
2495 checkGLcall("glDrawBuffer GL_FRONT");
2498 /* Bind the texture */
2499 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2500 checkGLcall("glBindTexture");
2502 /* No filtering for blts */
2503 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2504 GL_NEAREST);
2505 checkGLcall("glTexParameteri");
2506 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2507 GL_NEAREST);
2508 checkGLcall("glTexParameteri");
2509 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2510 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2511 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2512 checkGLcall("glTexEnvi");
2514 /* This is for color keying */
2515 if(Flags & DDBLT_KEYSRC) {
2516 glEnable(GL_ALPHA_TEST);
2517 checkGLcall("glEnable GL_ALPHA_TEST");
2518 glAlphaFunc(GL_NOTEQUAL, 0.0);
2519 checkGLcall("glAlphaFunc\n");
2520 } else {
2521 glDisable(GL_ALPHA_TEST);
2522 checkGLcall("glDisable GL_ALPHA_TEST");
2525 /* Draw a textured quad
2527 glBegin(GL_QUADS);
2529 glColor3d(1.0f, 1.0f, 1.0f);
2530 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
2531 glVertex3f(rect.x1,
2532 rect.y1,
2533 0.0);
2535 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
2536 glVertex3f(rect.x1, rect.y2, 0.0);
2538 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
2539 glVertex3f(rect.x2,
2540 rect.y2,
2541 0.0);
2543 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
2544 glVertex3f(rect.x2,
2545 rect.y1,
2546 0.0);
2547 glEnd();
2548 checkGLcall("glEnd");
2550 /* Unbind the texture */
2551 glBindTexture(GL_TEXTURE_2D, 0);
2552 checkGLcall("glEnable glBindTexture");
2554 if(This == (IWineD3DSurfaceImpl *) swapchain->frontBuffer && oldDraw == GL_BACK) {
2555 glDrawBuffer(oldDraw);
2558 /* Restore the color key flags */
2559 if(oldCKey != Src->CKeyFlags) {
2560 Src->CKeyFlags = oldCKey;
2563 /* Restore the old color key */
2564 if (Flags & (DDBLT_KEYSRC | DDBLT_KEYDEST))
2565 This->SrcBltCKey = oldBltCKey;
2567 LEAVE_GL();
2569 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
2570 This->Flags |= SFLAG_GLDIRTY;
2572 return WINED3D_OK;
2576 /* Blt from rendertarget to texture? */
2577 if( (SrcSurface == swapchain->frontBuffer) ||
2578 (swapchain->backBuffer && SrcSurface == swapchain->backBuffer[0]) ) {
2579 if( ( (IWineD3DSurface *) This != swapchain->frontBuffer) &&
2580 ( swapchain->backBuffer && (IWineD3DSurface *) This != swapchain->backBuffer[0]) ) {
2581 WINED3DRECT srect;
2582 BOOL upsideDown, stretchx;
2584 TRACE("Blt from rendertarget to texture\n");
2586 /* Call preload for the surface to make sure it isn't dirty */
2587 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2589 /* Make sure that the top pixel is always above the bottom pixel, and keep a seperate upside down flag
2590 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2592 if(SrcRect) {
2593 if(SrcRect->top < SrcRect->bottom) {
2594 srect.y1 = SrcRect->top;
2595 srect.y2 = SrcRect->bottom;
2596 upsideDown = FALSE;
2597 } else {
2598 srect.y1 = SrcRect->bottom;
2599 srect.y2 = SrcRect->top;
2600 upsideDown = TRUE;
2602 srect.x1 = SrcRect->left;
2603 srect.x2 = SrcRect->right;
2604 } else {
2605 srect.x1 = 0;
2606 srect.y1 = 0;
2607 srect.x2 = Src->currentDesc.Width;
2608 srect.y2 = Src->currentDesc.Height;
2609 upsideDown = FALSE;
2611 if(rect.x1 > rect.x2) {
2612 UINT tmp = rect.x2;
2613 rect.x2 = rect.x1;
2614 rect.x1 = tmp;
2615 upsideDown = !upsideDown;
2618 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2619 stretchx = TRUE;
2620 } else {
2621 stretchx = FALSE;
2624 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2625 * flip the image nor scale it. If GL_EXT_framebuffer_blit is available it can be used(hopefully,
2626 * not implemented by now). Otherwise:
2628 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2629 * -> If the app wants a image width an unscaled width, copy it line per line
2630 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2631 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2632 * back buffer. This is slower than reading line per line, thus not used for flipping
2633 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2634 * pixel by pixel
2636 if(FALSE /* GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) */) {
2637 TRACE("Using GL_EXT_framebuffer_blit for copying\n");
2638 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2639 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2640 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2641 fb_copy_to_texture_direct(This, SrcSurface, swapchain, &srect, &rect, upsideDown);
2642 } else {
2643 TRACE("Using hardware stretching to flip / stretch the texture\n");
2644 fb_copy_to_texture_hwstretch(This, SrcSurface, swapchain, &srect, &rect, upsideDown);
2647 if(!(This->Flags & SFLAG_DONOTFREE)) {
2648 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2649 This->resource.allocatedMemory = NULL;
2650 } else {
2651 This->Flags |= SFLAG_GLDIRTY;
2654 return WINED3D_OK;
2659 if (Flags & DDBLT_COLORFILL) {
2660 /* This is easy to handle for the D3D Device... */
2661 DWORD color;
2662 IWineD3DSwapChainImpl *implSwapChain;
2664 TRACE("Colorfill\n");
2666 /* The color as given in the Blt function is in the format of the frame-buffer...
2667 * 'clear' expect it in ARGB format => we need to do some conversion :-)
2669 if (This->resource.format == WINED3DFMT_P8) {
2670 if (This->palette) {
2671 color = ((0xFF000000) |
2672 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
2673 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
2674 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
2675 } else {
2676 color = 0xFF000000;
2679 else if (This->resource.format == WINED3DFMT_R5G6B5) {
2680 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
2681 color = 0xFFFFFFFF;
2682 } else {
2683 color = ((0xFF000000) |
2684 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
2685 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
2686 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
2689 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
2690 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
2691 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
2693 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
2694 color = DDBltFx->u5.dwFillColor;
2696 else {
2697 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
2698 return WINED3DERR_INVALIDCALL;
2701 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
2702 IWineD3DDevice_GetSwapChain((IWineD3DDevice *)myDevice, 0, (IWineD3DSwapChain **)&implSwapChain);
2703 IWineD3DSwapChain_Release( (IWineD3DSwapChain *) implSwapChain );
2704 if(implSwapChain->backBuffer && This == (IWineD3DSurfaceImpl*) implSwapChain->backBuffer[0]) {
2705 glDrawBuffer(GL_BACK);
2706 checkGLcall("glDrawBuffer(GL_BACK)");
2708 else if (This == (IWineD3DSurfaceImpl*) implSwapChain->frontBuffer) {
2709 glDrawBuffer(GL_FRONT);
2710 checkGLcall("glDrawBuffer(GL_FRONT)");
2712 else {
2713 ERR("Wrong surface type for BLT override(not on swapchain) !\n");
2714 return WINED3DERR_INVALIDCALL;
2717 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
2719 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
2720 1 /* Number of rectangles */,
2721 &rect,
2722 WINED3DCLEAR_TARGET,
2723 color,
2724 0.0 /* Z */,
2725 0 /* Stencil */);
2727 /* Restore the original draw buffer */
2728 if(implSwapChain->backBuffer && implSwapChain->backBuffer[0]) {
2729 glDrawBuffer(GL_BACK);
2730 vcheckGLcall("glDrawBuffer");
2733 return WINED3D_OK;
2736 /* Default: Fall back to the generic blt */
2737 return WINED3DERR_INVALIDCALL;
2740 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2741 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2742 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2743 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2744 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2745 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
2747 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair */
2748 if(myDevice->inScene &&
2749 (iface == myDevice->stencilBufferTarget ||
2750 (SrcSurface && SrcSurface == myDevice->stencilBufferTarget))) {
2751 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2752 return WINED3DERR_INVALIDCALL;
2755 /* Special cases for RenderTargets */
2756 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2757 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2758 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) return WINED3D_OK;
2761 /* For the rest call the X11 surface implementation.
2762 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
2763 * other Blts are rather rare
2765 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2768 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
2769 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2770 TRACE("(%p)->(%x)\n", This, Flags);
2772 switch (Flags)
2774 case DDGBS_CANBLT:
2775 case DDGBS_ISBLTDONE:
2776 return DD_OK;
2778 default:
2779 return DDERR_INVALIDPARAMS;
2783 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
2784 /* XXX: DDERR_INVALIDSURFACETYPE */
2786 TRACE("(%p)->(%08x)\n",iface,Flags);
2787 switch (Flags) {
2788 case DDGFS_CANFLIP:
2789 case DDGFS_ISFLIPDONE:
2790 return DD_OK;
2792 default:
2793 return DDERR_INVALIDPARAMS;
2797 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
2798 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2799 TRACE("(%p)\n", This);
2801 return This->Flags & SFLAG_LOST ? DDERR_SURFACELOST : WINED3D_OK;
2804 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
2805 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2806 TRACE("(%p)\n", This);
2808 /* So far we don't lose anything :) */
2809 This->Flags &= ~SFLAG_LOST;
2810 return WINED3D_OK;
2813 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
2814 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2815 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
2816 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2817 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
2819 if(myDevice->inScene &&
2820 (iface == myDevice->stencilBufferTarget ||
2821 (Source && Source == myDevice->stencilBufferTarget))) {
2822 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2823 return WINED3DERR_INVALIDCALL;
2826 /* Special cases for RenderTargets */
2827 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2828 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2830 RECT SrcRect, DstRect;
2831 DWORD Flags=0;
2833 if(rsrc) {
2834 SrcRect.left = rsrc->left;
2835 SrcRect.top= rsrc->top;
2836 SrcRect.bottom = rsrc->bottom;
2837 SrcRect.right = rsrc->right;
2838 } else {
2839 SrcRect.left = 0;
2840 SrcRect.top = 0;
2841 SrcRect.right = srcImpl->currentDesc.Width;
2842 SrcRect.bottom = srcImpl->currentDesc.Height;
2845 DstRect.left = dstx;
2846 DstRect.top=dsty;
2847 DstRect.right = dstx + SrcRect.right - SrcRect.left;
2848 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
2850 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
2851 if(trans & DDBLTFAST_SRCCOLORKEY)
2852 Flags |= DDBLT_KEYSRC;
2853 if(trans & DDBLTFAST_DESTCOLORKEY)
2854 Flags |= DDBLT_KEYDEST;
2855 if(trans & DDBLTFAST_WAIT)
2856 Flags |= DDBLT_WAIT;
2857 if(trans & DDBLTFAST_DONOTWAIT)
2858 Flags |= DDBLT_DONOTWAIT;
2860 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL) == WINED3D_OK) return WINED3D_OK;
2864 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
2867 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
2868 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2869 TRACE("(%p)->(%p)\n", This, Pal);
2871 *Pal = (IWineD3DPalette *) This->palette;
2872 return DD_OK;
2875 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
2876 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2877 RGBQUAD col[256];
2878 IWineD3DPaletteImpl *pal = This->palette;
2879 unsigned int n;
2880 TRACE("(%p)\n", This);
2882 if(This->resource.format == WINED3DFMT_P8 ||
2883 This->resource.format == WINED3DFMT_A8P8)
2885 TRACE("Dirtifying surface\n");
2886 This->Flags |= SFLAG_DIRTY;
2889 if(This->Flags & SFLAG_DIBSECTION) {
2890 TRACE("(%p): Updating the hdc's palette\n", This);
2891 for (n=0; n<256; n++) {
2892 if(pal) {
2893 col[n].rgbRed = pal->palents[n].peRed;
2894 col[n].rgbGreen = pal->palents[n].peGreen;
2895 col[n].rgbBlue = pal->palents[n].peBlue;
2896 } else {
2897 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2898 /* Use the default device palette */
2899 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
2900 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
2901 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
2903 col[n].rgbReserved = 0;
2905 SetDIBColorTable(This->hDC, 0, 256, col);
2908 return WINED3D_OK;
2911 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
2912 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2913 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
2914 TRACE("(%p)->(%p)\n", This, Pal);
2916 if(This->palette != NULL)
2917 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
2918 This->palette->Flags &= ~DDPCAPS_PRIMARYSURFACE;
2920 if(PalImpl != NULL) {
2921 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2922 /* Set the device's main palette if the palette
2923 * wasn't a primary palette before
2925 if(!(PalImpl->Flags & DDPCAPS_PRIMARYSURFACE)) {
2926 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2927 unsigned int i;
2929 for(i=0; i < 256; i++) {
2930 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
2934 (PalImpl)->Flags |= DDPCAPS_PRIMARYSURFACE;
2937 This->palette = PalImpl;
2939 return IWineD3DSurface_RealizePalette(iface);
2942 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, DDCOLORKEY *CKey) {
2943 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2944 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
2946 if ((Flags & DDCKEY_COLORSPACE) != 0) {
2947 FIXME(" colorkey value not supported (%08x) !\n", Flags);
2948 return DDERR_INVALIDPARAMS;
2951 /* Dirtify the surface, but only if a key was changed */
2952 if(CKey) {
2953 switch (Flags & ~DDCKEY_COLORSPACE) {
2954 case DDCKEY_DESTBLT:
2955 This->DestBltCKey = *CKey;
2956 This->CKeyFlags |= DDSD_CKDESTBLT;
2957 break;
2959 case DDCKEY_DESTOVERLAY:
2960 This->DestOverlayCKey = *CKey;
2961 This->CKeyFlags |= DDSD_CKDESTOVERLAY;
2962 break;
2964 case DDCKEY_SRCOVERLAY:
2965 This->SrcOverlayCKey = *CKey;
2966 This->CKeyFlags |= DDSD_CKSRCOVERLAY;
2967 break;
2969 case DDCKEY_SRCBLT:
2970 This->SrcBltCKey = *CKey;
2971 This->CKeyFlags |= DDSD_CKSRCBLT;
2972 break;
2975 else {
2976 switch (Flags & ~DDCKEY_COLORSPACE) {
2977 case DDCKEY_DESTBLT:
2978 This->CKeyFlags &= ~DDSD_CKDESTBLT;
2979 break;
2981 case DDCKEY_DESTOVERLAY:
2982 This->CKeyFlags &= ~DDSD_CKDESTOVERLAY;
2983 break;
2985 case DDCKEY_SRCOVERLAY:
2986 This->CKeyFlags &= ~DDSD_CKSRCOVERLAY;
2987 break;
2989 case DDCKEY_SRCBLT:
2990 This->CKeyFlags &= ~DDSD_CKSRCBLT;
2991 break;
2995 return WINED3D_OK;
2998 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
2999 /** Check against the maximum texture sizes supported by the video card **/
3000 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3002 TRACE("%p\n", This);
3003 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3004 /* one of three options
3005 1: Do the same as we do with nonpow 2 and scale the texture, (any texture ops would require the texture to be scaled which is potentially slow)
3006 2: Set the texture to the maxium size (bad idea)
3007 3: WARN and return WINED3DERR_NOTAVAILABLE;
3008 4: Create the surface, but allow it to be used only for DirectDraw Blts. Some apps(e.g. Swat 3) create textures with a Height of 16 and a Width > 3000 and blt 16x16 letter areas from them to the render target.
3010 WARN("(%p) Creating an oversized surface\n", This);
3011 This->Flags |= SFLAG_OVERSIZE;
3013 /* This will be initialized on the first blt */
3014 This->glRect.left = 0;
3015 This->glRect.top = 0;
3016 This->glRect.right = 0;
3017 This->glRect.bottom = 0;
3018 } else {
3019 /* No oversize, gl rect is the full texture size */
3020 This->Flags &= ~SFLAG_OVERSIZE;
3021 This->glRect.left = 0;
3022 This->glRect.top = 0;
3023 This->glRect.right = This->pow2Width;
3024 This->glRect.bottom = This->pow2Height;
3027 return WINED3D_OK;
3030 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3031 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3032 DWORD ret;
3033 TRACE("(%p)\n", This);
3035 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3036 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3037 ie pitch = (width/4) * bytes per block */
3038 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3039 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3040 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3041 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3042 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3043 else {
3044 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3045 /* Surfaces are 32 bit aligned */
3046 ret = (ret + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
3048 TRACE("(%p) Returning %d\n", This, ret);
3049 return ret;
3052 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3053 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3055 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3057 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3059 TRACE("(%p): Not an overlay surface\n", This);
3060 return DDERR_NOTAOVERLAYSURFACE;
3063 return WINED3D_OK;
3066 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3067 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3069 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3071 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3073 TRACE("(%p): Not an overlay surface\n", This);
3074 return DDERR_NOTAOVERLAYSURFACE;
3077 return WINED3D_OK;
3080 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3081 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3082 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3084 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3086 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3088 TRACE("(%p): Not an overlay surface\n", This);
3089 return DDERR_NOTAOVERLAYSURFACE;
3092 return WINED3D_OK;
3095 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3096 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3097 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3098 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3100 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3102 TRACE("(%p): Not an overlay surface\n", This);
3103 return DDERR_NOTAOVERLAYSURFACE;
3106 return WINED3D_OK;
3109 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3111 /* IUnknown */
3112 IWineD3DSurfaceImpl_QueryInterface,
3113 IWineD3DSurfaceImpl_AddRef,
3114 IWineD3DSurfaceImpl_Release,
3115 /* IWineD3DResource */
3116 IWineD3DSurfaceImpl_GetParent,
3117 IWineD3DSurfaceImpl_GetDevice,
3118 IWineD3DSurfaceImpl_SetPrivateData,
3119 IWineD3DSurfaceImpl_GetPrivateData,
3120 IWineD3DSurfaceImpl_FreePrivateData,
3121 IWineD3DSurfaceImpl_SetPriority,
3122 IWineD3DSurfaceImpl_GetPriority,
3123 IWineD3DSurfaceImpl_PreLoad,
3124 IWineD3DSurfaceImpl_GetType,
3125 /* IWineD3DSurface */
3126 IWineD3DSurfaceImpl_GetContainer,
3127 IWineD3DSurfaceImpl_GetDesc,
3128 IWineD3DSurfaceImpl_LockRect,
3129 IWineD3DSurfaceImpl_UnlockRect,
3130 IWineD3DSurfaceImpl_GetDC,
3131 IWineD3DSurfaceImpl_ReleaseDC,
3132 IWineD3DSurfaceImpl_Flip,
3133 IWineD3DSurfaceImpl_Blt,
3134 IWineD3DSurfaceImpl_GetBltStatus,
3135 IWineD3DSurfaceImpl_GetFlipStatus,
3136 IWineD3DSurfaceImpl_IsLost,
3137 IWineD3DSurfaceImpl_Restore,
3138 IWineD3DSurfaceImpl_BltFast,
3139 IWineD3DSurfaceImpl_GetPalette,
3140 IWineD3DSurfaceImpl_SetPalette,
3141 IWineD3DSurfaceImpl_RealizePalette,
3142 IWineD3DSurfaceImpl_SetColorKey,
3143 IWineD3DSurfaceImpl_GetPitch,
3144 IWineD3DSurfaceImpl_SetMem,
3145 IWineD3DSurfaceImpl_SetOverlayPosition,
3146 IWineD3DSurfaceImpl_GetOverlayPosition,
3147 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3148 IWineD3DSurfaceImpl_UpdateOverlay,
3149 /* Internal use: */
3150 IWineD3DSurfaceImpl_CleanDirtyRect,
3151 IWineD3DSurfaceImpl_AddDirtyRect,
3152 IWineD3DSurfaceImpl_LoadTexture,
3153 IWineD3DSurfaceImpl_SaveSnapshot,
3154 IWineD3DSurfaceImpl_SetContainer,
3155 IWineD3DSurfaceImpl_SetPBufferState,
3156 IWineD3DSurfaceImpl_SetGlTextureDesc,
3157 IWineD3DSurfaceImpl_GetGlDesc,
3158 IWineD3DSurfaceImpl_GetData,
3159 IWineD3DSurfaceImpl_SetFormat,
3160 IWineD3DSurfaceImpl_PrivateSetup