⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 surface.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 5 页
字号:
    }

    glReadPixels(rect->left, rect->top,
                 rect->right - rect->left,
                 rect->bottom - rect->top,
                 fmt, type, mem);
    vcheckGLcall("glReadPixels");

    /* TODO: Merge this with the palettization loop below for P8 targets */

    if(!srcUpsideDown) {
        UINT len, off;
        /* glReadPixels returns the image upside down, and there is no way to prevent this.
            Flip the lines in software */
        len = (rect->right - rect->left) * bpp;
        off = rect->left * bpp;

        row = HeapAlloc(GetProcessHeap(), 0, len);
        if(!row) {
            ERR("Out of memory\n");
            if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
            return;
        }

        top = mem + pitch * rect->top;
        bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
        for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
            memcpy(row, top + off, len);
            memcpy(top + off, bottom + off, len);
            memcpy(bottom + off, row, len);
            top += pitch;
            bottom -= pitch;
        }
        HeapFree(GetProcessHeap(), 0, row);
    }

    if(This->resource.format == WINED3DFMT_P8) {
        PALETTEENTRY *pal;
        DWORD width = pitch / 3;
        int x, y, c;
        if(This->palette) {
            pal = This->palette->palents;
        } else {
            pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
        }

        for(y = rect->top; y < rect->bottom; y++) {
            for(x = rect->left; x < rect->right; x++) {
                /*                      start              lines            pixels      */
                BYTE *blue =  (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
                BYTE *green = blue  + 1;
                BYTE *red =   green + 1;

                for(c = 0; c < 256; c++) {
                    if(*red   == pal[c].peRed   &&
                       *green == pal[c].peGreen &&
                       *blue  == pal[c].peBlue)
                    {
                        *((BYTE *) dest + y * width + x) = c;
                        break;
                    }
                }
            }
        }
        HeapFree(GetProcessHeap(), 0, mem);
    }
}

