// Copyright (c) 2013 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. package org.cef.browser; import com.jogamp.opengl.GL2; import java.awt.Rectangle; import java.nio.ByteBuffer; import java.nio.FloatBuffer; class CefRenderer { private boolean transparent_; private GL2 initialized_context_ = null; private int[] texture_id_ = new int[1]; private int view_width_ = 0; private int view_height_ = 0; private float spin_x_ = 0f; private float spin_y_ = 0f; private Rectangle popup_rect_ = new Rectangle(0, 0, 0, 0); private Rectangle original_popup_rect_ = new Rectangle(0, 0, 0, 0); private boolean use_draw_pixels_ = false; protected CefRenderer(boolean transparent) { transparent_ = transparent; } protected boolean isTransparent() { return transparent_; } protected int getTextureID() { return texture_id_[0]; } @SuppressWarnings("static-access") protected void initialize(GL2 gl2) { if (initialized_context_ == gl2) return; initialized_context_ = gl2; if (!gl2.getContext().isHardwareRasterizer()) { // Workaround for Windows Remote Desktop which requires pot textures. System.out.println( "opengl rendering may be slow as hardware rendering isn't available"); use_draw_pixels_ = true; return; } gl2.glHint(gl2.GL_POLYGON_SMOOTH_HINT, gl2.GL_NICEST); gl2.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Necessary for non-power-of-2 textures to render correctly. gl2.glPixelStorei(gl2.GL_UNPACK_ALIGNMENT, 1); // Create the texture. gl2.glGenTextures(1, texture_id_, 0); assert (texture_id_[0] != 0); gl2.glBindTexture(gl2.GL_TEXTURE_2D, texture_id_[0]); gl2.glTexParameteri(gl2.GL_TEXTURE_2D, gl2.GL_TEXTURE_MIN_FILTER, gl2.GL_NEAREST); gl2.glTexParameteri(gl2.GL_TEXTURE_2D, gl2.GL_TEXTURE_MAG_FILTER, gl2.GL_NEAREST); gl2.glTexEnvf(gl2.GL_TEXTURE_ENV, gl2.GL_TEXTURE_ENV_MODE, gl2.GL_MODULATE); } protected void cleanup(GL2 gl2) { if (texture_id_[0] != 0) gl2.glDeleteTextures(1, texture_id_, 0); view_width_ = view_height_ = 0; } @SuppressWarnings("static-access") protected void render(GL2 gl2) { if (use_draw_pixels_ || view_width_ == 0 || view_height_ == 0) return; assert (initialized_context_ != null); final float[] vertex_data = {// tu, tv, x, y, z 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}; FloatBuffer vertices = FloatBuffer.wrap(vertex_data); gl2.glClear(gl2.GL_COLOR_BUFFER_BIT | gl2.GL_DEPTH_BUFFER_BIT); gl2.glMatrixMode(gl2.GL_MODELVIEW); gl2.glLoadIdentity(); // Match GL units to screen coordinates. gl2.glViewport(0, 0, view_width_, view_height_); gl2.glMatrixMode(gl2.GL_PROJECTION); gl2.glLoadIdentity(); // Draw the background gradient. gl2.glPushAttrib(gl2.GL_ALL_ATTRIB_BITS); gl2.glBegin(gl2.GL_QUADS); gl2.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); // red gl2.glVertex2f(-1.0f, -1.0f); gl2.glVertex2f(1.0f, -1.0f); gl2.glColor4f(0.0f, 0.0f, 1.0f, 1.0f); // blue gl2.glVertex2f(1.0f, 1.0f); gl2.glVertex2f(-1.0f, 1.0f); gl2.glEnd(); gl2.glPopAttrib(); // Rotate the view based on the mouse spin. if (spin_x_ != 0) gl2.glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f); if (spin_y_ != 0) gl2.glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f); if (transparent_) { // Alpha blending style. Texture values have premultiplied alpha. gl2.glBlendFunc(gl2.GL_ONE, gl2.GL_ONE_MINUS_SRC_ALPHA); // Enable alpha blending. gl2.glEnable(gl2.GL_BLEND); } // Enable 2D textures. gl2.glEnable(gl2.GL_TEXTURE_2D); // Draw the facets with the texture. assert (texture_id_[0] != 0); gl2.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); gl2.glBindTexture(gl2.GL_TEXTURE_2D, texture_id_[0]); gl2.glInterleavedArrays(gl2.GL_T2F_V3F, 0, vertices); gl2.glDrawArrays(gl2.GL_QUADS, 0, 4); // Disable 2D textures. gl2.glDisable(gl2.GL_TEXTURE_2D); if (transparent_) { // Disable alpha blending. gl2.glDisable(gl2.GL_BLEND); } } protected void onPopupSize(Rectangle rect) { if (rect.width <= 0 || rect.height <= 0) return; original_popup_rect_ = rect; popup_rect_ = getPopupRectInWebView(original_popup_rect_); } protected Rectangle getPopupRect() { return (Rectangle) popup_rect_.clone(); } protected Rectangle getPopupRectInWebView(Rectangle original_rect) { Rectangle 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; } protected void clearPopupRects() { popup_rect_.setBounds(0, 0, 0, 0); original_popup_rect_.setBounds(0, 0, 0, 0); } @SuppressWarnings("static-access") protected void onPaint(GL2 gl2, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height) { initialize(gl2); if (use_draw_pixels_) { gl2.glRasterPos2f(-1, 1); gl2.glPixelZoom(1, -1); gl2.glDrawPixels(width, height, GL2.GL_BGRA, GL2.GL_UNSIGNED_BYTE, buffer); return; } if (transparent_) { // Enable alpha blending. gl2.glEnable(gl2.GL_BLEND); } // Enable 2D textures. gl2.glEnable(gl2.GL_TEXTURE_2D); assert (texture_id_[0] != 0); gl2.glBindTexture(gl2.GL_TEXTURE_2D, texture_id_[0]); if (!popup) { int old_width = view_width_; int old_height = view_height_; view_width_ = width; view_height_ = height; gl2.glPixelStorei(gl2.GL_UNPACK_ROW_LENGTH, view_width_); if (old_width != view_width_ || old_height != view_height_) { // Update/resize the whole texture. gl2.glPixelStorei(gl2.GL_UNPACK_SKIP_PIXELS, 0); gl2.glPixelStorei(gl2.GL_UNPACK_SKIP_ROWS, 0); gl2.glTexImage2D(gl2.GL_TEXTURE_2D, 0, gl2.GL_RGBA, view_width_, view_height_, 0, gl2.GL_BGRA, gl2.GL_UNSIGNED_INT_8_8_8_8_REV, buffer); } else { // Update just the dirty rectangles. for (int i = 0; i < dirtyRects.length; ++i) { Rectangle rect = dirtyRects[i]; gl2.glPixelStorei(gl2.GL_UNPACK_SKIP_PIXELS, rect.x); gl2.glPixelStorei(gl2.GL_UNPACK_SKIP_ROWS, rect.y); gl2.glTexSubImage2D(gl2.GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width, rect.height, gl2.GL_BGRA, gl2.GL_UNSIGNED_INT_8_8_8_8_REV, buffer); } } } else if (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. gl2.glPixelStorei(gl2.GL_UNPACK_ROW_LENGTH, width); gl2.glPixelStorei(gl2.GL_UNPACK_SKIP_PIXELS, skip_pixels); gl2.glPixelStorei(gl2.GL_UNPACK_SKIP_ROWS, skip_rows); gl2.glTexSubImage2D(gl2.GL_TEXTURE_2D, 0, x, y, w, h, gl2.GL_BGRA, gl2.GL_UNSIGNED_INT_8_8_8_8_REV, buffer); } // Disable 2D textures. gl2.glDisable(gl2.GL_TEXTURE_2D); if (transparent_) { // Disable alpha blending. gl2.glDisable(gl2.GL_BLEND); } } protected void setSpin(float spinX, float spinY) { spin_x_ = spinX; spin_y_ = spinY; } protected void incrementSpin(float spinDX, float spinDY) { spin_x_ -= spinDX; spin_y_ -= spinDY; } }