/* -*- mode: c; c-basic-offset: 3 -*-
 *
 * Copyright 2000 VA Linux Systems Inc., Fremont, California.
 *
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * VA LINUX SYSTEMS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Original rewrite:
 *	Gareth Hughes <gareth@valinux.com>, 29 Sep - 1 Oct 2000
 *
 * Authors:
 *	Gareth Hughes <gareth@valinux.com>
 *	Brian Paul <brianp@valinux.com>
 *	Nathan Hand <nhand@valinux.com>
 *
 */

#include "tdfx_context.h"
#include "tdfx_dd.h"
#include "tdfx_lock.h"
#include "tdfx_vb.h"
#include "tdfx_pixels.h"
#include "tdfx_render.h"

#include "swrast/swrast.h"

#include "main/image.h"


#define FX_grLfbWriteRegion(fxMesa,dst_buffer,dst_x,dst_y,src_format,src_width,src_height,src_stride,src_data)		\
  do {				\
    LOCK_HARDWARE(fxMesa);		\
    fxMesa->Glide.grLfbWriteRegion(dst_buffer,dst_x,dst_y,src_format,src_width,src_height,FXFALSE,src_stride,src_data);	\
    UNLOCK_HARDWARE(fxMesa);		\
  } while(0)


#define FX_grLfbReadRegion(fxMesa,src_buffer,src_x,src_y,src_width,src_height,dst_stride,dst_data)			\
  do {				\
    LOCK_HARDWARE(fxMesa);		\
    fxMesa->Glide.grLfbReadRegion(src_buffer,src_x,src_y,src_width,src_height,dst_stride,dst_data);				\
    UNLOCK_HARDWARE(fxMesa);		\
  } while (0);


#if 0
static FxBool
FX_grLfbLock(tdfxContextPtr fxMesa, GrLock_t type, GrBuffer_t buffer,
             GrLfbWriteMode_t writeMode, GrOriginLocation_t origin,
             FxBool pixelPipeline, GrLfbInfo_t * info)
{
   FxBool result;

   LOCK_HARDWARE(fxMesa);
   result = fxMesa->Glide.grLfbLock(type, buffer, writeMode, origin, pixelPipeline, info);
   UNLOCK_HARDWARE(fxMesa);
   return result;
}
#endif


#define FX_grLfbUnlock(fxMesa, t, b)	\
  do {					\
    LOCK_HARDWARE(fxMesa);		\
    fxMesa->Glide.grLfbUnlock(t, b);	\
    UNLOCK_HARDWARE(fxMesa);		\
  } while (0)



#if 0
/* test if window coord (px,py) is visible */
static GLboolean
inClipRects(tdfxContextPtr fxMesa, int px, int py)
{
    int i;
    for (i = 0; i < fxMesa->numClipRects; i++) {
        if ((px >= fxMesa->pClipRects[i].x1) &&
            (px < fxMesa->pClipRects[i].x2) &&
            (py >= fxMesa->pClipRects[i].y1) &&
            (py < fxMesa->pClipRects[i].y2)) return GL_TRUE;
    }
    return GL_FALSE;
}
#endif

/* test if rectangle of pixels (px,py) (px+width,py+height) is visible */
static GLboolean
inClipRects_Region(tdfxContextPtr fxMesa, int x, int y, int width, int height)
{
    int i;
    int x1, y1, x2, y2;
    int xmin, xmax, ymin, ymax, pixelsleft;

    y1 = y - height + 1; y2 = y;
    x1 = x; x2 = x + width - 1;
    pixelsleft = width * height;

    for (i = 0; i < fxMesa->numClipRects; i++)
    {
        /* algorithm requires x1 < x2 and y1 < y2 */
        if ((fxMesa->pClipRects[i].x1 < fxMesa->pClipRects[i].x2)) {
            xmin = fxMesa->pClipRects[i].x1;
            xmax = fxMesa->pClipRects[i].x2-1;
        } else {
            xmin = fxMesa->pClipRects[i].x2;
            xmax = fxMesa->pClipRects[i].x1-1;
        }
        if ((fxMesa->pClipRects[i].y1 < fxMesa->pClipRects[i].y2)) {
            ymin = fxMesa->pClipRects[i].y1;
            ymax = fxMesa->pClipRects[i].y2-1;
        } else {
            ymin = fxMesa->pClipRects[i].y2;
            ymax = fxMesa->pClipRects[i].y1-1;
        }

        /* reject trivial cases */
        if (xmax < x1) continue;
        if (ymax < y1) continue;
        if (xmin > x2) continue;
        if (ymin > y2) continue;

        /* find the intersection */
        if (xmin < x1) xmin = x1;
        if (ymin < y1) ymin = y1;
        if (xmax > x2) xmax = x2;
        if (ymax > y2) ymax = y2;

        pixelsleft -= (xmax-xmin+1) * (ymax-ymin+1);
    }

    return pixelsleft == 0;
}

