/* * $Id:$ * * $Date$ * $Revision$ * * (C) 1999-2005 The MiniGL team * All rights reserved * * This file is part of the MiniGL library project * See the file Licence.txt for more details * */ #include "displaylists.h" #include "sysinc.h" #include #include #include #include #include "mgl/gl.h" #include "mgl/mgltypes.h" #include #include "smartlock.h" #include #define min(x,y) (x) < (y) ? (x) : (y) #define max(x,y) (x) > (y) ? (x) : (y) extern struct FormatTypeToUnpack ft2u[]; 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); _glTransferFn MGLSelectTransfer(GLenum format, struct GLpixel_state *pixelState); extern GLboolean MGLTransferImage(GLcontext context, GLsizei width, GLsizei height, GLimage_info *src, GLpixel_store *srcPs, GLimage_info *dst, GLpixel_store *dstPs, struct GLpixel_state *pixelState); extern GLboolean MGLUnpackImage(GLcontext context, GLsizei width, GLsizei height, GLimage_info *src, GLimage_info *dst, GLboolean doPixelState); extern GLboolean MGLPackImage(GLcontext context, GLsizei width, GLsizei height, GLimage_info *src, GLimage_info *dst, GLboolean doPixelState); extern _glUnpackFn MGLSelectUnpacker(GLenum format, GLenum type, GLuint *pixelStride); extern GLboolean isPixelStateNeutral(GLcontext context); typedef struct { float x,y,z; float u,v,w; } DPVertex; #define DPVERTEX_FORMAT (W3D_VFORMAT_TCOORD_0) void cgl_GLDrawPixels(struct GLContextIFace *Self, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) { GLcontext context = GET_INSTANCE(Self); if(dl_IsDLActive(Self)){ dl_save_DrawPixels(Self, width, height, format, type, pixels); if(!dl_CompileAndExecuteMode(Self)){ return; } } uint32 oldCullState, oldTexmappingState, oldGouraudState; uint32 error; GLuint src_pixel_stride; 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); /* Check for valid raster pos. If invalid, just return */ if (!context->current.RasterPosValid) return; /* Check input parameters */ GLFlagError(context, pixels == NULL, GL_INVALID_VALUE); GLFlagError(context, width < 0, GL_INVALID_VALUE); GLFlagError(context, height < 0, GL_INVALID_VALUE); _glUnpackFn unpacker = MGLSelectUnpacker(format, type, &src_pixel_stride); int32 internal = SelectInternalFormat(context, GL_RGBA); GLFlagError(context, unpacker == NULL, GL_INVALID_ENUM); /* Select a transfer function */ _glTransferFn transfer = MGLSelectTransfer(format, &context->pixel); /* 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++; void *convertBuffer = alloca(4 * texWidthReal); if (!EnsureBitmapBackingStore(context, texWidthReal * texHeightReal * i2w3d[internal].w3dBpp)) return; int x, y, i; uint32 w3dFormat = W3D_R8G8B8A8; if (!(context->textureSupport[W3D_R8G8B8A8] & W3D_TEXFMT_FAST) || transfer) w3dFormat = i2w3d[internal].w3dFormat; W3D_Texture *tex = IWarp3D->W3D_AllocTexObjTags(context->w3dContext, NULL, W3D_ATO_IMAGE, context->CurrentBitmapBackingStore, W3D_ATO_FORMAT, w3dFormat, W3D_ATO_WIDTH, texWidthReal, W3D_ATO_HEIGHT, texHeightReal, TAG_DONE); /* 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); static W3D_Color color; color.r = 1.0; color.g = 1.0; color.b = 1.0; color.a = 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); if(context->LockMode == MGL_LOCK_SMART) smartlock_beginDraw(context->smartLock); else if (context->w3dLocked == GL_FALSE) IWarp3D->W3D_LockHardware(context->w3dContext); GLint baseX = context->current.RasterPos.x; GLint baseY = context->current.RasterPos.y; uint32 lineWidth = (max(context->pixel_store.unpack.row_length, width)) * src_pixel_stride; /* Setup src image */ GLimage_info src_image; src_image.data = (GLubyte *)pixels; src_image.stride = lineWidth; src_image.format = format; src_image.type = type; src_image.pixel_stride = src_pixel_stride; for (x = 0; x < tilesWidth; x++) { for (y = 0; y < tilesHeight; y++) { /* Convert the input data to a texture */ src_image.current = (uint8 *)pixels + (lineWidth * context->pixel_store.unpack.skip_rows) + (lineWidth * (y * texWidthReal)) + (context->pixel_store.unpack.skip_pixels * src_pixel_stride) + (x * texWidthReal * src_pixel_stride); uint8 *baseDst = context->CurrentBitmapBackingStore; /* If the remaining 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); /* Actual conversion */ for (i = 0; i < tileHeight; i++) { if (w3dFormat == W3D_R8G8B8A8 && !transfer) unpacker(context, tileWidth, &src_image, &context->pixel_store.unpack, baseDst); //convertBuffer); else { unpacker(context, tileWidth, &src_image, &context->pixel_store.unpack, convertBuffer); if (transfer) transfer(context, width, convertBuffer, &context->pixel); i2w3d[internal].write(convertBuffer, baseDst, tileWidth, 4); } src_image.current += lineWidth; baseDst += (4 * texWidthReal); } /* 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 = 0.0; /* 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 = 0.0; /* 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 = (float)tileHeight; /* 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 = (float)tileHeight; IWarp3D->W3D_UpdateTexImage(context->w3dContext, tex, context->CurrentBitmapBackingStore, 0, NULL); IWarp3D->W3D_BindTexture(context->w3dContext, 0, tex); IWarp3D->W3D_InterleavedArray(context->w3dContext, quad, sizeof(DPVertex), DPVERTEX_FORMAT, 0); error = IWarp3D->W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, 0, 4); } } 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); if(context->LockMode == MGL_LOCK_SMART){ smartlock_endDraw(context->smartLock); } else if (context->w3dLocked == GL_FALSE) IWarp3D->W3D_UnLockHardware(context->w3dContext); // 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; } void cgl_GLPixelZoom(struct GLContextIFace *Self, GLfloat zoomX, GLfloat zoomY) { GLcontext context = GET_INSTANCE(Self); if(dl_IsDLActive(Self)){ dl_save_PixelZoom(Self, zoomX, zoomY); if(!dl_CompileAndExecuteMode(Self)){ return; } } context->pixel.zoom_x = zoomX; context->pixel.zoom_y = zoomY; context->pixel.pixel_state_neutral = isPixelStateNeutral(context); }