/*
* Mesa 3-D graphics library
* Version: 7.1
*
* Copyright (C) 1999-2007 Brian Paul 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 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
* BRIAN PAUL 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.
*/
#include "main/glheader.h"
#include "main/bufferobj.h"
#include "main/colormac.h"
#include "main/condrender.h"
#include "main/context.h"
#include "main/format_pack.h"
#include "main/image.h"
#include "main/imports.h"
#include "main/macros.h"
#include "main/pack.h"
#include "main/pbo.h"
#include "main/pixeltransfer.h"
#include "main/state.h"
#include "s_context.h"
#include "s_span.h"
#include "s_stencil.h"
#include "s_zoom.h"
/**
* Handle a common case of drawing GL_RGB/GL_UNSIGNED_BYTE into a
* MESA_FORMAT_XRGB888 or MESA_FORMAT_ARGB888 renderbuffer.
*/
static void
fast_draw_rgb_ubyte_pixels(struct gl_context *ctx,
struct gl_renderbuffer *rb,
GLint x, GLint y,
GLsizei width, GLsizei height,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels)
{
const GLubyte *src = (const GLubyte *)
_mesa_image_address2d(unpack, pixels, width,
height, GL_RGB, GL_UNSIGNED_BYTE, 0, 0);
const GLint srcRowStride = _mesa_image_row_stride(unpack, width,
GL_RGB, GL_UNSIGNED_BYTE);
GLint i, j;
GLubyte *dst;
GLint dstRowStride;
ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
GL_MAP_WRITE_BIT, &dst, &dstRowStride);
if (!dst) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
return;
}
if (ctx->Pixel.ZoomY == -1.0f) {
dst = dst + (height - 1) * dstRowStride;
dstRowStride = -dstRowStride;
}
for (i = 0; i < height; i++) {
GLuint *dst4 = (GLuint *) dst;
for (j = 0; j < width; j++) {
dst4[j] = PACK_COLOR_8888(0xff, src[j*3+0], src[j*3+1], src[j*3+2]);
}
dst += dstRowStride;
src += srcRowStride;
}
ctx->Driver.UnmapRenderbuffer(ctx, rb);
}
/**
* Handle a common case of drawing GL_RGBA/GL_UNSIGNED_BYTE into a
* MESA_FORMAT_ARGB888 or MESA_FORMAT_xRGB888 renderbuffer.
*/
static void
fast_draw_rgba_ubyte_pixels(struct gl_context *ctx,
struct gl_renderbuffer *rb,
GLint x, GLint y,
GLsizei width, GLsizei height,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels)
{
const GLubyte *src = (const GLubyte *)
_mesa_image_address2d(unpack, pixels, width,
height, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
const GLint srcRowStride =
_mesa_image_row_stride(unpack, width, GL_RGBA, GL_UNSIGNED_BYTE);
GLint i, j;
GLubyte *dst;
GLint dstRowStride;
ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
GL_MAP_WRITE_BIT, &dst, &dstRowStride);
if (!dst) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
return;
}
if (ctx->Pixel.ZoomY == -1.0f) {
dst = dst + (height - 1) * dstRowStride;
dstRowStride = -dstRowStride;
}
for (i = 0; i < height; i++) {
GLuint *dst4 = (GLuint *) dst;
for (j = 0; j < width; j++) {
dst4[j] = PACK_COLOR_8888(src[j*4+3], src[j*4+0],
src[j*4+1], src[j*4+2]);
}
dst += dstRowStride;
src += srcRowStride;
}
ctx->Driver.UnmapRenderbuffer(ctx, rb);
}
/**
* Handle a common case of drawing a format/type combination that
* exactly matches the renderbuffer format.
*/
static void
fast_draw_generic_pixels(struct gl_context *ctx,
struct gl_renderbuffer *rb,
GLint x, GLint y,
GLsizei width, GLsizei height,
GLenum format, GLenum type,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels)
{
const GLubyte *src = (const GLubyte *)
_mesa_image_address2d(unpack, pixels, width,
height, format, type, 0, 0);
const GLint srcRowStride =
_mesa_image_row_stride(unpack, width, format, type);
const GLint rowLength = width * _mesa_get_format_bytes(rb->Format);
GLint i;
GLubyte *dst;
GLint dstRowStride;
ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height,
GL_MAP_WRITE_BIT, &dst, &dstRowStride);
if (!dst) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
return;
}
if (ctx->Pixel.ZoomY == -1.0f) {
dst = dst + (height - 1) * dstRowStride;
dstRowStride = -dstRowStride;
}
for (i = 0; i < height; i++) {
memcpy(dst, src, rowLength);
dst += dstRowStride;
src += srcRowStride;
}
ctx->Driver.UnmapRenderbuffer(ctx, rb);
}
/**
* Try to do a fast and simple RGB(a) glDrawPixels.
* Return: GL_TRUE if success, GL_FALSE if slow path must be used instead
*/
static GLboolean
fast_draw_rgba_pixels(struct gl_context *ctx, GLint x, GLint y,
GLsizei width, GLsizei height,
GLenum format, GLenum type,
const struct gl_pixelstore_attrib *userUnpack,
const GLvoid *pixels)
{
struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0];
SWcontext *swrast = SWRAST_CONTEXT(ctx);
struct gl_pixelstore_attrib unpack;
if (!rb)
return GL_TRUE; /* no-op */
if (ctx->DrawBuffer->_NumColorDrawBuffers > 1 ||
(swrast->_RasterMask & ~CLIP_BIT) ||
ctx->Texture._EnabledCoordUnits ||
userUnpack->SwapBytes ||
ctx->Pixel.ZoomX != 1.0f ||
fabsf(ctx->Pixel.ZoomY) != 1.0f ||
ctx->_ImageTransferState) {
/* can't handle any of those conditions */
return GL_FALSE;
}
unpack = *userUnpack;
/* clipping */
if (!_mesa_clip_drawpixels(ctx, &x, &y, &width, &height, &unpack)) {
/* image was completely clipped: no-op, all done */
return GL_TRUE;
}
if (format == GL_RGB &&
type == GL_UNSIGNED_BYTE &&
(rb->Format == MESA_FORMAT_XRGB8888 ||
rb->Format == MESA_FORMAT_ARGB8888)) {
fast_draw_rgb_ubyte_pixels(ctx, rb, x, y, width, height,
&unpack, pixels);
return GL_TRUE;
}
if (format == GL_RGBA &&
type == GL_UNSIGNED_BYTE &&
(rb->Format == MESA_FORMAT_XRGB8888 ||
rb->Format == MESA_FORMAT_ARGB8888)) {
fast_draw_rgba_ubyte_pixels(ctx, rb, x, y, width, height,
&unpack, pixels);
return GL_TRUE;
}
if (_mesa_format_matches_format_and_type(rb->Format, format, type,
ctx->Unpack.SwapBytes)) {
fast_draw_generic_pixels(ctx, rb, x, y, width, height,
format, type, &unpack, pixels);
return GL_TRUE;
}
/* can't handle this pixel format and/or data type */
return GL_FALSE;
}
/*
* Draw stencil image.
*/
static void
draw_stencil_pixels( struct gl_context *ctx, GLint x, GLint y,
GLsizei width, GLsizei height,
GLenum type,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels )
{
const GLboolean zoom = ctx->Pixel.ZoomX != 1.0 || ctx->Pixel.ZoomY != 1.0;
const GLenum destType = GL_UNSIGNED_BYTE;
GLint row;
GLubyte *values;
values = (GLubyte *) malloc(width * sizeof(GLubyte));
if (!values) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
return;
}
for (row = 0; row < height; row++) {
const GLvoid *source = _mesa_image_address2d(unpack, pixels,
width, height,
GL_STENCIL_INDEX, type,
row, 0);
_mesa_unpack_stencil_span(ctx, width, destType, values,
type, source, unpack,
ctx->_ImageTransferState);
if (zoom) {
_swrast_write_zoomed_stencil_span(ctx, x, y, width,
x, y, values);
}
else {
_swrast_write_stencil_span(ctx, width, x, y, values);
}
y++;
}
free(values);
}
/*
* Draw depth image.
*/
static void
draw_depth_pixels( struct gl_context *ctx, GLint x, GLint y,
GLsizei width, GLsizei height,
GLenum type,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels )
{
const GLboolean scaleOrBias
= ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0;
const GLboolean zoom = ctx->Pixel.ZoomX != 1.0 || ctx->Pixel.ZoomY != 1.0;
SWspan span;
INIT_SPAN(span, GL_BITMAP);
span.arrayMask = SPAN_Z;
_swrast_span_default_attribs(ctx, &span);
if (type == GL_UNSIGNED_SHORT
&& ctx->DrawBuffer->Visual.depthBits == 16
&& !scaleOrBias
&& !zoom
&& width <= SWRAST_MAX_WIDTH
&& !unpack->SwapBytes) {
/* Special case: directly write 16-bit depth values */
GLint row;
for (row = 0; row < height; row++) {
const GLushort *zSrc = (const GLushort *)
_mesa_image_address2d(unpack, pixels, width, height,
GL_DEPTH_COMPONENT, type, row, 0);
GLint i;
for (i = 0; i < width; i++)
span.array->z[i] = zSrc[i];
span.x = x;
span.y = y + row;
span.end = width;
_swrast_write_rgba_span(ctx, &span);
}
}
else if (type == GL_UNSIGNED_INT
&& !scaleOrBias
&& !zoom
&& width <= SWRAST_MAX_WIDTH
&& !unpack->SwapBytes) {
/* Special case: shift 32-bit values down to Visual.depthBits */
const GLint shift = 32 - ctx->DrawBuffer->Visual.depthBits;
GLint row;
for (row = 0; row < height; row++) {
const GLuint *zSrc = (const GLuint *)
_mesa_image_address2d(unpack, pixels, width, height,
GL_DEPTH_COMPONENT, type, row, 0);
if (shift == 0) {
memcpy(span.array->z, zSrc, width * sizeof(GLuint));
}
else {
GLint col;
for (col = 0; col < width; col++)
span.array->z[col] = zSrc[col] >> shift;
}
span.x = x;
span.y = y + row;
span.end = width;
_swrast_write_rgba_span(ctx, &span);
}
}
else {
/* General case */
const GLuint depthMax = ctx->DrawBuffer->_DepthMax;
GLint skipPixels = 0;
/* in case width > SWRAST_MAX_WIDTH do the copy in chunks */
while (skipPixels < width) {
const GLint spanWidth = MIN2(width - skipPixels, SWRAST_MAX_WIDTH);
GLint row;
ASSERT(span.end <= SWRAST_MAX_WIDTH);
for (row = 0; row < height; row++) {
const GLvoid *zSrc = _mesa_image_address2d(unpack,
pixels, width, height,
GL_DEPTH_COMPONENT, type,
row, skipPixels);
/* Set these for each row since the _swrast_write_* function may
* change them while clipping.
*/
span.x = x + skipPixels;
span.y = y + row;
span.end = spanWidth;
_mesa_unpack_depth_span(ctx, spanWidth,
GL_UNSIGNED_INT, span.array->z, depthMax,
type, zSrc, unpack);
if (zoom) {
_swrast_write_zoomed_depth_span(ctx, x, y, &span);
}
else {
_swrast_write_rgba_span(ctx, &span);
}
}
skipPixels += spanWidth;
}
}
}
/**
* Draw RGBA image.
*/
static void
draw_rgba_pixels( struct gl_context *ctx, GLint x, GLint y,
GLsizei width, GLsizei height,
GLenum format, GLenum type,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels )
{
const GLint imgX = x, imgY = y;
const GLboolean zoom = ctx->Pixel.ZoomX!=1.0 || ctx->Pixel.ZoomY!=1.0;
GLfloat *convImage = NULL;
GLbitfield transferOps = ctx->_ImageTransferState;
SWspan span;
/* Try an optimized glDrawPixels first */
if (fast_draw_rgba_pixels(ctx, x, y, width, height, format, type,
unpack, pixels)) {
return;
}
swrast_render_start(ctx);
INIT_SPAN(span, GL_BITMAP);
_swrast_span_default_attribs(ctx, &span);
span.arrayMask = SPAN_RGBA;
span.arrayAttribs = FRAG_BIT_COL0; /* we're fill in COL0 attrib values */
if (ctx->DrawBuffer->_NumColorDrawBuffers > 0) {
GLenum datatype = _mesa_get_format_datatype(
ctx->DrawBuffer->_ColorDrawBuffers[0]->Format);
if (datatype != GL_FLOAT &&
ctx->Color.ClampFragmentColor != GL_FALSE) {
/* need to clamp colors before applying fragment ops */
transferOps |= IMAGE_CLAMP_BIT;
}
}
/*
* General solution
*/
{
const GLbitfield interpMask = span.interpMask;
const GLbitfield arrayMask = span.arrayMask;
const GLint srcStride
= _mesa_image_row_stride(unpack, width, format, type);
GLint skipPixels = 0;
/* use span array for temp color storage */
GLfloat *rgba = (GLfloat *) span.array->attribs[FRAG_ATTRIB_COL0];
/* if the span is wider than SWRAST_MAX_WIDTH we have to do it in chunks */
while (skipPixels < width) {
const GLint spanWidth = MIN2(width - skipPixels, SWRAST_MAX_WIDTH);
const GLubyte *source
= (const GLubyte *) _mesa_image_address2d(unpack, pixels,
width, height, format,
type, 0, skipPixels);
GLint row;
for (row = 0; row < height; row++) {
/* get image row as float/RGBA */
_mesa_unpack_color_span_float(ctx, spanWidth, GL_RGBA, rgba,
format, type, source, unpack,
transferOps);
/* Set these for each row since the _swrast_write_* functions
* may change them while clipping/rendering.
*/
span.array->ChanType = GL_FLOAT;
span.x = x + skipPixels;
span.y = y + row;
span.end = spanWidth;
span.arrayMask = arrayMask;
span.interpMask = interpMask;
if (zoom) {
_swrast_write_zoomed_rgba_span(ctx, imgX, imgY, &span, rgba);
}
else {
_swrast_write_rgba_span(ctx, &span);
}
source += srcStride;
} /* for row */
skipPixels += spanWidth;
} /* while skipPixels < width */
/* XXX this is ugly/temporary, to undo above change */
span.array->ChanType = CHAN_TYPE;
}
if (convImage) {
free(convImage);
}
swrast_render_finish(ctx);
}
/**
* Draw depth+stencil values into a MESA_FORAMT_Z24_S8 or MESA_FORMAT_S8_Z24
* renderbuffer. No masking, zooming, scaling, etc.
*/
static void
fast_draw_depth_stencil(struct gl_context *ctx, GLint x, GLint y,
GLsizei width, GLsizei height,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels)
{
const GLenum format = GL_DEPTH_STENCIL_EXT;
const GLenum type = GL_UNSIGNED_INT_24_8;
struct gl_renderbuffer *rb =
ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer;
struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
GLubyte *src, *dst;
GLint srcRowStride, dstRowStride;
GLint i;
src = _mesa_image_address2d(unpack, pixels, width, height,
format, type, 0, 0);
srcRowStride = _mesa_image_row_stride(unpack, width, format, type);
dst = _swrast_pixel_address(rb, x, y);
dstRowStride = srb->RowStride;
for (i = 0; i < height; i++) {
_mesa_pack_uint_24_8_depth_stencil_row(rb->Format, width,
(const GLuint *) src, dst);
dst += dstRowStride;
src += srcRowStride;
}
}
/**
* This is a bit different from drawing GL_DEPTH_COMPONENT pixels.
* The only per-pixel operations that apply are depth scale/bias,
* stencil offset/shift, GL_DEPTH_WRITEMASK and GL_STENCIL_WRITEMASK,
* and pixel zoom.
* Also, only the depth buffer and stencil buffers are touched, not the
* color buffer(s).
*/
static void
draw_depth_stencil_pixels(struct gl_context *ctx, GLint x, GLint y,
GLsizei width, GLsizei height, GLenum type,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels)
{
const GLint imgX = x, imgY = y;
const GLboolean scaleOrBias
= ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0;
const GLuint stencilMask = ctx->Stencil.WriteMask[0];
const GLenum stencilType = GL_UNSIGNED_BYTE;
const GLboolean zoom = ctx->Pixel.ZoomX != 1.0 || ctx->Pixel.ZoomY != 1.0;
struct gl_renderbuffer *depthRb, *stencilRb;
struct gl_pixelstore_attrib clippedUnpack = *unpack;
if (!zoom) {
if (!_mesa_clip_drawpixels(ctx, &x, &y, &width, &height,
&clippedUnpack)) {
/* totally clipped */
return;
}
}
depthRb = ctx->ReadBuffer->Attachment[BUFFER_DEPTH].Renderbuffer;
stencilRb = ctx->ReadBuffer->Attachment[BUFFER_STENCIL].Renderbuffer;
ASSERT(depthRb);
ASSERT(stencilRb);
if (depthRb == stencilRb &&
(depthRb->Format == MESA_FORMAT_Z24_S8 ||
depthRb->Format == MESA_FORMAT_S8_Z24) &&
type == GL_UNSIGNED_INT_24_8 &&
!scaleOrBias &&
!zoom &&
ctx->Depth.Mask &&
(stencilMask & 0xff) == 0xff) {
fast_draw_depth_stencil(ctx, x, y, width, height,
&clippedUnpack, pixels);
}
else {
/* sub-optimal cases:
* Separate depth/stencil buffers, or pixel transfer ops required.
*/
/* XXX need to handle very wide images (skippixels) */
GLuint *zValues; /* 32-bit Z values */
GLint i;
zValues = (GLuint *) malloc(width * sizeof(GLuint));
if (!zValues) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels");
return;
}
for (i = 0; i < height; i++) {
const GLuint *depthStencilSrc = (const GLuint *)
_mesa_image_address2d(&clippedUnpack, pixels, width, height,
GL_DEPTH_STENCIL_EXT, type, i, 0);
if (ctx->Depth.Mask) {
_mesa_unpack_depth_span(ctx, width,
GL_UNSIGNED_INT, /* dest type */
zValues, /* dest addr */
0xffffffff, /* depth max */
type, /* src type */
depthStencilSrc, /* src addr */
&clippedUnpack);
if (zoom) {
_swrast_write_zoomed_z_span(ctx, imgX, imgY, width, x,
y + i, zValues);
}
else {
GLubyte *dst = _swrast_pixel_address(depthRb, x, y + i);
_mesa_pack_uint_z_row(depthRb->Format, width, zValues, dst);
}
}
if (stencilMask != 0x0) {
GLubyte *stencilValues = (GLubyte *) zValues; /* re-use buffer */
/* get stencil values, with shift/offset/mapping */
_mesa_unpack_stencil_span(ctx, width, stencilType, stencilValues,
type, depthStencilSrc, &clippedUnpack,
ctx->_ImageTransferState);
if (zoom)
_swrast_write_zoomed_stencil_span(ctx, imgX, imgY, width,
x, y + i, stencilValues);
else
_swrast_write_stencil_span(ctx, width, x, y + i, stencilValues);
}
}
free(zValues);
}
}
/**
* Execute software-based glDrawPixels.
* By time we get here, all error checking will have been done.
*/
void
_swrast_DrawPixels( struct gl_context *ctx,
GLint x, GLint y,
GLsizei width, GLsizei height,
GLenum format, GLenum type,
const struct gl_pixelstore_attrib *unpack,
const GLvoid *pixels )
{
SWcontext *swrast = SWRAST_CONTEXT(ctx);
GLboolean save_vp_override = ctx->VertexProgram._Overriden;
if (!_mesa_check_conditional_render(ctx))
return; /* don't draw */
/* We are creating fragments directly, without going through vertex
* programs.
*
* This override flag tells the fragment processing code that its input
* comes from a non-standard source, and it may therefore not rely on
* optimizations that assume e.g. constant color if there is no color
* vertex array.
*/
_mesa_set_vp_override(ctx, GL_TRUE);
if (ctx->NewState)
_mesa_update_state(ctx);
if (swrast->NewState)
_swrast_validate_derived( ctx );
pixels = _mesa_map_pbo_source(ctx, unpack, pixels);
if (!pixels) {
_mesa_set_vp_override(ctx, save_vp_override);
return;
}
/*
* By time we get here, all error checking should have been done.
*/
switch (format) {
case GL_STENCIL_INDEX:
swrast_render_start(ctx);
draw_stencil_pixels( ctx, x, y, width, height, type, unpack, pixels );
swrast_render_finish(ctx);
break;
case GL_DEPTH_COMPONENT:
swrast_render_start(ctx);
draw_depth_pixels( ctx, x, y, width, height, type, unpack, pixels );
swrast_render_finish(ctx);
break;
case GL_DEPTH_STENCIL_EXT:
swrast_render_start(ctx);
draw_depth_stencil_pixels(ctx, x, y, width, height, type, unpack, pixels);
swrast_render_finish(ctx);
break;
default:
/* all other formats should be color formats */
draw_rgba_pixels(ctx, x, y, width, height, format, type, unpack, pixels);
}
_mesa_set_vp_override(ctx, save_vp_override);
_mesa_unmap_pbo_source(ctx, unpack);
}