1 // This code is D 1.0 2 3 /** 4 Part of my old D 1.0 game helper code that used SDL. I keep it compiling on new D compilers too, but it is not meant to be used in new projects. 5 */ 6 module arsd.screen; 7 8 import sdl.SDL; 9 import sdl.SDL_image; 10 import sdl.SDL_ttf; 11 import std.string; 12 13 import std.stdio; 14 import std.format; 15 16 import arsd.engine; 17 18 version(D_Version2) 19 static import stdcstring = core.stdc.string; 20 else 21 static import stdcstring = std.c.string; 22 23 version(none) 24 char[] fmt(...){ 25 char[] o; 26 void putc(dchar c) 27 { 28 o ~= c; 29 } 30 31 std.format.doFormat(&putc, _arguments, _argptr); 32 33 return o; 34 } 35 36 37 extern(C){ 38 void glGetIntegerv(int, void*); 39 void glMatrixMode(int); 40 void glPushMatrix(); 41 void glLoadIdentity(); 42 void glOrtho(double, double, double, double, double, double); 43 void glPopMatrix(); 44 void glEnable(int); 45 void glDisable(int); 46 void glClear(int); 47 void glBegin(int); 48 void glVertex2f(float, float); 49 void glEnd(); 50 void glColor3b(ubyte, ubyte, ubyte); 51 void glColor3i(int, int, int); 52 void glColor3f(float, float, float); 53 void glColor4f(float, float, float, float); 54 void glTranslatef(float, float, float); 55 56 void glRotatef(float, float, float, float); 57 58 uint glGetError(); 59 60 void glDeleteTextures(int, uint*); 61 62 char* gluErrorString(uint); 63 64 void glRasterPos2i(int, int); 65 void glDrawPixels(int, int, uint, uint, void*); 66 void glClearColor(float, float, float, float); 67 68 69 70 void glGenTextures(uint, uint*); 71 void glBindTexture(int, int); 72 void glTexParameteri(uint, uint, int); 73 void glTexImage2D(int, int, int, int, int, int, int, int, void*); 74 75 76 void glTexCoord2f(float, float); 77 void glVertex2i(int, int); 78 void glBlendFunc (int, int); 79 void glViewport(int, int, int, int); 80 81 void glReadBuffer(uint); 82 void glReadPixels(int, int, int, int, int, int, void*); 83 84 85 const uint GL_FRONT = 0x0404; 86 87 const uint GL_BLEND = 0x0be2; 88 const uint GL_SRC_ALPHA = 0x0302; 89 const uint GL_ONE_MINUS_SRC_ALPHA = 0x0303; 90 91 92 const uint GL_UNSIGNED_BYTE = 0x1401; 93 const uint GL_RGB = 0x1907; 94 const uint GL_BGRA = 0x80e1; 95 const uint GL_RGBA = 0x1908; 96 const uint GL_TEXTURE_2D = 0x0DE1; 97 const uint GL_TEXTURE_MIN_FILTER = 0x2801; 98 const uint GL_NEAREST = 0x2600; 99 const uint GL_LINEAR = 0x2601; 100 const uint GL_TEXTURE_MAG_FILTER = 0x2800; 101 102 const uint GL_NO_ERROR = 0; 103 104 105 106 const int GL_VIEWPORT = 0x0BA2; 107 const int GL_MODELVIEW = 0x1700; 108 const int GL_TEXTURE = 0x1702; 109 const int GL_PROJECTION = 0x1701; 110 const int GL_DEPTH_TEST = 0x0B71; 111 112 const int GL_COLOR_BUFFER_BIT = 0x00004000; 113 const int GL_ACCUM_BUFFER_BIT = 0x00000200; 114 const int GL_DEPTH_BUFFER_BIT = 0x00000100; 115 116 const int GL_POINTS = 0x0000; 117 const int GL_LINES = 0x0001; 118 const int GL_LINE_LOOP = 0x0002; 119 const int GL_LINE_STRIP = 0x0003; 120 const int GL_TRIANGLES = 0x0004; 121 const int GL_TRIANGLE_STRIP = 5; 122 const int GL_TRIANGLE_FAN = 6; 123 const int GL_QUADS = 7; 124 const int GL_QUAD_STRIP = 8; 125 const int GL_POLYGON = 9; 126 127 } 128 129 public struct Point{ 130 int x; 131 int y; 132 Point opAddAssign(Point p){ 133 x += p.x; 134 y += p.y; 135 version(D_Version2) 136 return this; 137 else 138 return *this; 139 } 140 141 Point opAdd(Point p){ 142 Point a; 143 a.x = x + p.x; 144 a.y = y + p.y; 145 return a; 146 } 147 148 Point opSub(Point p){ 149 Point a; 150 a.x = x - p.x; 151 a.y = y - p.y; 152 return a; 153 } 154 } 155 156 Point XY(int x, int y){ 157 Point p; 158 p.x = x; 159 p.y = y; 160 return p; 161 } 162 163 Point XY(float x, float y){ 164 Point p; 165 p.x = cast(int)x; 166 p.y = cast(int)y; 167 return p; 168 } 169 170 public struct Color{ 171 int r; 172 int g; 173 int b; 174 int a; 175 176 uint toInt(){ 177 return r << 24 | g << 16 | b << 8 | a; 178 } 179 180 void fromInt(uint i){ 181 r = i >> 24; 182 g = (i >> 16) & 0x0ff; 183 b = (i >> 8) & 0x0ff; 184 a = i & 0x0ff; 185 } 186 } 187 188 Color white = {255, 255, 255, 255}; 189 Color black = {0, 0, 0, 255}; 190 191 Color RGB(int r, int g, int b){ 192 Color c; 193 c.r = r; 194 c.g = g; 195 c.b = b; 196 c.a = 255; 197 return c; 198 } 199 200 Color RGBA(int r, int g, int b, int a){ 201 Color c; 202 c.r = r; 203 c.g = g; 204 c.b = b; 205 c.a = a; 206 return c; 207 } 208 209 Color XRGB(Color c, int alpha = -1){ 210 Color a; 211 a.r = c.r ^ 255; 212 a.g = c.g ^ 255; 213 a.b = c.b ^ 255; 214 if(alpha == -1) 215 a.a = c.a;// ^ 255; 216 else 217 a.a = alpha; 218 return a; 219 } 220 221 class FontEngine{ 222 public: 223 224 static FontEngine instance; 225 226 ~this(){ 227 foreach(a; fonts) 228 if(a != null) 229 TTF_CloseFont(a); 230 TTF_Quit(); 231 } 232 233 void loadFont(in char[] font, int size = 12, int index = 0){ 234 if(fonts[index] != null) 235 freeFont(index); 236 TTF_Font* temp; 237 temp = TTF_OpenFont(std..string.toStringz(font), size); 238 if(temp == null) 239 throw new Exception("load font"); 240 241 fonts[index] = temp; 242 } 243 244 void freeFont(int index = 0){ 245 if(fonts[index] != null){ 246 TTF_CloseFont(fonts[index]); 247 fonts[index] = null; 248 } 249 } 250 251 Image renderText(in char[] text, Color foreground = RGB(255,255,255), int font = 0){ 252 Image* a = immutableString(text) in cache[font]; 253 if(a !is null) 254 return *a; 255 SDL_Color f; 256 f.r = cast(ubyte) foreground.r; 257 f.g = cast(ubyte) foreground.g; 258 f.b = cast(ubyte) foreground.b; 259 f.unused = cast(ubyte) foreground.a; 260 261 SDL_Surface* s = TTF_RenderText_Blended(fonts[font], std..string.toStringz(text), f); 262 Image i = new Image(s); 263 cache[font][text]/*[font]*/ = i; 264 265 return i; 266 } 267 268 int textHeight(in char[] text=" ",int font = 0){ 269 int w, h; 270 TTF_SizeText(FontEngine.instance.fonts[font], std..string.toStringz(text), &w, &h); 271 return h; 272 } 273 274 void textSize(in char[] text, out int w, out int h, int font = 0){ 275 TTF_SizeText(fonts[font], std..string.toStringz(text), &w, &h); 276 } 277 private: 278 static this() { 279 instance = new FontEngine; 280 } 281 282 this(){ 283 if(TTF_Init() == -1) 284 throw new Exception("TTF_Init"); 285 286 } 287 288 TTF_Font*[8] fonts; 289 Image[char[]][8] cache; 290 } 291 292 interface Drawable{ 293 public: 294 void flip(); 295 int width(); 296 int height(); 297 int bpp(); 298 /* 299 uint toGL(); 300 float texWidth(); 301 float texHeight(); 302 */ 303 protected: 304 SDL_Surface* surface(); 305 } 306 307 int total = 0; 308 309 310 class Image : Drawable{ 311 public: 312 this(SDL_Surface* s){ 313 if(s == null) 314 throw new Exception("Image"); 315 sur = s; 316 } 317 318 /// Loads an image with the filename checking to see if it has already been loaded into the cache 319 /// loads it as read-only 320 // static Image load(char[] filename){ 321 322 // } 323 324 this(char[] filename){ 325 sur = IMG_Load(std..string.toStringz(filename)); 326 if(sur == null) 327 throw new Exception(immutableString("Load " ~ filename)); 328 name = filename; 329 } 330 331 332 void replace(char[] filename){ 333 if(t){ 334 glDeleteTextures(1, &tex); 335 total--; 336 writef("[%s]OpenGL texture destroyed %d. %d remain\n", name, tex, total); 337 t = 0; 338 } 339 if(sur){ 340 SDL_FreeSurface(sur); 341 sur = null; 342 } 343 sur = IMG_Load(std..string.toStringz(filename)); 344 if(sur == null) 345 throw new Exception(immutableString("Load " ~ filename)); 346 name = filename; 347 } 348 349 350 // loads a slice of an image 351 this(char[] filename, int x, int y, int wid, int hei){ 352 /* 353 Image i = new Image(filename); 354 this(wid, hei); 355 356 scope Painter p = new Painter(this); 357 for(int a = 0; a < wid; a++) 358 for(int b = 0; b < hei; b++) 359 p.putpixel(XY(a, b), i.getPixel(XY(a + x, b + y))); 360 */ 361 362 SDL_Surface* s1; 363 364 s1 = IMG_Load(std..string.toStringz(filename)); 365 if(s1 == null) 366 throw new Exception(immutableString("Loading " ~ filename)); 367 scope(exit) 368 SDL_FreeSurface(s1); 369 370 371 sur = SDL_CreateRGBSurface(SDL_SWSURFACE, wid, hei, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000); 372 if(sur == null) 373 throw new Exception(immutableString("Create")); 374 375 376 for(int b = 0; b < hei; b++){ 377 for(int a = 0; a < wid; a++){ 378 if(b+y >= s1.h || a+x >= s1.w){ 379 break; 380 // throw new Exception("eat my cum"); 381 } 382 ubyte* wtf; 383 if(s1.format.BitsPerPixel == 32){ 384 wtf = cast(ubyte*)(cast(ubyte*)s1.pixels + (b+y)*s1.pitch + (a+x) * 4); 385 } 386 else 387 if(s1.format.BitsPerPixel == 24) 388 wtf = cast(ubyte*)(cast(ubyte*)s1.pixels + (b+y)*s1.pitch + (a+x) * 3); 389 else 390 throw new Exception("fuck me in the ass"); 391 392 ubyte* good = cast(ubyte*)(cast(ubyte*)sur.pixels + b*sur.pitch + a * 4); 393 394 good[0] = wtf[2]; 395 good[1] = wtf[1]; 396 good[2] = wtf[0]; 397 good[3] = wtf[3]; 398 399 } 400 } 401 402 403 /* 404 SDL_Rect r; 405 r.x = x; 406 r.y = y; 407 r.w = wid; 408 r.h = hei; 409 410 SDL_Rect r2; 411 r2.x = 0; 412 r2.y = 0; 413 r2.w = wid; 414 r2.h = hei; 415 if(SDL_BlitSurface(s1, &r, sur, &r2)) 416 throw new Exception("Blit"); 417 */ 418 } 419 420 this(int wid, int hei){ 421 sur = SDL_CreateRGBSurface(SDL_SWSURFACE, wid, hei, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000); 422 if(sur == null) 423 throw new Exception("Create"); 424 t = false; 425 } 426 427 ~this(){ 428 if(t){ 429 glDeleteTextures(1, &tex); 430 total--; 431 writef("[%s]OpenGL texture destroyed %d. %d remain\n", name, tex, total); 432 } 433 if(sur) 434 SDL_FreeSurface(sur); 435 } 436 437 void flip(){ 438 439 } 440 441 int width(){ 442 return surface.w; 443 } 444 445 int height(){ 446 return surface.h; 447 } 448 449 int bpp(){ 450 return sur.format.BitsPerPixel; 451 } 452 453 Color getPixel(Point p){ 454 ubyte* bufp; 455 Color a; 456 457 if(bpp == 32){ 458 bufp = cast(ubyte*)(cast(ubyte*)surface.pixels + p.y*surface.pitch + p.x * 4); 459 a.a = bufp[3]; 460 a.r = bufp[2]; 461 a.g = bufp[1]; 462 a.b = bufp[0]; 463 }else{ 464 bufp = cast(ubyte*)(cast(ubyte*)surface.pixels + p.y*surface.pitch + p.x * 3); 465 a.a = 255; 466 a.r = bufp[2]; 467 a.g = bufp[1]; 468 a.b = bufp[0]; 469 } 470 471 return a; 472 } 473 474 uint toGL(){ 475 if(t) 476 return tex; 477 else{ 478 float[4] f; 479 tex = SDL_GL_LoadTexture(surface, f.ptr); 480 t = true; 481 total++; 482 texWidth = f[2]; 483 texHeight = f[3]; 484 485 // total++; 486 // writef("OpenGL texture created %d. %d exist\n", tex, total); 487 488 return tex; 489 } 490 } 491 protected: 492 SDL_Surface* surface(){ 493 return sur; 494 } 495 private: 496 SDL_Surface* sur; 497 uint tex; 498 bool t; 499 float texWidth; 500 float texHeight; 501 char[] name; 502 } 503 504 bool useGL; 505 506 class Screen : Drawable{ 507 public: 508 this(int xres = 1024, int yres = 768, int bpp = 24, bool oGL = false, bool fullScreen = false){//true){ 509 // oGL = false; 510 oGL = true; 511 if(!oGL) 512 screen = SDL_SetVideoMode(xres, yres, bpp/*32*/, SDL_SWSURFACE); 513 else{ 514 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); 515 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); 516 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); 517 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); 518 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); 519 if(fullScreen){ 520 screen = SDL_SetVideoMode(xres, yres, 24, SDL_OPENGL| SDL_FULLSCREEN); 521 } 522 else 523 screen = SDL_SetVideoMode(xres, yres, 0, SDL_OPENGL); 524 //screen = SDL_SetVideoMode(xres, yres, bpp, SDL_OPENGL); 525 if(screen is null) 526 throw new Exception("screen"); 527 528 glMatrixMode(GL_PROJECTION); 529 glLoadIdentity(); 530 //glOrtho(0, 1000, yres, 0, 0, 1); 531 glOrtho(0, xres, yres, 0, 0, 1); 532 glMatrixMode(GL_MODELVIEW); 533 534 glDisable(GL_DEPTH_TEST); 535 536 glEnable(GL_TEXTURE_2D); 537 538 539 glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 540 541 542 glClearColor(0,0,0,0); 543 544 // glViewport(0,0,1024,1024); 545 } 546 547 useGL = oGL; 548 549 if(screen == null) 550 throw new Exception("screen"); 551 552 // SDL_SetAlpha(screen, SDL_SRCALPHA | SDL_RLEACCEL, 128); 553 554 xr = xres; 555 yr = yres; 556 } 557 558 void switchSplitScreenMode(int player, int numberOfPlayers, bool horizontal){ 559 switch(numberOfPlayers){ 560 default: assert(0); 561 case 1: 562 return; 563 // glViewport(0, 0, xr, yr); 564 break; 565 case 2: 566 switch(player){ 567 default: assert(0); 568 case 0: 569 if(horizontal) 570 glViewport(0, yr / 2, xr, yr / 2); 571 else 572 glViewport(0, 0, xr / 2, yr); 573 break; 574 case 1: 575 if(horizontal) 576 glViewport(0, 0, xr, yr / 2); 577 else 578 glViewport(xr / 2, 0, xr / 2, yr); 579 break; 580 } 581 break; 582 case 3: 583 case 4: 584 switch(player){ 585 default: assert(0); 586 case 0: 587 glViewport(0, yr / 2, xr / 2, yr / 2); 588 break; 589 case 1: 590 glViewport(xr / 2, yr / 2, xr / 2, yr / 2); 591 break; 592 case 2: 593 glViewport(0, 0, xr / 2, yr / 2); 594 break; 595 case 3: 596 glViewport(xr / 2, 0, xr / 2, yr / 2); 597 break; 598 } 599 600 break; 601 } 602 glMatrixMode(GL_PROJECTION); 603 glLoadIdentity(); 604 glOrtho(0, xr, yr, 0, 0, 1); 605 } 606 607 608 ~this(){ 609 delete FontEngine.instance; 610 } 611 612 Image screenshot(){ 613 if(!useGL) 614 throw new Exception("Not yet implemented"); 615 Image image = new Image(xr, yr); 616 glReadBuffer(GL_FRONT); 617 glReadPixels(0, 0, xr, yr, GL_BGRA, GL_UNSIGNED_BYTE, image.sur.pixels); 618 619 Image temp = new Image(xr, yr); 620 621 622 // FIXME 623 version(Windows) 624 return image; 625 626 // FIXME: this crashes on Windows 627 for(int i = 0; i < yr; i++) 628 stdcstring.memcpy(temp.sur.pixels + 4 * xr * i, image.sur.pixels + 4 * xr * (yr-1 - i), 4 * xr); 629 // memcpy(image.sur.pixels, tem.psur.pixels, xres * yres * 4); 630 631 return temp; 632 } 633 634 635 636 637 void flip(){ 638 if(useGL) 639 SDL_GL_SwapBuffers(); 640 else 641 SDL_Flip(screen); 642 } 643 644 int width(){ 645 return xr; 646 } 647 648 int height(){ 649 return yr; 650 } 651 652 int bpp(){ 653 return 124; 654 } 655 /* 656 uint toGL(){ 657 throw new Error; 658 } 659 float texWidth(){ 660 return 1.0; 661 } 662 float texHeight(){ 663 return 1.0; 664 } 665 */ 666 protected: 667 SDL_Surface* surface(){ 668 return screen; 669 } 670 671 private: 672 SDL_Surface* screen; 673 int xr; 674 int yr; 675 } 676 677 scope class Painter{ 678 public: 679 bool special; 680 bool manualFlipped; 681 Point translate; 682 this(Painter p, Point t){ 683 s = p.s; 684 special = true; 685 translate = t; 686 } 687 688 this(Drawable d){ 689 /+ 690 in { 691 assert(!(s is null)); 692 } 693 +/ 694 s = d; 695 if(s is null) 696 throw new Exception("christ what were you thinking"); 697 698 if ( !(useGL 699 && s.bpp() == 124) 700 && SDL_MUSTLOCK(s.surface()) ) { 701 if ( SDL_LockSurface(s.surface()) < 0 ) { 702 throw new Exception("locking"); 703 } 704 locked = true; 705 } 706 } 707 708 ~this(){ 709 if(!manualFlipped){ 710 if(glbegin) 711 endDrawingShapes(); 712 if(!special){ 713 if (locked){ 714 SDL_UnlockSurface(s.surface); 715 } 716 s.flip(); 717 } 718 } 719 } 720 721 void manualFlip(){ 722 if(glbegin) 723 endDrawingShapes(); 724 if(!special){ 725 if (locked){ 726 SDL_UnlockSurface(s.surface); 727 } 728 s.flip(); 729 } 730 manualFlipped = true; 731 } 732 733 void setGLColor(Color color){ 734 if(useGL && s.bpp == 124){ 735 glColor4f(cast(float)color.r/255.0, cast(float)color.g/255.0, cast(float)color.b/255.0, cast(float)color.a / 255.0); 736 return; 737 } 738 } 739 740 void putpixel(Point where, Color color){ 741 if(special) where += translate; 742 if(glbegin) 743 throw new Exception("Must end shape before doing anything else"); 744 745 // if(color.a == 255) 746 // return; 747 int x = where.x; 748 int y = where.y; 749 if(x < 0 || x >= s.width || y < 0 || y >= s.height) 750 return; 751 752 753 754 if(useGL && s.bpp == 124){ 755 // y = 480 - y; 756 glBegin(GL_POINTS); 757 setGLColor(color); 758 glVertex2f(cast(float)x, cast(float)y); 759 glEnd(); 760 return; 761 } 762 763 764 ubyte *bufp; 765 766 if(s.bpp == 32){ 767 bufp = cast(ubyte*)(cast(ubyte*)s.surface.pixels + y*s.surface.pitch + x * 4); 768 bufp[3] = cast(ubyte) color.a; 769 bufp[2] = cast(ubyte) color.r; 770 bufp[1] = cast(ubyte) color.g; 771 bufp[0] = cast(ubyte) color.b; 772 }else{ 773 bufp = cast(ubyte*)(cast(ubyte*)s.surface.pixels + y*s.surface.pitch + x * 3); 774 if(color.a == 255){ 775 bufp[2] = cast(ubyte) color.r; 776 bufp[1] = cast(ubyte) color.g; 777 bufp[0] = cast(ubyte) color.b; 778 } 779 else{ 780 bufp[2] = cast(ubyte)(bufp[2] * (255-color.a) + (color.r * (color.a)) / 255); 781 bufp[1] = cast(ubyte)(bufp[1] * (255-color.a) + (color.g * (color.a)) / 255); 782 bufp[0] = cast(ubyte)(bufp[0] * (255-color.a) + (color.b * (color.a)) / 255); 783 } 784 } 785 } 786 787 void beginDrawingLines(){ 788 if(glbegin) 789 throw new Exception("Can only draw one kind at a time"); 790 glbegin = true; 791 if(useGL && s.bpp == 124) 792 glBegin(GL_LINES); 793 } 794 void beginDrawingConnectedLines(){ 795 if(glbegin) 796 throw new Exception("Can only draw one kind at a time"); 797 glbegin = true; 798 if(useGL && s.bpp == 124) 799 glBegin(GL_LINE_STRIP); 800 } 801 void beginDrawingPolygon(){ 802 if(glbegin) 803 throw new Exception("Can only draw one kind at a time"); 804 glbegin = true; 805 if(useGL && s.bpp == 124) 806 glBegin(GL_POLYGON); 807 } 808 void beginDrawingTriangles(){ 809 if(glbegin) 810 throw new Exception("Can only draw one kind at a time"); 811 glbegin = true; 812 if(useGL && s.bpp == 124) 813 glBegin(GL_TRIANGLES); 814 } 815 void beginDrawingBoxes(){ 816 if(glbegin) 817 throw new Exception("Can only draw one kind at a time"); 818 glbegin = true; 819 if(useGL && s.bpp == 124) 820 glBegin(GL_QUADS); 821 } 822 void beginDrawingPoints(){ 823 if(glbegin) 824 throw new Exception("Can only draw one kind at a time"); 825 glbegin = true; 826 if(useGL && s.bpp == 124) 827 glBegin(GL_POINTS); 828 } 829 830 void endDrawingShapes(){ 831 if(!glbegin) 832 return; 833 glbegin = false; 834 if(useGL && s.bpp == 124) 835 glEnd(); 836 } 837 void vertex(Point p){ 838 if(special) p += translate; 839 if(!glbegin) 840 throw new Exception("Can't use vertex without beginning first"); 841 if(useGL && s.bpp == 124) 842 glVertex2i(p.x, p.y); 843 } 844 bool glbegin; 845 846 void drawImageRotated(Point where, Image i, float a, Color color = RGB(255,255,255)){ 847 if(i is null) 848 return; 849 glPushMatrix(); 850 // glRotatef(a, cast(float)(where.x + 32) / s.width, cast(float)(where.y + 32) / s.height, 1); 851 glTranslatef(where.x, where.y, 0); 852 glRotatef(a, 0,0, 1); 853 drawImage(XY(-i.width/2,-i.height/2), i, i.width, i.height, color); 854 glPopMatrix(); 855 } 856 857 void drawImage(Point where, Image i, Color c){ 858 drawImage(where, i, 0, 0, c); 859 } 860 861 void drawImage(Point where, Image i, int W = 0, int H = 0, Color c = RGBA(255,255,255,255)){ 862 if(i is null) 863 return; 864 865 if(special) where += translate; 866 if(glbegin) 867 throw new Exception("Must end shape before doing anything else"); 868 if(useGL && s.bpp == 124){ 869 int x = where.x; 870 int y = where.y; 871 int w = W == 0 ? i.width : W; 872 int h = H == 0 ? i.height : H; 873 874 // glColor4f(.5,.5,.5,1); 875 setGLColor(c); 876 glBindTexture(GL_TEXTURE_2D, i.toGL); 877 glBegin(GL_QUADS); 878 glTexCoord2f(0, 0); glVertex2i(x, y); 879 glTexCoord2f(i.texWidth, 0); glVertex2i(x+w, y); 880 glTexCoord2f(i.texWidth, i.texHeight); glVertex2i(x+w, y+h); 881 glTexCoord2f(0, i.texHeight); glVertex2i(x, y+h); 882 glEnd(); 883 884 glBindTexture(GL_TEXTURE_2D, 0); // unbind the texture... I guess 885 // I don't actually understand why that is needed 886 // but without it, everything drawn after it is wrong (too light or dark) 887 return; 888 } 889 890 if((W == 0 && H == 0) || (i.width == W && i.height == H)){ 891 SDL_Rect r; 892 r.x = cast(short)( where.x); 893 r.y = cast(short)( where.y); 894 r.w = cast(short)( i.width); 895 r.h = cast(short)( i.height); 896 if(locked) 897 SDL_UnlockSurface(s.surface); 898 899 if(SDL_BlitSurface(i.surface, null, s.surface, &r) == -1) 900 throw new Exception("blit"); 901 902 if ( SDL_MUSTLOCK(s.surface) ) { 903 if ( SDL_LockSurface(s.surface) < 0 ) { 904 throw new Exception("lock"); 905 } 906 locked = true; 907 } 908 } else { // quick and dirty scaling needed 909 float dx = cast(float)i.width / cast(float)W; 910 float dy = cast(float)i.height / cast(float)H; 911 int X = where.x, Y = where.y; 912 913 for(float y = 0; y < i.height; y += dy){ 914 for(float x = 0; x < i.width; x += dx){ 915 putpixel(XY(X, Y), i.getPixel(XY(cast(int) x, cast(int) y))); 916 X++; 917 } 918 X = where.x; 919 Y++; 920 } 921 922 } 923 924 } 925 926 void drawText(Point where, in char[] text, Color foreground = RGB(255,255,255), int font = 0){ 927 if(glbegin) 928 throw new Exception("Must end shape before doing anything else"); 929 930 if(useGL && s.bpp == 124){ 931 Image i = FontEngine.instance.renderText(text, RGB(255,255,255), font); 932 drawImage(where, i, foreground); 933 }else{ 934 Image i = FontEngine.instance.renderText(text, foreground, font); 935 drawImage(where, i); 936 } 937 } 938 939 version(D_Version2) { 940 import std.format; 941 void drawTextf(T...)(Point where, T args) { 942 char[] t; 943 t.length = 80; 944 int a = 0; 945 void putc(dchar c){ 946 if(a == t.length) 947 t.length = t.length + 80; 948 t[a] = cast(char) c; 949 a++; 950 } 951 formattedWrite(&putc, args); 952 t.length = a; 953 954 drawText(where, t); 955 } 956 } else 957 void drawTextf(Point where, ...){ 958 char[] t; 959 t.length = 80; 960 int a = 0; 961 void putc(dchar c){ 962 if(a == t.length) 963 t.length = t.length + 80; 964 t[a] = cast(char) c; 965 a++; 966 } 967 std.format.doFormat(&putc, _arguments, _argptr); 968 t.length = a; 969 970 drawText(where, t); 971 } 972 973 int wordLength(in char[] w, int font = 0){ 974 int a,b; 975 FontEngine.instance.textSize(w, a, b, font); 976 return a; 977 } 978 979 int drawTextBoxed(Point where, char[] text, int width, int height, Color foreground = RGB(255,255,255), int font = 0){ 980 if(glbegin) 981 throw new Exception("Must end shape before doing anything else"); 982 983 984 int xc; 985 int yc = TTF_FontLineSkip(FontEngine.instance.fonts[font]); 986 987 int w = 0; 988 int h = 0; 989 990 int l; 991 992 char[] getWord(){ 993 int a = l; 994 while(a < text.length && text[a] != ' ' && text[a] != '\n' && text[a] != '\t') 995 a++; 996 return text[l..a]; 997 } 998 999 int wordLength(in char[] w){ 1000 int a,b; 1001 FontEngine.instance.textSize(w, a, b, font); 1002 return a; 1003 } 1004 1005 Point ww = where; 1006 while(l < text.length){ 1007 if(text[l] == '\n'){ 1008 l++; 1009 goto newline; 1010 } 1011 if(wordLength(getWord()) + w > width){ 1012 goto newline; 1013 } 1014 1015 if(!(w == 0 && text[l] == ' ')){ 1016 TTF_GlyphMetrics(FontEngine.instance.fonts[font], text[l], null,null,null,null,&xc); 1017 drawText(ww, text[l..(l+1)], foreground, font); 1018 w+=xc; 1019 ww.x += xc; 1020 } 1021 l++; 1022 if(w > (width - xc)){ 1023 newline: 1024 w = 0; 1025 h += yc; 1026 ww.x = cast(short)(where.x); 1027 ww.y += cast(short)(yc); 1028 1029 if(h > (height - yc)) 1030 break; 1031 } 1032 } 1033 return l; 1034 } 1035 1036 void drawTextCenteredHoriz(int top, char[] text, Color foreground, int font = 0){ 1037 Point where; 1038 where.y = top; 1039 int w, h; 1040 TTF_SizeText(FontEngine.instance.fonts[font], std..string.toStringz(text), &w, &h); 1041 where.x = (s.width - w) / 2; 1042 drawText(where, text, foreground, font); 1043 } 1044 1045 void line(Point start, Point end, Color color){ 1046 if(special){ start += translate; end += translate; } 1047 if(glbegin) 1048 throw new Exception("Must end shape before doing anything else"); 1049 1050 if(useGL && s.bpp == 124){ 1051 setGLColor(color); 1052 glBegin(GL_LINES); 1053 glVertex2i(start.x, start.y); 1054 glVertex2i(end.x, end.y); 1055 glEnd(); 1056 } 1057 } 1058 1059 void hline(Point start, int width, Color color){ 1060 if(useGL && s.bpp == 124){ 1061 line(start, XY(start.x + width, start.y), color); 1062 return; 1063 } 1064 Point point = start; 1065 for(int a = 0; a < width; a++){ 1066 putpixel(point, color); 1067 point.x++; 1068 } 1069 } 1070 1071 void vline(Point start, int height, Color color){ 1072 if(useGL && s.bpp == 124){ 1073 line(start, XY(start.x, start.y + height), color); 1074 return; 1075 } 1076 1077 Point point = start; 1078 for(int a = 0; a < height; a++){ 1079 putpixel(point, color); 1080 point.y++; 1081 } 1082 } 1083 1084 1085 void circle(Point center, int radius, Color color){ 1086 if(special) center += translate; 1087 if(glbegin) 1088 throw new Exception("Must end shape before doing anything else"); 1089 1090 } 1091 1092 void arc(Point center, int radius, float start, float end, Color color){ 1093 if(special) center += translate; 1094 if(glbegin) 1095 throw new Exception("Must end shape before doing anything else"); 1096 1097 // for(float a = start; a <= end; a+= (3.14159265358 / 50.0)) 1098 // putpixel((int)(cos(a) * (float)radius + center.x()),(int)( sin(a) * (float) radius + center.y()), color); 1099 } 1100 1101 void box(Point upperLeft, Point lowerRight, Color color){ 1102 if(special) { upperLeft += translate; lowerRight += translate; } 1103 if(glbegin) 1104 throw new Exception("Must end shape before doing anything else"); 1105 1106 if(useGL && s.bpp == 124){ 1107 int x1 = upperLeft.x; 1108 int y1 = upperLeft.y; 1109 int x2 = lowerRight.x; 1110 int y2 = lowerRight.y; 1111 glBegin(GL_QUADS); 1112 //glColor3b(color.r, color.g, color.b); 1113 setGLColor(color); 1114 //glColor4f(1,1,1,1); 1115 glVertex2i(x1, y1); 1116 glVertex2i(x2, y1); 1117 glVertex2i(x2, y2); 1118 glVertex2i(x1, y2); 1119 glEnd(); 1120 return; 1121 } 1122 SDL_Rect r; 1123 r.x = cast(short) upperLeft.x; 1124 r.y = cast(short) upperLeft.y; 1125 r.w = cast(short) (lowerRight.x - upperLeft.x); 1126 r.h = cast(short) (lowerRight.y - upperLeft.y); 1127 if(s.bpp == 32) 1128 SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b); 1129 else 1130 SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b); 1131 } 1132 1133 void rect(Point upperLeft, Point lowerRight, Color color){ 1134 if(special) { upperLeft += translate; lowerRight += translate; } 1135 if(glbegin) 1136 throw new Exception("Must end shape before doing anything else"); 1137 1138 if(useGL && s.bpp == 124){ 1139 int x1 = upperLeft.x; 1140 int y1 = upperLeft.y; 1141 int x2 = lowerRight.x; 1142 int y2 = lowerRight.y; 1143 glBegin(GL_LINE_LOOP); 1144 //glColor3b(color.r, color.g, color.b); 1145 setGLColor(color); 1146 //glColor4f(1,1,1,1); 1147 glVertex2i(x1, y1); 1148 glVertex2i(x2+1, y1); 1149 glVertex2i(x2, y2); 1150 glVertex2i(x1, y2); 1151 glEnd(); 1152 return; 1153 } 1154 /* 1155 SDL_Rect r; 1156 r.x = upperLeft.x; 1157 r.y = upperLeft.y; 1158 r.w = lowerRight.x - upperLeft.x; 1159 r.h = lowerRight.y - upperLeft.y; 1160 if(s.bpp == 32) 1161 SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b); 1162 else 1163 SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b); 1164 */ 1165 } 1166 1167 void gbox(Point upperLeft, Point lowerRight, Color color1, Color color2, Color color3, Color color4){ 1168 if(special) { upperLeft += translate; lowerRight += translate; } 1169 if(glbegin) 1170 throw new Exception("Must end shape before doing anything else"); 1171 1172 Color color = color1; 1173 if(useGL && s.bpp == 124){ 1174 int x1 = upperLeft.x; 1175 int y1 = upperLeft.y; 1176 int x2 = lowerRight.x; 1177 int y2 = lowerRight.y; 1178 glBegin(GL_QUADS); 1179 setGLColor(color1); 1180 glVertex2i(x1, y1); 1181 setGLColor(color2); 1182 glVertex2i(x2, y1); 1183 setGLColor(color4); 1184 glVertex2i(x2, y2); 1185 setGLColor(color3); 1186 glVertex2i(x1, y2); 1187 glEnd(); 1188 return; 1189 } 1190 SDL_Rect r; 1191 r.x = cast(short) upperLeft.x; 1192 r.y = cast(short) upperLeft.y; 1193 r.w = cast(short) (lowerRight.x - upperLeft.x); 1194 r.h = cast(short) (lowerRight.y - upperLeft.y); 1195 if(s.bpp == 32) 1196 SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b); 1197 else 1198 SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b); 1199 } 1200 1201 1202 void clear(){ 1203 if(useGL && s.bpp == 124){ 1204 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT); 1205 return; 1206 } 1207 box(XY(0,0), XY(s.width, s.height), RGB(0,0,0)); 1208 } 1209 1210 void fill(Color color){ 1211 box(XY(0,0), XY(s.width, s.height), color); 1212 } 1213 1214 void blend(Color color){ 1215 if(useGL && s.bpp == 124){ 1216 box(XY(0,0), XY(s.width, s.height), color); 1217 return; 1218 } 1219 1220 ubyte *bufp; 1221 1222 bufp = cast(ubyte*)s.surface.pixels; 1223 for(int y = 0; y < s.height; y++) 1224 for(int x = 0; x < s.width; x++){ 1225 1226 bufp[2] = cast(ubyte)((bufp[2] * (255-color.a) + color.r * color.a) / 255); 1227 bufp[1] = cast(ubyte)((bufp[1] * (255-color.a) + color.g * color.a) / 255); 1228 bufp[0] = cast(ubyte)((bufp[0] * (255-color.a) + color.b * color.a) / 255); 1229 1230 bufp += (s.bpp == 24 ? 3 : 4); 1231 } 1232 } 1233 1234 private: 1235 Drawable s; 1236 bool locked; 1237 } 1238 1239 1240 1241 1242 1243 1244 1245 int SDL_BlitSurface 1246 (SDL_Surface *src, SDL_Rect *srcrect, 1247 SDL_Surface *dst, SDL_Rect *dstrect) 1248 { 1249 return SDL_UpperBlit(src, srcrect, dst, dstrect); 1250 } 1251 1252 bit SDL_MUSTLOCK(SDL_Surface *surface) 1253 { 1254 return surface.offset || ((surface.flags & 1255 (SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_RLEACCEL)) != 0); 1256 } 1257 1258 /* Quick utility function for texture creation */ 1259 int power_of_two(int input) 1260 { 1261 int value = 1; 1262 1263 while ( value < input ) { 1264 value <<= 1; 1265 } 1266 return value; 1267 } 1268 1269 uint SDL_GL_LoadTexture(SDL_Surface *surface, float *texcoord) 1270 { 1271 uint texture; 1272 int w, h; 1273 SDL_Surface *image; 1274 SDL_Rect area; 1275 uint saved_flags; 1276 ubyte saved_alpha; 1277 1278 /* Use the surface width and height expanded to powers of 2 */ 1279 w = power_of_two(surface.w); 1280 h = power_of_two(surface.h); 1281 texcoord[0] = 0.0f; /* Min X */ 1282 texcoord[1] = 0.0f; /* Min Y */ 1283 texcoord[2] = cast(float)surface.w / cast(float)w; /* Max X */ 1284 texcoord[3] = cast(float)surface.h / cast(float)h; /* Max Y */ 1285 1286 image = SDL_CreateRGBSurface( 1287 SDL_SWSURFACE, 1288 w, h, 1289 32, 1290 0x000000FF, 1291 0x0000FF00, 1292 0x00FF0000, 1293 0xFF000000 1294 ); 1295 if ( image == null) { 1296 throw new Exception("make image"); 1297 } 1298 1299 1300 /* Save the alpha blending attributes */ 1301 saved_flags = surface.flags&(SDL_SRCALPHA|SDL_RLEACCELOK); 1302 saved_alpha = surface.format.alpha; 1303 if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { 1304 SDL_SetAlpha(surface, 0, 0); 1305 } 1306 1307 /* Copy the surface into the GL texture image */ 1308 area.x = 0; 1309 area.y = 0; 1310 area.w = cast(ushort) surface.w; 1311 area.h = cast(ushort) surface.h; 1312 SDL_BlitSurface(surface, &area, image, &area); 1313 1314 /* Restore the alpha blending attributes */ 1315 if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { 1316 SDL_SetAlpha(surface, saved_flags, saved_alpha); 1317 } 1318 1319 /* Create an OpenGL texture for the image */ 1320 glGenTextures(1, &texture); 1321 1322 glBindTexture(GL_TEXTURE_2D, texture); 1323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1324 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1325 1326 glTexImage2D(GL_TEXTURE_2D, 1327 0, 1328 GL_RGBA, 1329 w, h, 1330 0, 1331 GL_RGBA, 1332 GL_UNSIGNED_BYTE, 1333 image.pixels); 1334 SDL_FreeSurface(image); /* No longer needed */ 1335 1336 1337 1338 return texture; 1339 } 1340 1341 1342 1343 1344 1345 1346 Color c1; 1347 Color c2; 1348 Color c3; 1349 Color c4; 1350 static this(){ 1351 c1 = RGBA(0,0,255,160); 1352 c2 = RGBA(0,0,255,160); 1353 c3 = RGBA(0,0,255,160); 1354 c4 = RGBA(0,0,0,160); 1355 } 1356 1357 void drawHighlightBox(Painter p, Point where, int width, int height = 16){ 1358 p.gbox(where, where + XY(width, height), XRGB(c1, 128), XRGB(c2, 128), XRGB(c3, 128), XRGB(c4, 128)); 1359 } 1360 1361 // Real size is width + 8, height + 8. Size given if of the client area 1362 /* 1363 Point drawShadedRect(Painter p, Point where, int width, int height){ 1364 int x = where.x; 1365 int y = where.y; 1366 1367 Color gray = RGB(128,128,128); 1368 1369 p.box(XY(x + 2, y), XY( x + 2 + width + 2, y + 4), gray); 1370 p.box(XY(x + 2, y + height + 4), XY( x + 2 + width + 2, y + 4 + height + 4 ), gray); 1371 1372 p.box(XY(x, y + 2), XY(x + 4, y + 2 + height + 2), gray); 1373 p.box(XY(x + 4 + width, y + 2), XY(x + 4 + width + 4, y + 2 + height + 2), gray); 1374 1375 // p.putpixel(XY(x + 1, y + 1), gray); 1376 // p.putpixel(XY(x + 1, y + 4 + 3 + height), gray); 1377 // p.putpixel(XY(x + 4 + width + 3, y + 1), gray); 1378 // p.putpixel(XY(x + 4 + width + 3, y + 4 + 3 + height), gray); 1379 1380 1381 p.hline(XY(x + 4, y + 1), width + 2, white); 1382 p.hline(XY(x + 4 - 2, y + 4 + height + 1), width + 2 + 1, white); 1383 1384 p.vline(XY(x + 1 - 1, y + 3), height + 2, white); 1385 p.vline(XY(x + 4 + width + 3, y + 3), height + 2, white); 1386 1387 p.gbox(XY(x + 4, y + 4), XY(x + width + 4, y + height + 4), c1, c2, c3, c4); 1388 1389 return XY(x + 4, y + 4); 1390 } 1391 */ 1392 1393 const int BORDER_WIDTH = 4; 1394 1395 Point drawShadedRect(Painter p, Point where, int width, int height){ 1396 Color gray = RGB(128,128,128); 1397 1398 Point w; 1399 1400 // top section 1401 w = where + XY( BORDER_WIDTH, 0); 1402 p.box( w + XY(0, 0 * BORDER_WIDTH / 4), 1403 w + XY(width, 1 * BORDER_WIDTH / 4), 1404 gray); 1405 p.box( w + XY(-BORDER_WIDTH / 2, 1 * BORDER_WIDTH / 4), 1406 w + XY(BORDER_WIDTH / 2 + width, 3 * BORDER_WIDTH / 4), 1407 white); 1408 p.box( w + XY( -1 * BORDER_WIDTH / 4, 3 * BORDER_WIDTH / 4), 1409 w + XY( 1 * BORDER_WIDTH / 4 + width , 4 * BORDER_WIDTH / 4), 1410 black); 1411 // bottom section 1412 w = where + XY(BORDER_WIDTH, height + BORDER_WIDTH); 1413 p.box( w + XY(-1 * BORDER_WIDTH / 4, 0 * BORDER_WIDTH / 4), 1414 w + XY(1 * BORDER_WIDTH / 4 + width, 1 * BORDER_WIDTH / 4), 1415 black); 1416 p.box( w + XY(-BORDER_WIDTH / 2, 1 * BORDER_WIDTH / 4), 1417 w + XY(BORDER_WIDTH / 2 + width, 3 * BORDER_WIDTH / 4), 1418 white); 1419 p.box( w + XY(-1 *BORDER_WIDTH / 4, 3 * BORDER_WIDTH / 4), 1420 w + XY( 1 *BORDER_WIDTH / 4 + width, 4 * BORDER_WIDTH / 4), 1421 gray); 1422 1423 // left section 1424 w = where + XY( 0, BORDER_WIDTH); 1425 p.box( w + XY(0 * BORDER_WIDTH / 4, -1), 1426 w + XY(1 * BORDER_WIDTH / 4, height + 1), 1427 gray); 1428 p.box( w + XY(1 * BORDER_WIDTH / 4, -BORDER_WIDTH / 2), 1429 w + XY(3 * BORDER_WIDTH / 4, BORDER_WIDTH / 2 + height), 1430 white); 1431 p.box( w + XY(3 * BORDER_WIDTH / 4, 0), 1432 w + XY(4 * BORDER_WIDTH / 4, height), 1433 black); 1434 1435 // right section 1436 w = where + XY( BORDER_WIDTH + width, BORDER_WIDTH); 1437 p.box( w + XY(0 * BORDER_WIDTH / 4, 0), 1438 w + XY(1 * BORDER_WIDTH / 4, height), 1439 black); 1440 p.box( w + XY(1 * BORDER_WIDTH / 4, -BORDER_WIDTH / 2), 1441 w + XY(3 * BORDER_WIDTH / 4, BORDER_WIDTH / 2 + height), 1442 white); 1443 p.box( w + XY(3 * BORDER_WIDTH / 4, -1), 1444 w + XY(4 * BORDER_WIDTH / 4, 1 + height), 1445 gray); 1446 w = where + XY(BORDER_WIDTH, BORDER_WIDTH); 1447 p.gbox(w, w + XY(width, height), c1, c2, c3, c4); 1448 return w; 1449 } 1450