mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-02-28 01:47:43 +01:00
Adds support for the OnAcceleratedPaint callback. Verified to work on macOS and Windows. Linux support is present but not implemented for cefclient, so it is not verified to work. To test: Run `cefclient --off-screen-rendering-enabled --shared-texture-enabled`
1099 lines
28 KiB
C++
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(OS_WIN)
|
|
// 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(OS_WIN)
|
|
// End disable NSOpenGL deprecation warnings.
|
|
#pragma clang diagnostic pop
|
|
#endif
|