/* * Mesa 3-D graphics library * * Copyright (C) 1999-2008 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 * THE AUTHORS OR COPYRIGHT HOLDERS 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/macros.h" #include "main/imports.h" #include "main/format_pack.h" #include "main/colormac.h" #include "s_context.h" #include "s_span.h" #include "s_stencil.h" #include "s_zoom.h" /** * Compute the bounds of the region resulting from zooming a pixel span. * The resulting region will be entirely inside the window/scissor bounds * so no additional clipping is needed. * \param imageX, imageY position of the mage being drawn (gl WindowPos) * \param spanX, spanY position of span being drawing * \param width number of pixels in span * \param x0, x1 returned X bounds of zoomed region [x0, x1) * \param y0, y1 returned Y bounds of zoomed region [y0, y1) * \return GL_TRUE if any zoomed pixels visible, GL_FALSE if totally clipped */ static GLboolean compute_zoomed_bounds(struct gl_context *ctx, GLint imageX, GLint imageY, GLint spanX, GLint spanY, GLint width, GLint *x0, GLint *x1, GLint *y0, GLint *y1) { const struct gl_framebuffer *fb = ctx->DrawBuffer; GLint c0, c1, r0, r1; ASSERT(spanX >= imageX); ASSERT(spanY >= imageY); /* * Compute destination columns: [c0, c1) */ c0 = imageX + (GLint) ((spanX - imageX) * ctx->Pixel.ZoomX); c1 = imageX + (GLint) ((spanX + width - imageX) * ctx->Pixel.ZoomX); if (c1 < c0) { /* swap */ GLint tmp = c1; c1 = c0; c0 = tmp; } c0 = CLAMP(c0, fb->_Xmin, fb->_Xmax); c1 = CLAMP(c1, fb->_Xmin, fb->_Xmax); if (c0 == c1) { return GL_FALSE; /* no width */ } /* * Compute destination rows: [r0, r1) */ r0 = imageY + (GLint) ((spanY - imageY) * ctx->Pixel.ZoomY); r1 = imageY + (GLint) ((spanY + 1 - imageY) * ctx->Pixel.ZoomY); if (r1 < r0) { /* swap */ GLint tmp = r1; r1 = r0; r0 = tmp; } r0 = CLAMP(r0, fb->_Ymin, fb->_Ymax); r1 = CLAMP(r1, fb->_Ymin, fb->_Ymax); if (r0 == r1) { return GL_FALSE; /* no height */ } *x0 = c0; *x1 = c1; *y0 = r0; *y1 = r1; return GL_TRUE; } /** * Convert a zoomed x image coordinate back to an unzoomed x coord. * 'zx' is screen position of a pixel in the zoomed image, who's left edge * is at 'imageX'. * return corresponding x coord in the original, unzoomed image. * This can use this for unzooming X or Y values. */ static inline GLint unzoom_x(GLfloat zoomX, GLint imageX, GLint zx) { /* zx = imageX + (x - imageX) * zoomX; zx - imageX = (x - imageX) * zoomX; (zx - imageX) / zoomX = x - imageX; */ GLint x; if (zoomX < 0.0) zx++; x = imageX + (GLint) ((zx - imageX) / zoomX); return x; } /** * Helper function called from _swrast_write_zoomed_rgba/rgb/ * index/depth_span(). */ static void zoom_span( struct gl_context *ctx, GLint imgX, GLint imgY, const SWspan *span, const GLvoid *src, GLenum format ) { SWcontext *swrast = SWRAST_CONTEXT(ctx); SWspan zoomed; GLint x0, x1, y0, y1; GLint zoomedWidth; if (!compute_zoomed_bounds(ctx, imgX, imgY, span->x, span->y, span->end, &x0, &x1, &y0, &y1)) { return; /* totally clipped */ } if (!swrast->ZoomedArrays) { /* allocate on demand */ swrast->ZoomedArrays = (SWspanarrays *) calloc(1, sizeof(SWspanarrays)); if (!swrast->ZoomedArrays) return; } zoomedWidth = x1 - x0; ASSERT(zoomedWidth > 0); ASSERT(zoomedWidth <= SWRAST_MAX_WIDTH); /* no pixel arrays! must be horizontal spans. */ ASSERT((span->arrayMask & SPAN_XY) == 0); ASSERT(span->primitive == GL_BITMAP); INIT_SPAN(zoomed, GL_BITMAP); zoomed.x = x0; zoomed.end = zoomedWidth; zoomed.array = swrast->ZoomedArrays; zoomed.array->ChanType = span->array->ChanType; if (zoomed.array->ChanType == GL_UNSIGNED_BYTE) zoomed.array->rgba = (GLchan (*)[4]) zoomed.array->rgba8; else if (zoomed.array->ChanType == GL_UNSIGNED_SHORT) zoomed.array->rgba = (GLchan (*)[4]) zoomed.array->rgba16; else zoomed.array->rgba = (GLchan (*)[4]) zoomed.array->attribs[VARYING_SLOT_COL0]; COPY_4V(zoomed.attrStart[VARYING_SLOT_POS], span->attrStart[VARYING_SLOT_POS]); COPY_4V(zoomed.attrStepX[VARYING_SLOT_POS], span->attrStepX[VARYING_SLOT_POS]); COPY_4V(zoomed.attrStepY[VARYING_SLOT_POS], span->attrStepY[VARYING_SLOT_POS]); zoomed.attrStart[VARYING_SLOT_FOGC][0] = span->attrStart[VARYING_SLOT_FOGC][0]; zoomed.attrStepX[VARYING_SLOT_FOGC][0] = span->attrStepX[VARYING_SLOT_FOGC][0]; zoomed.attrStepY[VARYING_SLOT_FOGC][0] = span->attrStepY[VARYING_SLOT_FOGC][0]; if (format == GL_RGBA || format == GL_RGB) { /* copy Z info */ zoomed.z = span->z; zoomed.zStep = span->zStep; /* we'll generate an array of colorss */ zoomed.interpMask = span->interpMask & ~SPAN_RGBA; zoomed.arrayMask |= SPAN_RGBA; zoomed.arrayAttribs |= VARYING_BIT_COL0; /* we'll produce these values */ ASSERT(span->arrayMask & SPAN_RGBA); } else if (format == GL_DEPTH_COMPONENT) { /* Copy color info */ zoomed.red = span->red; zoomed.green = span->green; zoomed.blue = span->blue; zoomed.alpha = span->alpha; zoomed.redStep = span->redStep; zoomed.greenStep = span->greenStep; zoomed.blueStep = span->blueStep; zoomed.alphaStep = span->alphaStep; /* we'll generate an array of depth values */ zoomed.interpMask = span->interpMask & ~SPAN_Z; zoomed.arrayMask |= SPAN_Z; ASSERT(span->arrayMask & SPAN_Z); } else { _mesa_problem(ctx, "Bad format in zoom_span"); return; } /* zoom the span horizontally */ if (format == GL_RGBA) { if (zoomed.array->ChanType == GL_UNSIGNED_BYTE) { const GLubyte (*rgba)[4] = (const GLubyte (*)[4]) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); COPY_4UBV(zoomed.array->rgba8[i], rgba[j]); } } else if (zoomed.array->ChanType == GL_UNSIGNED_SHORT) { const GLushort (*rgba)[4] = (const GLushort (*)[4]) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); COPY_4V(zoomed.array->rgba16[i], rgba[j]); } } else { const GLfloat (*rgba)[4] = (const GLfloat (*)[4]) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); COPY_4V(zoomed.array->attribs[VARYING_SLOT_COL0][i], rgba[j]); } } } else if (format == GL_RGB) { if (zoomed.array->ChanType == GL_UNSIGNED_BYTE) { const GLubyte (*rgb)[3] = (const GLubyte (*)[3]) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); zoomed.array->rgba8[i][0] = rgb[j][0]; zoomed.array->rgba8[i][1] = rgb[j][1]; zoomed.array->rgba8[i][2] = rgb[j][2]; zoomed.array->rgba8[i][3] = 0xff; } } else if (zoomed.array->ChanType == GL_UNSIGNED_SHORT) { const GLushort (*rgb)[3] = (const GLushort (*)[3]) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); zoomed.array->rgba16[i][0] = rgb[j][0]; zoomed.array->rgba16[i][1] = rgb[j][1]; zoomed.array->rgba16[i][2] = rgb[j][2]; zoomed.array->rgba16[i][3] = 0xffff; } } else { const GLfloat (*rgb)[3] = (const GLfloat (*)[3]) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); zoomed.array->attribs[VARYING_SLOT_COL0][i][0] = rgb[j][0]; zoomed.array->attribs[VARYING_SLOT_COL0][i][1] = rgb[j][1]; zoomed.array->attribs[VARYING_SLOT_COL0][i][2] = rgb[j][2]; zoomed.array->attribs[VARYING_SLOT_COL0][i][3] = 1.0F; } } } else if (format == GL_DEPTH_COMPONENT) { const GLuint *zValues = (const GLuint *) src; GLint i; for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x; ASSERT(j >= 0); ASSERT(j < (GLint) span->end); zoomed.array->z[i] = zValues[j]; } /* Now, fall into the RGB path below */ format = GL_RGBA; } /* write the span in rows [r0, r1) */ if (format == GL_RGBA || format == GL_RGB) { /* Writing the span may modify the colors, so make a backup now if we're * going to call _swrast_write_zoomed_span() more than once. * Also, clipping may change the span end value, so store it as well. */ const GLint end = zoomed.end; /* save */ void *rgbaSave; const GLint pixelSize = (zoomed.array->ChanType == GL_UNSIGNED_BYTE) ? 4 * sizeof(GLubyte) : ((zoomed.array->ChanType == GL_UNSIGNED_SHORT) ? 4 * sizeof(GLushort) : 4 * sizeof(GLfloat)); rgbaSave = malloc(zoomed.end * pixelSize); if (!rgbaSave) { return; } if (y1 - y0 > 1) { memcpy(rgbaSave, zoomed.array->rgba, zoomed.end * pixelSize); } for (zoomed.y = y0; zoomed.y < y1; zoomed.y++) { _swrast_write_rgba_span(ctx, &zoomed); zoomed.end = end; /* restore */ if (y1 - y0 > 1) { /* restore the colors */ memcpy(zoomed.array->rgba, rgbaSave, zoomed.end * pixelSize); } } free(rgbaSave); } } void _swrast_write_zoomed_rgba_span(struct gl_context *ctx, GLint imgX, GLint imgY, const SWspan *span, const GLvoid *rgba) { zoom_span(ctx, imgX, imgY, span, rgba, GL_RGBA); } void _swrast_write_zoomed_rgb_span(struct gl_context *ctx, GLint imgX, GLint imgY, const SWspan *span, const GLvoid *rgb) { zoom_span(ctx, imgX, imgY, span, rgb, GL_RGB); } void _swrast_write_zoomed_depth_span(struct gl_context *ctx, GLint imgX, GLint imgY, const SWspan *span) { zoom_span(ctx, imgX, imgY, span, (const GLvoid *) span->array->z, GL_DEPTH_COMPONENT); } /** * Zoom/write stencil values. * No per-fragment operations are applied. */ void _swrast_write_zoomed_stencil_span(struct gl_context *ctx, GLint imgX, GLint imgY, GLint width, GLint spanX, GLint spanY, const GLubyte stencil[]) { GLubyte *zoomedVals; GLint x0, x1, y0, y1, y; GLint i, zoomedWidth; if (!compute_zoomed_bounds(ctx, imgX, imgY, spanX, spanY, width, &x0, &x1, &y0, &y1)) { return; /* totally clipped */ } zoomedWidth = x1 - x0; ASSERT(zoomedWidth > 0); ASSERT(zoomedWidth <= SWRAST_MAX_WIDTH); zoomedVals = malloc(zoomedWidth * sizeof(GLubyte)); if (!zoomedVals) return; /* zoom the span horizontally */ for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - spanX; ASSERT(j >= 0); ASSERT(j < width); zoomedVals[i] = stencil[j]; } /* write the zoomed spans */ for (y = y0; y < y1; y++) { _swrast_write_stencil_span(ctx, zoomedWidth, x0, y, zoomedVals); } free(zoomedVals); } /** * Zoom/write 32-bit Z values. * No per-fragment operations are applied. */ void _swrast_write_zoomed_z_span(struct gl_context *ctx, GLint imgX, GLint imgY, GLint width, GLint spanX, GLint spanY, const GLuint *zVals) { struct gl_renderbuffer *rb = ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer; GLuint *zoomedVals; GLint x0, x1, y0, y1, y; GLint i, zoomedWidth; if (!compute_zoomed_bounds(ctx, imgX, imgY, spanX, spanY, width, &x0, &x1, &y0, &y1)) { return; /* totally clipped */ } zoomedWidth = x1 - x0; ASSERT(zoomedWidth > 0); ASSERT(zoomedWidth <= SWRAST_MAX_WIDTH); zoomedVals = malloc(zoomedWidth * sizeof(GLuint)); if (!zoomedVals) return; /* zoom the span horizontally */ for (i = 0; i < zoomedWidth; i++) { GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - spanX; ASSERT(j >= 0); ASSERT(j < width); zoomedVals[i] = zVals[j]; } /* write the zoomed spans */ for (y = y0; y < y1; y++) { GLubyte *dst = _swrast_pixel_address(rb, x0, y); _mesa_pack_uint_z_row(rb->Format, zoomedWidth, zoomedVals, dst); } free(zoomedVals); }