README: explain further musl-specific tweaks
[rofl0r-df-libgraphics.git] / g_src / graphics.cpp
blobe255ea843a18aeb61e500a174cedf0622746cf68
1 #include "platform.h"
2 #include <string.h>
3 #include <math.h>
4 #include <iosfwd>
5 #include <iostream>
6 #include <ios>
7 #include <streambuf>
8 #include <istream>
9 #include <ostream>
10 #include <iomanip>
11 #include <sstream>
12 #include <cstdlib>
13 #include <fstream>
14 #include <zlib.h>
15 #include <cassert>
17 #include "svector.h"
18 #include "ttf_manager.hpp"
20 #ifdef WIN32
23 #ifndef INTEGER_TYPES
24 #define INTEGER_TYPES
25 typedef short int16_t;
26 typedef int int32_t;
27 typedef long long int64_t;
28 typedef unsigned short uint16_t;
29 typedef unsigned int uint32_t;
30 typedef unsigned long long uint64_t;
31 #endif
34 typedef int32_t VIndex;
35 typedef int32_t Ordinal;
37 #endif
39 #include "random.h"
41 #include "endian.h"
43 #include "files.h"
45 #include "enabler.h"
47 #include "textlines.h"
49 #include "find_files.h"
51 #include "basics.h"
53 #include "g_basics.h"
55 #include "texture_handler.h"
57 #include "graphics.h"
59 #include "music_and_sound_g.h"
61 #include "init.h"
63 #include "interface.h"
65 #include "curses.h"
67 using namespace std;
70 #pragma comment( lib, "opengl32.lib" ) // Search For OpenGL32.lib While Linking
71 #pragma comment( lib, "glu32.lib" ) // Search For GLu32.lib While Linking
73 extern enablerst enabler;
74 extern texture_handlerst texture;
75 graphicst gps;
76 extern interfacest gview;
78 extern string errorlog_prefix;
80 void process_object_lines(textlinesst &lines,string &chktype,string &graphics_dir);
82 // Add, then increment to the (possible) PBO alignment requirement
83 static void align(size_t &sz, off_t inc) {
84 sz += inc;
85 while (sz%64) sz++; // So.. tired.. FIXME.
88 void graphicst::resize(int x, int y) {
89 dimx = x; dimy = y;
90 init.display.grid_x = x;
91 init.display.grid_y = y;
92 setclipping(0, x-1, 0, y-1);
93 force_full_display_count++;
94 screen_limit = screen + dimx * dimy * 4;
97 void graphicst::addcoloredst(const char *str,const char *colorstr)
99 const int slen = strlen(str);
100 int s;
101 for(s=0; s < slen && screenx < init.display.grid_x; s++)
103 if(screenx<0)
105 s-=screenx;
106 screenx=0;
107 if (s >= slen) break;
110 changecolor((colorstr[s] & 7),((colorstr[s] & 56))>>3,((colorstr[s] & 64))>>6);
111 addchar(str[s]);
115 static list<ttf_id> ttfstr;
117 static void abbreviate_string_helper_hackaroundmissingcode(string &str, int len) {
118 if(str.length()>=2)
120 if((str[0]=='A'||str[0]=='a')&&
121 str[1]==' ')
123 str.erase(str.begin()+1);
124 str.erase(str.begin());
126 if(str.length()<=len)return;
129 if(str.length()>=3)
131 if((str[0]=='A'||str[0]=='a')&&
132 (str[1]=='N'||str[1]=='n')&&
133 str[2]==' ')
135 str.erase(str.begin()+2);
136 str.erase(str.begin()+1);
137 str.erase(str.begin());
139 if(str.length()<=len)return;
142 if(str.length()>=4)
144 if((str[0]=='T'||str[0]=='t')&&
145 (str[1]=='H'||str[1]=='h')&&
146 (str[2]=='E'||str[2]=='e')&&
147 str[3]==' ')
149 str.erase(str.begin()+3);
150 str.erase(str.begin()+2);
151 str.erase(str.begin()+1);
152 str.erase(str.begin());
154 if(str.length()<=len)return;
160 int32_t l;
161 for(l=(int32_t)str.length()-1;l>=1;l--)
163 if(str[l-1]==' ')continue;
165 if(str[l]=='a'||
166 str[l]=='e'||
167 str[l]=='i'||
168 str[l]=='o'||
169 str[l]=='u'||
170 str[l]=='A'||
171 str[l]=='E'||
172 str[l]=='I'||
173 str[l]=='O'||
174 str[l]=='U')
176 str.erase(str.begin()+l);
177 if(str.length()<=len)return;
181 if(str.length()>len)str.resize(len);
185 static void abbreviate_string_hackaroundmissingcode(string &str, int32_t len)
187 if (ttf_manager.ttf_active()) {
188 // We'll need to use TTF-aware text shrinking.
189 while (ttf_manager.size_text(str) > len)
190 abbreviate_string_helper_hackaroundmissingcode(str, str.length() - 1);
191 } else if(str.length()>len){
192 // 1 letter = 1 tile.
193 abbreviate_string_helper_hackaroundmissingcode(str, len);
198 void graphicst::addst(const string &str_orig, justification just, int space)
200 if (!str_orig.size())
201 return;
202 string str = str_orig;
203 if (space)
204 abbreviate_string_hackaroundmissingcode(str, space);
205 if (just == not_truetype || !ttf_manager.ttf_active()) {
206 int s;
207 for(s=0;s<str.length()&&screenx<init.display.grid_x;s++)
209 if(screenx<0)
211 s-=screenx;
212 screenx=0;
213 if(s>=str.length())break;
216 addchar(str[s]);
218 } else {
219 // Truetype
220 if (str.size() > 2 && str[0] == ':' && str[1] == ' ')
221 str[1] = '\t'; // EVIL HACK
222 struct ttf_id id = {str, screenf, screenb, screenbright};
223 ttfstr.push_back(id);
224 // if (str.size() == 80) {
225 // cout << "(" << int(str[0]) << ") ";
226 // }
227 // cout << screeny << "," << str.size() << ":" << str;
228 // if (just == justify_cont)
229 // cout << "|";
230 // else
231 // cout << endl;
232 if (just == justify_cont)
233 return; // More later
234 // This string is done. Time to render.
235 ttf_details details = ttf_manager.get_handle(ttfstr, just);
236 const int handle = details.handle;
237 const int offset = details.offset;
238 int width = details.width;
239 const int ourx = screenx + offset;
240 unsigned int * const s = ((unsigned int*)screen + ourx*dimy + screeny);
241 if (s < (unsigned int*)screen_limit)
242 s[0] = (((unsigned int)GRAPHICSTYPE_TTF) << 24) | handle;
243 // Also set the other tiles this text covers, but don't write past the end.
244 if (width + ourx >= dimx)
245 width = dimx - ourx - 1;
246 for (int x = 1; x < width; ++x)
247 s[x * dimy] = (((unsigned int)GRAPHICSTYPE_TTFCONT) << 24) | handle;
248 // Clean up, prepare for next string.
249 screenx = ourx + width;
250 ttfstr.clear();
254 void graphicst::erasescreen_clip()
256 changecolor(0,0,0);
257 short x2,y2;
258 for(x2=clipx[0];x2<=clipx[1];x2++)
260 for(y2=clipy[0];y2<=clipy[1];y2++)
262 locate(y2,x2);
263 addchar(' ');
268 void graphicst::erasescreen_rect(int x1, int x2, int y1, int y2)
270 changecolor(0,0,0);
271 for (int x = x1; x <= x2; x++) {
272 for (int y = y1; y <= y2; y++) {
273 locate(y, x);
274 addchar(' ');
279 void graphicst::erasescreen()
281 memset(screen, 0, dimx*dimy*4);
283 memset(screentexpos, 0, dimx*dimy*sizeof(long));
286 void graphicst::setclipping(long x1,long x2,long y1,long y2)
288 if(x1<0)x1=0;
289 if(x2>init.display.grid_x-1)x2=init.display.grid_x-1;
290 if(y1<0)y1=0;
291 if(y2>init.display.grid_y-1)y2=init.display.grid_y-1;
293 clipx[0]=x1;
294 clipx[1]=x2;
295 clipy[0]=y1;
296 clipy[1]=y2;
299 void graphicst::dim_colors(long x,long y,char dim)
301 if(x>=clipx[0]&&x<=clipx[1]&&
302 y>=clipy[0]&&y<=clipy[1])
304 switch(dim)
306 case 4:
307 switch(screen[x*dimy*4 + y*4 + 2])
309 case 4:
310 case 5:
311 case 6:
312 screen[x*dimy*4 + y*4 + 2]=1;
313 break;
314 case 2:
315 case 7:
316 screen[x*dimy*4 + y*4 + 2]=3;
317 break;
319 switch(screen[x*dimy*4 + y*4 + 1])
321 case 4:
322 case 5:
323 case 6:
324 screen[x*dimy*4 + y*4 + 1]=1;
325 break;
326 case 2:
327 case 7:
328 screen[x*dimy*4 + y*4 + 1]=3;
329 break;
331 if(screen[x*dimy*4 + y*4 + 1]==screen[x*dimy*4 + y*4 + 2])screen[x*dimy*4 + y*4 + 1]=0;
332 screen[x*dimy*4 + y*4 + 3]=0;
333 if(screen[x*dimy*4 + y*4 + 1]==0&&screen[x*dimy*4 + y*4 + 2]==0&&screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 3]=1;
334 break;
335 case 3:
336 switch(screen[x*dimy*4 + y*4 + 2])
338 case 4:
339 case 5:
340 screen[x*dimy*4 + y*4 + 2]=6;
341 break;
342 case 2:
343 case 7:
344 screen[x*dimy*4 + y*4 + 2]=3;
345 break;
347 switch(screen[x*dimy*4 + y*4 + 1])
349 case 1:
350 screen[x*dimy*4 + y*4 + 3]=0;
351 break;
352 case 4:
353 case 5:
354 screen[x*dimy*4 + y*4 + 1]=6;
355 break;
356 case 2:
357 screen[x*dimy*4 + y*4 + 1]=3;
358 break;
359 case 7:
360 screen[x*dimy*4 + y*4 + 1]=3;
361 break;
363 if(screen[x*dimy*4 + y*4 + 1]!=7)screen[x*dimy*4 + y*4 + 3]=0;
364 if(screen[x*dimy*4 + y*4 + 1]==screen[x*dimy*4 + y*4 + 2]&&
365 screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 1]=0;
366 if(screen[x*dimy*4 + y*4 + 1]==0&&screen[x*dimy*4 + y*4 + 2]==0&&screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 3]=1;
367 break;
368 case 2:
369 switch(screen[x*dimy*4 + y*4 + 2])
371 case 4:
372 case 5:
373 screen[x*dimy*4 + y*4 + 2]=6;
374 break;
376 switch(screen[x*dimy*4 + y*4 + 1])
378 case 4:
379 case 5:
380 screen[x*dimy*4 + y*4 + 1]=6;
381 break;
383 if(screen[x*dimy*4 + y*4 + 1]!=7)screen[x*dimy*4 + y*4 + 3]=0;
384 if(screen[x*dimy*4 + y*4 + 1]==screen[x*dimy*4 + y*4 + 2]&&
385 screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 1]=0;
386 if(screen[x*dimy*4 + y*4 + 1]==0&&screen[x*dimy*4 + y*4 + 2]==0&&screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 3]=1;
387 break;
388 case 1:
389 if(screen[x*dimy*4 + y*4 + 1]!=7)screen[x*dimy*4 + y*4 + 3]=0;
390 if(screen[x*dimy*4 + y*4 + 1]==screen[x*dimy*4 + y*4 + 2]&&
391 screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 1]=0;
392 if(screen[x*dimy*4 + y*4 + 1]==0&&screen[x*dimy*4 + y*4 + 2]==0&&screen[x*dimy*4 + y*4 + 3]==0)screen[x*dimy*4 + y*4 + 3]=1;
393 break;
398 void graphicst::rain_color_square(long x,long y)
400 if(x>=clipx[0]&&x<=clipx[1]&&
401 y>=clipy[0]&&y<=clipy[1])
403 screen[x*dimy*4 + y*4 + 1]=1;
404 screen[x*dimy*4 + y*4 + 2]=0;
405 screen[x*dimy*4 + y*4 + 3]=1;
409 void graphicst::snow_color_square(long x,long y)
411 if(x>=clipx[0]&&x<=clipx[1]&&
412 y>=clipy[0]&&y<=clipy[1])
414 screen[x*dimy*4 + y*4 + 1]=7;
415 screen[x*dimy*4 + y*4 + 2]=0;
416 screen[x*dimy*4 + y*4 + 3]=1;
420 void graphicst::color_square(long x,long y,unsigned char f,unsigned char b,unsigned char br)
422 if(x>=clipx[0]&&x<=clipx[1]&&
423 y>=clipy[0]&&y<=clipy[1])
425 screen[x*dimy*4 + y*4 + 1]=f;
426 screen[x*dimy*4 + y*4 + 2]=b;
427 screen[x*dimy*4 + y*4 + 3]=br;
431 void graphicst::prepare_graphics(string &src_dir)
433 if(!init.display.flag.has_flag(INIT_DISPLAY_FLAG_USE_GRAPHICS))return;
435 texture.clean();
437 //GET READY TO LOAD
438 svector<char *> processfilename;
439 long f;
440 textlinesst setuplines;
441 char str[400];
443 //LOAD THE OBJECT FILES UP INTO MEMORY
444 //MUST INSURE THAT THEY ARE LOADED IN THE PROPER ORDER, IN CASE THEY REFER TO EACH OTHER
445 string chk=src_dir;
446 chk+="graphics/graphics_*";
447 #ifdef WIN32
448 chk+=".*";
449 #endif
450 find_files_by_pattern_with_exception(chk.c_str(),processfilename,"text");
452 string chktype="GRAPHICS";
453 for(f=0;f<processfilename.size();f++)
455 strcpy(str,src_dir.c_str());
456 strcat(str,"graphics/");
457 strcat(str,processfilename[f]);
458 setuplines.load_raw_to_lines(str);
460 errorlog_prefix="*** Error(s) found in the file \"";
461 errorlog_prefix+=str;
462 errorlog_prefix+='\"';
463 process_object_lines(setuplines,chktype,src_dir);
464 errorlog_prefix.clear();
467 delete[] processfilename[f];
469 processfilename.clear();
471 enabler.reset_textures();
474 void graphicst::add_tile(long texp,char addcolor)
476 if(screenx>=clipx[0]&&screenx<=clipx[1]&&
477 screeny>=clipy[0]&&screeny<=clipy[1])
479 screentexpos[screenx*dimy + screeny]=texp;
480 screentexpos_addcolor[screenx*dimy + screeny]=addcolor;
481 screentexpos_grayscale[screenx*dimy + screeny]=0;
485 void graphicst::add_tile_grayscale(long texp,char cf,char cbr)
487 if(screenx>=clipx[0]&&screenx<=clipx[1]&&
488 screeny>=clipy[0]&&screeny<=clipy[1])
490 screentexpos[screenx*dimy + screeny]=texp;
491 screentexpos_addcolor[screenx*dimy + screeny]=0;
492 screentexpos_grayscale[screenx*dimy + screeny]=1;
493 screentexpos_cf[screenx*dimy + screeny]=cf;
494 screentexpos_cbr[screenx*dimy + screeny]=cbr;
498 void graphicst::draw_border(int x1, int x2, int y1, int y2) {
499 // Upper and lower
500 for (int x = x1; x <= x2; x++) {
501 locate(y1, x);
502 addchar(' ');
503 locate(y2, x);
504 addchar(' ');
506 // Left and right
507 for (int y = y1+1; y < y2; y++) {
508 locate(y, x1);
509 addchar(' ');
510 locate(y, x2);
511 addchar(' ');
515 void graphicst::get_mouse_text_coords(int32_t &mx, int32_t &my) {
516 mx = mouse_x; my = mouse_y;
519 void render_things()
521 //GRAB CURRENT SCREEN AT THE END OF THE LIST
522 viewscreenst *currentscreen=&gview.view;
523 while(currentscreen->child!=NULL)currentscreen=currentscreen->child;
525 //NO INTERFACE LEFT, LEAVE
526 if(currentscreen==&gview.view)return;
528 if(currentscreen->breakdownlevel==INTERFACE_BREAKDOWN_NONE)
530 currentscreen->render();
532 else gps.erasescreen();
534 // Render REC when recording macros. Definitely want this screen-specific. Or do we?
535 const Time now = SDL_GetTicks();
536 if (enabler.is_recording() && now % 1000 > 500) {
537 gps.locate(0, 20);
538 gps.changecolor(4,1,1);
539 gps.addst("REC");
541 // Render PLAY when playing a macro
542 if (enabler.is_macro_playing() && now % 1000 <= 500) {
543 gps.locate(0,20);
544 gps.changecolor(2,1,1);
545 gps.addst("PLAY");
547 // Render # <i> when building a repetition prefix
548 if (enabler.prefix_building()) {
549 gps.locate(0,20);
550 gps.changecolor(3,1,1);
551 gps.addst("#" + enabler.prefix());
553 if (gps.display_frames) {
554 ostringstream fps_stream;
555 fps_stream << "FPS: " << setw(3) << enabler.calculate_fps() << " (" << enabler.calculate_gfps() << ")";
556 string fps = fps_stream.str();
557 gps.changecolor(7,3,1);
558 static gps_locator fps_locator(0, 25);
559 fps_locator(fps.size());
560 gps.addst(fps);