Пример #1
0
/*
 * Read R, G, B, A, RGB, L, or LA pixels.
 */
static void
read_rgba_pixels( struct gl_context *ctx,
                  GLint x, GLint y,
                  GLsizei width, GLsizei height,
                  GLenum format, GLenum type, GLvoid *pixels,
                  const struct gl_pixelstore_attrib *packing )
{
    GLbitfield transferOps;
    bool dst_is_integer, convert_rgb_to_lum, needs_rebase;
    int dst_stride, src_stride, rb_stride;
    uint32_t dst_format, src_format;
    GLubyte *dst, *map;
    mesa_format rb_format;
    bool needs_rgba;
    void *rgba, *src;
    bool src_is_uint = false;
    uint8_t rebase_swizzle[4];
    struct gl_framebuffer *fb = ctx->ReadBuffer;
    struct gl_renderbuffer *rb = fb->_ColorReadBuffer;
    GLenum dstBaseFormat = _mesa_unpack_format_to_base_format(format);

    if (!rb)
        return;

    transferOps = _mesa_get_readpixels_transfer_ops(ctx, rb->Format, format,
                  type, GL_FALSE);
    /* Describe the dst format */
    dst_is_integer = _mesa_is_enum_format_integer(format);
    dst_stride = _mesa_image_row_stride(packing, width, format, type);
    dst_format = _mesa_format_from_format_and_type(format, type);
    convert_rgb_to_lum =
        _mesa_need_rgb_to_luminance_conversion(rb->_BaseFormat, dstBaseFormat);
    dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
                                            format, type, 0, 0);

    /* Map the source render buffer */
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
                                &map, &rb_stride);
    if (!map) {
        _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
        return;
    }
    rb_format = _mesa_get_srgb_format_linear(rb->Format);

    /*
     * Depending on the base formats involved in the conversion we might need to
     * rebase some values, so for these formats we compute a rebase swizzle.
     */
    if (rb->_BaseFormat == GL_LUMINANCE || rb->_BaseFormat == GL_INTENSITY) {
        needs_rebase = true;
        rebase_swizzle[0] = MESA_FORMAT_SWIZZLE_X;
        rebase_swizzle[1] = MESA_FORMAT_SWIZZLE_ZERO;
        rebase_swizzle[2] = MESA_FORMAT_SWIZZLE_ZERO;
        rebase_swizzle[3] = MESA_FORMAT_SWIZZLE_ONE;
    } else if (rb->_BaseFormat == GL_LUMINANCE_ALPHA) {
        needs_rebase = true;
        rebase_swizzle[0] = MESA_FORMAT_SWIZZLE_X;
        rebase_swizzle[1] = MESA_FORMAT_SWIZZLE_ZERO;
        rebase_swizzle[2] = MESA_FORMAT_SWIZZLE_ZERO;
        rebase_swizzle[3] = MESA_FORMAT_SWIZZLE_W;
    } else if (_mesa_get_format_base_format(rb_format) != rb->_BaseFormat) {
        needs_rebase =
            _mesa_compute_rgba2base2rgba_component_mapping(rb->_BaseFormat,
                    rebase_swizzle);
    } else {
        needs_rebase = false;
    }

    /* Since _mesa_format_convert does not handle transferOps we need to handle
     * them before we call the function. This requires to convert to RGBA float
     * first so we can call _mesa_apply_rgba_transfer_ops. If the dst format is
     * integer transferOps do not apply.
     *
     * Converting to luminance also requires converting to RGBA first, so we can
     * then compute luminance values as L=R+G+B. Notice that this is different
     * from GetTexImage, where we compute L=R.
     */
    assert(!transferOps || (transferOps && !dst_is_integer));

    needs_rgba = transferOps || convert_rgb_to_lum;
    rgba = NULL;
    if (needs_rgba) {
        uint32_t rgba_format;
        int rgba_stride;
        bool need_convert;

        /* Convert to RGBA float or int/uint depending on the type of the src */
        if (dst_is_integer) {
            src_is_uint = _mesa_is_format_unsigned(rb_format);
            if (src_is_uint) {
                rgba_format = RGBA32_UINT;
                rgba_stride = width * 4 * sizeof(GLuint);
            } else {
                rgba_format = RGBA32_INT;
                rgba_stride = width * 4 * sizeof(GLint);
            }
        } else {
            rgba_format = RGBA32_FLOAT;
            rgba_stride = width * 4 * sizeof(GLfloat);
        }

        /* If we are lucky and the dst format matches the RGBA format we need to
         * convert to, then we can convert directly into the dst buffer and avoid
         * the final conversion/copy from the rgba buffer to the dst buffer.
         */
        if (dst_format == rgba_format &&
                dst_stride == rgba_stride) {
            need_convert = false;
            rgba = dst;
        } else {
            need_convert = true;
            rgba = malloc(height * rgba_stride);
            if (!rgba) {
                _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
                goto done_unmap;
            }
        }

        /* Convert to RGBA now */
        _mesa_format_convert(rgba, rgba_format, rgba_stride,
                             map, rb_format, rb_stride,
                             width, height,
                             needs_rebase ? rebase_swizzle : NULL);

        /* Handle transfer ops if necessary */
        if (transferOps)
            _mesa_apply_rgba_transfer_ops(ctx, transferOps, width * height, rgba);

        /* If we had to rebase, we have already taken care of that */
        needs_rebase = false;

        /* If we were lucky and our RGBA conversion matches the dst format, then
         * we are done.
         */
        if (!need_convert)
            goto done_swap;

        /* Otherwise, we need to convert from RGBA to dst next */
        src = rgba;
        src_format = rgba_format;
        src_stride = rgba_stride;
    } else {
        /* No RGBA conversion needed, convert directly to dst */
        src = map;
        src_format = rb_format;
        src_stride = rb_stride;
    }

    /* Do the conversion.
     *
     * If the dst format is Luminance, we need to do the conversion by computing
     * L=R+G+B values.
     */
    if (!convert_rgb_to_lum) {
        _mesa_format_convert(dst, dst_format, dst_stride,
                             src, src_format, src_stride,
                             width, height,
                             needs_rebase ? rebase_swizzle : NULL);
    } else if (!dst_is_integer) {
        /* Compute float Luminance values from RGBA float */
        int luminance_stride, luminance_bytes;
        void *luminance;
        uint32_t luminance_format;

        luminance_stride = width * sizeof(GL_FLOAT);
        if (format == GL_LUMINANCE_ALPHA)
            luminance_stride *= 2;
        luminance_bytes = height * luminance_stride;
        luminance = malloc(luminance_bytes);
        if (!luminance) {
            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
            free(rgba);
            goto done_unmap;
        }
        _mesa_pack_luminance_from_rgba_float(width * height, src,
                                             luminance, format, transferOps);

        /* Convert from Luminance float to dst (this will hadle type conversion
         * from float to the type of dst if necessary)
         */
        luminance_format = _mesa_format_from_format_and_type(format, GL_FLOAT);
        _mesa_format_convert(dst, dst_format, dst_stride,
                             luminance, luminance_format, luminance_stride,
                             width, height, NULL);
        free(luminance);
    } else {
        _mesa_pack_luminance_from_rgba_integer(width * height, src, !src_is_uint,
                                               dst, format, type);
    }

    if (rgba)
        free(rgba);

