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