cef/tests/cefclient/browser/osr_renderer.cc

1099 lines
28 KiB
C++

// Copyright (c) 2012 The Chromium Embedded Framework 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 "tests/cefclient/browser/osr_renderer.h"
#if defined(__clang__)
// Begin disable NSOpenGL deprecation warnings.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#if USE_SHADERS
// Expose prototypes for OpenGL shader functions.
#define GL_GLEXT_PROTOTYPES
#endif
#include <math.h>
#if defined(OS_WIN)
#include <gl/gl.h>
#elif defined(OS_MAC)
#if USE_SHADERS
#include <OpenGL/gl3.h>
#else
#include <OpenGL/gl.h>
#endif
#elif defined(OS_LINUX)
#include <GL/gl.h>
#else
#error Platform is not supported.
#endif
#include "include/base/cef_logging.h"
#include "include/wrapper/cef_helpers.h"
#ifndef GL_BGR
#define GL_BGR 0x80E0
#endif
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#endif
// DCHECK on gl errors.
#if DCHECK_IS_ON()
#define VERIFY_NO_ERROR \
{ \
int _gl_error = glGetError(); \
DCHECK(_gl_error == GL_NO_ERROR) << "glGetError returned " << _gl_error; \
}
#else
#define VERIFY_NO_ERROR
#endif
namespace client {
#if USE_SHADERS
namespace {
const char* kScreenVertexShader =
"#version 330 core\n"
"out vec2 texCoord;\n"
"uniform mat4 transform;\n"
"void main() {\n"
"\tfloat x = float(((uint(gl_VertexID) + 2u) / 3u)\%2u);\n"
"\tfloat y = float(((uint(gl_VertexID) + 1u) / 3u)\%2u);\n"
"\tvec4 pos = vec4(-1.0f + x*2.0f, -1.0f + y*2.0f, 0.0f, 1.0f);\n"
"\tgl_Position = transform * pos;\n"
"\ttexCoord = vec2(x, -y);\n"
"}";
const char* kScreenFragmentShader =
"#version 330 core\n"
"out vec4 fColor;\n"
"in vec2 texCoord;\n"
"uniform sampler2D texture;\n"
"void main() {\n"
"\tfColor = texture2D(texture, texCoord);\n"
"}";
const char* kUpdateRectVertexShader =
"#version 330 core\n"
"layout (location = 0) in vec2 pos;\n"
"layout (location = 1) in vec3 color;\n"
"out vec4 vColor;\n"
"uniform mat4 transform;\n"
"void main() {\n"
"\tgl_Position = transform * vec4(pos, 0.0f, 1.0f);\n"
"\tvColor = vec4(color, 1.0f);\n"
"}";
const char* kUpdateRectFragmentShader =
"#version 330 core\n"
"out vec4 fColor;\n"
"in vec4 vColor;\n"
"void main() {\n"
"\tfColor = vColor;\n"
"}";
// clang-format off
constexpr float kLineVertices[] = {
// pos // color
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f
};
// clang-format on
using mat4x4_t = float[16];
void mat4x4_identity(mat4x4_t& matrix) {
// row 0
matrix[0] = 1.0f;
matrix[1] = 0.0f;
matrix[2] = 0.0f;
matrix[3] = 0.0f;
// row 1
matrix[4] = 0.0f;
matrix[5] = 1.0f;
matrix[6] = 0.0f;
matrix[7] = 0.0f;
// row 2
matrix[8] = 0.0f;
matrix[9] = 0.0f;
matrix[10] = 1.0f;
matrix[11] = 0.0f;
// row 3
matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
matrix[15] = 1.0f;
}
void mat4x4_rotate(mat4x4_t& matrix, float angle, float x, float y, float z) {
float c, s, t;
float length, theta;
// degrees to radians
theta = angle * (M_PI / 180.0f);
// normalize
length = sqrtf(x * x + y * y + z * z);
// too close to 0, can't make normalized vector
if (length < 0.0001f) {
return;
}
x /= length;
y /= length;
z /= length;
c = cosf(theta);
s = sinf(theta);
t = 1.0f - c;
// row 0
matrix[0] = t * x * x + c;
matrix[1] = t * x * y - s * z;
matrix[2] = t * x * z + s * y;
matrix[3] = 0.0f;
// row 1
matrix[4] = t * y * x + s * z;
matrix[5] = t * y * y + c;
matrix[6] = t * y * z - s * x;
matrix[7] = 0.0f;
// row 2
matrix[8] = t * x * z - s * y;
matrix[9] = t * y * z + s * x;
matrix[10] = t * z * z + c;
matrix[11] = 0.0f;
// row 3
matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
matrix[15] = 1.0f;
}
void mat4x4_ortho(mat4x4_t& matrix,
float left,
float right,
float bottom,
float top,
float near,
float far) {
// row 0
matrix[0] = 2.0f / (right - left);
matrix[1] = 0.0f;
matrix[2] = 0.0f;
matrix[3] = 0.0f;
// row 1
matrix[4] = 0.0f;
matrix[5] = 2.0f / (top - bottom);
matrix[6] = 0.0f;
matrix[7] = 0.0f;
// row 2
matrix[8] = 0.0f;
matrix[9] = 0.0f;
matrix[10] = -2.0f / (far - near);
matrix[11] = 0.0f;
// row 3
matrix[12] = -(right + left) / (right - left);
matrix[13] = -(top + bottom) / (top - bottom);
matrix[14] = -(far + near) / (far - near);
matrix[15] = 1.0f;
}
void mat4x4_multiply(mat4x4_t& c, const mat4x4_t& a, const mat4x4_t& b) {
// row 0
c[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8];
c[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9];
c[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10];
c[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3];
// row 1
c[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8];
c[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9];
c[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10];
c[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7];
// row 2
c[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8];
c[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9];
c[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10];
c[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11];
// row 3
c[12] = 0.0f;
c[13] = 0.0f;
c[14] = 0.0f;
c[15] = 1.0f;
}
} // namespace
#endif // USE_SHADERS
OsrRenderer::OsrRenderer(const OsrRendererSettings& settings)
: settings_(settings)
#if USE_SHADERS
,
line_vertices_(kLineVertices, kLineVertices + std::size(kLineVertices))
#endif
{
}
OsrRenderer::~OsrRenderer() {
Cleanup();
}
void OsrRenderer::Initialize() {
if (initialized_) {
return;
}
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
VERIFY_NO_ERROR;
if (IsTransparent()) {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
VERIFY_NO_ERROR;
} else {
glClearColor(float(CefColorGetR(settings_.background_color)) / 255.0f,
float(CefColorGetG(settings_.background_color)) / 255.0f,
float(CefColorGetB(settings_.background_color)) / 255.0f,
1.0f);
VERIFY_NO_ERROR;
}
#if USE_SHADERS
glGenVertexArrays(1, &vao_id_);
VERIFY_NO_ERROR;
glGenBuffers(1, &vbo_id_);
VERIFY_NO_ERROR;
glBindVertexArray(vao_id_);
VERIFY_NO_ERROR;
glBindBuffer(GL_ARRAY_BUFFER, vbo_id_);
VERIFY_NO_ERROR;
glBufferData(GL_ARRAY_BUFFER, line_vertices_.size() * sizeof(float), nullptr,
GL_STATIC_DRAW);
VERIFY_NO_ERROR;
int success;
unsigned int vertex_shader_id;
unsigned int fragment_shader_id;
char infoLog[512];
// create & compile screen vertex shader program
vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
VERIFY_NO_ERROR;
glShaderSource(vertex_shader_id, 1, &kScreenVertexShader, NULL);
VERIFY_NO_ERROR;
glCompileShader(vertex_shader_id);
VERIFY_NO_ERROR;
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &success);
VERIFY_NO_ERROR;
if (!success) {
glGetShaderInfoLog(vertex_shader_id, 512, NULL, infoLog);
VERIFY_NO_ERROR;
LOG(ERROR) << "Vertex shader compile error: " << infoLog;
}
// create & compile screen fragment shader program
fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
VERIFY_NO_ERROR;
glShaderSource(fragment_shader_id, 1, &kScreenFragmentShader, NULL);
VERIFY_NO_ERROR;
glCompileShader(fragment_shader_id);
VERIFY_NO_ERROR;
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &success);
VERIFY_NO_ERROR;
if (!success) {
glGetShaderInfoLog(fragment_shader_id, 512, NULL, infoLog);
VERIFY_NO_ERROR;
LOG(ERROR) << "Fragment shader compile error: " << infoLog;
}
// create final shader program
screen_shader_program_id_ = glCreateProgram();
VERIFY_NO_ERROR;
glAttachShader(screen_shader_program_id_, vertex_shader_id);
VERIFY_NO_ERROR;
glAttachShader(screen_shader_program_id_, fragment_shader_id);
VERIFY_NO_ERROR;
glLinkProgram(screen_shader_program_id_);
VERIFY_NO_ERROR;
glGetProgramiv(screen_shader_program_id_, GL_LINK_STATUS, &success);
VERIFY_NO_ERROR;
if (!success) {
glGetProgramInfoLog(screen_shader_program_id_, 512, NULL, infoLog);
VERIFY_NO_ERROR;
LOG(ERROR) << "Shader program link error: " << infoLog;
}
// delete the shader's as they're linked into our program now
glDeleteShader(vertex_shader_id);
VERIFY_NO_ERROR;
glDeleteShader(fragment_shader_id);
VERIFY_NO_ERROR;
// create & compile update rect vertex shader program
vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
VERIFY_NO_ERROR;
glShaderSource(vertex_shader_id, 1, &kUpdateRectVertexShader, NULL);
VERIFY_NO_ERROR;
glCompileShader(vertex_shader_id);
VERIFY_NO_ERROR;
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &success);
VERIFY_NO_ERROR;
if (!success) {
glGetShaderInfoLog(vertex_shader_id, 512, NULL, infoLog);
VERIFY_NO_ERROR;
LOG(ERROR) << "Vertex shader compile error: " << infoLog;
}
// create & compile update rect fragment shader program
fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
VERIFY_NO_ERROR;
glShaderSource(fragment_shader_id, 1, &kUpdateRectFragmentShader, NULL);
VERIFY_NO_ERROR;
glCompileShader(fragment_shader_id);
VERIFY_NO_ERROR;
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &success);
VERIFY_NO_ERROR;
if (!success) {
glGetShaderInfoLog(fragment_shader_id, 512, NULL, infoLog);
VERIFY_NO_ERROR;
LOG(ERROR) << "Fragment shader compile error: " << infoLog;
}
// create final shader program
update_rect_shader_program_id_ = glCreateProgram();
VERIFY_NO_ERROR;
glAttachShader(update_rect_shader_program_id_, vertex_shader_id);
VERIFY_NO_ERROR;
glAttachShader(update_rect_shader_program_id_, fragment_shader_id);
VERIFY_NO_ERROR;
glLinkProgram(update_rect_shader_program_id_);
VERIFY_NO_ERROR;
glGetProgramiv(update_rect_shader_program_id_, GL_LINK_STATUS, &success);
VERIFY_NO_ERROR;
if (!success) {
glGetProgramInfoLog(update_rect_shader_program_id_, 512, NULL, infoLog);
VERIFY_NO_ERROR;
LOG(ERROR) << "Shader program link error: " << infoLog;
}
// delete the shader's as they're linked into our program now
glDeleteShader(vertex_shader_id);
VERIFY_NO_ERROR;
glDeleteShader(fragment_shader_id);
VERIFY_NO_ERROR;
#endif // USE_SHADERS
// Necessary for non-power-of-2 textures to render correctly.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
VERIFY_NO_ERROR;
// Create the texture.
glGenTextures(1, &texture_id_);
VERIFY_NO_ERROR;
DCHECK_NE(texture_id_, 0U);
VERIFY_NO_ERROR;
glBindTexture(GL_TEXTURE_2D, texture_id_);
VERIFY_NO_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
VERIFY_NO_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
VERIFY_NO_ERROR;
#if USE_SHADERS
glUseProgram(screen_shader_program_id_);
VERIFY_NO_ERROR;
glUniform1i(glGetUniformLocation(screen_shader_program_id_, "texture"), 0);
VERIFY_NO_ERROR;
#else
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
VERIFY_NO_ERROR;
#endif
initialized_ = true;
}
void OsrRenderer::Cleanup() {
if (texture_id_ != 0) {
glDeleteTextures(1, &texture_id_);
}
#if USE_SHADERS
if (vao_id_ != 0) {
glDeleteVertexArrays(1, &vao_id_);
}
if (screen_shader_program_id_ != 0) {
glDeleteProgram(screen_shader_program_id_);
}
if (update_rect_shader_program_id_ != 0) {
glDeleteProgram(screen_shader_program_id_);
}
#endif
}
void OsrRenderer::Render() {
if (view_width_ == 0 || view_height_ == 0) {
return;
}
DCHECK(initialized_);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
VERIFY_NO_ERROR;
// Match GL units to screen coordinates.
glViewport(0, 0, view_width_, view_height_);
VERIFY_NO_ERROR;
#if USE_SHADERS
mat4x4_t transformation;
mat4x4_t rotX, rotY;
mat4x4_identity(transformation);
mat4x4_identity(rotX);
mat4x4_identity(rotY);
// Rotate the view based on the mouse spin.
if (spin_x_ != 0) {
mat4x4_rotate(rotX, -spin_x_, 1.0f, 0.0f, 0.0f);
}
if (spin_y_ != 0) {
mat4x4_rotate(rotY, -spin_y_, 0.0f, 1.0f, 0.0f);
}
mat4x4_multiply(transformation, rotX, rotY);
if (IsTransparent()) {
// Alpha blending style. Texture values have premultiplied alpha.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
VERIFY_NO_ERROR;
// Enable alpha blending.
glEnable(GL_BLEND);
VERIFY_NO_ERROR;
}
glActiveTexture(GL_TEXTURE0);
VERIFY_NO_ERROR;
glBindTexture(GL_TEXTURE_2D, texture_id_);
VERIFY_NO_ERROR;
glUseProgram(screen_shader_program_id_);
VERIFY_NO_ERROR;
glUniformMatrix4fv(
glGetUniformLocation(screen_shader_program_id_, "transform"), 1, GL_FALSE,
transformation);
VERIFY_NO_ERROR;
glDrawArrays(GL_TRIANGLES, 0, 6);
VERIFY_NO_ERROR;
if (IsTransparent()) {
// Disable alpha blending.
glDisable(GL_BLEND);
VERIFY_NO_ERROR;
}
// Draw a rectangle around the update region.
if (settings_.show_update_rect && !update_rect_.IsEmpty()) {
mat4x4_t projection;
int left = update_rect_.x;
int right = update_rect_.x + update_rect_.width;
int top = update_rect_.y;
int bottom = update_rect_.y + update_rect_.height;
float* vertices = line_vertices_.data();
#if defined(OS_LINUX)
// Shrink the box so that top & right sides are drawn.
top += 1;
right -= 1;
#else
// Shrink the box so that left & bottom sides are drawn.
left += 1;
bottom -= 1;
#endif
mat4x4_ortho(projection, 0.0f, view_width_, view_height_, 0.0f, 0.0f, 1.0f);
// v0
vertices[0] = left;
vertices[1] = top;
// v1
vertices[5] = right;
vertices[6] = top;
// v2
vertices[10] = right;
vertices[11] = bottom;
// v3
vertices[15] = left;
vertices[16] = bottom;
// v4
vertices[20] = left;
vertices[21] = top;
void* ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
VERIFY_NO_ERROR;
memcpy(ptr, line_vertices_.data(), line_vertices_.size() * sizeof(float));
glUnmapBuffer(GL_ARRAY_BUFFER);
VERIFY_NO_ERROR;
glLineWidth(1.0f);
VERIFY_NO_ERROR;
glUseProgram(update_rect_shader_program_id_);
VERIFY_NO_ERROR;
glUniformMatrix4fv(
glGetUniformLocation(update_rect_shader_program_id_, "transform"), 1,
GL_FALSE, projection);
VERIFY_NO_ERROR;
glEnableVertexAttribArray(0);
VERIFY_NO_ERROR;
glEnableVertexAttribArray(1);
VERIFY_NO_ERROR;
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float),
(void*)0);
VERIFY_NO_ERROR;
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float),
(void*)(2 * sizeof(float)));
VERIFY_NO_ERROR;
glDrawArrays(GL_LINE_LOOP, 0, 5);
VERIFY_NO_ERROR;
glDisableVertexAttribArray(0);
VERIFY_NO_ERROR;
glDisableVertexAttribArray(1);
VERIFY_NO_ERROR;
}
#else // !USE_SHADERS
struct {
float tu, tv;
float x, y, z;
} static vertices[] = {{0.0f, 1.0f, -1.0f, -1.0f, 0.0f},
{1.0f, 1.0f, 1.0f, -1.0f, 0.0f},
{1.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 1.0f, 0.0f}};
glMatrixMode(GL_MODELVIEW);
VERIFY_NO_ERROR;
glLoadIdentity();
VERIFY_NO_ERROR;
glMatrixMode(GL_PROJECTION);
VERIFY_NO_ERROR;
glLoadIdentity();
VERIFY_NO_ERROR;
// Draw the background gradient.
glPushAttrib(GL_ALL_ATTRIB_BITS);
VERIFY_NO_ERROR;
// Don't check for errors until glEnd().
glBegin(GL_QUADS);
glColor4f(1.0, 0.0, 0.0, 1.0); // red
glVertex2f(-1.0, -1.0);
glVertex2f(1.0, -1.0);
glColor4f(0.0, 0.0, 1.0, 1.0); // blue
glVertex2f(1.0, 1.0);
glVertex2f(-1.0, 1.0);
glEnd();
VERIFY_NO_ERROR;
glPopAttrib();
VERIFY_NO_ERROR;
// Rotate the view based on the mouse spin.
if (spin_x_ != 0) {
glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f);
VERIFY_NO_ERROR;
}
if (spin_y_ != 0) {
glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f);
VERIFY_NO_ERROR;
}
if (IsTransparent()) {
// Alpha blending style. Texture values have premultiplied alpha.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
VERIFY_NO_ERROR;
// Enable alpha blending.
glEnable(GL_BLEND);
VERIFY_NO_ERROR;
}
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
VERIFY_NO_ERROR;
// Draw the facets with the texture.
DCHECK_NE(texture_id_, 0U);
VERIFY_NO_ERROR;
glBindTexture(GL_TEXTURE_2D, texture_id_);
VERIFY_NO_ERROR;
glInterleavedArrays(GL_T2F_V3F, 0, vertices);
VERIFY_NO_ERROR;
glDrawArrays(GL_QUADS, 0, 4);
VERIFY_NO_ERROR;
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
VERIFY_NO_ERROR;
if (IsTransparent()) {
// Disable alpha blending.
glDisable(GL_BLEND);
VERIFY_NO_ERROR;
}
// Draw a rectangle around the update region.
if (settings_.show_update_rect && !update_rect_.IsEmpty()) {
int left = update_rect_.x;
int right = update_rect_.x + update_rect_.width;
int top = update_rect_.y;
int bottom = update_rect_.y + update_rect_.height;
#if defined(OS_LINUX)
// Shrink the box so that top & right sides are drawn.
top += 1;
right -= 1;
#else
// Shrink the box so that left & bottom sides are drawn.
left += 1;
bottom -= 1;
#endif
glPushAttrib(GL_ALL_ATTRIB_BITS);
VERIFY_NO_ERROR
glMatrixMode(GL_PROJECTION);
VERIFY_NO_ERROR;
glPushMatrix();
VERIFY_NO_ERROR;
glLoadIdentity();
VERIFY_NO_ERROR;
glOrtho(0, view_width_, view_height_, 0, 0, 1);
VERIFY_NO_ERROR;
glLineWidth(1);
VERIFY_NO_ERROR;
glColor3f(1.0f, 0.0f, 0.0f);
VERIFY_NO_ERROR;
// Don't check for errors until glEnd().
glBegin(GL_LINE_STRIP);
glVertex2i(left, top);
glVertex2i(right, top);
glVertex2i(right, bottom);
glVertex2i(left, bottom);
glVertex2i(left, top);
glEnd();
VERIFY_NO_ERROR;
glPopMatrix();
VERIFY_NO_ERROR;
glPopAttrib();
VERIFY_NO_ERROR;
}
#endif // !USE_SHADERS
}
void OsrRenderer::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
if (!show) {
// Clear the popup rectangle.
ClearPopupRects();
}
}
void OsrRenderer::OnPopupSize(CefRefPtr<CefBrowser> browser,
const CefRect& rect) {
if (rect.width <= 0 || rect.height <= 0) {
return;
}
original_popup_rect_ = rect;
popup_rect_ = GetPopupRectInWebView(original_popup_rect_);
}
CefRect OsrRenderer::GetPopupRectInWebView(const CefRect& original_rect) {
CefRect rc(original_rect);
// if x or y are negative, move them to 0.
if (rc.x < 0) {
rc.x = 0;
}
if (rc.y < 0) {
rc.y = 0;
}
// if popup goes outside the view, try to reposition origin
if (rc.x + rc.width > view_width_) {
rc.x = view_width_ - rc.width;
}
if (rc.y + rc.height > view_height_) {
rc.y = view_height_ - rc.height;
}
// if x or y became negative, move them to 0 again.
if (rc.x < 0) {
rc.x = 0;
}
if (rc.y < 0) {
rc.y = 0;
}
return rc;
}
void OsrRenderer::ClearPopupRects() {
popup_rect_.Set(0, 0, 0, 0);
original_popup_rect_.Set(0, 0, 0, 0);
}
void OsrRenderer::OnAcceleratedPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
unsigned int io_surface_tex,
int width,
int height) {
if (!initialized_) {
Initialize();
}
#if !defined(OS_WIN)
if (width != view_width_ || height != view_height_) {
// Width or height has changed, so proceed to update the texture
view_width_ = width;
view_height_ = height;
// Rebind texture_id as the active texture
glBindTexture(GL_TEXTURE_2D, texture_id_);
VERIFY_NO_ERROR;
// Allocate a new storage for texture_id with the new dimensions
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
VERIFY_NO_ERROR;
}
if (IsTransparent()) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
VERIFY_NO_ERROR;
// Enable alpha blending.
glEnable(GL_BLEND);
VERIFY_NO_ERROR;
}
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
VERIFY_NO_ERROR;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
VERIFY_NO_ERROR;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture_id_, 0);
VERIFY_NO_ERROR;
// Check framebuffer status
DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glPushAttrib(GL_ALL_ATTRIB_BITS);
VERIFY_NO_ERROR;
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
VERIFY_NO_ERROR;
// Setup OpenGL states
glViewport(0, 0, width, height);
VERIFY_NO_ERROR;
// Bind the GL_TEXTURE_RECTANGLE_ARB texture
glActiveTexture(GL_TEXTURE0);
VERIFY_NO_ERROR;
glClientActiveTexture(GL_TEXTURE0);
VERIFY_NO_ERROR;
glMatrixMode(GL_TEXTURE);
VERIFY_NO_ERROR;
glPushMatrix();
VERIFY_NO_ERROR;
glLoadIdentity();
VERIFY_NO_ERROR;
glMatrixMode(GL_PROJECTION);
VERIFY_NO_ERROR;
glPushMatrix();
VERIFY_NO_ERROR;
glLoadIdentity();
VERIFY_NO_ERROR;
glOrtho(0, width, 0, height, -1, 1);
VERIFY_NO_ERROR;
glMatrixMode(GL_MODELVIEW);
VERIFY_NO_ERROR;
glPushMatrix();
VERIFY_NO_ERROR;
glLoadIdentity();
VERIFY_NO_ERROR;
// rectangleTexture is the GL_TEXTURE_RECTANGLE_ARB
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, io_surface_tex);
VERIFY_NO_ERROR;
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
VERIFY_NO_ERROR;
glColor4f(1.0, 1.0, 1.0, 1.0);
VERIFY_NO_ERROR;
bool isFlipped = false;
GLfloat tex_coords[8];
GLfloat texOriginX = 0;
GLfloat texOriginY = 0;
GLfloat texExtentX = width;
GLfloat texExtentY = height;
// X
tex_coords[0] = texOriginX;
tex_coords[2] = texOriginX;
tex_coords[4] = texExtentX;
tex_coords[6] = texExtentX;
// Y
if (!isFlipped) {
tex_coords[1] = texOriginY;
tex_coords[3] = texExtentY;
tex_coords[5] = texExtentY;
tex_coords[7] = texOriginY;
} else {
tex_coords[1] = texExtentY;
tex_coords[3] = texOriginY;
tex_coords[5] = texOriginY;
tex_coords[7] = texExtentY;
}
GLfloat verts[] = {
0.0f, 0.0f, 0.0f, (float)height,
(float)width, (float)height, (float)width, 0.0f,
};
// Ought to cache the GL_ARRAY_BUFFER_BINDING,
// GL_ELEMENT_ARRAY_BUFFER_BINDING, set buffer to 0, and reset
GLint arrayBuffer, elementArrayBuffer;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elementArrayBuffer);
VERIFY_NO_ERROR;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &arrayBuffer);
VERIFY_NO_ERROR;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
VERIFY_NO_ERROR;
glBindBuffer(GL_ARRAY_BUFFER, 0);
VERIFY_NO_ERROR;
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
VERIFY_NO_ERROR;
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords);
VERIFY_NO_ERROR;
glEnableClientState(GL_VERTEX_ARRAY);
VERIFY_NO_ERROR;
glVertexPointer(2, GL_FLOAT, 0, verts);
VERIFY_NO_ERROR;
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
VERIFY_NO_ERROR;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayBuffer);
VERIFY_NO_ERROR;
glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
VERIFY_NO_ERROR;
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
VERIFY_NO_ERROR;
// Restore OpenGL states
glMatrixMode(GL_MODELVIEW);
VERIFY_NO_ERROR;
glPopMatrix();
VERIFY_NO_ERROR;
glMatrixMode(GL_PROJECTION);
VERIFY_NO_ERROR;
glPopMatrix();
VERIFY_NO_ERROR;
glMatrixMode(GL_TEXTURE);
VERIFY_NO_ERROR;
glPopMatrix();
VERIFY_NO_ERROR;
glPopClientAttrib();
VERIFY_NO_ERROR;
glPopAttrib();
VERIFY_NO_ERROR;
if (IsTransparent()) {
// Enable alpha blending.
glDisable(GL_BLEND);
VERIFY_NO_ERROR;
}
// 0 unbinds the FBO, reverting to the default buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
VERIFY_NO_ERROR;
glDeleteFramebuffers(1, &framebuffer);
VERIFY_NO_ERROR;
// Delete the rectangle texture
glDeleteTextures(1, &io_surface_tex);
VERIFY_NO_ERROR;
glDisable(GL_TEXTURE_RECTANGLE_ARB);
VERIFY_NO_ERROR;
#endif // !defined(OS_WIN)
}
void OsrRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer,
int width,
int height) {
if (!initialized_) {
Initialize();
}
if (IsTransparent()) {
// Enable alpha blending.
glEnable(GL_BLEND);
VERIFY_NO_ERROR;
}
#if !USE_SHADERS
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
VERIFY_NO_ERROR;
#endif
DCHECK_NE(texture_id_, 0U);
glBindTexture(GL_TEXTURE_2D, texture_id_);
VERIFY_NO_ERROR;
if (type == PET_VIEW) {
int old_width = view_width_;
int old_height = view_height_;
view_width_ = width;
view_height_ = height;
if (settings_.show_update_rect) {
update_rect_ = dirtyRects[0];
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_);
VERIFY_NO_ERROR;
if (old_width != view_width_ || old_height != view_height_ ||
(dirtyRects.size() == 1 &&
dirtyRects[0] == CefRect(0, 0, view_width_, view_height_))) {
// Update/resize the whole texture.
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
VERIFY_NO_ERROR;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
VERIFY_NO_ERROR;
} else {
// Update just the dirty rectangles.
CefRenderHandler::RectList::const_iterator i = dirtyRects.begin();
for (; i != dirtyRects.end(); ++i) {
const CefRect& rect = *i;
DCHECK(rect.x + rect.width <= view_width_);
DCHECK(rect.y + rect.height <= view_height_);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
VERIFY_NO_ERROR;
glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width,
rect.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
buffer);
VERIFY_NO_ERROR;
}
}
} else if (type == PET_POPUP && popup_rect_.width > 0 &&
popup_rect_.height > 0) {
int skip_pixels = 0, x = popup_rect_.x;
int skip_rows = 0, y = popup_rect_.y;
int w = width;
int h = height;
// Adjust the popup to fit inside the view.
if (x < 0) {
skip_pixels = -x;
x = 0;
}
if (y < 0) {
skip_rows = -y;
y = 0;
}
if (x + w > view_width_) {
w -= x + w - view_width_;
}
if (y + h > view_height_) {
h -= y + h - view_height_;
}
// Update the popup rectangle.
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
VERIFY_NO_ERROR;
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
VERIFY_NO_ERROR;
}
#if !USE_SHADERS
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
VERIFY_NO_ERROR;
#endif
if (IsTransparent()) {
// Disable alpha blending.
glDisable(GL_BLEND);
VERIFY_NO_ERROR;
}
}
void OsrRenderer::SetSpin(float spinX, float spinY) {
spin_x_ = spinX;
spin_y_ = spinY;
}
void OsrRenderer::IncrementSpin(float spinDX, float spinDY) {
spin_x_ -= spinDX;
spin_y_ -= spinDY;
}
} // namespace client
#if defined(__clang__)
// End disable NSOpenGL deprecation warnings.
#pragma clang diagnostic pop
#endif