• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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