/* * $Id$ * * $Date$ * $Revision$ * * (C) 1999 by Hyperion * All rights reserved * * This file is part of the MiniGL library project * See the file Licence.txt for more details * */ #include "sysinc.h" #include #include #include #include #include #include #include "displaylists.h" #include "smartlock.h" #include "util.h" #include "mgl_profileitems.h" #define min(x,y) (x) < (y) ? (x) : (y) #define max(x,y) (x) > (y) ? (x) : (y) extern struct InternalToW3D i2w3d[]; BOOL EnsureBitmapBackingStore(GLcontext context, uint32 dataSize); extern GLsizei next_pwr(GLsizei x); extern void m_CombineMatrices(GLcontext context); extern void RebindTextures(GLcontext context); extern void tex_EstablishEnvCombine(GLcontext context); extern int32 SelectInternalFormat(GLcontext context, GLenum internalformat); extern float CLAMPF(float a); typedef struct { float x,y,z; float u,v,w; } DPVertex; #define DPVERTEX_FORMAT (W3D_VFORMAT_TCOORD_0) void cgl_GLCopyPixels(struct GLContextIFace *Self, GLint xp, GLint yp, GLsizei width, GLsizei height, GLenum type) { GLcontext context = GET_INSTANCE(Self); DL_CHECK(CopyPixels(Self, xp, yp, width, height, type)); PROFILE_ENTRY(FID_CGL_GL_COPY_PIXELS); /* Check for valid raster pos. If invalid, just return */ if (!context->current.RasterPosValid) { return; } /* Check input parameters */ GLFlagError(context, width < 0, GL_INVALID_VALUE); GLFlagError(context, height < 0, GL_INVALID_VALUE); GLFlagError(context, type != GL_COLOR, GL_INVALID_ENUM); /* Need to flush what was rendered before reading it */ IWarp3D->W3D_FlushFrame(context->w3dContext); IWarp3D->W3D_WaitIdle(context->w3dContext); if (type == GL_COLOR && !context->enable.DepthTest && context->pixel.pixel_state_neutral) { // In this state, a direct copy using the graphics library is faster than using Warp3D yp = context->readBuffer->height - 1 - (yp + height); // Need to flip the y-axis #ifndef W3D_DOESNT_LOCK_P96 // Currently graphics operations and Warp3D cannot be used simultaneously, this speeds it up by // forcing an immediate unlock smartlock_forceUnlock(context->smartLock); #endif int32 succ UNUSED = IGraphics->BltBitMapTags( BLITA_Source, context->readBuffer->bitMap, BLITA_SrcType, BLITT_BITMAP, BLITA_SrcX, xp, BLITA_SrcY, yp, BLITA_Width, width, BLITA_Height, height, BLITA_Dest, context->drawBuffer->bitMap, BLITA_DestType, BLITT_BITMAP, BLITA_DestX, (GLuint)context->current.RasterPos.x, BLITA_DestY, (GLuint)context->current.RasterPos.y - height, BLITA_Minterm, 0xc0, TAG_DONE ); context->fbDirty = GL_TRUE; PROFILE_EXIT(FID_CGL_GL_COPY_PIXELS); return; } uint32 oldCullState, oldTexmappingState, oldGouraudState; uint32 error; GLsizei rw = next_pwr(width); GLsizei rh = next_pwr(height); uint32 maxTextureSize = IWarp3D->W3D_Query(context->w3dContext, W3D_Q_MAXTEXWIDTH, 0); uint32 texWidthReal = MIN(maxTextureSize, rw); uint32 texHeightReal = MIN(maxTextureSize, rh); /* Principle of operation: * glDrawPixels transfers pixel from the source into the frame buffer at * the current raster position. The image data is not going through the * pixel pipeline, i.e. these aren't fragments that are produced. * * What we do is the following: We create texture(s) with the data, and draw * one or more quads covering the destination area. * * We need to make sure we do not run into trouble concerning the upper * bounds of the supported texture size. */ uint32 tilesWidth = width / texWidthReal; uint32 tilesHeight = height / texHeightReal; if ((texWidthReal * tilesWidth) < width) { tilesWidth++; } if ((texHeightReal * tilesHeight) < height) { tilesHeight++; } if (!EnsureBitmapBackingStore(context, texWidthReal * texHeightReal * 4)) { return; } int x, y; uint32 w3dFormat = W3D_A8R8G8B8; // if (!(context->textureSupport[W3D_R8G8B8A8] & W3D_TEXFMT_FAST)) // w3dFormat = i2w3d[internal].w3dFormat; W3D_Texture *tex = IWarp3D->W3D_AllocTexObjTags( context->w3dContext, &error, W3D_ATO_IMAGE, context->CurrentBitmapBackingStore, W3D_ATO_FORMAT, w3dFormat, W3D_ATO_WIDTH, texWidthReal, W3D_ATO_HEIGHT, texHeightReal, TAG_DONE ); if (!tex || error != W3D_SUCCESS) { dprintf("Couldn't allocate texture, error %d\n", error); return; } /* Set texture parameters */ IWarp3D->W3D_SetFilter(context->w3dContext, tex, W3D_NEAREST, W3D_NEAREST); IWarp3D->W3D_SetTexEnv(context->w3dContext, tex, W3D_MODULATE, NULL); IWarp3D->W3D_SetWrapMode(context->w3dContext, tex, W3D_CLAMP, W3D_CLAMP, 0); oldCullState = IWarp3D->W3D_GetState(context->w3dContext, W3D_CULLFACE); IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, W3D_DISABLE); oldTexmappingState = IWarp3D->W3D_GetState(context->w3dContext, W3D_TEXMAPPING); IWarp3D->W3D_SetState(context->w3dContext, W3D_TEXMAPPING, W3D_ENABLE); oldGouraudState = IWarp3D->W3D_GetState(context->w3dContext, W3D_GOURAUD); IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD, W3D_DISABLE); W3D_Color color = { 1.0, 1.0, 1.0, 1.0 }; IWarp3D->W3D_SetCurrentColor(context->w3dContext, &color); IWarp3D->W3D_SetTextureBlendTags( context->w3dContext, W3D_BLEND_STAGE, 0, W3D_ENV_MODE, W3D_REPLACE, W3D_BLEND_STAGE, 1, W3D_ENV_MODE, W3D_OFF, TAG_DONE ); #ifdef W3D_DOESNT_LOCK_P96 if (context->LockMode == MGL_LOCK_SMART) { smartlock_beginDraw(context->smartLock); } else if (context->w3dLocked == GL_FALSE) { IWarp3D->W3D_LockHardware(context->w3dContext); } #endif GLint baseX = context->current.RasterPos.x; GLint baseY = context->current.RasterPos.y; for (x = 0; x < tilesWidth; x++) { for (y = 0; y < tilesHeight; y++) { /* Convert the input data to a texture */ uint8 *baseDst = context->CurrentBitmapBackingStore; /* If the remaing pixels do not cover the current tile, clear it to * get black pixels */ BOOL doClear = FALSE; uint32 tileWidth = texWidthReal; if ((texWidthReal * x + texWidthReal) > width) { tileWidth = width - (texWidthReal * x); doClear = TRUE; } uint32 tileHeight = texHeightReal; if ((texHeightReal * y + texHeightReal) > height) { tileHeight = height - (texHeightReal * y); doClear = TRUE; } if (doClear) { IUtility->ClearMem(baseDst, texWidthReal * texHeightReal * 4); } // IExec->DebugPrintF("e: srcX: %d, srcY: %d, tileWidth: %d, tileHeight: %d, texWidthReal: %d\n", xp + x * tileWidth, // context->readBuffer->height - 1 - (yp + (y+1) * tileHeight), // tileWidth, tileHeight, texWidthReal); // IExec->DebugPrintF("bitmap: %p, drawBuffer: %p bitmapBackingStore: %p\n", context->readBuffer->bitMap, context->drawBuffer->bitMap, context->CurrentBitmapBackingStore); #ifndef W3D_DOESNT_LOCK_P96 // Currently graphics operations and Warp3D cannot be used simultaneously smartlock_forceUnlock(context->smartLock); #endif /* FIXME: Add color scale/bias */ int32 succ UNUSED = IGraphics->BltBitMapTags( BLITA_Source, context->readBuffer->bitMap, BLITA_SrcType, BLITT_BITMAP, BLITA_SrcX, xp + x * tileWidth, BLITA_SrcY, context->readBuffer->height - 1 - (yp + (y+1) * tileHeight), /* Grows upwards */ BLITA_Width, tileWidth, BLITA_Height, tileHeight, BLITA_Dest, context->CurrentBitmapBackingStore, BLITA_DestType, BLITT_ARGB32, BLITA_DestX, 0, BLITA_DestY, 0, BLITA_DestBytesPerRow, texWidthReal * sizeof(uint32), BLITA_Minterm, 0xc0, TAG_DONE ); #ifndef W3D_DOESNT_LOCK_P96 if (context->LockMode == MGL_LOCK_SMART) { smartlock_beginDraw(context->smartLock); } else if (context->w3dLocked == GL_FALSE) { IWarp3D->W3D_LockHardware(context->w3dContext); } #endif IWarp3D->W3D_UpdateTexImage( context->w3dContext, tex, context->CurrentBitmapBackingStore, 0, NULL ); IWarp3D->W3D_BindTexture(context->w3dContext, 0, tex); /* Draw a quad with the converted texture */ DPVertex quad[4]; /* Lower left corner */ quad[0].x = baseX + x * texWidthReal * context->pixel.zoom_x; quad[0].y = baseY - y * texHeightReal * context->pixel.zoom_y; quad[0].z = context->current.RasterDistance; quad[0].w = 1.0 - context->current.RasterDistance; quad[0].u = 0.0; quad[0].v = (float)tileHeight; /* Lower right corner */ quad[1].x = baseX + x * texWidthReal * context->pixel.zoom_x + tileWidth* context->pixel.zoom_x; quad[1].y = baseY - y * texHeightReal * context->pixel.zoom_y; quad[1].z = context->current.RasterDistance; quad[1].w = 1.0 - context->current.RasterDistance; quad[1].u = (float)tileWidth; quad[1].v = (float)tileHeight; /* Upper right corner */ quad[2].x = baseX + x * texWidthReal * context->pixel.zoom_x + tileWidth * context->pixel.zoom_x; quad[2].y = baseY - y * texHeightReal * context->pixel.zoom_y - tileHeight * context->pixel.zoom_y; quad[2].z = context->current.RasterDistance; quad[2].w = 1.0 - context->current.RasterDistance; quad[2].u = (float)tileWidth; quad[2].v = 0.0; /* Upper left corner */ quad[3].x = baseX + x * texWidthReal * context->pixel.zoom_x; quad[3].y = baseY - y * texHeightReal * context->pixel.zoom_y - tileHeight * context->pixel.zoom_y; quad[3].z = context->current.RasterDistance; quad[3].w = 1.0 - context->current.RasterDistance; quad[3].u = 0.0; quad[3].v = 0.0; IWarp3D->W3D_InterleavedArray( context->w3dContext, quad, sizeof(DPVertex), DPVERTEX_FORMAT, 0 ); error = IWarp3D->W3D_DrawArray( context->w3dContext, W3D_PRIMITIVE_TRIFAN, 0, 4 ); #ifndef W3D_DOESNT_LOCK_P96 if (context->LockMode == MGL_LOCK_SMART) { smartlock_endDraw(context->smartLock); } else if (context->w3dLocked == GL_FALSE) { IWarp3D->W3D_UnLockHardware(context->w3dContext); } #endif } } color.r = CLAMPF(context->current.CurrentColor.r); color.g = CLAMPF(context->current.CurrentColor.g); color.b = CLAMPF(context->current.CurrentColor.b); color.a = CLAMPF(context->current.CurrentColor.a); IWarp3D->W3D_SetCurrentColor(context->w3dContext, &color); #ifdef W3D_DOESNT_LOCK_P96 if (context->LockMode == MGL_LOCK_SMART) { smartlock_endDraw(context->smartLock); } else if (context->w3dLocked == GL_FALSE) { IWarp3D->W3D_UnLockHardware(context->w3dContext); } #endif // Might not need this, but it doesn't hurt. IWarp3D->W3D_InterleavedArray( context->w3dContext, context->VertexBuffer, sizeof (MGLVertex), context->VertexFormat, W3D_TEXCOORD_NORMALIZED ); IWarp3D->W3D_FreeTexObj(context->w3dContext, tex); IWarp3D->W3D_SetState(context->w3dContext, W3D_CULLFACE, oldCullState); IWarp3D->W3D_SetState(context->w3dContext, W3D_TEXMAPPING, oldTexmappingState); IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD, oldGouraudState); RebindTextures(context); tex_EstablishEnvCombine(context); context->fbDirty = GL_TRUE; PROFILE_EXIT(FID_CGL_GL_COPY_PIXELS); }