1/*
2 * Mesa 3-D graphics library
3 * Version:  7.1
4 *
5 * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include "main/glheader.h"
26#include "main/macros.h"
27#include "main/imports.h"
28#include "main/format_pack.h"
29#include "main/colormac.h"
30
31#include "s_context.h"
32#include "s_span.h"
33#include "s_stencil.h"
34#include "s_zoom.h"
35
36
37/**
38 * Compute the bounds of the region resulting from zooming a pixel span.
39 * The resulting region will be entirely inside the window/scissor bounds
40 * so no additional clipping is needed.
41 * \param imageX, imageY  position of the mage being drawn (gl WindowPos)
42 * \param spanX, spanY  position of span being drawing
43 * \param width  number of pixels in span
44 * \param x0, x1  returned X bounds of zoomed region [x0, x1)
45 * \param y0, y1  returned Y bounds of zoomed region [y0, y1)
46 * \return GL_TRUE if any zoomed pixels visible, GL_FALSE if totally clipped
47 */
48static GLboolean
49compute_zoomed_bounds(struct gl_context *ctx, GLint imageX, GLint imageY,
50                      GLint spanX, GLint spanY, GLint width,
51                      GLint *x0, GLint *x1, GLint *y0, GLint *y1)
52{
53   const struct gl_framebuffer *fb = ctx->DrawBuffer;
54   GLint c0, c1, r0, r1;
55
56   ASSERT(spanX >= imageX);
57   ASSERT(spanY >= imageY);
58
59   /*
60    * Compute destination columns: [c0, c1)
61    */
62   c0 = imageX + (GLint) ((spanX - imageX) * ctx->Pixel.ZoomX);
63   c1 = imageX + (GLint) ((spanX + width - imageX) * ctx->Pixel.ZoomX);
64   if (c1 < c0) {
65      /* swap */
66      GLint tmp = c1;
67      c1 = c0;
68      c0 = tmp;
69   }
70   c0 = CLAMP(c0, fb->_Xmin, fb->_Xmax);
71   c1 = CLAMP(c1, fb->_Xmin, fb->_Xmax);
72   if (c0 == c1) {
73      return GL_FALSE; /* no width */
74   }
75
76   /*
77    * Compute destination rows: [r0, r1)
78    */
79   r0 = imageY + (GLint) ((spanY - imageY) * ctx->Pixel.ZoomY);
80   r1 = imageY + (GLint) ((spanY + 1 - imageY) * ctx->Pixel.ZoomY);
81   if (r1 < r0) {
82      /* swap */
83      GLint tmp = r1;
84      r1 = r0;
85      r0 = tmp;
86   }
87   r0 = CLAMP(r0, fb->_Ymin, fb->_Ymax);
88   r1 = CLAMP(r1, fb->_Ymin, fb->_Ymax);
89   if (r0 == r1) {
90      return GL_FALSE; /* no height */
91   }
92
93   *x0 = c0;
94   *x1 = c1;
95   *y0 = r0;
96   *y1 = r1;
97
98   return GL_TRUE;
99}
100
101
102/**
103 * Convert a zoomed x image coordinate back to an unzoomed x coord.
104 * 'zx' is screen position of a pixel in the zoomed image, who's left edge
105 * is at 'imageX'.
106 * return corresponding x coord in the original, unzoomed image.
107 * This can use this for unzooming X or Y values.
108 */
109static inline GLint
110unzoom_x(GLfloat zoomX, GLint imageX, GLint zx)
111{
112   /*
113   zx = imageX + (x - imageX) * zoomX;
114   zx - imageX = (x - imageX) * zoomX;
115   (zx - imageX) / zoomX = x - imageX;
116   */
117   GLint x;
118   if (zoomX < 0.0)
119      zx++;
120   x = imageX + (GLint) ((zx - imageX) / zoomX);
121   return x;
122}
123
124
125
126/**
127 * Helper function called from _swrast_write_zoomed_rgba/rgb/
128 * index/depth_span().
129 */
130static void
131zoom_span( struct gl_context *ctx, GLint imgX, GLint imgY, const SWspan *span,
132           const GLvoid *src, GLenum format )
133{
134   SWcontext *swrast = SWRAST_CONTEXT(ctx);
135   SWspan zoomed;
136   GLint x0, x1, y0, y1;
137   GLint zoomedWidth;
138
139   if (!compute_zoomed_bounds(ctx, imgX, imgY, span->x, span->y, span->end,
140                              &x0, &x1, &y0, &y1)) {
141      return;  /* totally clipped */
142   }
143
144   if (!swrast->ZoomedArrays) {
145      /* allocate on demand */
146      swrast->ZoomedArrays = (SWspanarrays *) CALLOC(sizeof(SWspanarrays));
147      if (!swrast->ZoomedArrays)
148         return;
149   }
150
151   zoomedWidth = x1 - x0;
152   ASSERT(zoomedWidth > 0);
153   ASSERT(zoomedWidth <= SWRAST_MAX_WIDTH);
154
155   /* no pixel arrays! must be horizontal spans. */
156   ASSERT((span->arrayMask & SPAN_XY) == 0);
157   ASSERT(span->primitive == GL_BITMAP);
158
159   INIT_SPAN(zoomed, GL_BITMAP);
160   zoomed.x = x0;
161   zoomed.end = zoomedWidth;
162   zoomed.array = swrast->ZoomedArrays;
163   zoomed.array->ChanType = span->array->ChanType;
164   if (zoomed.array->ChanType == GL_UNSIGNED_BYTE)
165      zoomed.array->rgba = (GLchan (*)[4]) zoomed.array->rgba8;
166   else if (zoomed.array->ChanType == GL_UNSIGNED_SHORT)
167      zoomed.array->rgba = (GLchan (*)[4]) zoomed.array->rgba16;
168   else
169      zoomed.array->rgba = (GLchan (*)[4]) zoomed.array->attribs[FRAG_ATTRIB_COL0];
170
171   COPY_4V(zoomed.attrStart[FRAG_ATTRIB_WPOS], span->attrStart[FRAG_ATTRIB_WPOS]);
172   COPY_4V(zoomed.attrStepX[FRAG_ATTRIB_WPOS], span->attrStepX[FRAG_ATTRIB_WPOS]);
173   COPY_4V(zoomed.attrStepY[FRAG_ATTRIB_WPOS], span->attrStepY[FRAG_ATTRIB_WPOS]);
174
175   zoomed.attrStart[FRAG_ATTRIB_FOGC][0] = span->attrStart[FRAG_ATTRIB_FOGC][0];
176   zoomed.attrStepX[FRAG_ATTRIB_FOGC][0] = span->attrStepX[FRAG_ATTRIB_FOGC][0];
177   zoomed.attrStepY[FRAG_ATTRIB_FOGC][0] = span->attrStepY[FRAG_ATTRIB_FOGC][0];
178
179   if (format == GL_RGBA || format == GL_RGB) {
180      /* copy Z info */
181      zoomed.z = span->z;
182      zoomed.zStep = span->zStep;
183      /* we'll generate an array of colorss */
184      zoomed.interpMask = span->interpMask & ~SPAN_RGBA;
185      zoomed.arrayMask |= SPAN_RGBA;
186      zoomed.arrayAttribs |= FRAG_BIT_COL0;  /* we'll produce these values */
187      ASSERT(span->arrayMask & SPAN_RGBA);
188   }
189   else if (format == GL_DEPTH_COMPONENT) {
190      /* Copy color info */
191      zoomed.red = span->red;
192      zoomed.green = span->green;
193      zoomed.blue = span->blue;
194      zoomed.alpha = span->alpha;
195      zoomed.redStep = span->redStep;
196      zoomed.greenStep = span->greenStep;
197      zoomed.blueStep = span->blueStep;
198      zoomed.alphaStep = span->alphaStep;
199      /* we'll generate an array of depth values */
200      zoomed.interpMask = span->interpMask & ~SPAN_Z;
201      zoomed.arrayMask |= SPAN_Z;
202      ASSERT(span->arrayMask & SPAN_Z);
203   }
204   else {
205      _mesa_problem(ctx, "Bad format in zoom_span");
206      return;
207   }
208
209   /* zoom the span horizontally */
210   if (format == GL_RGBA) {
211      if (zoomed.array->ChanType == GL_UNSIGNED_BYTE) {
212         const GLubyte (*rgba)[4] = (const GLubyte (*)[4]) src;
213         GLint i;
214         for (i = 0; i < zoomedWidth; i++) {
215            GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
216            ASSERT(j >= 0);
217            ASSERT(j < (GLint) span->end);
218            COPY_4UBV(zoomed.array->rgba8[i], rgba[j]);
219         }
220      }
221      else if (zoomed.array->ChanType == GL_UNSIGNED_SHORT) {
222         const GLushort (*rgba)[4] = (const GLushort (*)[4]) src;
223         GLint i;
224         for (i = 0; i < zoomedWidth; i++) {
225            GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
226            ASSERT(j >= 0);
227            ASSERT(j < (GLint) span->end);
228            COPY_4V(zoomed.array->rgba16[i], rgba[j]);
229         }
230      }
231      else {
232         const GLfloat (*rgba)[4] = (const GLfloat (*)[4]) src;
233         GLint i;
234         for (i = 0; i < zoomedWidth; i++) {
235            GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
236            ASSERT(j >= 0);
237            ASSERT(j < span->end);
238            COPY_4V(zoomed.array->attribs[FRAG_ATTRIB_COL0][i], rgba[j]);
239         }
240      }
241   }
242   else if (format == GL_RGB) {
243      if (zoomed.array->ChanType == GL_UNSIGNED_BYTE) {
244         const GLubyte (*rgb)[3] = (const GLubyte (*)[3]) src;
245         GLint i;
246         for (i = 0; i < zoomedWidth; i++) {
247            GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
248            ASSERT(j >= 0);
249            ASSERT(j < (GLint) span->end);
250            zoomed.array->rgba8[i][0] = rgb[j][0];
251            zoomed.array->rgba8[i][1] = rgb[j][1];
252            zoomed.array->rgba8[i][2] = rgb[j][2];
253            zoomed.array->rgba8[i][3] = 0xff;
254         }
255      }
256      else if (zoomed.array->ChanType == GL_UNSIGNED_SHORT) {
257         const GLushort (*rgb)[3] = (const GLushort (*)[3]) src;
258         GLint i;
259         for (i = 0; i < zoomedWidth; i++) {
260            GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
261            ASSERT(j >= 0);
262            ASSERT(j < (GLint) span->end);
263            zoomed.array->rgba16[i][0] = rgb[j][0];
264            zoomed.array->rgba16[i][1] = rgb[j][1];
265            zoomed.array->rgba16[i][2] = rgb[j][2];
266            zoomed.array->rgba16[i][3] = 0xffff;
267         }
268      }
269      else {
270         const GLfloat (*rgb)[3] = (const GLfloat (*)[3]) src;
271         GLint i;
272         for (i = 0; i < zoomedWidth; i++) {
273            GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
274            ASSERT(j >= 0);
275            ASSERT(j < span->end);
276            zoomed.array->attribs[FRAG_ATTRIB_COL0][i][0] = rgb[j][0];
277            zoomed.array->attribs[FRAG_ATTRIB_COL0][i][1] = rgb[j][1];
278            zoomed.array->attribs[FRAG_ATTRIB_COL0][i][2] = rgb[j][2];
279            zoomed.array->attribs[FRAG_ATTRIB_COL0][i][3] = 1.0F;
280         }
281      }
282   }
283   else if (format == GL_DEPTH_COMPONENT) {
284      const GLuint *zValues = (const GLuint *) src;
285      GLint i;
286      for (i = 0; i < zoomedWidth; i++) {
287         GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - span->x;
288         ASSERT(j >= 0);
289         ASSERT(j < (GLint) span->end);
290         zoomed.array->z[i] = zValues[j];
291      }
292      /* Now, fall into the RGB path below */
293      format = GL_RGBA;
294   }
295
296   /* write the span in rows [r0, r1) */
297   if (format == GL_RGBA || format == GL_RGB) {
298      /* Writing the span may modify the colors, so make a backup now if we're
299       * going to call _swrast_write_zoomed_span() more than once.
300       * Also, clipping may change the span end value, so store it as well.
301       */
302      const GLint end = zoomed.end; /* save */
303      void *rgbaSave;
304      const GLint pixelSize =
305         (zoomed.array->ChanType == GL_UNSIGNED_BYTE) ? 4 * sizeof(GLubyte) :
306         ((zoomed.array->ChanType == GL_UNSIGNED_SHORT) ? 4 * sizeof(GLushort)
307          : 4 * sizeof(GLfloat));
308
309      rgbaSave = malloc(zoomed.end * pixelSize);
310      if (!rgbaSave) {
311         return;
312      }
313
314      if (y1 - y0 > 1) {
315         memcpy(rgbaSave, zoomed.array->rgba, zoomed.end * pixelSize);
316      }
317      for (zoomed.y = y0; zoomed.y < y1; zoomed.y++) {
318         _swrast_write_rgba_span(ctx, &zoomed);
319         zoomed.end = end;  /* restore */
320         if (y1 - y0 > 1) {
321            /* restore the colors */
322            memcpy(zoomed.array->rgba, rgbaSave, zoomed.end * pixelSize);
323         }
324      }
325
326      free(rgbaSave);
327   }
328}
329
330
331void
332_swrast_write_zoomed_rgba_span(struct gl_context *ctx, GLint imgX, GLint imgY,
333                               const SWspan *span, const GLvoid *rgba)
334{
335   zoom_span(ctx, imgX, imgY, span, rgba, GL_RGBA);
336}
337
338
339void
340_swrast_write_zoomed_rgb_span(struct gl_context *ctx, GLint imgX, GLint imgY,
341                              const SWspan *span, const GLvoid *rgb)
342{
343   zoom_span(ctx, imgX, imgY, span, rgb, GL_RGB);
344}
345
346
347void
348_swrast_write_zoomed_depth_span(struct gl_context *ctx, GLint imgX, GLint imgY,
349                                const SWspan *span)
350{
351   zoom_span(ctx, imgX, imgY, span,
352             (const GLvoid *) span->array->z, GL_DEPTH_COMPONENT);
353}
354
355
356/**
357 * Zoom/write stencil values.
358 * No per-fragment operations are applied.
359 */
360void
361_swrast_write_zoomed_stencil_span(struct gl_context *ctx, GLint imgX, GLint imgY,
362                                  GLint width, GLint spanX, GLint spanY,
363                                  const GLubyte stencil[])
364{
365   GLubyte *zoomedVals;
366   GLint x0, x1, y0, y1, y;
367   GLint i, zoomedWidth;
368
369   if (!compute_zoomed_bounds(ctx, imgX, imgY, spanX, spanY, width,
370                              &x0, &x1, &y0, &y1)) {
371      return;  /* totally clipped */
372   }
373
374   zoomedWidth = x1 - x0;
375   ASSERT(zoomedWidth > 0);
376   ASSERT(zoomedWidth <= SWRAST_MAX_WIDTH);
377
378   zoomedVals = (GLubyte *) malloc(zoomedWidth * sizeof(GLubyte));
379   if (!zoomedVals)
380      return;
381
382   /* zoom the span horizontally */
383   for (i = 0; i < zoomedWidth; i++) {
384      GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - spanX;
385      ASSERT(j >= 0);
386      ASSERT(j < width);
387      zoomedVals[i] = stencil[j];
388   }
389
390   /* write the zoomed spans */
391   for (y = y0; y < y1; y++) {
392      _swrast_write_stencil_span(ctx, zoomedWidth, x0, y, zoomedVals);
393   }
394
395   free(zoomedVals);
396}
397
398// http://crbug.com/348397
399#ifdef _MSC_VER
400#pragma optimize("", off)
401#endif
402
403/**
404 * Zoom/write 32-bit Z values.
405 * No per-fragment operations are applied.
406 */
407void
408_swrast_write_zoomed_z_span(struct gl_context *ctx, GLint imgX, GLint imgY,
409                            GLint width, GLint spanX, GLint spanY,
410                            const GLuint *zVals)
411{
412   struct gl_renderbuffer *rb =
413      ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer;
414   GLuint *zoomedVals;
415   GLint x0, x1, y0, y1, y;
416   GLint i, zoomedWidth;
417
418   if (!compute_zoomed_bounds(ctx, imgX, imgY, spanX, spanY, width,
419                              &x0, &x1, &y0, &y1)) {
420      return;  /* totally clipped */
421   }
422
423   zoomedWidth = x1 - x0;
424   ASSERT(zoomedWidth > 0);
425   ASSERT(zoomedWidth <= SWRAST_MAX_WIDTH);
426
427   zoomedVals = (GLuint *) malloc(zoomedWidth * sizeof(GLuint));
428   if (!zoomedVals)
429      return;
430
431   /* zoom the span horizontally */
432   for (i = 0; i < zoomedWidth; i++) {
433      GLint j = unzoom_x(ctx->Pixel.ZoomX, imgX, x0 + i) - spanX;
434      ASSERT(j >= 0);
435      ASSERT(j < width);
436      zoomedVals[i] = zVals[j];
437   }
438
439   /* write the zoomed spans */
440   for (y = y0; y < y1; y++) {
441      GLubyte *dst = _swrast_pixel_address(rb, x0, y);
442      _mesa_pack_uint_z_row(rb->Format, zoomedWidth, zoomedVals, dst);
443   }
444
445   free(zoomedVals);
446}
447
448#ifdef _MSC_VER
449#pragma optimize("", off)
450#endif
451