done_swap:
    /* Handle byte swapping if required */
    if (packing->SwapBytes) {
        _mesa_swap_bytes_2d_image(format, type, packing,
                                  width, height, dst, dst);
    }

done_unmap:
    ctx->Driver.UnmapRenderbuffer(ctx, rb);
}
Пример #2
0
bool
_mesa_meta_pbo_GetTexSubImage(struct gl_context *ctx, GLuint dims,
                              struct gl_texture_image *tex_image,
                              int xoffset, int yoffset, int zoffset,
                              int width, int height, int depth,
                              GLenum format, GLenum type, const void *pixels,
                              const struct gl_pixelstore_attrib *packing)
{
   struct gl_buffer_object *pbo = NULL;
   GLuint pbo_tex = 0;
   struct gl_framebuffer *readFb;
   struct gl_framebuffer *drawFb;
   int image_height;
   struct gl_texture_image *pbo_tex_image;
   struct gl_renderbuffer *rb = NULL;
   GLenum dstBaseFormat = _mesa_unpack_format_to_base_format(format);
   GLenum status, src_base_format;
   bool success = false, clear_channels_to_zero = false;
   float save_clear_color[4];
   int z;

   if (!_mesa_is_bufferobj(packing->BufferObj))
      return false;

   if (format == GL_DEPTH_COMPONENT ||
       format == GL_DEPTH_STENCIL ||
       format == GL_STENCIL_INDEX ||
       format == GL_COLOR_INDEX)
      return false;

   /* Don't use meta path for readpixels in below conditions. */
   if (!tex_image) {
      rb = ctx->ReadBuffer->_ColorReadBuffer;

      /* _mesa_get_readpixels_transfer_ops() includes the cases of read
       * color clamping along with the ctx->_ImageTransferState.
       */
      if (_mesa_get_readpixels_transfer_ops(ctx, rb->Format, format,
                                            type, GL_FALSE))
         return false;

      if (_mesa_need_rgb_to_luminance_conversion(rb->_BaseFormat,
                                                 dstBaseFormat))
         return false;

      /* This function rely on BlitFramebuffer to fill in the pixel data for
       * ReadPixels. But, BlitFrameBuffer doesn't support signed to unsigned
       * or unsigned to signed integer conversions. OpenGL spec expects an
       * invalid operation in that case.
       */
      if (need_signed_unsigned_int_conversion(rb->Format, format, type))
         return false;
   } else {
      if (need_signed_unsigned_int_conversion(tex_image->TexFormat, format, type))
         return false;
   }

   /* For arrays, use a tall (height * depth) 2D texture but taking into
    * account the inter-image padding specified with the image height packing
    * property.
    */
   image_height = packing->ImageHeight == 0 ? height : packing->ImageHeight;

   _mesa_meta_begin(ctx, ~(MESA_META_PIXEL_TRANSFER |
                           MESA_META_PIXEL_STORE));

   pbo_tex_image = create_texture_for_pbo(ctx, false, GL_PIXEL_PACK_BUFFER,
                                          dims, width, height, depth,
                                          format, type, pixels, packing,
                                          &pbo, &pbo_tex);

   if (!pbo_tex_image) {
      _mesa_meta_end(ctx);
      return false;
   }

   /* GL_CLAMP_FRAGMENT_COLOR doesn't affect ReadPixels and GettexImage */
   if (ctx->Extensions.ARB_color_buffer_float)
      _mesa_ClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);

   readFb = ctx->Driver.NewFramebuffer(ctx, 0xDEADBEEF);
   if (readFb == NULL)
      goto fail;

   drawFb = ctx->Driver.NewFramebuffer(ctx, 0xDEADBEEF);
   if (drawFb == NULL)
      goto fail;

   if (tex_image && tex_image->TexObject->Target == GL_TEXTURE_1D_ARRAY) {
      assert(depth == 1);
      assert(zoffset == 0);
      depth = height;
      height = 1;
      image_height = 1;
      zoffset = yoffset;
      yoffset = 0;
   }

   /* If we were given a texture, bind it to the read framebuffer.  If not,
    * we're doing a ReadPixels and we should just use whatever framebuffer
    * the client has bound.
    */
   _mesa_bind_framebuffers(ctx, drawFb, tex_image ? readFb : ctx->ReadBuffer);
   if (tex_image) {
      _mesa_meta_framebuffer_texture_image(ctx, ctx->ReadBuffer,
                                           GL_COLOR_ATTACHMENT0,
                                           tex_image, zoffset);
      /* If this passes on the first layer it should pass on the others */
      status = _mesa_check_framebuffer_status(ctx, ctx->ReadBuffer);
      if (status != GL_FRAMEBUFFER_COMPLETE)
         goto fail;
   } else {
      assert(depth == 1);
   }

   _mesa_meta_framebuffer_texture_image(ctx, ctx->DrawBuffer,
                                        GL_COLOR_ATTACHMENT0,
                                        pbo_tex_image, 0);
   /* If this passes on the first layer it should pass on the others */
   status = _mesa_check_framebuffer_status(ctx, ctx->DrawBuffer);
   if (status != GL_FRAMEBUFFER_COMPLETE)
      goto fail;

   /* Explicitly disable sRGB encoding */
   ctx->DrawBuffer->Visual.sRGBCapable = false;

   _mesa_update_state(ctx);

   if (_mesa_meta_BlitFramebuffer(ctx, ctx->ReadBuffer, ctx->DrawBuffer,
                                  xoffset, yoffset,
                                  xoffset + width, yoffset + height,
                                  0, 0, width, height,
                                  GL_COLOR_BUFFER_BIT, GL_NEAREST))
      goto fail;

   src_base_format = tex_image ?
                     tex_image->_BaseFormat :
                     ctx->ReadBuffer->_ColorReadBuffer->_BaseFormat;

   /* Depending on the base formats involved we might need to rebase some
    * values. For example if we download from a Luminance format to RGBA
    * format, we want G=0 and B=0.
    */
   clear_channels_to_zero =
      _mesa_need_luminance_to_rgb_conversion(src_base_format,
                                             pbo_tex_image->_BaseFormat);

   if (clear_channels_to_zero) {
      memcpy(save_clear_color, ctx->Color.ClearColor.f, 4 * sizeof(float));
      /* Clear the Green, Blue channels. */
      _mesa_ColorMask(GL_FALSE, GL_TRUE, GL_TRUE,
                      src_base_format != GL_LUMINANCE_ALPHA);
      _mesa_ClearColor(0.0, 0.0, 0.0, 1.0);
      _mesa_Clear(GL_COLOR_BUFFER_BIT);
   }

   for (z = 1; z < depth; z++) {
      _mesa_meta_framebuffer_texture_image(ctx, ctx->ReadBuffer,
                                           GL_COLOR_ATTACHMENT0,
                                           tex_image, zoffset + z);

      _mesa_update_state(ctx);

      _mesa_meta_BlitFramebuffer(ctx, ctx->ReadBuffer, ctx->DrawBuffer,
                                 xoffset, yoffset,
                                 xoffset + width, yoffset + height,
                                 0, z * image_height,
                                 width, z * image_height + height,
                                 GL_COLOR_BUFFER_BIT, GL_NEAREST);
      if (clear_channels_to_zero)
         _mesa_Clear(GL_COLOR_BUFFER_BIT);
   }

   /* Unmask the color channels and restore the saved clear color values. */
   if (clear_channels_to_zero) {
      _mesa_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
      _mesa_ClearColor(save_clear_color[0], save_clear_color[1],
                       save_clear_color[2], save_clear_color[3]);
   }

   success = true;

fail:
   _mesa_reference_framebuffer(&drawFb, NULL);
   _mesa_reference_framebuffer(&readFb, NULL);
   _mesa_DeleteTextures(1, &pbo_tex);
   _mesa_reference_buffer_object(ctx, &pbo, NULL);

   _mesa_meta_end(ctx);

   return success;
}