#if 0
GLboolean
tdfx_bitmap_R5G6B5(GLcontext * ctx, GLint px, GLint py,
		   GLsizei width, GLsizei height,
		   const struct gl_pixelstore_attrib *unpack,
		   const GLubyte * bitmap)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GrLfbInfo_t info;
   TdfxU16 color;
   const struct gl_pixelstore_attrib *finalUnpack;
   struct gl_pixelstore_attrib scissoredUnpack;

   /* check if there's any raster operations enabled which we can't handle */
   if (ctx->RasterMask & (ALPHATEST_BIT |
			  BLEND_BIT |
			  DEPTH_BIT |
			  FOG_BIT |
			  LOGIC_OP_BIT |
			  SCISSOR_BIT |
			  STENCIL_BIT |
			  MASKING_BIT |
			  MULTI_DRAW_BIT)) return GL_FALSE;

   if (ctx->Scissor.Enabled) {
      /* This is a bit tricky, but by carefully adjusting the px, py,
       * width, height, skipPixels and skipRows values we can do
       * scissoring without special code in the rendering loop.
       */

      /* we'll construct a new pixelstore struct */
      finalUnpack = &scissoredUnpack;
      scissoredUnpack = *unpack;
      if (scissoredUnpack.RowLength == 0)
	 scissoredUnpack.RowLength = width;

      /* clip left */
      if (px < ctx->Scissor.X) {
	 scissoredUnpack.SkipPixels += (ctx->Scissor.X - px);
	 width -= (ctx->Scissor.X - px);
	 px = ctx->Scissor.X;
      }
      /* clip right */
      if (px + width >= ctx->Scissor.X + ctx->Scissor.Width) {
	 width -= (px + width - (ctx->Scissor.X + ctx->Scissor.Width));
      }
      /* clip bottom */
      if (py < ctx->Scissor.Y) {
	 scissoredUnpack.SkipRows += (ctx->Scissor.Y - py);
	 height -= (ctx->Scissor.Y - py);
	 py = ctx->Scissor.Y;
      }
      /* clip top */
      if (py + height >= ctx->Scissor.Y + ctx->Scissor.Height) {
	 height -= (py + height - (ctx->Scissor.Y + ctx->Scissor.Height));
      }

      if (width <= 0 || height <= 0)
	 return GL_TRUE;     /* totally scissored away */
   }
   else {
      finalUnpack = unpack;
   }

   /* compute pixel value */
   {
      GLint r = (GLint) (ctx->Current.RasterColor[0] * 255.0f);
      GLint g = (GLint) (ctx->Current.RasterColor[1] * 255.0f);
      GLint b = (GLint) (ctx->Current.RasterColor[2] * 255.0f);
      /*GLint a = (GLint)(ctx->Current.RasterColor[3]*255.0f); */
      if (fxMesa->bgrOrder) {
	 color = (TdfxU16)
	    (((TdfxU16) 0xf8 & b) << (11 - 3)) |
	    (((TdfxU16) 0xfc & g) << (5 - 3 + 1)) |
	    (((TdfxU16) 0xf8 & r) >> 3);
      }
      else
	 color = (TdfxU16)
	    (((TdfxU16) 0xf8 & r) << (11 - 3)) |
	    (((TdfxU16) 0xfc & g) << (5 - 3 + 1)) |
	    (((TdfxU16) 0xf8 & b) >> 3);
   }

   info.size = sizeof(info);
   if (!TDFX_grLfbLock(fxMesa,
		     GR_LFB_WRITE_ONLY,
		     fxMesa->currentFB,
		     GR_LFBWRITEMODE_565,
		     GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) {
#ifndef TDFX_SILENT
      fprintf(stderr, "tdfx Driver: error locking the linear frame buffer\n");
#endif
      return GL_TRUE;
   }

   {
      const GLint winX = fxMesa->x_offset;
      const GLint winY = fxMesa->y_offset + fxMesa->height - 1;
      /* The dest stride depends on the hardware and whether we're drawing
       * to the front or back buffer.  This compile-time test seems to do
       * the job for now.
       */
      const GLint dstStride = (fxMesa->glCtx->Color.DrawBuffer[0] == GL_FRONT)
	 ? (fxMesa->screen_width) : (info.strideInBytes / 2);
      GLint row;
      /* compute dest address of bottom-left pixel in bitmap */
      GLushort *dst = (GLushort *) info.lfbPtr
	 + (winY - py) * dstStride + (winX + px);

      for (row = 0; row < height; row++) {
	 const GLubyte *src =
	    (const GLubyte *) _mesa_image_address2d(finalUnpack,
                                                    bitmap, width, height,
                                                    GL_COLOR_INDEX,
                                                    GL_BITMAP, row, 0);
	 if (finalUnpack->LsbFirst) {
	    /* least significan bit first */
	    GLubyte mask = 1U << (finalUnpack->SkipPixels & 0x7);
	    GLint col;
	    for (col = 0; col < width; col++) {
	       if (*src & mask) {
		  if (inClipRects(fxMesa, winX + px + col, winY - py - row))
		     dst[col] = color;
	       }
	       if (mask == 128U) {
		  src++;
		  mask = 1U;
	       }
	       else {
		  mask = mask << 1;
	       }
	    }
	    if (mask != 1)
	       src++;
	 }
	 else {
	    /* most significan bit first */
	    GLubyte mask = 128U >> (finalUnpack->SkipPixels & 0x7);
	    GLint col;
	    for (col = 0; col < width; col++) {
	       if (*src & mask) {
		  if (inClipRects(fxMesa, winX + px + col, winY - py - row))
		     dst[col] = color;
	       }
	       if (mask == 1U) {
		  src++;
		  mask = 128U;
	       }
	       else {
		  mask = mask >> 1;
	       }
	    }
	    if (mask != 128)
	       src++;
	 }
	 dst -= dstStride;
      }
   }

   TDFX_grLfbUnlock(fxMesa, GR_LFB_WRITE_ONLY, fxMesa->currentFB);
   return GL_TRUE;
}
#endif