static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
    IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
    IWineD3DDeviceImpl  *myDevice = This->resource.wineD3DDevice;
    IWineD3DSwapChainImpl *swapchain = NULL;

    TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);

    if (!(This->Flags & SFLAG_LOCKABLE)) {
        /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
              texture regions, and since the destination is an unlockable region we need
              to tolerate this                                                           */
        TRACE("Warning: trying to lock unlockable surf@%p\n", This);
        /*return WINED3DERR_INVALIDCALL; */
    }

    pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);

    /* Mark the surface locked */
    This->Flags |= SFLAG_LOCKED;

    /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
    if(!This->resource.allocatedMemory) {
        This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
        This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
    }

    /* Calculate the correct start address to report */
    if (NULL == pRect) {
        pLockedRect->pBits = This->resource.allocatedMemory;
        This->lockedRect.left   = 0;
        This->lockedRect.top    = 0;
        This->lockedRect.right  = This->currentDesc.Width;
        This->lockedRect.bottom = This->currentDesc.Height;
        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);
    } else {
        TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);

        /* DXTn textures are based on compressed blocks of 4x4 pixels, each
         * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
         * slightly different meaning compared to regular textures. For DXTn
         * textures Pitch is the size of a row of blocks, 4 high and "width"
         * long. The x offset is calculated differently as well, since moving 4
         * pixels to the right actually moves an entire 4x4 block to right, ie
         * 16 bytes (8 in case of DXT1). */
        if (This->resource.format == WINED3DFMT_DXT1) {
            pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
        } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
                || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
            pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
        } else {
            pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
        }
        This->lockedRect.left   = pRect->left;
        This->lockedRect.top    = pRect->top;
        This->lockedRect.right  = pRect->right;
        This->lockedRect.bottom = pRect->bottom;
    }

    if (This->Flags & SFLAG_NONPOW2) {
        TRACE("Locking non-power 2 texture\n");
    }

    /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
     * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
     * changed
     */
    if(!(This->Flags & SFLAG_DYNLOCK)) {
        This->lockCount++;
        /* MAXLOCKCOUNT is defined in wined3d_private.h */
        if(This->lockCount > MAXLOCKCOUNT) {
            TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
            This->Flags |= SFLAG_DYNLOCK;
        }
    }

    if (Flags & WINED3DLOCK_DISCARD) {
        /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
        TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
        This->Flags |= SFLAG_INSYSMEM;
    }

    if (This->Flags & SFLAG_INSYSMEM) {
        TRACE("Local copy is up to date, not downloading data\n");
        goto lock_end;
    }

    /* Now download the surface content from opengl
     * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
     * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
     */
    IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
    if(swapchain || iface == myDevice->render_targets[0]) {
        BOOL srcIsUpsideDown;

        if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
            static BOOL warned = FALSE;
            if(!warned) {
                ERR("The application tries to lock the render target, but render target locking is disabled\n");
                warned = TRUE;
            }
            if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
            return WINED3D_OK;
        }

        /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
         * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
         * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
         * context->last_was_blit set on the unlock.
         */
        ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
        ENTER_GL();

        /* Select the correct read buffer, and give some debug output.
         * There is no need to keep track of the current read buffer or reset it, every part of the code
         * that reads sets the read buffer as desired.
         */
        if(!swapchain) {
            /* Locking the primary render target which is not on a swapchain(=offscreen render target).
             * Read from the back buffer
             */
            TRACE("Locking offscreen render target\n");
            glReadBuffer(myDevice->offscreenBuffer);
            srcIsUpsideDown = TRUE;
        } else {
            GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
            TRACE("Locking %#x buffer\n", buffer);
            glReadBuffer(buffer);
            checkGLcall("glReadBuffer");

            IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
            srcIsUpsideDown = FALSE;
        }

        switch(wined3d_settings.rendertargetlock_mode) {
            case RTL_AUTO:
            case RTL_READDRAW:
            case RTL_READTEX:
                read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
                break;

            case RTL_TEXDRAW:
            case RTL_TEXTEX:
                read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
                FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
                break;
        }
        LEAVE_GL();

        /* Mark the local copy up to date if a full download was done */
        if(This->lockedRect.left == 0 &&
           This->lockedRect.top == 0 &&
           This->lockedRect.right == This->currentDesc.Width &&
           This->lockedRect.bottom == This->currentDesc.Height) {
            This->Flags |= SFLAG_INSYSMEM;
        }
    } else if(iface == myDevice->stencilBufferTarget) {
        /** the depth stencil in openGL has a format of GL_FLOAT
         * which should be good for WINED3DFMT_D16_LOCKABLE
         * and WINED3DFMT_D16
         * it is unclear what format the stencil buffer is in except.
         * 'Each index is converted to fixed point...
         * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
         * mappings in the table GL_PIXEL_MAP_S_TO_S.
         * glReadPixels(This->lockedRect.left,
         *             This->lockedRect.bottom - j - 1,
         *             This->lockedRect.right - This->lockedRect.left,
         *             1,
         *             GL_DEPTH_COMPONENT,
         *             type,
         *             (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
         *
         * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
         * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
         * none of that is the case the problem is not in this function :-)
         ********************************************/
        FIXME("Depth stencil locking not supported yet\n");
    } else {
        /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
        TRACE("locking an ordinary surface\n");

        if (0 != This->glDescription.textureName) {
            /* Now I have to copy thing bits back */

            if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
                ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
            }

            ENTER_GL();
            /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
            if (GL_SUPPORT(ARB_MULTITEXTURE)) {
                GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
                checkGLcall("glActiveTextureARB");
            }
            IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
            IWineD3DSurface_PreLoad(iface);

            surface_download_data(This);
            LEAVE_GL();
        }
    }

lock_end:
    if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
        /* Don't dirtify */
    } else {
        IWineD3DBaseTexture *pBaseTexture;
        /**
         * Dirtify on lock
         * as seen in msdn docs
         */
        IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);

        /** Dirtify Container if needed */
        if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
            TRACE("Making container dirty\n");
            IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
            IWineD3DBaseTexture_Release(pBaseTexture);
        } else {
            TRACE("Surface is standalone, no need to dirty the container\n");
        }
    }

    TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
          This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
    return WINED3D_OK;
}

static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
    GLint  prev_store;
    GLint  prev_rasterpos[4];
    GLint skipBytes = 0;
    BOOL storechanged = FALSE, memory_allocated = FALSE;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -