46 procedure RenderWeaponTooltip(atype: TAmmoType); |
38 procedure RenderWeaponTooltip(atype: TAmmoType); |
47 procedure ShowWeaponTooltip(x, y: LongInt); |
39 procedure ShowWeaponTooltip(x, y: LongInt); |
48 procedure FreeWeaponTooltip; |
40 procedure FreeWeaponTooltip; |
49 |
41 |
50 implementation |
42 implementation |
51 uses uMisc, uConsole, uLocale, uMobile, uVariables, uUtils, uTextures, uIO, uRender; |
43 uses uMisc, uConsole, uLocale, uMobile, uVariables, uUtils, uTextures, uIO, uRender, uRenderUtils; |
52 |
44 |
53 type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple); |
45 type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple); |
54 |
46 |
55 var HHTexture: PTexture; |
47 var HHTexture: PTexture; |
56 MaxTextureSize: LongInt; |
48 MaxTextureSize: LongInt; |
57 cGPUVendor: TGPUVendor; |
49 cGPUVendor: TGPUVendor; |
58 |
|
59 procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean); |
|
60 var r: TSDL_Rect; |
|
61 begin |
|
62 r:= rect^; |
|
63 if Clear then SDL_FillRect(Surface, @r, 0); |
|
64 |
|
65 BorderColor:= SDL_MapRGB(Surface^.format, BorderColor shr 16, BorderColor shr 8, BorderColor and $FF); |
|
66 FillColor:= SDL_MapRGB(Surface^.format, FillColor shr 16, FillColor shr 8, FillColor and $FF); |
|
67 |
|
68 r.y:= rect^.y + 1; |
|
69 r.h:= rect^.h - 2; |
|
70 SDL_FillRect(Surface, @r, BorderColor); |
|
71 r.x:= rect^.x + 1; |
|
72 r.w:= rect^.w - 2; |
|
73 r.y:= rect^.y; |
|
74 r.h:= rect^.h; |
|
75 SDL_FillRect(Surface, @r, BorderColor); |
|
76 r.x:= rect^.x + 2; |
|
77 r.y:= rect^.y + 1; |
|
78 r.w:= rect^.w - 4; |
|
79 r.h:= rect^.h - 2; |
|
80 SDL_FillRect(Surface, @r, FillColor); |
|
81 r.x:= rect^.x + 1; |
|
82 r.y:= rect^.y + 2; |
|
83 r.w:= rect^.w - 2; |
|
84 r.h:= rect^.h - 4; |
|
85 SDL_FillRect(Surface, @r, FillColor) |
|
86 end; |
|
87 |
|
88 function WriteInRoundRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect; |
|
89 var w, h: LongInt; |
|
90 tmpsurf: PSDL_Surface; |
|
91 clr: TSDL_Color; |
|
92 finalRect: TSDL_Rect; |
|
93 begin |
|
94 w:= 0; h:= 0; // avoid compiler hints |
|
95 TTF_SizeUTF8(Fontz[Font].Handle, Str2PChar(s), w, h); |
|
96 finalRect.x:= X; |
|
97 finalRect.y:= Y; |
|
98 finalRect.w:= w + FontBorder * 2 + 4; |
|
99 finalRect.h:= h + FontBorder * 2; |
|
100 DrawRoundRect(@finalRect, cWhiteColor, endian(cNearBlackColorChannels.value), Surface, true); |
|
101 clr.r:= (Color shr 16) and $FF; |
|
102 clr.g:= (Color shr 8) and $FF; |
|
103 clr.b:= Color and $FF; |
|
104 tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(s), clr); |
|
105 finalRect.x:= X + FontBorder + 2; |
|
106 finalRect.y:= Y + FontBorder; |
|
107 SDLTry(tmpsurf <> nil, true); |
|
108 SDL_UpperBlit(tmpsurf, nil, Surface, @finalRect); |
|
109 SDL_FreeSurface(tmpsurf); |
|
110 finalRect.x:= X; |
|
111 finalRect.y:= Y; |
|
112 finalRect.w:= w + FontBorder * 2 + 4; |
|
113 finalRect.h:= h + FontBorder * 2; |
|
114 WriteInRoundRect:= finalRect; |
|
115 end; |
|
116 |
50 |
117 function WriteInRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect; |
51 function WriteInRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect; |
118 var w, h: LongInt; |
52 var w, h: LongInt; |
119 tmpsurf: PSDL_Surface; |
53 tmpsurf: PSDL_Surface; |
120 clr: TSDL_Color; |
54 clr: TSDL_Color; |
495 FreeTexture(ropeIconTex); |
429 FreeTexture(ropeIconTex); |
496 FreeTexture(HHTexture); |
430 FreeTexture(HHTexture); |
497 end; |
431 end; |
498 |
432 |
499 |
433 |
500 function CheckCJKFont(s: ansistring; font: THWFont): THWFont; |
|
501 var l, i : LongInt; |
|
502 u: WideChar; |
|
503 tmpstr: array[0..256] of WideChar; |
|
504 begin |
|
505 |
|
506 {$IFNDEF IPHONEOS} |
|
507 // remove chinese fonts for now |
|
508 if (font >= CJKfnt16) or (length(s) = 0) then |
|
509 {$ENDIF} |
|
510 exit(font); |
|
511 |
|
512 l:= Utf8ToUnicode(@tmpstr, Str2PChar(s), length(s))-1; |
|
513 i:= 0; |
|
514 while i < l do |
|
515 begin |
|
516 u:= tmpstr[i]; |
|
517 if (#$2E80 <= u) and ( |
|
518 (u <= #$2FDF ) or // CJK Radicals Supplement / Kangxi Radicals |
|
519 ((#$2FF0 <= u) and (u <= #$303F)) or // Ideographic Description Characters / CJK Radicals Supplement |
|
520 ((#$31C0 <= u) and (u <= #$31EF)) or // CJK Strokes |
|
521 ((#$3200 <= u) and (u <= #$4DBF)) or // Enclosed CJK Letters and Months / CJK Compatibility / CJK Unified Ideographs Extension A |
|
522 ((#$4E00 <= u) and (u <= #$9FFF)) or // CJK Unified Ideographs |
|
523 ((#$F900 <= u) and (u <= #$FAFF)) or // CJK Compatibility Ideographs |
|
524 ((#$FE30 <= u) and (u <= #$FE4F))) // CJK Compatibility Forms |
|
525 then exit(THWFont( ord(font) + ((ord(High(THWFont))+1) div 2) )); |
|
526 inc(i) |
|
527 end; |
|
528 exit(font); |
|
529 (* two more to check. pascal WideChar is only 16 bit though |
|
530 ((#$20000 <= u) and (u >= #$2A6DF)) or // CJK Unified Ideographs Extension B |
|
531 ((#$2F800 <= u) and (u >= #$2FA1F))) // CJK Compatibility Ideographs Supplement *) |
|
532 end; |
|
533 |
|
534 function RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture; |
|
535 var w, h: LongInt; |
|
536 finalSurface: PSDL_Surface; |
|
537 begin |
|
538 if length(s) = 0 then s:= ' '; |
|
539 font:= CheckCJKFont(s, font); |
|
540 w:= 0; h:= 0; // avoid compiler hints |
|
541 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h); |
|
542 |
|
543 finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, w + FontBorder * 2 + 4, h + FontBorder * 2, |
|
544 32, RMask, GMask, BMask, AMask); |
|
545 |
|
546 TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true); |
|
547 |
|
548 WriteInRoundRect(finalSurface, 0, 0, Color, font, s); |
|
549 |
|
550 TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true); |
|
551 |
|
552 RenderStringTex:= Surface2Tex(finalSurface, false); |
|
553 |
|
554 SDL_FreeSurface(finalSurface); |
|
555 end; |
|
556 |
|
557 function RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture; |
|
558 var textWidth, textHeight, x, y, w, h, i, j, pos, prevpos, line, numLines, edgeWidth, edgeHeight, cornerWidth, cornerHeight: LongInt; |
|
559 finalSurface, tmpsurf, rotatedEdge: PSDL_Surface; |
|
560 rect: TSDL_Rect; |
|
561 chars: TSysCharSet = [#9,' ','.',';',':','?','!',',']; |
|
562 substr: shortstring; |
|
563 edge, corner, tail: TSPrite; |
|
564 begin |
|
565 |
|
566 case SpeechType of |
|
567 1: begin; |
|
568 edge:= sprSpeechEdge; |
|
569 corner:= sprSpeechCorner; |
|
570 tail:= sprSpeechTail; |
|
571 end; |
|
572 2: begin; |
|
573 edge:= sprThoughtEdge; |
|
574 corner:= sprThoughtCorner; |
|
575 tail:= sprThoughtTail; |
|
576 end; |
|
577 3: begin; |
|
578 edge:= sprShoutEdge; |
|
579 corner:= sprShoutCorner; |
|
580 tail:= sprShoutTail; |
|
581 end; |
|
582 end; |
|
583 edgeHeight:= SpritesData[edge].Height; |
|
584 edgeWidth:= SpritesData[edge].Width; |
|
585 cornerWidth:= SpritesData[corner].Width; |
|
586 cornerHeight:= SpritesData[corner].Height; |
|
587 // This one screws up WrapText |
|
588 //s:= 'This is the song that never ends. ''cause it goes on and on my friends. Some people, started singing it not knowing what it was. And they''ll just go on singing it forever just because... This is the song that never ends...'; |
|
589 // This one does not |
|
590 //s:= 'This is the song that never ends. cause it goes on and on my friends. Some people, started singing it not knowing what it was. And they will go on singing it forever just because... This is the song that never ends... '; |
|
591 |
|
592 numLines:= 0; |
|
593 |
|
594 if length(s) = 0 then s:= '...'; |
|
595 font:= CheckCJKFont(s, font); |
|
596 w:= 0; h:= 0; // avoid compiler hints |
|
597 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h); |
|
598 if w<8 then w:= 8; |
|
599 j:= 0; |
|
600 if (length(s) > 20) then |
|
601 begin |
|
602 w:= 0; |
|
603 i:= round(Sqrt(length(s)) * 2); |
|
604 s:= WrapText(s, #1, chars, i); |
|
605 pos:= 1; prevpos:= 0; line:= 0; |
|
606 // Find the longest line for the purposes of centring the text. Font dependant. |
|
607 while pos <= length(s) do |
|
608 begin |
|
609 if (s[pos] = #1) or (pos = length(s)) then |
|
610 begin |
|
611 inc(numlines); |
|
612 if s[pos] <> #1 then inc(pos); |
|
613 while s[prevpos+1] = ' ' do inc(prevpos); |
|
614 substr:= copy(s, prevpos+1, pos-prevpos-1); |
|
615 i:= 0; j:= 0; |
|
616 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(substr), i, j); |
|
617 if i > w then w:= i; |
|
618 prevpos:= pos; |
|
619 end; |
|
620 inc(pos); |
|
621 end; |
|
622 end |
|
623 else numLines := 1; |
|
624 |
|
625 textWidth:=((w-(cornerWidth-edgeWidth)*2) div edgeWidth)*edgeWidth+edgeWidth; |
|
626 textHeight:=(((numlines * h + 2)-((cornerHeight-edgeWidth)*2)) div edgeWidth)*edgeWidth; |
|
627 |
|
628 textHeight:=max(textHeight,edgeWidth); |
|
629 //textWidth:=max(textWidth,SpritesData[tail].Width); |
|
630 rect.x:= 0; |
|
631 rect.y:= 0; |
|
632 rect.w:= textWidth + (cornerWidth * 2); |
|
633 rect.h:= textHeight + cornerHeight*2 - edgeHeight + SpritesData[tail].Height; |
|
634 //s:= inttostr(w) + ' ' + inttostr(numlines) + ' ' + inttostr(rect.x) + ' '+inttostr(rect.y) + ' ' + inttostr(rect.w) + ' ' + inttostr(rect.h); |
|
635 |
|
636 finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, rect.w, rect.h, 32, RMask, GMask, BMask, AMask); |
|
637 |
|
638 TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true); |
|
639 |
|
640 //////////////////////////////// CORNERS /////////////////////////////// |
|
641 copyToXY(SpritesData[corner].Surface, finalSurface, 0, 0); /////////////////// NW |
|
642 |
|
643 flipSurface(SpritesData[corner].Surface, true); // store all 4 versions in memory to avoid repeated flips? |
|
644 x:= 0; |
|
645 y:= textHeight + cornerHeight -1; |
|
646 copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SW |
|
647 |
|
648 flipSurface(SpritesData[corner].Surface, false); |
|
649 x:= rect.w-cornerWidth-1; |
|
650 y:= textHeight + cornerHeight -1; |
|
651 copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SE |
|
652 |
|
653 flipSurface(SpritesData[corner].Surface, true); |
|
654 x:= rect.w-cornerWidth-1; |
|
655 y:= 0; |
|
656 copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// NE |
|
657 flipSurface(SpritesData[corner].Surface, false); // restore original position |
|
658 //////////////////////////////// END CORNERS /////////////////////////////// |
|
659 |
|
660 //////////////////////////////// EDGES ////////////////////////////////////// |
|
661 x:= cornerWidth; |
|
662 y:= 0; |
|
663 while x < rect.w-cornerWidth-1 do |
|
664 begin |
|
665 copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// top edge |
|
666 inc(x,edgeWidth); |
|
667 end; |
|
668 flipSurface(SpritesData[edge].Surface, true); |
|
669 x:= cornerWidth; |
|
670 y:= textHeight + cornerHeight*2 - edgeHeight-1; |
|
671 while x < rect.w-cornerWidth-1 do |
|
672 begin |
|
673 copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// bottom edge |
|
674 inc(x,edgeWidth); |
|
675 end; |
|
676 flipSurface(SpritesData[edge].Surface, true); // restore original position |
|
677 |
|
678 rotatedEdge:= SDL_CreateRGBSurface(SDL_SWSURFACE, edgeHeight, edgeWidth, 32, RMask, GMask, BMask, AMask); |
|
679 x:= rect.w - edgeHeight - 1; |
|
680 y:= cornerHeight; |
|
681 //// initially was going to rotate in place, but the SDL spec claims width/height are read only |
|
682 copyRotatedSurface(SpritesData[edge].Surface,rotatedEdge); |
|
683 while y < textHeight + cornerHeight do |
|
684 begin |
|
685 copyToXY(rotatedEdge, finalSurface, x, y); |
|
686 inc(y,edgeWidth); |
|
687 end; |
|
688 flipSurface(rotatedEdge, false); // restore original position |
|
689 x:= 0; |
|
690 y:= cornerHeight; |
|
691 while y < textHeight + cornerHeight do |
|
692 begin |
|
693 copyToXY(rotatedEdge, finalSurface, x, y); |
|
694 inc(y,edgeWidth); |
|
695 end; |
|
696 //////////////////////////////// END EDGES ////////////////////////////////////// |
|
697 |
|
698 x:= cornerWidth; |
|
699 y:= textHeight + cornerHeight * 2 - edgeHeight - 1; |
|
700 copyToXY(SpritesData[tail].Surface, finalSurface, x, y); |
|
701 |
|
702 rect.x:= edgeHeight; |
|
703 rect.y:= edgeHeight; |
|
704 rect.w:= rect.w - edgeHeight * 2; |
|
705 rect.h:= textHeight + cornerHeight * 2 - edgeHeight * 2; |
|
706 i:= rect.w; |
|
707 j:= rect.h; |
|
708 SDL_FillRect(finalSurface, @rect, cWhiteColor); |
|
709 |
|
710 pos:= 1; prevpos:= 0; line:= 0; |
|
711 while pos <= length(s) do |
|
712 begin |
|
713 if (s[pos] = #1) or (pos = length(s)) then |
|
714 begin |
|
715 if s[pos] <> #1 then inc(pos); |
|
716 while s[prevpos+1] = ' 'do inc(prevpos); |
|
717 substr:= copy(s, prevpos+1, pos-prevpos-1); |
|
718 if Length(substr) <> 0 then |
|
719 begin |
|
720 tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(substr), cNearBlackColorChannels); |
|
721 rect.x:= edgeHeight + 1 + ((i - w) div 2); |
|
722 // trying to more evenly position the text, vertically |
|
723 rect.y:= edgeHeight + ((j-(numLines*h)) div 2) + line * h; |
|
724 SDLTry(tmpsurf <> nil, true); |
|
725 SDL_UpperBlit(tmpsurf, nil, finalSurface, @rect); |
|
726 SDL_FreeSurface(tmpsurf); |
|
727 inc(line); |
|
728 prevpos:= pos; |
|
729 end; |
|
730 end; |
|
731 inc(pos); |
|
732 end; |
|
733 |
|
734 //TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true); |
|
735 RenderSpeechBubbleTex:= Surface2Tex(finalSurface, true); |
|
736 |
|
737 SDL_FreeSurface(rotatedEdge); |
|
738 SDL_FreeSurface(finalSurface); |
|
739 end; |
|
740 |
|
741 procedure RenderHealth(var Hedgehog: THedgehog); |
434 procedure RenderHealth(var Hedgehog: THedgehog); |
742 var s: shortstring; |
435 var s: shortstring; |
743 begin |
436 begin |
744 str(Hedgehog.Gear^.Health, s); |
437 str(Hedgehog.Gear^.Health, s); |
745 if Hedgehog.HealthTagTex <> nil then |
438 if Hedgehog.HealthTagTex <> nil then |
961 WriteLnToConsole('Freeing progress surface... '); |
654 WriteLnToConsole('Freeing progress surface... '); |
962 FreeTexture(ProgrTex); |
655 FreeTexture(ProgrTex); |
963 perfExt_FinishProgress(); |
656 perfExt_FinishProgress(); |
964 end; |
657 end; |
965 |
658 |
966 procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean); |
|
967 var y, x, i, j: LongInt; |
|
968 tmpPixel: Longword; |
|
969 pixels: PLongWordArray; |
|
970 begin |
|
971 TryDo(Surface^.format^.BytesPerPixel = 4, 'flipSurface failed, expecting 32 bit surface', true); |
|
972 pixels:= Surface^.pixels; |
|
973 if Vertical then |
|
974 for y := 0 to (Surface^.h div 2) - 1 do |
|
975 for x := 0 to Surface^.w - 1 do |
|
976 begin |
|
977 i:= y * Surface^.w + x; |
|
978 j:= (Surface^.h - y - 1) * Surface^.w + x; |
|
979 tmpPixel:= pixels^[i]; |
|
980 pixels^[i]:= pixels^[j]; |
|
981 pixels^[j]:= tmpPixel; |
|
982 end |
|
983 else |
|
984 for x := 0 to (Surface^.w div 2) - 1 do |
|
985 for y := 0 to Surface^.h -1 do |
|
986 begin |
|
987 i:= y*Surface^.w + x; |
|
988 j:= y*Surface^.w + (Surface^.w - x - 1); |
|
989 tmpPixel:= pixels^[i]; |
|
990 pixels^[i]:= pixels^[j]; |
|
991 pixels^[j]:= tmpPixel; |
|
992 end; |
|
993 end; |
|
994 |
|
995 procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); |
|
996 var srcX, srcY, i, j, maxDest: LongInt; |
|
997 srcPixels, destPixels: PLongWordArray; |
|
998 r0, g0, b0, a0, r1, g1, b1, a1: Byte; |
|
999 begin |
|
1000 maxDest:= (dest^.pitch div 4) * dest^.h; |
|
1001 srcPixels:= src^.pixels; |
|
1002 destPixels:= dest^.pixels; |
|
1003 |
|
1004 for srcX:= 0 to src^.w - 1 do |
|
1005 for srcY:= 0 to src^.h - 1 do |
|
1006 begin |
|
1007 i:= (destY + srcY) * (dest^.pitch div 4) + destX + srcX; |
|
1008 j:= srcY * (src^.pitch div 4) + srcX; |
|
1009 if (i < maxDest) and (srcPixels^[j] and AMask <> 0) then |
|
1010 begin |
|
1011 SDL_GetRGBA(destPixels^[i], dest^.format, @r0, @g0, @b0, @a0); |
|
1012 SDL_GetRGBA(srcPixels^[j], src^.format, @r1, @g1, @b1, @a1); |
|
1013 r0:= (r0 * (255 - LongInt(a1)) + r1 * LongInt(a1)) div 255; |
|
1014 g0:= (g0 * (255 - LongInt(a1)) + g1 * LongInt(a1)) div 255; |
|
1015 b0:= (b0 * (255 - LongInt(a1)) + b1 * LongInt(a1)) div 255; |
|
1016 a0:= (a0 * (255 - LongInt(a1)) + a1 * LongInt(a1)) div 255; |
|
1017 destPixels^[i]:= SDL_MapRGBA(dest^.format, r0, g0, b0, a0); |
|
1018 end; |
|
1019 end; |
|
1020 end; |
|
1021 |
|
1022 procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL, apparently |
|
1023 var y, x, i, j: LongInt; |
|
1024 srcPixels, destPixels: PLongWordArray; |
|
1025 begin |
|
1026 TryDo(src^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true); |
|
1027 TryDo(dest^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true); |
|
1028 |
|
1029 srcPixels:= src^.pixels; |
|
1030 destPixels:= dest^.pixels; |
|
1031 |
|
1032 j:= 0; |
|
1033 for x := 0 to src^.w - 1 do |
|
1034 for y := 0 to src^.h - 1 do |
|
1035 begin |
|
1036 i:= (src^.h - 1 - y) * (src^.pitch div 4) + x; |
|
1037 destPixels^[j]:= srcPixels^[i]; |
|
1038 inc(j) |
|
1039 end; |
|
1040 end; |
|
1041 |
|
1042 function RenderHelpWindow(caption, subcaption, description, extra: ansistring; extracolor: LongInt; iconsurf: PSDL_Surface; iconrect: PSDL_Rect): PTexture; |
659 function RenderHelpWindow(caption, subcaption, description, extra: ansistring; extracolor: LongInt; iconsurf: PSDL_Surface; iconrect: PSDL_Rect): PTexture; |
1043 var tmpsurf: PSDL_SURFACE; |
660 var tmpsurf: PSDL_SURFACE; |
1044 w, h, i, j: LongInt; |
661 w, h, i, j: LongInt; |
1045 font: THWFont; |
662 font: THWFont; |
1046 r, r2: TSDL_Rect; |
663 r, r2: TSDL_Rect; |