#if 0
GLboolean
tdfx_bitmap_R8G8B8A8(GLcontext * ctx, GLint px, GLint py,
		     GLsizei width, GLsizei height,
		     const struct gl_pixelstore_attrib *unpack,
		     const GLubyte * bitmap)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GrLfbInfo_t info;
   GLuint color;
   const struct gl_pixelstore_attrib *finalUnpack;
   struct gl_pixelstore_attrib scissoredUnpack;

   /* check if there's any raster operations enabled which we can't handle */
   if (ctx->RasterMask & (ALPHATEST_BIT |
			  BLEND_BIT |
			  DEPTH_BIT |
			  FOG_BIT |
			  LOGIC_OP_BIT |
			  SCISSOR_BIT |
			  STENCIL_BIT |
			  MASKING_BIT |
			  MULTI_DRAW_BIT)) return GL_FALSE;

   if (ctx->Scissor.Enabled) {
      /* This is a bit tricky, but by carefully adjusting the px, py,
       * width, height, skipPixels and skipRows values we can do
       * scissoring without special code in the rendering loop.
       */

      /* we'll construct a new pixelstore struct */
      finalUnpack = &scissoredUnpack;
      scissoredUnpack = *unpack;
      if (scissoredUnpack.RowLength == 0)
	 scissoredUnpack.RowLength = width;

      /* clip left */
      if (px < ctx->Scissor.X) {
	 scissoredUnpack.SkipPixels += (ctx->Scissor.X - px);
	 width -= (ctx->Scissor.X - px);
	 px = ctx->Scissor.X;
      }
      /* clip right */
      if (px + width >= ctx->Scissor.X + ctx->Scissor.Width) {
	 width -= (px + width - (ctx->Scissor.X + ctx->Scissor.Width));
      }
      /* clip bottom */
      if (py < ctx->Scissor.Y) {
	 scissoredUnpack.SkipRows += (ctx->Scissor.Y - py);
	 height -= (ctx->Scissor.Y - py);
	 py = ctx->Scissor.Y;
      }
      /* clip top */
      if (py + height >= ctx->Scissor.Y + ctx->Scissor.Height) {
	 height -= (py + height - (ctx->Scissor.Y + ctx->Scissor.Height));
      }

      if (width <= 0 || height <= 0)
	 return GL_TRUE;     /* totally scissored away */
   }
   else {
      finalUnpack = unpack;
   }

   /* compute pixel value */
   {
      GLint r = (GLint) (ctx->Current.RasterColor[0] * 255.0f);
      GLint g = (GLint) (ctx->Current.RasterColor[1] * 255.0f);
      GLint b = (GLint) (ctx->Current.RasterColor[2] * 255.0f);
      GLint a = (GLint) (ctx->Current.RasterColor[3] * 255.0f);
      color = PACK_BGRA32(r, g, b, a);
   }

   info.size = sizeof(info);
   if (!TDFX_grLfbLock(fxMesa, GR_LFB_WRITE_ONLY,
		     fxMesa->currentFB, GR_LFBWRITEMODE_8888,
		     GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) {
#ifndef TDFX_SILENT
      fprintf(stderr, "tdfx Driver: error locking the linear frame buffer\n");
#endif
      return GL_TRUE;
   }

   {
      const GLint winX = fxMesa->x_offset;
      const GLint winY = fxMesa->y_offset + fxMesa->height - 1;
      GLint dstStride;
      GLuint *dst;
      GLint row;

      if (fxMesa->glCtx->Color.DrawBuffer[0] == GL_FRONT) {
	 dstStride = fxMesa->screen_width;
	 dst =
	    (GLuint *) info.lfbPtr + (winY - py) * dstStride + (winX +
								px);
      }
      else {
	 dstStride = info.strideInBytes / 4;
	 dst =
	    (GLuint *) info.lfbPtr + (winY - py) * dstStride + (winX +
								px);
      }

      /* compute dest address of bottom-left pixel in bitmap */
      for (row = 0; row < height; row++) {
	 const GLubyte *src =
	    (const GLubyte *) _mesa_image_address2d(finalUnpack,
                                                    bitmap, width, height,
                                                    GL_COLOR_INDEX,
                                                    GL_BITMAP, row, 0);
	 if (finalUnpack->LsbFirst) {
	    /* least significan bit first */
	    GLubyte mask = 1U << (finalUnpack->SkipPixels & 0x7);
	    GLint col;
	    for (col = 0; col < width; col++) {
	       if (*src & mask) {
		  if (inClipRects(fxMesa, winX + px + col, winY - py - row))
		     dst[col] = color;
	       }
	       if (mask == 128U) {
		  src++;
		  mask = 1U;
	       }
	       else {
		  mask = mask << 1;
	       }
	    }
	    if (mask != 1)
	       src++;
	 }
	 else {
	    /* most significan bit first */
	    GLubyte mask = 128U >> (finalUnpack->SkipPixels & 0x7);
	    GLint col;
	    for (col = 0; col < width; col++) {
	       if (*src & mask) {
		  if (inClipRects(fxMesa, winX + px + col, winY - py - row))
		     dst[col] = color;
	       }
	       if (mask == 1U) {
		  src++;
		  mask = 128U;
	       }
	       else {
		  mask = mask >> 1;
	       }
	    }
	    if (mask != 128)
	       src++;
	 }
	 dst -= dstStride;
      }
   }

   TDFX_grLfbUnlock(fxMesa, GR_LFB_WRITE_ONLY, fxMesa->currentFB);
   return GL_TRUE;
}
#endif

