1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006, 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27 
28 #include "GL/glew.h"
29 
30 #include <SDL/SDL.h>
31 #include <SDL/SDL_opengl.h>
32 
33 #include <algorithm>
34 #include <cmath>
35 #include <cstdio>
36 #include <fstream>
37 #include <functional>
38 #include <iostream>
39 #include <sstream>
40 #include <string>
41 
42 #include "pygame/alphablit.h"
43 #include "systems/base/colour.h"
44 #include "systems/base/graphics_object.h"
45 #include "systems/base/graphics_object_data.h"
46 #include "systems/base/system_error.h"
47 #include "systems/sdl/sdl_graphics_system.h"
48 #include "systems/sdl/sdl_surface.h"
49 #include "systems/sdl/sdl_utils.h"
50 #include "systems/sdl/shaders.h"
51 #include "systems/sdl/texture.h"
52 
53 unsigned int Texture::s_screen_width = 0;
54 unsigned int Texture::s_screen_height = 0;
55 
56 unsigned int Texture::s_upload_buffer_size = 0;
57 std::unique_ptr<char[]> Texture::s_upload_buffer;
58 
59 // -----------------------------------------------------------------------
60 
SetScreenSize(const Size & s)61 void Texture::SetScreenSize(const Size& s) {
62   s_screen_width = s.width();
63   s_screen_height = s.height();
64 }
65 
ScreenHeight()66 int Texture::ScreenHeight() { return s_screen_height; }
67 
68 // -----------------------------------------------------------------------
69 // Texture
70 // -----------------------------------------------------------------------
Texture(SDL_Surface * surface,int x,int y,int w,int h,unsigned int bytes_per_pixel,int byte_order,int byte_type)71 Texture::Texture(SDL_Surface* surface,
72                  int x,
73                  int y,
74                  int w,
75                  int h,
76                  unsigned int bytes_per_pixel,
77                  int byte_order,
78                  int byte_type)
79     : x_offset_(x),
80       y_offset_(y),
81       logical_width_(w),
82       logical_height_(h),
83       total_width_(surface->w),
84       total_height_(surface->h),
85       texture_width_(SafeSize(logical_width_)),
86       texture_height_(SafeSize(logical_height_)),
87       back_texture_id_(0),
88       is_upside_down_(false) {
89   glGenTextures(1, &texture_id_);
90   glBindTexture(GL_TEXTURE_2D, texture_id_);
91   DebugShowGLErrors();
92   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
93   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
94   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
95   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
96 
97   if (w == total_width_ && h == total_height_) {
98     SDL_LockSurface(surface);
99     glTexImage2D(GL_TEXTURE_2D,
100                  0,
101                  bytes_per_pixel,
102                  texture_width_,
103                  texture_height_,
104                  0,
105                  byte_order,
106                  byte_type,
107                  NULL);
108     DebugShowGLErrors();
109 
110     glTexSubImage2D(GL_TEXTURE_2D,
111                     0,
112                     0,
113                     0,
114                     surface->w,
115                     surface->h,
116                     byte_order,
117                     byte_type,
118                     surface->pixels);
119     DebugShowGLErrors();
120 
121     SDL_UnlockSurface(surface);
122   } else {
123     // Cut out the current piece
124     char* pixel_data = uploadBuffer(surface->format->BytesPerPixel * w * h);
125     char* cur_dst_ptr = pixel_data;
126 
127     SDL_LockSurface(surface);
128     {
129       char* cur_src_ptr = (char*)surface->pixels;
130       cur_src_ptr += surface->pitch * y;
131 
132       int row_start = surface->format->BytesPerPixel * x;
133       int subrow_size = surface->format->BytesPerPixel * w;
134       for (int current_row = 0; current_row < h; ++current_row) {
135         memcpy(cur_dst_ptr, cur_src_ptr + row_start, subrow_size);
136         cur_dst_ptr += subrow_size;
137         cur_src_ptr += surface->pitch;
138       }
139     }
140     SDL_UnlockSurface(surface);
141 
142     glTexImage2D(GL_TEXTURE_2D,
143                  0,
144                  bytes_per_pixel,
145                  texture_width_,
146                  texture_height_,
147                  0,
148                  byte_order,
149                  byte_type,
150                  NULL);
151     DebugShowGLErrors();
152 
153     glTexSubImage2D(
154         GL_TEXTURE_2D, 0, 0, 0, w, h, byte_order, byte_type, pixel_data);
155     DebugShowGLErrors();
156   }
157 }
158 
159 // -----------------------------------------------------------------------
160 
Texture(render_to_texture,int width,int height)161 Texture::Texture(render_to_texture, int width, int height)
162     : x_offset_(0),
163       y_offset_(0),
164       logical_width_(width),
165       logical_height_(height),
166       total_width_(width),
167       total_height_(height),
168       texture_width_(0),
169       texture_height_(0),
170       texture_id_(0),
171       back_texture_id_(0),
172       is_upside_down_(true) {
173   glGenTextures(1, &texture_id_);
174   glBindTexture(GL_TEXTURE_2D, texture_id_);
175   DebugShowGLErrors();
176   //  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
177   //  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
178   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
179   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
180 
181   texture_width_ = SafeSize(logical_width_);
182   texture_height_ = SafeSize(logical_height_);
183 
184   // This may fail.
185   glTexImage2D(GL_TEXTURE_2D,
186                0,
187                GL_RGBA,
188                texture_width_,
189                texture_height_,
190                0,
191                GL_RGB,
192                GL_UNSIGNED_BYTE,
193                NULL);
194   DebugShowGLErrors();
195 
196   glCopyTexSubImage2D(
197       GL_TEXTURE_2D, 0, 0, 0, 0, 0, logical_width_, logical_height_);
198   DebugShowGLErrors();
199 }
200 
201 // -----------------------------------------------------------------------
202 
~Texture()203 Texture::~Texture() {
204   glDeleteTextures(1, &texture_id_);
205 
206   if (back_texture_id_)
207     glDeleteTextures(1, &back_texture_id_);
208 
209   DebugShowGLErrors();
210 }
211 
212 // -----------------------------------------------------------------------
213 
uploadBuffer(unsigned int size)214 char* Texture::uploadBuffer(unsigned int size) {
215   if (!s_upload_buffer || size > s_upload_buffer_size) {
216     s_upload_buffer.reset(new char[size]);
217     s_upload_buffer_size = size;
218   }
219 
220   return s_upload_buffer.get();
221 }
222 
223 // -----------------------------------------------------------------------
224 
reupload(SDL_Surface * surface,int offset_x,int offset_y,int x,int y,int w,int h,unsigned int bytes_per_pixel,int byte_order,int byte_type)225 void Texture::reupload(SDL_Surface* surface,
226                        int offset_x,
227                        int offset_y,
228                        int x,
229                        int y,
230                        int w,
231                        int h,
232                        unsigned int bytes_per_pixel,
233                        int byte_order,
234                        int byte_type) {
235   glBindTexture(GL_TEXTURE_2D, texture_id_);
236 
237   if (w == total_width_ && h == total_height_) {
238     SDL_LockSurface(surface);
239 
240     glTexSubImage2D(GL_TEXTURE_2D,
241                     0,
242                     0,
243                     0,
244                     surface->w,
245                     surface->h,
246                     byte_order,
247                     byte_type,
248                     surface->pixels);
249     DebugShowGLErrors();
250 
251     SDL_UnlockSurface(surface);
252   } else {
253     // Cut out the current piece
254     char* pixel_data = uploadBuffer(surface->format->BytesPerPixel * w * h);
255     char* cur_dst_ptr = pixel_data;
256 
257     SDL_LockSurface(surface);
258     {
259       char* cur_src_ptr = (char*)surface->pixels;
260       cur_src_ptr += surface->pitch * y;
261 
262       int row_start = surface->format->BytesPerPixel * x;
263       int subrow_size = surface->format->BytesPerPixel * w;
264       for (int current_row = 0; current_row < h; ++current_row) {
265         memcpy(cur_dst_ptr, cur_src_ptr + row_start, subrow_size);
266         cur_dst_ptr += subrow_size;
267         cur_src_ptr += surface->pitch;
268       }
269     }
270     SDL_UnlockSurface(surface);
271 
272     glTexSubImage2D(GL_TEXTURE_2D,
273                     0,
274                     offset_x,
275                     offset_y,
276                     w,
277                     h,
278                     byte_order,
279                     byte_type,
280                     pixel_data);
281     DebugShowGLErrors();
282   }
283 }
284 
285 // -----------------------------------------------------------------------
286 
readTextFile(const std::string & file)287 std::string readTextFile(const std::string& file) {
288   std::ifstream ifs(file.c_str());
289   if (!ifs) {
290     std::ostringstream oss;
291     oss << "Can't open text file: " << file;
292     throw SystemError(oss.str());
293   }
294 
295   std::string out, line;
296   while (std::getline(ifs, line)) {
297     out += line;
298     out += '\n';
299   }
300 
301   return out;
302 }
303 
304 // -----------------------------------------------------------------------
305 
306 // This is really broken and brain dead.
RenderToScreen(const Rect & src,const Rect & dst,int opacity)307 void Texture::RenderToScreen(const Rect& src, const Rect& dst, int opacity) {
308   int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
309   int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
310   if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
311     return;
312 
313   // For the time being, we are dumb and assume that it's one texture
314 
315   float thisx1 = float(x1) / texture_width_;
316   float thisy1 = float(y1) / texture_height_;
317   float thisx2 = float(x2) / texture_width_;
318   float thisy2 = float(y2) / texture_height_;
319 
320   if (is_upside_down_) {
321     thisy1 = float(logical_height_ - y1) / texture_height_;
322     thisy2 = float(logical_height_ - y2) / texture_height_;
323   }
324 
325   glBindTexture(GL_TEXTURE_2D, texture_id_);
326 
327   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
328   glBegin(GL_QUADS);
329   {
330     glColor4ub(255, 255, 255, opacity);
331     glTexCoord2f(thisx1, thisy1);
332     glVertex2i(fdx1, fdy1);
333     glTexCoord2f(thisx2, thisy1);
334     glVertex2i(fdx2, fdy1);
335     glTexCoord2f(thisx2, thisy2);
336     glVertex2i(fdx2, fdy2);
337     glTexCoord2f(thisx1, thisy2);
338     glVertex2i(fdx1, fdy2);
339   }
340   glEnd();
341   glBlendFunc(GL_ONE, GL_ZERO);
342 }
343 
344 // -----------------------------------------------------------------------
345 
346 // TODO(erg): A function of this hairiness needs super more amounts of
347 // documentation.
RenderToScreenAsColorMask(const Rect & src,const Rect & dst,const RGBAColour & rgba,int filter)348 void Texture::RenderToScreenAsColorMask(const Rect& src,
349                                         const Rect& dst,
350                                         const RGBAColour& rgba,
351                                         int filter) {
352   if (filter == 0) {
353     if (GLEW_ARB_fragment_shader && GLEW_ARB_multitexture) {
354       render_to_screen_as_colour_mask_subtractive_glsl(src, dst, rgba);
355     } else {
356       render_to_screen_as_colour_mask_subtractive_fallback(src, dst, rgba);
357     }
358   } else {
359     render_to_screen_as_colour_mask_additive(src, dst, rgba);
360   }
361 }
362 
363 // -----------------------------------------------------------------------
364 
render_to_screen_as_colour_mask_subtractive_glsl(const Rect & src,const Rect & dst,const RGBAColour & rgba)365 void Texture::render_to_screen_as_colour_mask_subtractive_glsl(
366     const Rect& src,
367     const Rect& dst,
368     const RGBAColour& rgba) {
369   int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
370   int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
371   if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
372     return;
373 
374   float thisx1 = float(x1) / texture_width_;
375   float thisy1 = float(y1) / texture_height_;
376   float thisx2 = float(x2) / texture_width_;
377   float thisy2 = float(y2) / texture_height_;
378 
379   if (is_upside_down_) {
380     thisy1 = float(logical_height_ - y1) / texture_height_;
381     thisy2 = float(logical_height_ - y2) / texture_height_;
382   }
383 
384   // If we haven't already, allocate video memory for the back
385   // texture.
386   //
387   // NOTE: Does this code deal with changing the dimensions of the
388   // text box? Does it matter?
389   if (back_texture_id_ == 0) {
390     glGenTextures(1, &back_texture_id_);
391     glBindTexture(GL_TEXTURE_2D, back_texture_id_);
392     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
393     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
394 
395     // Generate this texture
396     glTexImage2D(GL_TEXTURE_2D,
397                  0,
398                  GL_RGBA,
399                  texture_width_,
400                  texture_height_,
401                  0,
402                  GL_RGB,
403                  GL_UNSIGNED_BYTE,
404                  NULL);
405     DebugShowGLErrors();
406   }
407 
408   // Copy the current value of the region where we're going to render
409   // to a texture for input to the shader
410   glBindTexture(GL_TEXTURE_2D, back_texture_id_);
411   int ystart = int(s_screen_height - fdy1 - (fdy2 - fdy1));
412   int idx1 = int(fdx1);
413   glCopyTexSubImage2D(
414       GL_TEXTURE_2D, 0, 0, 0, idx1, ystart, texture_width_, texture_height_);
415   DebugShowGLErrors();
416 
417   glUseProgramObjectARB(Shaders::getColorMaskProgram());
418 
419   // Put the back_texture in texture slot zero and set this to be the
420   // texture "current_values" in the above shader program.
421   glActiveTextureARB(GL_TEXTURE0_ARB);
422   glEnable(GL_TEXTURE_2D);
423   glBindTexture(GL_TEXTURE_2D, back_texture_id_);
424   glUniform1iARB(Shaders::getColorMaskUniformCurrentValues(), 0);
425 
426   // Put the mask in texture slot one and set this to be the
427   // texture "mask" in the above shader program.
428   glActiveTextureARB(GL_TEXTURE1_ARB);
429   glEnable(GL_TEXTURE_2D);
430   glBindTexture(GL_TEXTURE_2D, texture_id_);
431   glUniform1iARB(Shaders::getColorMaskUniformMask(), 1);
432 
433   glDisable(GL_BLEND);
434 
435   glBegin(GL_QUADS);
436   {
437     glColorRGBA(rgba);
438     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx1, thisy2);
439     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx1, thisy1);
440     glVertex2i(fdx1, fdy1);
441     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx2, thisy2);
442     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx2, thisy1);
443     glVertex2i(fdx2, fdy1);
444     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx2, thisy1);
445     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx2, thisy2);
446     glVertex2i(fdx2, fdy2);
447     glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx1, thisy1);
448     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx1, thisy2);
449     glVertex2i(fdx1, fdy2);
450   }
451   glEnd();
452 
453   glActiveTextureARB(GL_TEXTURE1_ARB);
454   glDisable(GL_TEXTURE_2D);
455   glActiveTextureARB(GL_TEXTURE0_ARB);
456 
457   glUseProgramObjectARB(0);
458   glEnable(GL_BLEND);
459   glBlendFunc(GL_ONE, GL_ZERO);
460 }
461 
462 // -----------------------------------------------------------------------
463 
464 // This fallback does not accurately render the scene according to
465 // standard RealLive. This only negatively shades according to the
466 // alpha value, ignoring the rest of the \#WINDOW_ATTR colour.
467 //
468 // This will probably only occur with mesa software and people with
469 // graphics cards > 5 years old.
render_to_screen_as_colour_mask_subtractive_fallback(const Rect & src,const Rect & dst,const RGBAColour & rgba)470 void Texture::render_to_screen_as_colour_mask_subtractive_fallback(
471     const Rect& src,
472     const Rect& dst,
473     const RGBAColour& rgba) {
474   int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
475   int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
476   if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
477     return;
478 
479   float thisx1 = float(x1) / texture_width_;
480   float thisy1 = float(y1) / texture_height_;
481   float thisx2 = float(x2) / texture_width_;
482   float thisy2 = float(y2) / texture_height_;
483 
484   if (is_upside_down_) {
485     thisy1 = float(logical_height_ - y1) / texture_height_;
486     thisy2 = float(logical_height_ - y2) / texture_height_;
487   }
488 
489   // First draw the mask
490   glBindTexture(GL_TEXTURE_2D, texture_id_);
491 
492   /// SERIOUS WTF: gl_blend_func_separate causes a segmentation fault
493   /// under the current i810 driver for linux.
494   //  glBlendFuncSeparate(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
495   //                      GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
496   glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA);
497 
498   glBegin(GL_QUADS);
499   {
500     glColorRGBA(rgba);
501     glTexCoord2f(thisx1, thisy1);
502     glVertex2i(fdx1, fdy1);
503     glTexCoord2f(thisx2, thisy1);
504     glVertex2i(fdx2, fdy1);
505     glTexCoord2f(thisx2, thisy2);
506     glVertex2i(fdx2, fdy2);
507     glTexCoord2f(thisx1, thisy2);
508     glVertex2i(fdx1, fdy2);
509   }
510   glEnd();
511 
512   glBlendFunc(GL_ONE, GL_ZERO);
513 }
514 
515 // -----------------------------------------------------------------------
516 
render_to_screen_as_colour_mask_additive(const Rect & src,const Rect & dst,const RGBAColour & rgba)517 void Texture::render_to_screen_as_colour_mask_additive(const Rect& src,
518                                                        const Rect& dst,
519                                                        const RGBAColour& rgba) {
520   int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
521   int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
522   if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
523     return;
524 
525   float thisx1 = float(x1) / texture_width_;
526   float thisy1 = float(y1) / texture_height_;
527   float thisx2 = float(x2) / texture_width_;
528   float thisy2 = float(y2) / texture_height_;
529 
530   if (is_upside_down_) {
531     thisy1 = float(logical_height_ - y1) / texture_height_;
532     thisy2 = float(logical_height_ - y2) / texture_height_;
533   }
534 
535   // First draw the mask
536   glBindTexture(GL_TEXTURE_2D, texture_id_);
537   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
538 
539   glBegin(GL_QUADS);
540   {
541     glColorRGBA(rgba);
542     glTexCoord2i(thisx1, thisy1);
543     glVertex2f(fdx1, fdy1);
544     glTexCoord2i(thisx2, thisy1);
545     glVertex2f(fdx2, fdy1);
546     glTexCoord2i(thisx2, thisy2);
547     glVertex2f(fdx2, fdy2);
548     glTexCoord2i(thisx1, thisy2);
549     glVertex2f(fdx1, fdy2);
550   }
551   glEnd();
552 
553   glBlendFunc(GL_ONE, GL_ZERO);
554 }
555 
556 // -----------------------------------------------------------------------
557 
RenderToScreen(const Rect & src,const Rect & dst,const int opacity[4])558 void Texture::RenderToScreen(const Rect& src,
559                              const Rect& dst,
560                              const int opacity[4]) {
561   // For the time being, we are dumb and assume that it's one texture
562   int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
563   int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
564   if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
565     return;
566 
567   float thisx1 = float(x1) / texture_width_;
568   float thisy1 = float(y1) / texture_height_;
569   float thisx2 = float(x2) / texture_width_;
570   float thisy2 = float(y2) / texture_height_;
571 
572   glBindTexture(GL_TEXTURE_2D, texture_id_);
573 
574   // Blend when we have less opacity
575   if (std::find_if(opacity, opacity + 4, [](int o) { return o < 255; }) !=
576       opacity + 4) {
577     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
578   }
579 
580   glBegin(GL_QUADS);
581   {
582     glColor4ub(255, 255, 255, opacity[0]);
583     glTexCoord2f(thisx1, thisy1);
584     glVertex2i(fdx1, fdy1);
585     glColor4ub(255, 255, 255, opacity[1]);
586     glTexCoord2f(thisx2, thisy1);
587     glVertex2i(fdx2, fdy1);
588     glColor4ub(255, 255, 255, opacity[2]);
589     glTexCoord2f(thisx2, thisy2);
590     glVertex2i(fdx2, fdy2);
591     glColor4ub(255, 255, 255, opacity[3]);
592     glTexCoord2f(thisx1, thisy2);
593     glVertex2i(fdx1, fdy2);
594   }
595   glEnd();
596   glBlendFunc(GL_ONE, GL_ZERO);
597 }
598 
599 // -----------------------------------------------------------------------
600 
RenderToScreenAsObject(const GraphicsObject & go,const SDLSurface & surface,const Rect & srcRect,const Rect & dstRect,int alpha)601 void Texture::RenderToScreenAsObject(const GraphicsObject& go,
602                                      const SDLSurface& surface,
603                                      const Rect& srcRect,
604                                      const Rect& dstRect,
605                                      int alpha) {
606   // This all needs to be pushed up out of the rendering code and down into
607   // either GraphicsObject or the individual GraphicsObjectData subclasses.
608 
609   // TODO(erg): Temporary hack while I wait to convert all of this machinery to
610   // Rects.
611   int xSrc1 = srcRect.x();
612   int ySrc1 = srcRect.y();
613   int xSrc2 = srcRect.x2();
614   int ySrc2 = srcRect.y2();
615 
616   int fdx1 = dstRect.x(), fdy1 = dstRect.y(), fdx2 = dstRect.x2(),
617       fdy2 = dstRect.y2();
618   if (!filterCoords(xSrc1, ySrc1, xSrc2, ySrc2, fdx1, fdy1, fdx2, fdy2)) {
619     return;
620   }
621 
622   // Convert the pixel coordinates into [0,1) texture coordinates
623   float thisx1 = float(xSrc1) / texture_width_;
624   float thisy1 = float(ySrc1) / texture_height_;
625   float thisx2 = float(xSrc2) / texture_width_;
626   float thisy2 = float(ySrc2) / texture_height_;
627 
628   glBindTexture(GL_TEXTURE_2D, texture_id_);
629 
630   glPushMatrix();
631   {
632     // Translate to where the object starts.
633     glTranslatef(fdx1, fdy1, 0);
634 
635     int width = fdx2 - fdx1;
636     int height = fdy2 - fdy1;
637 
638     // Rotate the texture around the point (origin + position + reporigin)
639     float x_rep = (width / 2.0f) + go.rep_origin_x();
640     float y_rep = (height / 2.0f) + go.rep_origin_y();
641 
642     glTranslatef(x_rep, y_rep, 0);
643     glRotatef(float(go.rotation()) / 10, 0, 0, 1);
644     glTranslatef(-x_rep, -y_rep, 0);
645 
646     // RealLive has its own complex shading/tinting system which we implement
647     // in a shader if available. It's costly enough that we make sure we need
648     // to use it.
649     bool using_shader = false;
650     if ((go.light() || go.tint() != RGBColour::Black() ||
651          go.colour() != RGBAColour::Clear() || go.mono() || go.invert()) &&
652         GLEW_ARB_fragment_shader && GLEW_ARB_multitexture) {
653       // Image
654       glActiveTexture(GL_TEXTURE0_ARB);
655       glEnable(GL_TEXTURE_2D);
656       glBindTexture(GL_TEXTURE_2D, texture_id_);
657       glUseProgramObjectARB(Shaders::GetObjectProgram());
658       glUniform1iARB(Shaders::GetObjectUniformImage(), 0);
659 
660       // Colour/Tint/Etc.
661       Shaders::loadObjectUniformFromGraphicsObject(go);
662 
663       // Alpha.
664       glUniform1fARB(Shaders::GetObjectUniformAlpha(), alpha / 255.0f);
665 
666       // Our final blending color has to be all white here.
667       using_shader = true;
668     } else {
669       // The shader takes care of the alpha for us, so we need to specify when
670       // not using it.
671       glColor4ub(255, 255, 255, alpha);
672     }
673 
674     // Make this so that when we have composite 1, we're doing a pure
675     // additive blend, (ignoring the alpha channel?)
676     switch (go.composite_mode()) {
677       case 0:
678         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
679         break;
680       case 1:
681         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
682         break;
683       case 2: {
684         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
685         glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
686         break;
687       }
688       default: {
689         std::ostringstream oss;
690         oss << "Invalid composite_mode in render: " << go.composite_mode();
691         throw SystemError(oss.str());
692       }
693     }
694 
695     glBegin(GL_QUADS);
696     {
697       glTexCoord2f(thisx1, thisy1);
698       glVertex2i(0, 0);
699       glTexCoord2f(thisx2, thisy1);
700       glVertex2i(width, 0);
701       glTexCoord2f(thisx2, thisy2);
702       glVertex2i(width, height);
703       glTexCoord2f(thisx1, thisy2);
704       glVertex2i(0, height);
705     }
706     glEnd();
707 
708     if (using_shader) {
709       glUseProgramObjectARB(0);
710     }
711 
712     glBlendEquation(GL_FUNC_ADD);
713     glBlendFunc(GL_ONE, GL_ZERO);
714   }
715   glPopMatrix();
716 
717   DebugShowGLErrors();
718 }
719 
720 // -----------------------------------------------------------------------
721 
our_round(float r)722 static float our_round(float r) {
723   return (r > 0.0f) ? floor(r + 0.5f) : ceil(r - 0.5f);
724 }
725 
filterCoords(int & x1,int & y1,int & x2,int & y2,int & dx1,int & dy1,int & dx2,int & dy2)726 bool Texture::filterCoords(int& x1,
727                            int& y1,
728                            int& x2,
729                            int& y2,
730                            int& dx1,
731                            int& dy1,
732                            int& dx2,
733                            int& dy2) {
734   // POINT
735   using std::max;
736   using std::min;
737 
738   // Input: raw image coordinates
739   // Output: false if this doesn't intersect with the texture piece we hold.
740   //         true otherwise, and set the local coordinates
741   int w1 = x2 - x1;
742   int h1 = y2 - y1;
743 
744   // First thing we do is an intersection test to see if this input
745   // range intersects the virtual range this Texture object holds.
746   //
747   /// @bug s/>/>=/?
748   if (x1 + w1 >= x_offset_ && x1 < x_offset_ + logical_width_ &&
749       y1 + h1 >= y_offset_ && y1 < y_offset_ + logical_height_) {
750     // Do an intersection test in terms of the virtual coordinates
751     int virX = max(x1, x_offset_);
752     int virY = max(y1, y_offset_);
753     int w = min(x1 + w1, x_offset_ + logical_width_) - max(x1, x_offset_);
754     int h = min(y1 + h1, y_offset_ + logical_height_) - max(y1, y_offset_);
755 
756     // Adjust the destination coordinates
757     int dx_width = dx2 - dx1;
758     int dy_height = dy2 - dy1;
759     float dx1Off = (virX - x1) / float(w1);
760     dx1 = our_round(dx1 + (dx_width * dx1Off));
761     float dx2Off = w / float(w1);
762     dx2 = our_round(dx1 + (dx_width * dx2Off));
763     float dy1Off = (virY - y1) / float(h1);
764     dy1 = our_round(dy1 + (dy_height * dy1Off));
765     float dy2Off = h / float(h1);
766     dy2 = our_round(dy1 + (dy_height * dy2Off));
767 
768     // Output the source intersection in real (instead of
769     // virtual) coordinates
770     x1 = virX - x_offset_;
771     x2 = x1 + w;
772     y1 = virY - y_offset_;
773     y2 = y1 + h;
774 
775     return true;
776   }
777 
778   return false;
779 }
780