cef/cef1/tests/cefclient/osrenderer.cpp

433 lines
12 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 "cefclient/osrenderer.h"
#include "cefclient/util.h"
#if defined(OS_WIN)
#include <gl/gl.h>
#include <gl/glu.h>
#elif defined(OS_MACOSX)
#include <OpenGL/gl.h>
#else
#error Platform is not supported.
#endif
namespace {
// Convert from BGRA to RGBA format and from upper-left to lower-left origin.
void ConvertToRGBA(const unsigned char* src, unsigned char* dst,
int width, int height) {
int sp = 0, dp = (height-1) * width * 4;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++, dp += 4, sp += 4) {
dst[dp] = src[sp+2]; // R
dst[dp+1] = src[sp+1]; // G
dst[dp+2] = src[sp]; // B
dst[dp+3] = src[sp+3]; // A
}
dp -= width * 8;
}
}
void ConvertToRGBARect(const CefRect& clipRect,
const unsigned char* src, unsigned char* dst,
int width, int height) {
int sp = 0, dp = (height-1) * width * 4;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++, dp += 4, sp += 4) {
if ((clipRect.x <= j) && (clipRect.x + clipRect.width > j) &&
(clipRect.y <= i) && (clipRect.y + clipRect.height > i)) {
dst[dp] = src[sp+2]; // R
dst[dp+1] = src[sp+1]; // G
dst[dp+2] = src[sp]; // B
dst[dp+3] = src[sp+3]; // A
}
}
dp -= width * 8;
}
}
// Convert from BGRA to RGB format and from upper-left to lower-left origin.
void ConvertToRGB(const unsigned char* src, unsigned char* dst,
int width, int height) {
int sp = 0, dp = (height-1) * width * 3;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++, dp += 3, sp += 4) {
dst[dp] = src[sp+2]; // R
dst[dp+1] = src[sp+1]; // G
dst[dp+2] = src[sp]; // B
}
dp -= width * 6;
}
}
void ConvertToRGBRect(const CefRect& clipRect,
const unsigned char* src, unsigned char* dst,
int width, int height) {
int sp = 0, dp = (height-1) * width * 3;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++, dp += 3, sp += 4) {
if ((clipRect.x <= j) && (clipRect.x + clipRect.width > j) &&
(clipRect.y <= i) && (clipRect.y + clipRect.height > i)) {
dst[dp] = src[sp+2]; // R
dst[dp+1] = src[sp+1]; // G
dst[dp+2] = src[sp]; // B
}
}
dp -= width * 6;
}
}
} // namespace
ClientOSRenderer::ClientOSRenderer(bool transparent)
: transparent_(transparent),
initialized_(false),
texture_id_(0),
view_buffer_(NULL),
view_buffer_size_(0),
popup_buffer_(NULL),
popup_buffer_size_(0),
view_width_(0),
view_height_(0),
spin_x_(0),
spin_y_(0) {
}
ClientOSRenderer::~ClientOSRenderer() {
Cleanup();
}
void ClientOSRenderer::Initialize() {
if (initialized_)
return;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Necessary for non-power-of-2 textures to render correctly.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (transparent_) {
// Alpha blending style. Texture values have premultiplied alpha.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
initialized_ = true;
}
void ClientOSRenderer::Cleanup() {
if (view_buffer_) {
delete [] view_buffer_;
view_buffer_ = NULL;
view_buffer_size_ = 0;
}
if (popup_buffer_) {
delete [] popup_buffer_;
popup_buffer_ = NULL;
popup_buffer_size_ = 0;
}
if (texture_id_ != 0)
glDeleteTextures(1, &texture_id_);
}
void ClientOSRenderer::SetSize(int width, int height) {
if (!initialized_)
Initialize();
// Match GL units to screen coordinates.
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 0, width, height, 0.1, 100.0);
if (transparent_) {
// Enable alpha blending.
glEnable(GL_BLEND);
}
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
GLuint new_texture_id = 0;
// Create a new texture.
glGenTextures(1, &new_texture_id);
ASSERT(new_texture_id != 0);
glBindTexture(GL_TEXTURE_2D, new_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Start with all white contents.
{
int size = width * height * (transparent_?4:3);
unsigned char* buffer = new unsigned char[size];
memset(buffer, 255, size);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, buffer);
delete [] buffer;
}
if (texture_id_ != 0) {
// Draw the existing view buffer to the new texture.
DrawViewBuffer(width, height);
// Delete the old texture.
glDeleteTextures(1, &texture_id_);
}
texture_id_ = new_texture_id;
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
if (transparent_) {
// Disable alpha blending.
glDisable(GL_BLEND);
}
}
void ClientOSRenderer::Render() {
ASSERT(initialized_);
struct {
float tu, tv;
float x, y, z;
} static vertices[] = {
{0.0f, 0.0f, -1.0f, -1.0f, 0.0f},
{1.0f, 0.0f, 1.0f, -1.0f, 0.0f},
{1.0f, 1.0f, 1.0f, 1.0f, 0.0f},
{0.0f, 1.0f, -1.0f, 1.0f, 0.0f}
};
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Draw the background gradient.
glPushAttrib(GL_ALL_ATTRIB_BITS);
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();
glPopAttrib();
// Rotate the view based on the mouse spin.
glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f);
glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f);
if (transparent_) {
// Enable alpha blending.
glEnable(GL_BLEND);
}
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
// Draw the facets with the texture.
ASSERT(texture_id_ != 0);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glInterleavedArrays(GL_T2F_V3F, 0, vertices);
glDrawArrays(GL_QUADS, 0, 4);
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
if (transparent_) {
// Disable alpha blending.
glDisable(GL_BLEND);
}
glFlush();
}
void ClientOSRenderer::OnPopupShow(CefRefPtr<CefBrowser> browser,
bool show) {
if (!show) {
// Clear the popup buffer.
popup_rect_.Set(0, 0, 0, 0);
if (popup_buffer_) {
delete [] popup_buffer_;
popup_buffer_ = NULL;
popup_buffer_size_ = 0;
}
}
}
void ClientOSRenderer::OnPopupSize(CefRefPtr<CefBrowser> browser,
const CefRect& rect) {
if (rect.width > 0) {
// Update the popup rectange. It should always be inside the view.
ASSERT(rect.x + rect.width < view_width_ &&
rect.y + rect.height < view_height_);
popup_rect_ = rect;
}
}
void ClientOSRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer) {
ASSERT(initialized_);
// Retrieve the current size of the browser view.
browser->GetSize(type, view_width_, view_height_);
if (transparent_) {
// Enable alpha blending.
glEnable(GL_BLEND);
}
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
ASSERT(texture_id_ != 0);
glBindTexture(GL_TEXTURE_2D, texture_id_);
if (type == PET_VIEW) {
SetBufferSize(view_width_, view_height_, true);
// Paint the view.
if (transparent_) {
CefRenderHandler::RectList::const_iterator i = dirtyRects.begin();
for (; i != dirtyRects.end(); ++i) {
ConvertToRGBARect(*i, (unsigned char*)buffer, view_buffer_,
view_width_, view_height_);
}
} else {
CefRenderHandler::RectList::const_iterator i = dirtyRects.begin();
for (; i != dirtyRects.end(); ++i) {
ConvertToRGBRect(*i, (unsigned char*)buffer, view_buffer_,
view_width_, view_height_);
}
}
// Update the whole texture. This is done for simplicity instead of
// updating just the dirty region.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_width_, view_height_,
transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, view_buffer_);
}
if (popup_rect_.width > 0) {
if (type == PET_POPUP) {
// Paint the popup.
if (transparent_)
SetRGBA(buffer, popup_rect_.width, popup_rect_.height, false);
else
SetRGB(buffer, popup_rect_.width, popup_rect_.height, false);
}
if (popup_buffer_) {
// Update the popup region.
glTexSubImage2D(GL_TEXTURE_2D, 0, popup_rect_.x,
view_height_ - popup_rect_.y - popup_rect_.height,
popup_rect_.width, popup_rect_.height,
transparent_?GL_RGBA:GL_RGB,
GL_UNSIGNED_BYTE, popup_buffer_);
}
}
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
if (transparent_) {
// Disable alpha blending.
glDisable(GL_BLEND);
}
}
void ClientOSRenderer::SetSpin(float spinX, float spinY) {
spin_x_ = spinX;
spin_y_ = spinY;
}
void ClientOSRenderer::IncrementSpin(float spinDX, float spinDY) {
spin_x_ -= spinDX;
spin_y_ -= spinDY;
}
bool ClientOSRenderer::GetPixelValue(int x, int y, unsigned char& r,
unsigned char& g, unsigned char& b,
unsigned char& a) {
if (!view_buffer_)
return false;
ASSERT(x >= 0 && x < view_width_);
ASSERT(y >= 0 && y < view_height_);
int pixel_bytes = transparent_ ? 4 : 3;
int y_flipped = view_height_ - y;
int index = (view_width_ * y_flipped * pixel_bytes) + (x * pixel_bytes);
r = view_buffer_[index];
g = view_buffer_[index+1];
b = view_buffer_[index+2];
if (transparent_)
a = view_buffer_[index+3];
return true;
}
void ClientOSRenderer::SetBufferSize(int width, int height, bool view) {
int dst_size = width * height * (transparent_?4:3);
// Allocate a new buffer if necesary.
if (view) {
if (dst_size > view_buffer_size_) {
if (view_buffer_)
delete [] view_buffer_;
view_buffer_ = new unsigned char[dst_size];
view_buffer_size_ = dst_size;
}
} else {
if (dst_size > popup_buffer_size_) {
if (popup_buffer_)
delete [] popup_buffer_;
popup_buffer_ = new unsigned char[dst_size];
popup_buffer_size_ = dst_size;
}
}
}
// Set the contents of the RGBA buffer.
void ClientOSRenderer::SetRGBA(const void* src, int width, int height,
bool view) {
SetBufferSize(width, height, view);
ConvertToRGBA((unsigned char*)src, view?view_buffer_:popup_buffer_, width,
height);
}
// Set the contents of the RGB buffer.
void ClientOSRenderer::SetRGB(const void* src, int width, int height,
bool view) {
SetBufferSize(width, height, view);
ConvertToRGB((unsigned char*)src, view?view_buffer_:popup_buffer_, width,
height);
}
void ClientOSRenderer::DrawViewBuffer(int max_width, int max_height) {
if (max_width < view_width_ || max_height < view_height_) {
// The requested max size is smaller than the current view buffer.
int width = std::min(max_width, view_width_);
int height = std::min(max_height, view_height_);
glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE,
view_buffer_);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_width_, view_height_,
transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, view_buffer_);
}
}