void
tdfx_readpixels_R5G6B5(GLcontext * ctx, GLint x, GLint y,
		       GLsizei width, GLsizei height,
		       GLenum format, GLenum type,
		       const struct gl_pixelstore_attrib *packing,
		       GLvoid * dstImage)
{
   if (format != GL_RGB ||
       type != GL_UNSIGNED_SHORT_5_6_5 ||
       (ctx->_ImageTransferState & (IMAGE_SCALE_BIAS_BIT|
				    IMAGE_MAP_COLOR_BIT)))
   {
      _swrast_ReadPixels( ctx, x, y, width, height, format, type, packing,
			  dstImage );
      return;
   }

   {
      tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
      GrLfbInfo_t info;
      __DRIdrawablePrivate *const readable = fxMesa->driReadable;
      const GLint winX = readable->x;
      const GLint winY = readable->y + readable->h - 1;
      const GLint scrX = winX + x;
      const GLint scrY = winY - y;

      LOCK_HARDWARE( fxMesa );
      info.size = sizeof(info);
      if (fxMesa->Glide.grLfbLock(GR_LFB_READ_ONLY,
		    fxMesa->ReadBuffer,
		    GR_LFBWRITEMODE_ANY,
		    GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) {
	 const GLint srcStride = (fxMesa->glCtx->Color.DrawBuffer[0] ==
	     GL_FRONT) ? (fxMesa->screen_width) : (info.strideInBytes / 2);
	 const GLushort *src = (const GLushort *) info.lfbPtr
	    + scrY * srcStride + scrX;
	 GLubyte *dst = (GLubyte *) _mesa_image_address2d(packing,
            dstImage, width, height, format, type, 0, 0);
	 const GLint dstStride = _mesa_image_row_stride(packing,
            width, format, type);

	 /* directly memcpy 5R6G5B pixels into client's buffer */
	 const GLint widthInBytes = width * 2;
	 GLint row;
	 for (row = 0; row < height; row++) {
	    MEMCPY(dst, src, widthInBytes);
	    dst += dstStride;
	    src -= srcStride;
	 }

	 fxMesa->Glide.grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->ReadBuffer);
      }
      UNLOCK_HARDWARE( fxMesa );
      return;
   }
}

