// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gl/gl_image_memory.h" #include #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_version_info.h" using gfx::BufferFormat; namespace gl { namespace { bool ValidInternalFormat(unsigned internalformat) { switch (internalformat) { case GL_ATC_RGB_AMD: case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case GL_ETC1_RGB8_OES: case GL_RED: case GL_RGB: case GL_RGBA: case GL_BGRA_EXT: return true; default: return false; } } bool ValidFormat(BufferFormat format) { switch (format) { case BufferFormat::ATC: case BufferFormat::ATCIA: case BufferFormat::DXT1: case BufferFormat::DXT5: case BufferFormat::ETC1: case BufferFormat::R_8: case BufferFormat::RGBA_4444: case BufferFormat::RGBX_8888: case BufferFormat::RGBA_8888: case BufferFormat::BGRX_8888: case BufferFormat::BGRA_8888: return true; case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: return false; } NOTREACHED(); return false; } bool IsCompressedFormat(BufferFormat format) { switch (format) { case BufferFormat::ATC: case BufferFormat::ATCIA: case BufferFormat::DXT1: case BufferFormat::DXT5: case BufferFormat::ETC1: return true; case BufferFormat::R_8: case BufferFormat::RGBA_4444: case BufferFormat::RGBX_8888: case BufferFormat::RGBA_8888: case BufferFormat::BGRX_8888: case BufferFormat::BGRA_8888: return false; case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: NOTREACHED(); return false; } NOTREACHED(); return false; } GLenum TextureFormat(BufferFormat format) { switch (format) { case BufferFormat::ATC: return GL_ATC_RGB_AMD; case BufferFormat::ATCIA: return GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD; case BufferFormat::DXT1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; case BufferFormat::DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case BufferFormat::ETC1: return GL_ETC1_RGB8_OES; case BufferFormat::R_8: return GL_RED; case BufferFormat::RGBA_4444: case BufferFormat::RGBA_8888: return GL_RGBA; case BufferFormat::BGRA_8888: return GL_BGRA_EXT; case BufferFormat::RGBX_8888: case BufferFormat::BGRX_8888: return GL_RGB; case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: NOTREACHED(); return 0; } NOTREACHED(); return 0; } GLenum DataFormat(BufferFormat format) { switch (format) { case BufferFormat::RGBX_8888: return GL_RGBA; case BufferFormat::BGRX_8888: return GL_BGRA_EXT; case BufferFormat::RGBA_4444: case BufferFormat::RGBA_8888: case BufferFormat::BGRA_8888: case BufferFormat::R_8: case BufferFormat::ATC: case BufferFormat::ATCIA: case BufferFormat::DXT1: case BufferFormat::DXT5: case BufferFormat::ETC1: return TextureFormat(format); case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: NOTREACHED(); return 0; } NOTREACHED(); return 0; } GLenum DataType(BufferFormat format) { switch (format) { case BufferFormat::RGBA_4444: return GL_UNSIGNED_SHORT_4_4_4_4; case BufferFormat::RGBX_8888: case BufferFormat::RGBA_8888: case BufferFormat::BGRX_8888: case BufferFormat::BGRA_8888: case BufferFormat::R_8: return GL_UNSIGNED_BYTE; case BufferFormat::ATC: case BufferFormat::ATCIA: case BufferFormat::DXT1: case BufferFormat::DXT5: case BufferFormat::ETC1: case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: NOTREACHED(); return 0; } NOTREACHED(); return 0; } GLint DataRowLength(size_t stride, BufferFormat format) { switch (format) { case BufferFormat::RGBA_4444: return base::checked_cast(stride) / 2; case BufferFormat::RGBX_8888: case BufferFormat::RGBA_8888: case BufferFormat::BGRX_8888: case BufferFormat::BGRA_8888: return base::checked_cast(stride) / 4; case BufferFormat::R_8: return base::checked_cast(stride); case BufferFormat::ATC: case BufferFormat::ATCIA: case BufferFormat::DXT1: case BufferFormat::DXT5: case BufferFormat::ETC1: case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: NOTREACHED(); return 0; } NOTREACHED(); return 0; } template scoped_ptr GLES2RGBData(const gfx::Size& size, BufferFormat format, size_t stride, const uint8_t* data, F const& data_to_rgb, GLenum* data_format, GLenum* data_type, GLint* data_row_length) { TRACE_EVENT2("gpu", "GLES2RGBData", "width", size.width(), "height", size.height()); // Four-byte row alignment as specified by glPixelStorei with argument // GL_UNPACK_ALIGNMENT set to 4. size_t gles2_rgb_data_stride = (size.width() * 3 + 3) & ~3; scoped_ptr gles2_rgb_data( new uint8_t[gles2_rgb_data_stride * size.height()]); for (int y = 0; y < size.height(); ++y) { for (int x = 0; x < size.width(); ++x) { data_to_rgb(&data[y * stride + x * 4], &gles2_rgb_data[y * gles2_rgb_data_stride + x * 3]); } } *data_format = GL_RGB; *data_type = GL_UNSIGNED_BYTE; *data_row_length = size.width(); return gles2_rgb_data; } scoped_ptr GLES2Data(const gfx::Size& size, BufferFormat format, size_t stride, const uint8_t* data, GLenum* data_format, GLenum* data_type, GLint* data_row_length) { TRACE_EVENT2("gpu", "GLES2Data", "width", size.width(), "height", size.height()); switch (format) { case BufferFormat::RGBX_8888: return GLES2RGBData(size, format, stride, data, [](const uint8_t* src, uint8_t* dst) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; }, data_format, data_type, data_row_length); case BufferFormat::BGRX_8888: return GLES2RGBData(size, format, stride, data, [](const uint8_t* src, uint8_t* dst) { dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; }, data_format, data_type, data_row_length); case BufferFormat::RGBA_4444: case BufferFormat::RGBA_8888: case BufferFormat::BGRA_8888: case BufferFormat::R_8: { size_t gles2_data_stride = RowSizeForBufferFormat(size.width(), format, 0); if (stride == gles2_data_stride || gfx::g_driver_gl.ext.b_GL_EXT_unpack_subimage) return nullptr; // No data conversion needed scoped_ptr gles2_data( new uint8_t[gles2_data_stride * size.height()]); for (int y = 0; y < size.height(); ++y) { memcpy(&gles2_data[y * gles2_data_stride], &data[y * stride], gles2_data_stride); } *data_row_length = size.width(); return gles2_data; } case BufferFormat::ATC: case BufferFormat::ATCIA: case BufferFormat::DXT1: case BufferFormat::DXT5: case BufferFormat::ETC1: return nullptr; // No data conversion needed case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: NOTREACHED(); return nullptr; } NOTREACHED(); return nullptr; } } // namespace GLImageMemory::GLImageMemory(const gfx::Size& size, unsigned internalformat) : size_(size), internalformat_(internalformat), memory_(nullptr), format_(BufferFormat::RGBA_8888), stride_(0) {} GLImageMemory::~GLImageMemory() { DCHECK(!memory_); } bool GLImageMemory::Initialize(const unsigned char* memory, BufferFormat format, size_t stride) { if (!ValidInternalFormat(internalformat_)) { LOG(ERROR) << "Invalid internalformat: " << internalformat_; return false; } if (!ValidFormat(format)) { LOG(ERROR) << "Invalid format: " << static_cast(format); return false; } if (stride < RowSizeForBufferFormat(size_.width(), format, 0) || stride & 3) { LOG(ERROR) << "Invalid stride: " << stride; return false; } DCHECK(memory); DCHECK(!memory_); DCHECK(!IsCompressedFormat(format) || size_.width() % 4 == 0); DCHECK(!IsCompressedFormat(format) || size_.height() % 4 == 0); memory_ = memory; format_ = format; stride_ = stride; return true; } void GLImageMemory::Destroy(bool have_context) { memory_ = nullptr; } gfx::Size GLImageMemory::GetSize() { return size_; } unsigned GLImageMemory::GetInternalFormat() { return internalformat_; } bool GLImageMemory::BindTexImage(unsigned target) { return false; } bool GLImageMemory::CopyTexImage(unsigned target) { TRACE_EVENT2("gpu", "GLImageMemory::CopyTexImage", "width", size_.width(), "height", size_.height()); // GL_TEXTURE_EXTERNAL_OES is not a supported target. if (target == GL_TEXTURE_EXTERNAL_OES) return false; if (IsCompressedFormat(format_)) { glCompressedTexImage2D( target, 0, TextureFormat(format_), size_.width(), size_.height(), 0, static_cast(BufferSizeForBufferFormat(size_, format_)), memory_); } else { GLenum data_format = DataFormat(format_); GLenum data_type = DataType(format_); GLint data_row_length = DataRowLength(stride_, format_); scoped_ptr gles2_data; if (gfx::GLContext::GetCurrent()->GetVersionInfo()->is_es) { gles2_data = GLES2Data(size_, format_, stride_, memory_, &data_format, &data_type, &data_row_length); } if (data_row_length != size_.width()) glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length); glTexImage2D(target, 0, TextureFormat(format_), size_.width(), size_.height(), 0, data_format, data_type, gles2_data ? gles2_data.get() : memory_); if (data_row_length != size_.width()) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } return true; } bool GLImageMemory::CopyTexSubImage(unsigned target, const gfx::Point& offset, const gfx::Rect& rect) { TRACE_EVENT2("gpu", "GLImageMemory::CopyTexSubImage", "width", rect.width(), "height", rect.height()); // GL_TEXTURE_EXTERNAL_OES is not a supported target. if (target == GL_TEXTURE_EXTERNAL_OES) return false; // Sub width is not supported. if (rect.width() != size_.width()) return false; const uint8_t* data = memory_ + rect.y() * stride_; if (IsCompressedFormat(format_)) { // Height must be a multiple of 4. if (rect.height() % 4) return false; glCompressedTexSubImage2D( target, 0, offset.x(), offset.y(), rect.width(), rect.height(), DataFormat(format_), static_cast(BufferSizeForBufferFormat(rect.size(), format_)), data); } else { GLenum data_format = DataFormat(format_); GLenum data_type = DataType(format_); GLint data_row_length = DataRowLength(stride_, format_); scoped_ptr gles2_data; if (gfx::GLContext::GetCurrent()->GetVersionInfo()->is_es) { gles2_data = GLES2Data(rect.size(), format_, stride_, data, &data_format, &data_type, &data_row_length); } if (data_row_length != rect.width()) glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length); glTexSubImage2D(target, 0, offset.x(), offset.y(), rect.width(), rect.height(), data_format, data_type, gles2_data ? gles2_data.get() : data); if (data_row_length != rect.width()) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } return true; } bool GLImageMemory::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, const gfx::RectF& crop_rect) { return false; } // static unsigned GLImageMemory::GetInternalFormatForTesting(BufferFormat format) { DCHECK(ValidFormat(format)); return TextureFormat(format); } } // namespace gl