1 // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/osr_renderer.h"
6
7 #if defined(OS_WIN)
8 #include <gl/gl.h>
9 #elif defined(OS_MAC)
10 #include <OpenGL/gl.h>
11 #elif defined(OS_LINUX)
12 #include <GL/gl.h>
13 #else
14 #error Platform is not supported.
15 #endif
16
17 #include "include/base/cef_logging.h"
18 #include "include/wrapper/cef_helpers.h"
19
20 #ifndef GL_BGR
21 #define GL_BGR 0x80E0
22 #endif
23 #ifndef GL_BGRA
24 #define GL_BGRA 0x80E1
25 #endif
26 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
27 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
28 #endif
29
30 // DCHECK on gl errors.
31 #if DCHECK_IS_ON()
32 #define VERIFY_NO_ERROR \
33 { \
34 int _gl_error = glGetError(); \
35 DCHECK(_gl_error == GL_NO_ERROR) << "glGetError returned " << _gl_error; \
36 }
37 #else
38 #define VERIFY_NO_ERROR
39 #endif
40
41 namespace client {
42
OsrRenderer(const OsrRendererSettings & settings)43 OsrRenderer::OsrRenderer(const OsrRendererSettings& settings)
44 : settings_(settings),
45 initialized_(false),
46 texture_id_(0),
47 view_width_(0),
48 view_height_(0),
49 spin_x_(0),
50 spin_y_(0) {}
51
~OsrRenderer()52 OsrRenderer::~OsrRenderer() {
53 Cleanup();
54 }
55
Initialize()56 void OsrRenderer::Initialize() {
57 if (initialized_)
58 return;
59
60 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
61 VERIFY_NO_ERROR;
62
63 if (IsTransparent()) {
64 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
65 VERIFY_NO_ERROR;
66 } else {
67 glClearColor(float(CefColorGetR(settings_.background_color)) / 255.0f,
68 float(CefColorGetG(settings_.background_color)) / 255.0f,
69 float(CefColorGetB(settings_.background_color)) / 255.0f,
70 1.0f);
71 VERIFY_NO_ERROR;
72 }
73
74 // Necessary for non-power-of-2 textures to render correctly.
75 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
76 VERIFY_NO_ERROR;
77
78 // Create the texture.
79 glGenTextures(1, &texture_id_);
80 VERIFY_NO_ERROR;
81 DCHECK_NE(texture_id_, 0U);
82 VERIFY_NO_ERROR;
83
84 glBindTexture(GL_TEXTURE_2D, texture_id_);
85 VERIFY_NO_ERROR;
86 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
87 VERIFY_NO_ERROR;
88 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
89 VERIFY_NO_ERROR;
90 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
91 VERIFY_NO_ERROR;
92
93 initialized_ = true;
94 }
95
Cleanup()96 void OsrRenderer::Cleanup() {
97 if (texture_id_ != 0)
98 glDeleteTextures(1, &texture_id_);
99 }
100
Render()101 void OsrRenderer::Render() {
102 if (view_width_ == 0 || view_height_ == 0)
103 return;
104
105 DCHECK(initialized_);
106
107 struct {
108 float tu, tv;
109 float x, y, z;
110 } static vertices[] = {{0.0f, 1.0f, -1.0f, -1.0f, 0.0f},
111 {1.0f, 1.0f, 1.0f, -1.0f, 0.0f},
112 {1.0f, 0.0f, 1.0f, 1.0f, 0.0f},
113 {0.0f, 0.0f, -1.0f, 1.0f, 0.0f}};
114
115 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
116 VERIFY_NO_ERROR;
117
118 glMatrixMode(GL_MODELVIEW);
119 VERIFY_NO_ERROR;
120 glLoadIdentity();
121 VERIFY_NO_ERROR;
122
123 // Match GL units to screen coordinates.
124 glViewport(0, 0, view_width_, view_height_);
125 VERIFY_NO_ERROR;
126 glMatrixMode(GL_PROJECTION);
127 VERIFY_NO_ERROR;
128 glLoadIdentity();
129 VERIFY_NO_ERROR;
130
131 // Draw the background gradient.
132 glPushAttrib(GL_ALL_ATTRIB_BITS);
133 VERIFY_NO_ERROR;
134 // Don't check for errors until glEnd().
135 glBegin(GL_QUADS);
136 glColor4f(1.0, 0.0, 0.0, 1.0); // red
137 glVertex2f(-1.0, -1.0);
138 glVertex2f(1.0, -1.0);
139 glColor4f(0.0, 0.0, 1.0, 1.0); // blue
140 glVertex2f(1.0, 1.0);
141 glVertex2f(-1.0, 1.0);
142 glEnd();
143 VERIFY_NO_ERROR;
144 glPopAttrib();
145 VERIFY_NO_ERROR;
146
147 // Rotate the view based on the mouse spin.
148 if (spin_x_ != 0) {
149 glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f);
150 VERIFY_NO_ERROR;
151 }
152 if (spin_y_ != 0) {
153 glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f);
154 VERIFY_NO_ERROR;
155 }
156
157 if (IsTransparent()) {
158 // Alpha blending style. Texture values have premultiplied alpha.
159 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
160 VERIFY_NO_ERROR;
161
162 // Enable alpha blending.
163 glEnable(GL_BLEND);
164 VERIFY_NO_ERROR;
165 }
166
167 // Enable 2D textures.
168 glEnable(GL_TEXTURE_2D);
169 VERIFY_NO_ERROR;
170
171 // Draw the facets with the texture.
172 DCHECK_NE(texture_id_, 0U);
173 VERIFY_NO_ERROR;
174 glBindTexture(GL_TEXTURE_2D, texture_id_);
175 VERIFY_NO_ERROR;
176 glInterleavedArrays(GL_T2F_V3F, 0, vertices);
177 VERIFY_NO_ERROR;
178 glDrawArrays(GL_QUADS, 0, 4);
179 VERIFY_NO_ERROR;
180
181 // Disable 2D textures.
182 glDisable(GL_TEXTURE_2D);
183 VERIFY_NO_ERROR;
184
185 if (IsTransparent()) {
186 // Disable alpha blending.
187 glDisable(GL_BLEND);
188 VERIFY_NO_ERROR;
189 }
190
191 // Draw a rectangle around the update region.
192 if (settings_.show_update_rect && !update_rect_.IsEmpty()) {
193 int left = update_rect_.x;
194 int right = update_rect_.x + update_rect_.width;
195 int top = update_rect_.y;
196 int bottom = update_rect_.y + update_rect_.height;
197
198 #if defined(OS_LINUX)
199 // Shrink the box so that top & right sides are drawn.
200 top += 1;
201 right -= 1;
202 #else
203 // Shrink the box so that left & bottom sides are drawn.
204 left += 1;
205 bottom -= 1;
206 #endif
207
208 glPushAttrib(GL_ALL_ATTRIB_BITS);
209 VERIFY_NO_ERROR
210 glMatrixMode(GL_PROJECTION);
211 VERIFY_NO_ERROR;
212 glPushMatrix();
213 VERIFY_NO_ERROR;
214 glLoadIdentity();
215 VERIFY_NO_ERROR;
216 glOrtho(0, view_width_, view_height_, 0, 0, 1);
217 VERIFY_NO_ERROR;
218
219 glLineWidth(1);
220 VERIFY_NO_ERROR;
221 glColor3f(1.0f, 0.0f, 0.0f);
222 VERIFY_NO_ERROR;
223 // Don't check for errors until glEnd().
224 glBegin(GL_LINE_STRIP);
225 glVertex2i(left, top);
226 glVertex2i(right, top);
227 glVertex2i(right, bottom);
228 glVertex2i(left, bottom);
229 glVertex2i(left, top);
230 glEnd();
231 VERIFY_NO_ERROR;
232
233 glPopMatrix();
234 VERIFY_NO_ERROR;
235 glPopAttrib();
236 VERIFY_NO_ERROR;
237 }
238 }
239
OnPopupShow(CefRefPtr<CefBrowser> browser,bool show)240 void OsrRenderer::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
241 if (!show) {
242 // Clear the popup rectangle.
243 ClearPopupRects();
244 }
245 }
246
OnPopupSize(CefRefPtr<CefBrowser> browser,const CefRect & rect)247 void OsrRenderer::OnPopupSize(CefRefPtr<CefBrowser> browser,
248 const CefRect& rect) {
249 if (rect.width <= 0 || rect.height <= 0)
250 return;
251 original_popup_rect_ = rect;
252 popup_rect_ = GetPopupRectInWebView(original_popup_rect_);
253 }
254
GetPopupRectInWebView(const CefRect & original_rect)255 CefRect OsrRenderer::GetPopupRectInWebView(const CefRect& original_rect) {
256 CefRect rc(original_rect);
257 // if x or y are negative, move them to 0.
258 if (rc.x < 0)
259 rc.x = 0;
260 if (rc.y < 0)
261 rc.y = 0;
262 // if popup goes outside the view, try to reposition origin
263 if (rc.x + rc.width > view_width_)
264 rc.x = view_width_ - rc.width;
265 if (rc.y + rc.height > view_height_)
266 rc.y = view_height_ - rc.height;
267 // if x or y became negative, move them to 0 again.
268 if (rc.x < 0)
269 rc.x = 0;
270 if (rc.y < 0)
271 rc.y = 0;
272 return rc;
273 }
274
ClearPopupRects()275 void OsrRenderer::ClearPopupRects() {
276 popup_rect_.Set(0, 0, 0, 0);
277 original_popup_rect_.Set(0, 0, 0, 0);
278 }
279
OnPaint(CefRefPtr<CefBrowser> browser,CefRenderHandler::PaintElementType type,const CefRenderHandler::RectList & dirtyRects,const void * buffer,int width,int height)280 void OsrRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
281 CefRenderHandler::PaintElementType type,
282 const CefRenderHandler::RectList& dirtyRects,
283 const void* buffer,
284 int width,
285 int height) {
286 if (!initialized_)
287 Initialize();
288
289 if (IsTransparent()) {
290 // Enable alpha blending.
291 glEnable(GL_BLEND);
292 VERIFY_NO_ERROR;
293 }
294
295 // Enable 2D textures.
296 glEnable(GL_TEXTURE_2D);
297 VERIFY_NO_ERROR;
298
299 DCHECK_NE(texture_id_, 0U);
300 glBindTexture(GL_TEXTURE_2D, texture_id_);
301 VERIFY_NO_ERROR;
302
303 if (type == PET_VIEW) {
304 int old_width = view_width_;
305 int old_height = view_height_;
306
307 view_width_ = width;
308 view_height_ = height;
309
310 if (settings_.show_update_rect)
311 update_rect_ = dirtyRects[0];
312
313 glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_);
314 VERIFY_NO_ERROR;
315
316 if (old_width != view_width_ || old_height != view_height_ ||
317 (dirtyRects.size() == 1 &&
318 dirtyRects[0] == CefRect(0, 0, view_width_, view_height_))) {
319 // Update/resize the whole texture.
320 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
321 VERIFY_NO_ERROR;
322 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
323 VERIFY_NO_ERROR;
324 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0,
325 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
326 VERIFY_NO_ERROR;
327 } else {
328 // Update just the dirty rectangles.
329 CefRenderHandler::RectList::const_iterator i = dirtyRects.begin();
330 for (; i != dirtyRects.end(); ++i) {
331 const CefRect& rect = *i;
332 DCHECK(rect.x + rect.width <= view_width_);
333 DCHECK(rect.y + rect.height <= view_height_);
334 glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
335 VERIFY_NO_ERROR;
336 glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
337 VERIFY_NO_ERROR;
338 glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width,
339 rect.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
340 buffer);
341 VERIFY_NO_ERROR;
342 }
343 }
344 } else if (type == PET_POPUP && popup_rect_.width > 0 &&
345 popup_rect_.height > 0) {
346 int skip_pixels = 0, x = popup_rect_.x;
347 int skip_rows = 0, y = popup_rect_.y;
348 int w = width;
349 int h = height;
350
351 // Adjust the popup to fit inside the view.
352 if (x < 0) {
353 skip_pixels = -x;
354 x = 0;
355 }
356 if (y < 0) {
357 skip_rows = -y;
358 y = 0;
359 }
360 if (x + w > view_width_)
361 w -= x + w - view_width_;
362 if (y + h > view_height_)
363 h -= y + h - view_height_;
364
365 // Update the popup rectangle.
366 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
367 VERIFY_NO_ERROR;
368 glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
369 VERIFY_NO_ERROR;
370 glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
371 VERIFY_NO_ERROR;
372 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_BGRA,
373 GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
374 VERIFY_NO_ERROR;
375 }
376
377 // Disable 2D textures.
378 glDisable(GL_TEXTURE_2D);
379 VERIFY_NO_ERROR;
380
381 if (IsTransparent()) {
382 // Disable alpha blending.
383 glDisable(GL_BLEND);
384 VERIFY_NO_ERROR;
385 }
386 }
387
SetSpin(float spinX,float spinY)388 void OsrRenderer::SetSpin(float spinX, float spinY) {
389 spin_x_ = spinX;
390 spin_y_ = spinY;
391 }
392
IncrementSpin(float spinDX,float spinDY)393 void OsrRenderer::IncrementSpin(float spinDX, float spinDY) {
394 spin_x_ -= spinDX;
395 spin_y_ -= spinDY;
396 }
397
398 } // namespace client
399