void
tdfx_readpixels_R8G8B8A8(GLcontext * ctx, GLint x, GLint y,
                         GLsizei width, GLsizei height,
                         GLenum format, GLenum type,
                         const struct gl_pixelstore_attrib *packing,
                         GLvoid * dstImage)
{
   if ((!(format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8) &&
	!(format == GL_BGRA && type == GL_UNSIGNED_BYTE)) ||
       (ctx->_ImageTransferState & (IMAGE_SCALE_BIAS_BIT|
				    IMAGE_MAP_COLOR_BIT)))
   {
      _swrast_ReadPixels( ctx, x, y, width, height, format, type, packing,
			  dstImage );
      return;
   }


   {
      tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
      GrLfbInfo_t info;
      __DRIdrawablePrivate *const readable = fxMesa->driReadable;
      const GLint winX = readable->x;
      const GLint winY = readable->y + readable->h - 1;
      const GLint scrX = winX + x;
      const GLint scrY = winY - y;

      LOCK_HARDWARE(fxMesa);
      info.size = sizeof(info);
      if (fxMesa->Glide.grLfbLock(GR_LFB_READ_ONLY,
                    fxMesa->ReadBuffer,
                    GR_LFBWRITEMODE_ANY,
                    GR_ORIGIN_UPPER_LEFT, FXFALSE, &info))
      {
         const GLint srcStride = (fxMesa->glCtx->Color.DrawBuffer[0] == GL_FRONT)
            ? (fxMesa->screen_width) : (info.strideInBytes / 4);
         const GLuint *src = (const GLuint *) info.lfbPtr
            + scrY * srcStride + scrX;
         const GLint dstStride =
            _mesa_image_row_stride(packing, width, format, type);
         GLubyte *dst = (GLubyte *) _mesa_image_address2d(packing,
            dstImage, width, height, format, type, 0, 0);
         const GLint widthInBytes = width * 4;

	 {
            GLint row;
            for (row = 0; row < height; row++) {
               MEMCPY(dst, src, widthInBytes);
               dst += dstStride;
               src -= srcStride;
            }
         }

         fxMesa->Glide.grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->ReadBuffer);
      }
      UNLOCK_HARDWARE(fxMesa);
   }
}

