// 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 "base/logging.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" namespace gfx { 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_R8: 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: case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: 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: case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: return TextureFormat(format); } 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; } template scoped_ptr GLES2RGBData(const Size& size, BufferFormat format, const uint8_t* data, F const& data_to_rgb, GLenum* data_format, GLenum* data_type) { 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()]); size_t data_stride = RowSizeForBufferFormat(size.width(), format, 0); for (int y = 0; y < size.height(); ++y) { for (int x = 0; x < size.width(); ++x) { data_to_rgb(&data[y * data_stride + x * 4], &gles2_rgb_data[y * gles2_rgb_data_stride + x * 3]); } } *data_format = GL_RGB; *data_type = GL_UNSIGNED_BYTE; return gles2_rgb_data.Pass(); } scoped_ptr GLES2Data(const Size& size, BufferFormat format, const uint8_t* data, GLenum* data_format, GLenum* data_type) { switch (format) { case BufferFormat::RGBX_8888: return GLES2RGBData(size, format, data, [](const uint8_t* src, uint8_t* dst) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; }, data_format, data_type); case BufferFormat::BGRX_8888: return GLES2RGBData(size, format, data, [](const uint8_t* src, uint8_t* dst) { dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; }, data_format, data_type); 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: case BufferFormat::YUV_420: case BufferFormat::YUV_420_BIPLANAR: case BufferFormat::UYVY_422: // No data conversion needed. return nullptr; } NOTREACHED(); return 0; } } // namespace GLImageMemory::GLImageMemory(const Size& size, unsigned internalformat) : size_(size), internalformat_(internalformat), memory_(nullptr), format_(BufferFormat::RGBA_8888) {} GLImageMemory::~GLImageMemory() { DCHECK(!memory_); } bool GLImageMemory::Initialize(const unsigned char* memory, BufferFormat format) { if (!ValidInternalFormat(internalformat_)) { LOG(ERROR) << "Invalid internalformat: " << internalformat_; return false; } if (!ValidFormat(format)) { LOG(ERROR) << "Invalid format: " << static_cast(format); return false; } DCHECK(memory); DCHECK(!memory_); DCHECK_IMPLIES(IsCompressedFormat(format), size_.width() % 4 == 0); DCHECK_IMPLIES(IsCompressedFormat(format), size_.height() % 4 == 0); memory_ = memory; format_ = format; return true; } void GLImageMemory::Destroy(bool have_context) { memory_ = nullptr; } 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 { scoped_ptr gles2_data; GLenum data_format = DataFormat(format_); GLenum data_type = DataType(format_); if (GLContext::GetCurrent()->GetVersionInfo()->is_es) gles2_data = GLES2Data(size_, format_, memory_, &data_format, &data_type); glTexImage2D(target, 0, TextureFormat(format_), size_.width(), size_.height(), 0, data_format, data_type, gles2_data ? gles2_data.get() : memory_); } return true; } bool GLImageMemory::CopyTexSubImage(unsigned target, const Point& offset, const 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; // Height must be a multiple of 4 if compressed. if (IsCompressedFormat(format_) && rect.height() % 4) return false; const uint8_t* data = memory_ + rect.y() * RowSizeForBufferFormat(size_.width(), format_, 0); if (IsCompressedFormat(format_)) { 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_); scoped_ptr gles2_data; if (GLContext::GetCurrent()->GetVersionInfo()->is_es) { gles2_data = GLES2Data(rect.size(), format_, data, &data_format, &data_type); } glTexSubImage2D(target, 0, offset.x(), offset.y(), rect.width(), rect.height(), data_format, data_type, gles2_data ? gles2_data.get() : data); } return true; } bool GLImageMemory::ScheduleOverlayPlane(AcceleratedWidget widget, int z_order, OverlayTransform transform, const Rect& bounds_rect, const RectF& crop_rect) { return false; } // static unsigned GLImageMemory::GetInternalFormatForTesting(BufferFormat format) { DCHECK(ValidFormat(format)); return TextureFormat(format); } } // namespace gfx