void
tdfx_drawpixels_R8G8B8A8(GLcontext * ctx, GLint x, GLint y,
                         GLsizei width, GLsizei height,
                         GLenum format, GLenum type,
                         const struct gl_pixelstore_attrib *unpack,
                         const GLvoid * pixels)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   if ((!(format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8) &&
	!(format == GL_BGRA && type == GL_UNSIGNED_BYTE)) ||
       ctx->Pixel.ZoomX != 1.0F || 
       ctx->Pixel.ZoomY != 1.0F ||
       (ctx->_ImageTransferState & (IMAGE_SCALE_BIAS_BIT|
				    IMAGE_MAP_COLOR_BIT)) ||
       ctx->Color.AlphaEnabled ||
       ctx->Depth.Test ||
       ctx->Fog.Enabled ||
       ctx->Scissor.Enabled ||
       ctx->Stencil._Enabled ||
       !ctx->Color.ColorMask[0] ||
       !ctx->Color.ColorMask[1] ||
       !ctx->Color.ColorMask[2] ||
       !ctx->Color.ColorMask[3] ||
       ctx->Color.ColorLogicOpEnabled ||
       ctx->Texture._EnabledUnits ||
       fxMesa->Fallback)       
   {
      _swrast_DrawPixels( ctx, x, y, width, height, format, type, 
			  unpack, pixels );
      return; 
   }

   {
      tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
      GrLfbInfo_t info;
      GLboolean result = GL_FALSE;

      const GLint winX = fxMesa->x_offset;
      const GLint winY = fxMesa->y_offset + fxMesa->height - 1;
      const GLint scrX = winX + x;
      const GLint scrY = winY - y;

      /* lock early to make sure cliprects are right */
      LOCK_HARDWARE(fxMesa);

      /* make sure hardware has latest blend funcs */
      if (ctx->Color.BlendEnabled) {
         fxMesa->dirty |= TDFX_UPLOAD_BLEND_FUNC;
         tdfxEmitHwStateLocked( fxMesa );
      }

      /* look for clipmasks, giveup if region obscured */
      if (fxMesa->glCtx->Color.DrawBuffer[0] == GL_FRONT) {
         if (!inClipRects_Region(fxMesa, scrX, scrY, width, height)) {
            UNLOCK_HARDWARE(fxMesa);
	    _swrast_DrawPixels( ctx, x, y, width, height, format, type, 
				unpack, pixels );
            return;
         }
      }

      info.size = sizeof(info);
      if (fxMesa->Glide.grLfbLock(GR_LFB_WRITE_ONLY,
                    fxMesa->DrawBuffer,
                    GR_LFBWRITEMODE_8888,
                    GR_ORIGIN_UPPER_LEFT, FXTRUE, &info))
      {
         const GLint dstStride = (fxMesa->glCtx->Color.DrawBuffer[0] == GL_FRONT)
            ? (fxMesa->screen_width * 4) : (info.strideInBytes);
         GLubyte *dst = (GLubyte *) info.lfbPtr
            + scrY * dstStride + scrX * 4;
         const GLint srcStride =
            _mesa_image_row_stride(unpack, width, format, type);
         const GLubyte *src = (GLubyte *) _mesa_image_address2d(unpack,
            pixels, width, height, format, type, 0, 0);
         const GLint widthInBytes = width * 4;

         if ((format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8) ||
             (format == GL_BGRA && type == GL_UNSIGNED_BYTE)) {
            GLint row;
            for (row = 0; row < height; row++) {
               MEMCPY(dst, src, widthInBytes);
               dst -= dstStride;
               src += srcStride;
            }
            result = GL_TRUE;
         }

         fxMesa->Glide.grLfbUnlock(GR_LFB_WRITE_ONLY, fxMesa->DrawBuffer);
      }
      UNLOCK_HARDWARE(fxMesa);
   }
}
