Author Topic: TextToImage and DisplayImage (v2.0+ compliant)  (Read 5021 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
TextToImage and DisplayImage (v2.0+ compliant)
« on: November 07, 2021, 06:14:45 am »
Two of my most used routines, updated to work properly with version 2.0 and above:

Code: QB64: [Select]
  1. 'SMcNeill  ref https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  2. Screen _NewImage(800, 700, 32)
  3. For i = 1 To 4
  4.     HW(i) = TextToImage("Hello World", 16, &HFFFFFF00, 0, i)
  5.  
  6.  
  7. Print "The first thing to showcase with these two routines is just how simple it is to turn"
  8. Print "text into an image with TextToImage."
  9. Print "First, let's print it forwards:"
  10. _PutImage (250, 48), HW(1)
  11. Print "Then, let's print it backwards:"
  12. _PutImage (250, 80), HW(2)
  13. Print "Then, let's print it up to down:"
  14. _PutImage (270, 112), HW(3)
  15. Locate 8, 40: Print "And let's finish with down to up:"
  16. _PutImage (580, 112), HW(4)
  17. Locate 20, 1
  18. Print "TextToImage simply transforms text into an image for us, with a few built in options"
  19. Print "to it for the direction we want we text to print."
  20. Print "It's as simple as a call with:"
  21. Print "    Handle = TextToImage(text$, fonthandle, fontcolor, backgroundcolor, mode"
  22. Print "        text$ is the string which we want to print.  (In this case 'Hello World'"
  23. Print "        fonthandle is the handle of the font which we _LOADFONT for use."
  24. Print "            (In this case, I choose the default _FONT 16.)"
  25. Print "        fontcolor is the color which we want our text in.  (Here, it's YELLOW.)"
  26. Print "        backgroundcolor is the background which we want for the text time.  (Clear this time.)"
  27. Print "        mode is how we decide to print forwards, backwards, up to down, or down to up."
  28. Print "Once we have an image handle, we can use that image just the same as we can with any other."
  29. Print "For those who don't need to do anything more than display the text as an image,"
  30. Print "feel free to use it as I have in the first part of this program with _PUTIMAGE."
  31. Print "Trust me -- TextToImage works just fine with _PUTIMAGE."
  32. Print "But....   If you need more..."
  33.  
  34. Cls , 0
  35.  
  36. Print "There's always DisplayImage to help you out!"
  37. DisplayImage HW(1), 300, 30, 1, 1, 0, 1
  38. Print "Display your image at a scale!"
  39. Print "Twice as wide! ";
  40. DisplayImage HW(1), 300, 60, 2, 1, 0, 1
  41. Print "Twice as tall! "
  42. DisplayImage HW(1), 500, 60, 1, 2, 0, 1
  43. Print "At an angle!"
  44. DisplayImage HW(1), 280, 90, 1, 1, -45, 1
  45. Print "Make it rotate!"
  46.     Line (355, 155)-Step(100, 100), &HFF000000, BF
  47.     DisplayImage HW(1), 400, 200, 1, 1, angle, 0
  48.  
  49.     angle = (angle + 1) Mod 360
  50.     _Limit 30
  51.     _Display
  52. Print "You can basically use DisplayImage just as you'd normally use RotoZoom, EXCEPT..."
  53. Print "You can choose which CORNER of the image you want to display at your coordinates."
  54. Line (350, 350)-Step(100, 100), -1, B
  55. Circle (400, 400), 10, -1
  56. Print "Top Left corner! ";
  57. DisplayImage HW(1), 400, 400, 2, 2, 0, 1
  58. Print "Bottom Left corner! ";
  59. DisplayImage HW(1), 400, 400, 2, 2, 0, 2
  60. Print "Top Right corner! ";
  61. DisplayImage HW(1), 400, 400, 2, 2, 0, 3
  62. Print "Bottom Right corner! "
  63. DisplayImage HW(1), 400, 400, 2, 2, 0, 4
  64. HW(1) = TextToImage("Hello World", 16, &HFFFF0000, &HFF0000FF, 1)
  65. Print "Or Centered!"
  66. DisplayImage HW(1), 400, 400, 2, 2, 0, 0
  67. Circle (400, 400), 10, -1
  68.  
  69.  
  70. Print "With TextToImage, you can turn text into an image...  It's that simple!"
  71. Print "With DisplayImage, you have a ton of options for how to display ANY image"
  72. Print "   (NOT just for use with text images!!)"
  73. Print "Scale them, stretch them, rotate them, position them by various corners..."
  74. Print "Between these two routines, I generally don't need anything else when working"
  75. Print "   with images in my programs.  ;)"
  76. Print "And that's THE END of this demo.  Post any questions you have on the forums for me!"
  77.  
  78.  
  79.  
  80.  
  81.  
  82. ' (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single,
  83. '  angle As Single, mode As _Byte)
  84.  
  85.  
  86.  
  87.  
  88. Function TextToImage& (text$, font&, fc&, bfc&, mode As _Byte)
  89.     'text$ is the text that we wish to transform into an image.
  90.     'font& is the handle of the font we want to use.
  91.     'fc& is the color of the font we want to use.
  92.     'bfc& is the background color of the font.
  93.  
  94.     'Mode 1 is print forwards
  95.     'Mode 2 is print backwards
  96.     'Mode 3 is print from top to bottom
  97.     'Mode 4 is print from bottom up
  98.     'Mode 0 got lost somewhere, but it's OK.  We check to see if our mode is < 1 or > 4 and compensate automatically if it is to make it one (default).
  99.  
  100.     If mode < 1 Or mode > 4 Then mode = 1
  101.     dc& = _DefaultColor: bgc& = _BackgroundColor
  102.     D = _Dest
  103.     F = _Font
  104.     T2Idown = CsrLin: T2Iright = Pos(0)
  105.     If font& <> 0 Then _Font font&
  106.     If mode < 3 Then
  107.         'print the text lengthwise
  108.         w& = _PrintWidth(text$): h& = _FontHeight
  109.     Else
  110.         'print the text vertically
  111.         For i = 1 To Len(text$)
  112.             If w& < _PrintWidth(Mid$(text$, i, 1)) Then w& = _PrintWidth(Mid$(text$, i, 1))
  113.         Next
  114.         h& = _FontHeight * (Len(text$))
  115.     End If
  116.  
  117.     TextToImage_temp& = _NewImage(w&, h&, 32)
  118.     TextToImage = TextToImage_temp&
  119.     _Dest TextToImage_temp&
  120.     If font& <> 0 Then _Font font&
  121.     Color fc&, bfc&
  122.  
  123.     Select Case mode
  124.         Case 1
  125.             'Print text forward
  126.             _PrintString (0, 0), text$
  127.         Case 2
  128.             'Print text backwards
  129.             temp$ = ""
  130.             For i = 0 To Len(text$) - 1
  131.                 temp$ = temp$ + Mid$(text$, Len(text$) - i, 1)
  132.             Next
  133.             _PrintString (0, 0), temp$
  134.         Case 3
  135.             'Print text upwards
  136.             'first lets reverse the text, so it's easy to place
  137.             temp$ = ""
  138.             For i = 0 To Len(text$) - 1
  139.                 temp$ = temp$ + Mid$(text$, Len(text$) - i, 1)
  140.             Next
  141.             'then put it where it belongs
  142.             For i = 1 To Len(text$)
  143.                 fx = (w& - _PrintWidth(Mid$(temp$, i, 1))) / 2 + .99 'This is to center any non-monospaced letters so they look better
  144.                 _PrintString (fx, _FontHeight * (i - 1)), Mid$(temp$, i, 1)
  145.             Next
  146.         Case 4
  147.             'Print text downwards
  148.             For i = 1 To Len(text$)
  149.                 fx = (w& - _PrintWidth(Mid$(text$, i, 1))) / 2 + .99 'This is to center any non-monospaced letters so they look better
  150.                 _PrintString (fx, _FontHeight * (i - 1)), Mid$(text$, i, 1)
  151.             Next
  152.     End Select
  153.     _Dest D
  154.     Color dc&, bgc&
  155.     _Font F
  156.     Locate T2Idown, T2Iright
  157.  
  158. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single, angle As Single, mode As _Byte)
  159.     'Image is the image handle which we use to reference our image.
  160.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  161.     'angle is the angle which we wish to rotate the image.
  162.     'mode determines HOW we place the image at point X,Y.
  163.     'Mode 0 we center the image at point X,Y
  164.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  165.     'Mode 2 is Bottom Left
  166.     'Mode 3 is Top Right
  167.     'Mode 4 is Bottom Right
  168.  
  169.  
  170.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  171.     Dim sinr As Single, cosr As Single, i As _Byte
  172.     w = _Width(Image): h = _Height(Image)
  173.     Select Case mode
  174.         Case 0 'center
  175.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  176.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  177.         Case 1 'top left
  178.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  179.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  180.         Case 2 'bottom left
  181.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  182.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  183.         Case 3 'top right
  184.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  185.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  186.         Case 4 'bottom right
  187.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  188.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  189.     End Select
  190.     sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  191.     For i = 0 To 3
  192.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  193.         px(i) = x2: py(i) = y2
  194.     Next
  195.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  196.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  197.  
  198.  

The fix to recursion in SUBs and FUNCTIONs keeps sneaking into my code from time to time, and I keep having to make little adjustments to things, but I'm surprised it took so long for me to notice the glitches in these two routines as I use them constantly!

Anywho...  Here they are, all spruced up and fixed for use with the newest versions of QB64.  If you're one of the people who have made use of these routines in the past, you might want to check your code and swap out to the updated versions here, in case you ever need to compile you EXE again in the future.  ;)
« Last Edit: November 07, 2021, 02:17:59 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #1 on: November 07, 2021, 09:41:30 am »
@SMcNeill

Your DisplayImage looks like an advancement to RotoZoom3 with the modes.

I wonder if it works better that RotoZoom3 that I tried yesterday with Pie Chart 2 Animation. Looked so raggedy I just hand drew the Pie Chart at the different angles. I will experiment.

Screen works fine at 800 X 700 no delays needed for:
Screen _NewImage(800, 700, 32)
_ScreenMove 200, 0

Nice stuff!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #2 on: November 07, 2021, 11:24:40 am »
Update just as raggedy.

@SMcNeill  should note that the angle offset is in degrees and that increasing it turns it counter clock wise.

This is counter intuitive because increase the angle (radian or degrees) in Sin and Cos takes you in clockwise direction around the center of a circle.

Here is my test code:
Code: QB64: [Select]
  1. _Title "Pie Chart 3 Test DisplayImage" ' b+ 2021-11-06 fix up Russian Circle Diagram
  2. 'ref https://www.qb64.org/forum/index.php?topic=4367.msg137864#msg137864
  3. '2021-11-07 PieChart 3 Test DisplayImage
  4. ' also move one for block out of loop, only need once
  5. N = 13
  6. Dim d(1 To N), sumD(1 To N), ra(1 To N)
  7. For i = 1 To N
  8.     d(i) = Int(Rnd * 90 + 9) ' min 9, max 99.99..
  9.     s = s + d(i)
  10.     sumD(i) = s
  11. For i = 1 To N ' better fill method?
  12.     ra(i) = sumD(i) * 2 * 3.1416 / s
  13. radius = _Height / 2.5
  14. Circle (_Width / 2 + 50, _Height / 2), radius, &HFFFFFFFF
  15. For i = 1 To N
  16.     x = _Width / 2 + 50 + radius * Cos(ra(i) + offset)
  17.     y = _Height / 2 + radius * Sin(ra(i) + offset)
  18.     Line (_Width / 2 + 50, _Height / 2)-(x, y), &HFFFFFFFF
  19.     Paint (_Width / 2 + 50 + (radius - 10) * Cos(ra(i) + offset - _Pi(1 / 36)), _Height / 2 + (radius - 10) * Sin(ra(i) + offset - _Pi(1 / 36))), i, &HFFFFFFFF
  20. For i = 1 To N
  21.     If i = 1 Then a = ra(1) / 2 + offset Else a = (ra(i) + ra(i - 1)) / 2 + offset
  22.     x = _Width / 2 + 50 + _Height / 3 * Cos(a)
  23.     y = _Height / 2 + _Height / 3 * Sin(a) ' <<<<<<<<<< NOT backwards circle function!!!
  24.     s$ = _Trim$(Str$(d(i)))
  25.     Color 0: _PrintString (x + 2 - 3.5 * Len(s$), y + 2 - 8), s$
  26.     Color 15: _PrintString (x - 3.5 * Len(s$), y - 8), _Trim$(Str$(d(i)))
  27. xc = _Width / 2 + 50
  28. yc = _Height / 2
  29. wheel& = _NewImage(2 * radius + 2, 2 * radius + 2, 12)
  30. _PutImage , 0, wheel&, (xc - radius - 1, yc - radius - 1)-(xc + radius + 1, yc + radius + 1)
  31. 'Cls ' check image capture
  32. '_PutImage , wheel&, 0
  33. 'Sleep
  34.  
  35. Cls ' do this once
  36. For i = 1 To N 'redraw
  37.     Color i: Print d(i); ra(i)
  38. Color 15: Print s; "= SUM "
  39.     offset = offset + _Pi(1 / 360)
  40.  
  41.     '           ==== Testing
  42.     ' Testing the two subroutines for image rotation, un-comment the one to try and comment out the other
  43.     'RotoZoom3 xc, yc, wheel&, 1, 1, offset ' very raggedy with bad edge on circle
  44.     DisplayImage wheel&, xc, yc, 1, 1, _R2D(offset), 0
  45.     ' ==================================================================================================
  46.  
  47.     _Display
  48.     _Limit 30
  49.  
  50. ' Description:
  51. ' Started from a mod of Galleon's in Wiki that both scales and rotates an image.
  52. ' This version scales the x-axis and y-axis independently allowing rotations of image just by changing X or Y Scales
  53. ' making this tightly coded routine a very powerful and versatile image tool.
  54. Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single)
  55.     ' This assumes you have set your drawing location with _DEST or default to screen.
  56.     ' X, Y - is where you want to put the middle of the image
  57.     ' Image - is the handle assigned with _LOADIMAGE
  58.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  59.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  60.     ' radianRotation is the Angle in Radian units to rotate the image
  61.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  62.     '       and saves a little time converting from degree.
  63.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  64.  
  65.     Dim px(3) As Single: Dim py(3) As Single ' simple arrays for x, y to hold the 4 corners of image
  66.     Dim W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  67.     W& = _Width(Image&): H& = _Height(Image&)
  68.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  69.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  70.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  71.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  72.     sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation) ' rotation helpers
  73.     For i& = 0 To 3 ' calc new point locations with rotation and zoom
  74.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  75.         px(i&) = x2&: py(i&) = y2&
  76.     Next
  77.     _MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  78.     _MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  79.  
  80. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single, angle As Single, mode As _Byte)
  81.     ' SMcNeill ref:  https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  82.     'Image is the image handle which we use to reference our image.
  83.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  84.     'angle is the angle which we wish to rotate the image.
  85.     'mode determines HOW we place the image at point X,Y.
  86.     'Mode 0 we center the image at point X,Y
  87.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  88.     'Mode 2 is Bottom Left
  89.     'Mode 3 is Top Right
  90.     'Mode 4 is Bottom Right
  91.  
  92.  
  93.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  94.     Dim sinr As Single, cosr As Single, i As _Byte
  95.     w = _Width(Image): h = _Height(Image)
  96.     Select Case mode
  97.         Case 0 'center
  98.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  99.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  100.         Case 1 'top left
  101.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  102.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  103.         Case 2 'bottom left
  104.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  105.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  106.         Case 3 'top right
  107.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  108.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  109.         Case 4 'bottom right
  110.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  111.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  112.     End Select
  113.     sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  114.     For i = 0 To 3
  115.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  116.         px(i) = x2: py(i) = y2
  117.     Next
  118.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  119.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  120.  
  121.  
  122.  

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #3 on: November 07, 2021, 12:31:32 pm »
They both produce the exact same screens.

Code: QB64: [Select]
  1. _Title "Pie Chart 3 Test DisplayImage" ' b+ 2021-11-06 fix up Russian Circle Diagram
  2. 'ref https://www.qb64.org/forum/index.php?topic=4367.msg137864#msg137864
  3. '2021-11-07 PieChart 3 Test DisplayImage
  4. ' also move one for block out of loop, only need once
  5. 'Screen 12
  6.  
  7. Dim Shared FirstScreen, SecondScreen
  8.  
  9. FirstScreen = _NewImage(640, 480, 256)
  10. SecondScreen = _NewImage(640, 480, 256)
  11. Screen FirstScreen
  12.  
  13. N = 13
  14. Dim d(1 To N), sumD(1 To N), ra(1 To N)
  15. For i = 1 To N
  16.     d(i) = Int(Rnd * 90 + 9) ' min 9, max 99.99..
  17.     S = S + d(i)
  18.     sumD(i) = S
  19. For i = 1 To N ' better fill method?
  20.     ra(i) = sumD(i) * 2 * 3.1416 / S
  21. radius = _Height / 2.5
  22. Circle (_Width / 2 + 50, _Height / 2), radius, &HFFFFFFFF
  23. For i = 1 To N
  24.     x = _Width / 2 + 50 + radius * Cos(ra(i) + offset)
  25.     y = _Height / 2 + radius * Sin(ra(i) + offset)
  26.     Line (_Width / 2 + 50, _Height / 2)-(x, y), &HFFFFFFFF
  27.     Paint (_Width / 2 + 50 + (radius - 10) * Cos(ra(i) + offset - _Pi(1 / 36)), _Height / 2 + (radius - 10) * Sin(ra(i) + offset - _Pi(1 / 36))), i, &HFFFFFFFF
  28. For i = 1 To N
  29.     If i = 1 Then a = ra(1) / 2 + offset Else a = (ra(i) + ra(i - 1)) / 2 + offset
  30.     x = _Width / 2 + 50 + _Height / 3 * Cos(a)
  31.     y = _Height / 2 + _Height / 3 * Sin(a) ' <<<<<<<<<< NOT backwards circle function!!!
  32.     s$ = _Trim$(Str$(d(i)))
  33.     Color 0: _PrintString (x + 2 - 3.5 * Len(s$), y + 2 - 8), s$
  34.     Color 15: _PrintString (x - 3.5 * Len(s$), y - 8), _Trim$(Str$(d(i)))
  35. xc = _Width / 2 + 50
  36. yc = _Height / 2
  37. wheel& = _NewImage(2 * radius + 2, 2 * radius + 2, 12)
  38.  
  39.  
  40. _PutImage , 0, wheel&, (xc - radius - 1, yc - radius - 1)-(xc + radius + 1, yc + radius + 1)
  41. 'Cls ' check image capture
  42. '_PutImage , wheel&, 0
  43. 'Sleep
  44.  
  45. Cls , 0 ' do this once
  46. For i = 1 To N 'redraw
  47.     '    Color i: Print d(i); ra(i)
  48. 'Color 15: Print S; "= SUM "
  49.     offset = offset + _Pi(1 / 360)
  50.  
  51.     '           ==== Testing
  52.     ' Testing the two subroutines for image rotation, un-comment the one to try and comment out the other
  53.     Screen FirstScreen
  54.     Cls , 0
  55.     Locate 1, 1: Print "First"
  56.     RotoZoom3 xc, yc, wheel&, 1, 1, offset ' very raggedy with bad edge on circle
  57.  
  58.     Sleep
  59.  
  60.     _Dest SecondScreen
  61.     Cls , 0
  62.     Screen SecondScreen
  63.     Locate 1, 1: Print "Second"
  64.     DisplayImage wheel&, xc, yc, 1, 1, offset, 0
  65.     Sleep
  66.     CompareScreens
  67.     ' ==================================================================================================
  68.  
  69.     '  _Display
  70.     _Limit 30
  71.  
  72.  
  73. Sub CompareScreens
  74.     Dim As Integer p1, p2
  75.     For x = 0 To 639
  76.         For y = 20 To 479
  77.             _Source FirstScreen
  78.             p1 = Point(x, y)
  79.             _Source SecondScreen
  80.             p2 = Point(x, y)
  81.             If p1 <> p2 Then _Title "Point mismatch = " + Str$(x) + "," + Str$(y) + Str$(p1) + Str$(p2)
  82.         Next
  83.     Next
  84.  
  85. ' Description:
  86. ' Started from a mod of Galleon's in Wiki that both scales and rotates an image.
  87. ' This version scales the x-axis and y-axis independently allowing rotations of image just by changing X or Y Scales
  88. ' making this tightly coded routine a very powerful and versatile image tool.
  89. Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single)
  90.     ' This assumes you have set your drawing location with _DEST or default to screen.
  91.     ' X, Y - is where you want to put the middle of the image
  92.     ' Image - is the handle assigned with _LOADIMAGE
  93.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  94.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  95.     ' radianRotation is the Angle in Radian units to rotate the image
  96.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  97.     '       and saves a little time converting from degree.
  98.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  99.  
  100.     Dim px(3) As Single: Dim py(3) As Single ' simple arrays for x, y to hold the 4 corners of image
  101.     Dim W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  102.     W& = _Width(Image&): H& = _Height(Image&)
  103.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  104.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  105.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  106.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  107.     sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation) ' rotation helpers
  108.     For i& = 0 To 3 ' calc new point locations with rotation and zoom
  109.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  110.         px(i&) = x2&: py(i&) = y2&
  111.     Next
  112.     _MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  113.     _MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  114.  
  115. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single, angle As Single, mode As _Byte)
  116.     ' SMcNeill ref:  https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  117.     'Image is the image handle which we use to reference our image.
  118.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  119.     'angle is the angle which we wish to rotate the image.
  120.     'mode determines HOW we place the image at point X,Y.
  121.     'Mode 0 we center the image at point X,Y
  122.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  123.     'Mode 2 is Bottom Left
  124.     'Mode 3 is Top Right
  125.     'Mode 4 is Bottom Right
  126.  
  127.  
  128.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  129.     Dim sinr As Single, cosr As Single, i As _Byte
  130.     w = _Width(Image): h = _Height(Image)
  131.     Select Case mode
  132.         Case 0 'center
  133.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  134.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  135.         Case 1 'top left
  136.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  137.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  138.         Case 2 'bottom left
  139.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  140.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  141.         Case 3 'top right
  142.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  143.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  144.         Case 4 'bottom right
  145.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  146.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  147.     End Select
  148.     'sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  149.     sinr! = Sin(-angle): cosr! = Cos(-angle) ' rotation helpers
  150.     For i = 0 To 3
  151.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  152.         px(i) = x2: py(i) = y2
  153.     Next
  154.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  155.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  156.  

As for the angle, that's just my personal preference at work, and it goes in the direction which one would draw on a piece of graph paper.  45 degrees is sloping from (0,0) up to (1,1) and beyond, not from (0,0) to (1,-1).  Your RotoZoom3 even reverses the angles for you automatically:   sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation).

Once I swapped from degree to reverse radian with DisplayScreen, everything works the same as yours, though it's slow as crap and requires you to hold down the spacebar or something to watch as it slowly rotates and compares every pixel from one screen against every pixel on the second screen for a discrepancy..

The only difference I made was this one in the SUB:
    FROM -- sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
    TO     -- sinr! = Sin(-angle): cosr! = Cos(-angle) ' rotation helpers

(along with the change to call without having to use _R2D for the angle)

If there's something raggedy at work, it's the same for both RotoZoom3 and DisplayImage.  Afterall, back when I did DisplayImage ages ago, I based it off RotoZoom at the time so that it'd be able to work as I needed it to for a program which was drawing boxes on the screen.  (I think it was a calendar type program...)  Each box needed to be positioned starting AT point (x,y) and not CENTERED at point (x,y), which is why I added the mode option.  And, as usual, I just figured, "If I'm going to sort it out for one corner, why not sort it out for all of them?"

Take the mode adjustment out and you basically have your RotoZoom3 line for line, I think.  (Except for mine is set for degrees by default rather than radians, and the angle isn't reversed as you have yours.)

If you draw a circle on paper, angles go counter-clockwise naturally.  45 degrees is in the first quadrant while 315 degrees is in the 4th quadrant.  You've made yours backwards just to have clockwise rotation.  :P



And something completely unrelated, but odd with the test code above:

If you take out all those SLEEP statements, the program goes 100% buggy weird, like so:
Code: QB64: [Select]
  1. _Title "Pie Chart 3 Test DisplayImage" ' b+ 2021-11-06 fix up Russian Circle Diagram
  2. 'ref https://www.qb64.org/forum/index.php?topic=4367.msg137864#msg137864
  3. '2021-11-07 PieChart 3 Test DisplayImage
  4. ' also move one for block out of loop, only need once
  5. 'Screen 12
  6.  
  7. Dim Shared FirstScreen, SecondScreen
  8.  
  9. FirstScreen = _NewImage(640, 480, 256)
  10. SecondScreen = _NewImage(640, 480, 256)
  11. Screen FirstScreen
  12.  
  13. N = 13
  14. Dim d(1 To N), sumD(1 To N), ra(1 To N)
  15. For i = 1 To N
  16.     d(i) = Int(Rnd * 90 + 9) ' min 9, max 99.99..
  17.     S = S + d(i)
  18.     sumD(i) = S
  19. For i = 1 To N ' better fill method?
  20.     ra(i) = sumD(i) * 2 * 3.1416 / S
  21. radius = _Height / 2.5
  22. Circle (_Width / 2 + 50, _Height / 2), radius, &HFFFFFFFF
  23. For i = 1 To N
  24.     x = _Width / 2 + 50 + radius * Cos(ra(i) + offset)
  25.     y = _Height / 2 + radius * Sin(ra(i) + offset)
  26.     Line (_Width / 2 + 50, _Height / 2)-(x, y), &HFFFFFFFF
  27.     Paint (_Width / 2 + 50 + (radius - 10) * Cos(ra(i) + offset - _Pi(1 / 36)), _Height / 2 + (radius - 10) * Sin(ra(i) + offset - _Pi(1 / 36))), i, &HFFFFFFFF
  28. For i = 1 To N
  29.     If i = 1 Then a = ra(1) / 2 + offset Else a = (ra(i) + ra(i - 1)) / 2 + offset
  30.     x = _Width / 2 + 50 + _Height / 3 * Cos(a)
  31.     y = _Height / 2 + _Height / 3 * Sin(a) ' <<<<<<<<<< NOT backwards circle function!!!
  32.     s$ = _Trim$(Str$(d(i)))
  33.     Color 0: _PrintString (x + 2 - 3.5 * Len(s$), y + 2 - 8), s$
  34.     Color 15: _PrintString (x - 3.5 * Len(s$), y - 8), _Trim$(Str$(d(i)))
  35. xc = _Width / 2 + 50
  36. yc = _Height / 2
  37. wheel& = _NewImage(2 * radius + 2, 2 * radius + 2, 12)
  38.  
  39.  
  40. _PutImage , 0, wheel&, (xc - radius - 1, yc - radius - 1)-(xc + radius + 1, yc + radius + 1)
  41. 'Cls ' check image capture
  42. '_PutImage , wheel&, 0
  43. 'Sleep
  44.  
  45. Cls , 0 ' do this once
  46. For i = 1 To N 'redraw
  47.     '    Color i: Print d(i); ra(i)
  48. 'Color 15: Print S; "= SUM "
  49.     offset = offset + _Pi(1 / 360)
  50.  
  51.     '           ==== Testing
  52.     ' Testing the two subroutines for image rotation, un-comment the one to try and comment out the other
  53.     Screen FirstScreen
  54.     Cls , 0
  55.     Locate 1, 1: Print "First"
  56.     RotoZoom3 xc, yc, wheel&, 1, 1, offset ' very raggedy with bad edge on circle
  57.  
  58.     'Sleep
  59.  
  60.     _Dest SecondScreen
  61.     Cls , 0
  62.     Screen SecondScreen
  63.     Locate 1, 1: Print "Second"
  64.     DisplayImage wheel&, xc, yc, 1, 1, offset, 0
  65.  
  66.  
  67.     'Sleep
  68.  
  69.  
  70.     'CompareScreens
  71.     ' ==================================================================================================
  72.  
  73.     '  _Display
  74.     _Limit 30
  75.  
  76.  
  77. Sub CompareScreens
  78.     Dim As Integer p1, p2
  79.     For x = 0 To 639
  80.         For y = 20 To 479
  81.             _Source FirstScreen
  82.             p1 = Point(x, y)
  83.             _Source SecondScreen
  84.             p2 = Point(x, y)
  85.             If p1 <> p2 Then _Title "Point mismatch = " + Str$(x) + "," + Str$(y) + Str$(p1) + Str$(p2)
  86.         Next
  87.     Next
  88.  
  89. ' Description:
  90. ' Started from a mod of Galleon's in Wiki that both scales and rotates an image.
  91. ' This version scales the x-axis and y-axis independently allowing rotations of image just by changing X or Y Scales
  92. ' making this tightly coded routine a very powerful and versatile image tool.
  93. Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single)
  94.     ' This assumes you have set your drawing location with _DEST or default to screen.
  95.     ' X, Y - is where you want to put the middle of the image
  96.     ' Image - is the handle assigned with _LOADIMAGE
  97.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  98.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  99.     ' radianRotation is the Angle in Radian units to rotate the image
  100.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  101.     '       and saves a little time converting from degree.
  102.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  103.  
  104.     Dim px(3) As Single: Dim py(3) As Single ' simple arrays for x, y to hold the 4 corners of image
  105.     Dim W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  106.     W& = _Width(Image&): H& = _Height(Image&)
  107.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  108.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  109.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  110.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  111.     sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation) ' rotation helpers
  112.     For i& = 0 To 3 ' calc new point locations with rotation and zoom
  113.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  114.         px(i&) = x2&: py(i&) = y2&
  115.     Next
  116.     _MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  117.     _MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  118.  
  119. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single, angle As Single, mode As _Byte)
  120.     ' SMcNeill ref:  https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  121.     'Image is the image handle which we use to reference our image.
  122.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  123.     'angle is the angle which we wish to rotate the image.
  124.     'mode determines HOW we place the image at point X,Y.
  125.     'Mode 0 we center the image at point X,Y
  126.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  127.     'Mode 2 is Bottom Left
  128.     'Mode 3 is Top Right
  129.     'Mode 4 is Bottom Right
  130.  
  131.  
  132.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  133.     Dim sinr As Single, cosr As Single, i As _Byte
  134.     w = _Width(Image): h = _Height(Image)
  135.     Select Case mode
  136.         Case 0 'center
  137.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  138.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  139.         Case 1 'top left
  140.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  141.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  142.         Case 2 'bottom left
  143.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  144.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  145.         Case 3 'top right
  146.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  147.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  148.         Case 4 'bottom right
  149.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  150.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  151.     End Select
  152.     'sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  153.     sinr! = Sin(-angle): cosr! = Cos(-angle) ' rotation helpers
  154.     For i = 0 To 3
  155.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  156.         px(i) = x2: py(i) = y2
  157.     Next
  158.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  159.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  160.  

Apparently, GL doesn't like the constant switching from SCREEN FirstScreen to SCREEN SecondScreen

WARNING: If you decide to test the second code, for the love of your PC, know how to open your Task Manager and end a process.  I've tested it several times and lets just say the results end up getting completely unreliable with us.  The red X in the top right *might* end and close the program.  It might not...   Alt-F4 may work...  ESC might stop it...  Then again, it may not...

The constant swap from one SCREEN to another 30 times per second ends up doing something to the video memory with GL.  Honestly, I'd suggest being prepared to do a system reboot once you're finished playing around with it and watching it die horribly.


Even more oddly, if you REPLACE those two SLEEP statements with _DISPLAY and make the screen manually update, the program works just fine.  It's just wuth _AUTODISPLAY at work that GL doesn't like the swapping of screen modes constantly.

Who knew?!!
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #4 on: November 07, 2021, 12:42:34 pm »
Quote
Once I swapped from degree to reverse radian with DisplayScreen, everything works the same as yours, though it's slow as crap and requires you to hold down the spacebar or something to watch as it slowly rotates and compares every pixel from one screen against every pixel on the second screen for a discrepancy..

Slow as crap because I am using angle in Radian Units and you are using angle in Degrees Units, notice in my code test I converted offset to Degrees before trying your DisplayImage.
Quote
    '           ==== Testing
    ' Testing the two subroutines for image rotation, un-comment the one to try and comment out the other
    'RotoZoom3 xc, yc, wheel&, 1, 1, offset ' very raggedy with bad edge on circle
    DisplayImage wheel&, xc, yc, 1, 1, _R2D(offset), 0
    ' ==================================================================================================

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #5 on: November 07, 2021, 01:12:49 pm »
Slow as crap because I am using angle in Radian Units and you are using angle in Degrees Units, notice in my code test I converted offset to Degrees before trying your DisplayImage.

I was talking about my second code where I made the changes to compare results as being slow as crap, and both routines there are using radian angles.  As far as Rotozoom and DisplayImage in the original, both run and rotate the screen at around 900+ FPS on my laptop -- more than fast enough for a program with a _LIMIT 30.  (And that's with us clearing and printing the text to both screens as well on each pass so they'll look identical.)  :P

If there's a difference in speed or performance in the two routines, I'm not seeing it. 

Code: QB64: [Select]
  1. _Title "Pie Chart 3 Test DisplayImage" ' b+ 2021-11-06 fix up Russian Circle Diagram
  2. 'ref https://www.qb64.org/forum/index.php?topic=4367.msg137864#msg137864
  3. '2021-11-07 PieChart 3 Test DisplayImage
  4. ' also move one for block out of loop, only need once
  5. N = 13
  6. Dim d(1 To N), sumD(1 To N), ra(1 To N)
  7. For i = 1 To N
  8.     d(i) = Int(Rnd * 90 + 9) ' min 9, max 99.99..
  9.     s = s + d(i)
  10.     sumD(i) = s
  11. For i = 1 To N ' better fill method?
  12.     ra(i) = sumD(i) * 2 * 3.1416 / s
  13. radius = _Height / 2.5
  14. Circle (_Width / 2 + 50, _Height / 2), radius, &HFFFFFFFF
  15. For i = 1 To N
  16.     x = _Width / 2 + 50 + radius * Cos(ra(i) + offset)
  17.     y = _Height / 2 + radius * Sin(ra(i) + offset)
  18.     Line (_Width / 2 + 50, _Height / 2)-(x, y), &HFFFFFFFF
  19.     Paint (_Width / 2 + 50 + (radius - 10) * Cos(ra(i) + offset - _Pi(1 / 36)), _Height / 2 + (radius - 10) * Sin(ra(i) + offset - _Pi(1 / 36))), i, &HFFFFFFFF
  20. For i = 1 To N
  21.     If i = 1 Then a = ra(1) / 2 + offset Else a = (ra(i) + ra(i - 1)) / 2 + offset
  22.     x = _Width / 2 + 50 + _Height / 3 * Cos(a)
  23.     y = _Height / 2 + _Height / 3 * Sin(a) ' <<<<<<<<<< NOT backwards circle function!!!
  24.     s$ = _Trim$(Str$(d(i)))
  25.     Color 0: _PrintString (x + 2 - 3.5 * Len(s$), y + 2 - 8), s$
  26.     Color 15: _PrintString (x - 3.5 * Len(s$), y - 8), _Trim$(Str$(d(i)))
  27. xc = _Width / 2 + 50
  28. yc = _Height / 2
  29. wheel& = _NewImage(2 * radius + 2, 2 * radius + 2, 12)
  30. _PutImage , 0, wheel&, (xc - radius - 1, yc - radius - 1)-(xc + radius + 1, yc + radius + 1)
  31. 'Cls ' check image capture
  32. '_PutImage , wheel&, 0
  33. 'Sleep
  34.  
  35.  
  36.  
  37.     k = _KeyHit
  38.  
  39.     Cls ' do this once
  40.     For i = 1 To N 'redraw
  41.         Color i: Print d(i); ra(i)
  42.     Next
  43.     Color 15: Print s; "= SUM "
  44.     If Roto Then
  45.         Print "RotoZoom"
  46.     Else
  47.         Print "Display Image"
  48.     End If
  49.     Print "FPS = "; maxFPS
  50.  
  51.     offset = offset + _Pi(1 / 360)
  52.     If k > 0 Then Roto = Not Roto
  53.     If Roto Then
  54.         RotoZoom3 xc, yc, wheel&, 1, 1, offset ' very raggedy with bad edge on circle
  55.     Else
  56.         DisplayImage wheel&, xc, yc, 1, 1, _R2D(-offset), 0
  57.     End If
  58.     If Timer > t# + 1 Then t# = Timer: maxFPS = FPS: FPS = 0
  59.     FPS = FPS + 1
  60.     _Display
  61.     ' _Limit 30
  62. Loop Until k = 27
  63.  
  64.  
  65. ' Description:
  66. ' Started from a mod of Galleon's in Wiki that both scales and rotates an image.
  67. ' This version scales the x-axis and y-axis independently allowing rotations of image just by changing X or Y Scales
  68. ' making this tightly coded routine a very powerful and versatile image tool.
  69. Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single)
  70.     ' This assumes you have set your drawing location with _DEST or default to screen.
  71.     ' X, Y - is where you want to put the middle of the image
  72.     ' Image - is the handle assigned with _LOADIMAGE
  73.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  74.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  75.     ' radianRotation is the Angle in Radian units to rotate the image
  76.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  77.     '       and saves a little time converting from degree.
  78.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  79.  
  80.     Dim px(3) As Single: Dim py(3) As Single ' simple arrays for x, y to hold the 4 corners of image
  81.     Dim W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  82.     W& = _Width(Image&): H& = _Height(Image&)
  83.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  84.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  85.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  86.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  87.     sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation) ' rotation helpers
  88.     For i& = 0 To 3 ' calc new point locations with rotation and zoom
  89.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  90.         px(i&) = x2&: py(i&) = y2&
  91.     Next
  92.     _MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  93.     _MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  94.  
  95. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single, angle As Single, mode As _Byte)
  96.     ' SMcNeill ref:  https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  97.     'Image is the image handle which we use to reference our image.
  98.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  99.     'angle is the angle which we wish to rotate the image.
  100.     'mode determines HOW we place the image at point X,Y.
  101.     'Mode 0 we center the image at point X,Y
  102.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  103.     'Mode 2 is Bottom Left
  104.     'Mode 3 is Top Right
  105.     'Mode 4 is Bottom Right
  106.  
  107.  
  108.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  109.     Dim sinr As Single, cosr As Single, i As _Byte
  110.     w = _Width(Image): h = _Height(Image)
  111.     Select Case mode
  112.         Case 0 'center
  113.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  114.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  115.         Case 1 'top left
  116.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  117.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  118.         Case 2 'bottom left
  119.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  120.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  121.         Case 3 'top right
  122.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  123.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  124.         Case 4 'bottom right
  125.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  126.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  127.     End Select
  128.     sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  129.     For i = 0 To 3
  130.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  131.         px(i) = x2: py(i) = y2
  132.     Next
  133.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  134.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  135.  

Hit any key to toggle display methods, ESC to end.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #6 on: November 07, 2021, 02:04:32 pm »
@SMcNeill

Yeah DisplayImage has similar results as Rotozoom3, I was hoping it would do better with the lines in the Pie Chart. They are fuzzy looking either way.

And yes, Rotozoom3 was a hack off the original just adding a separate scale for x and y axis which could be handy. So the handling of the goofy y axis increasing going down was not changed.

I am now testing TextToImage with a font, does not look good. What am I doing wrong?
I just changed top of program to this:
Code: QB64: [Select]
  1. 'SMcNeill  ref https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  2. Screen _NewImage(800, 700, 32)
  3. Dim HW(1 To 4)
  4. f& = _LoadFont("ariblk.ttf", 24) ' Test with Windows 10 font
  5. Print f& ' check font loaded
  6. If f& < 1 Then Print "Font did not load, goodbye.  ...zzz": Sleep: System
  7. For i = 1 To 4
  8.     HW(i) = TextToImage("Hello World", f&, &HFFFFFF00, 0, i)
  9.  

Is there more needed to be changed?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #7 on: November 07, 2021, 02:16:05 pm »
Good catch.  When I reworked it, I lost a font change in there.   This version should work with your custom font; I'll update the first post so it works as intended as well:

Code: QB64: [Select]
  1. 'SMcNeill  ref https://www.qb64.org/forum/index.php?topic=4374.msg137907#msg137907
  2. Screen _NewImage(800, 700, 32)
  3. Dim HW(1 To 4)
  4. f& = _LoadFont("ariblk.ttf", 24) ' Test with Windows 10 font
  5. Print f& ' check font loaded
  6. If f& < 1 Then Print "Font did not load, goodbye.  ...zzz": Sleep: System
  7. For i = 1 To 4
  8.     HW(i) = TextToImage("Hello World", f&, &HFFFFFF00, 0, i)
  9.  
  10.  
  11. Print "The first thing to showcase with these two routines is just how simple it is to turn"
  12. Print "text into an image with TextToImage."
  13. Print "First, let's print it forwards:"
  14. _PutImage (250, 48), HW(1)
  15. Print "Then, let's print it backwards:"
  16. _PutImage (250, 80), HW(2)
  17. Print "Then, let's print it up to down:"
  18. _PutImage (270, 112), HW(3)
  19. Locate 8, 40: Print "And let's finish with down to up:"
  20. _PutImage (580, 112), HW(4)
  21. Locate 20, 1
  22. Print "TextToImage simply transforms text into an image for us, with a few built in options"
  23. Print "to it for the direction we want we text to print."
  24. Print "It's as simple as a call with:"
  25. Print "    Handle = TextToImage(text$, fonthandle, fontcolor, backgroundcolor, mode"
  26. Print "        text$ is the string which we want to print.  (In this case 'Hello World'"
  27. Print "        fonthandle is the handle of the font which we _LOADFONT for use."
  28. Print "            (In this case, I choose the default _FONT 16.)"
  29. Print "        fontcolor is the color which we want our text in.  (Here, it's YELLOW.)"
  30. Print "        backgroundcolor is the background which we want for the text time.  (Clear this time.)"
  31. Print "        mode is how we decide to print forwards, backwards, up to down, or down to up."
  32. Print "Once we have an image handle, we can use that image just the same as we can with any other."
  33. Print "For those who don't need to do anything more than display the text as an image,"
  34. Print "feel free to use it as I have in the first part of this program with _PUTIMAGE."
  35. Print "Trust me -- TextToImage works just fine with _PUTIMAGE."
  36. Print "But....   If you need more..."
  37.  
  38. Cls , 0
  39.  
  40. Print "There's always DisplayImage to help you out!"
  41. DisplayImage HW(1), 300, 30, 1, 1, 0, 1
  42. Print "Display your image at a scale!"
  43. Print "Twice as wide! ";
  44. DisplayImage HW(1), 300, 60, 2, 1, 0, 1
  45. Print "Twice as tall! "
  46. DisplayImage HW(1), 500, 60, 1, 2, 0, 1
  47. Print "At an angle!"
  48. DisplayImage HW(1), 280, 90, 1, 1, -45, 1
  49. Print "Make it rotate!"
  50.     Line (355, 155)-Step(100, 100), &HFF000000, BF
  51.     DisplayImage HW(1), 400, 200, 1, 1, angle, 0
  52.  
  53.     angle = (angle + 1) Mod 360
  54.     _Limit 30
  55.     _Display
  56. Print "You can basically use DisplayImage just as you'd normally use RotoZoom, EXCEPT..."
  57. Print "You can choose which CORNER of the image you want to display at your coordinates."
  58. Line (350, 350)-Step(100, 100), -1, B
  59. Circle (400, 400), 10, -1
  60. Print "Top Left corner! ";
  61. DisplayImage HW(1), 400, 400, 2, 2, 0, 1
  62. Print "Bottom Left corner! ";
  63. DisplayImage HW(1), 400, 400, 2, 2, 0, 2
  64. Print "Top Right corner! ";
  65. DisplayImage HW(1), 400, 400, 2, 2, 0, 3
  66. Print "Bottom Right corner! "
  67. DisplayImage HW(1), 400, 400, 2, 2, 0, 4
  68. HW(1) = TextToImage("Hello World", 16, &HFFFF0000, &HFF0000FF, 1)
  69. Print "Or Centered!"
  70. DisplayImage HW(1), 400, 400, 2, 2, 0, 0
  71. Circle (400, 400), 10, -1
  72.  
  73.  
  74. Print "With TextToImage, you can turn text into an image...  It's that simple!"
  75. Print "With DisplayImage, you have a ton of options for how to display ANY image"
  76. Print "   (NOT just for use with text images!!)"
  77. Print "Scale them, stretch them, rotate them, position them by various corners..."
  78. Print "Between these two routines, I generally don't need anything else when working"
  79. Print "   with images in my programs.  ;)"
  80. Print "And that's THE END of this demo.  Post any questions you have on the forums for me!"
  81.  
  82.  
  83.  
  84.  
  85.  
  86. ' (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single,
  87. '  angle As Single, mode As _Byte)
  88.  
  89.  
  90.  
  91.  
  92. Function TextToImage& (text$, font&, fc&, bfc&, mode As _Byte)
  93.     'text$ is the text that we wish to transform into an image.
  94.     'font& is the handle of the font we want to use.
  95.     'fc& is the color of the font we want to use.
  96.     'bfc& is the background color of the font.
  97.  
  98.     'Mode 1 is print forwards
  99.     'Mode 2 is print backwards
  100.     'Mode 3 is print from top to bottom
  101.     'Mode 4 is print from bottom up
  102.     'Mode 0 got lost somewhere, but it's OK.  We check to see if our mode is < 1 or > 4 and compensate automatically if it is to make it one (default).
  103.  
  104.     If mode < 1 Or mode > 4 Then mode = 1
  105.     dc& = _DefaultColor: bgc& = _BackgroundColor
  106.     D = _Dest
  107.     F = _Font
  108.     T2Idown = CsrLin: T2Iright = Pos(0)
  109.     If font& <> 0 Then _Font font&
  110.     If mode < 3 Then
  111.         'print the text lengthwise
  112.         w& = _PrintWidth(text$): h& = _FontHeight
  113.     Else
  114.         'print the text vertically
  115.         For i = 1 To Len(text$)
  116.             If w& < _PrintWidth(Mid$(text$, i, 1)) Then w& = _PrintWidth(Mid$(text$, i, 1))
  117.         Next
  118.         h& = _FontHeight * (Len(text$))
  119.     End If
  120.  
  121.     TextToImage_temp& = _NewImage(w&, h&, 32)
  122.     TextToImage = TextToImage_temp&
  123.     _Dest TextToImage_temp&
  124.     If font& <> 0 Then _Font font&
  125.     Color fc&, bfc&
  126.  
  127.     Select Case mode
  128.         Case 1
  129.             'Print text forward
  130.             _PrintString (0, 0), text$
  131.         Case 2
  132.             'Print text backwards
  133.             temp$ = ""
  134.             For i = 0 To Len(text$) - 1
  135.                 temp$ = temp$ + Mid$(text$, Len(text$) - i, 1)
  136.             Next
  137.             _PrintString (0, 0), temp$
  138.         Case 3
  139.             'Print text upwards
  140.             'first lets reverse the text, so it's easy to place
  141.             temp$ = ""
  142.             For i = 0 To Len(text$) - 1
  143.                 temp$ = temp$ + Mid$(text$, Len(text$) - i, 1)
  144.             Next
  145.             'then put it where it belongs
  146.             For i = 1 To Len(text$)
  147.                 fx = (w& - _PrintWidth(Mid$(temp$, i, 1))) / 2 + .99 'This is to center any non-monospaced letters so they look better
  148.                 _PrintString (fx, _FontHeight * (i - 1)), Mid$(temp$, i, 1)
  149.             Next
  150.         Case 4
  151.             'Print text downwards
  152.             For i = 1 To Len(text$)
  153.                 fx = (w& - _PrintWidth(Mid$(text$, i, 1))) / 2 + .99 'This is to center any non-monospaced letters so they look better
  154.                 _PrintString (fx, _FontHeight * (i - 1)), Mid$(text$, i, 1)
  155.             Next
  156.     End Select
  157.     _Dest D
  158.     Color dc&, bgc&
  159.     _Font F
  160.     Locate T2Idown, T2Iright
  161.  
  162. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, yscale As Single, angle As Single, mode As _Byte)
  163.     'Image is the image handle which we use to reference our image.
  164.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  165.     'angle is the angle which we wish to rotate the image.
  166.     'mode determines HOW we place the image at point X,Y.
  167.     'Mode 0 we center the image at point X,Y
  168.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  169.     'Mode 2 is Bottom Left
  170.     'Mode 3 is Top Right
  171.     'Mode 4 is Bottom Right
  172.  
  173.  
  174.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  175.     Dim sinr As Single, cosr As Single, i As _Byte
  176.     w = _Width(Image): h = _Height(Image)
  177.     Select Case mode
  178.         Case 0 'center
  179.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  180.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  181.         Case 1 'top left
  182.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  183.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  184.         Case 2 'bottom left
  185.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  186.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  187.         Case 3 'top right
  188.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  189.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  190.         Case 4 'bottom right
  191.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  192.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  193.     End Select
  194.     sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  195.     For i = 0 To 3
  196.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  197.         px(i) = x2: py(i) = y2
  198.     Next
  199.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  200.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  201.  
  202.  
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #8 on: November 07, 2021, 03:40:42 pm »
Thanks @SMcNeill

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: TextToImage and DisplayImage (v2.0+ compliant)
« Reply #9 on: November 08, 2021, 04:56:15 am »
And WHY do I tend to use _DISPLAYIMAGE instead of RotoZoom, since they basically run the same speed and produce the same results?

Because DisplayImage can do *more*!  For example, try to work up this little routine using putimage or displayimage -- you might can do it, but you won't do it with anywhere near the number of lines.

Code: QB64: [Select]
  1. Screen _NewImage(640, 480, 32)
  2. Do Until _Width <> 0 And _Height <> 0: Loop 'This seems to work better than _SCREENEXISTS for an initial delay
  3.  
  4. hLine = TextToImage("                    ", 8, Red, Red, 1)
  5. vLine = TextToImage("                    ", 8, Red, Red, 3)
  6.  
  7.     Cls
  8.     k = _KeyHit
  9.     Select Case k
  10.         Case 18432, 19200
  11.             angle = (angle + 1) Mod 360
  12.         Case 20480, 19712
  13.             angle = (angle - 1) Mod 360
  14.     End Select
  15.     DisplayImage vLine, 155, 0, 2, 3, 0, 1
  16.     DisplayImage vLine, 475, 0, 2, 3, 0, 1
  17.     DisplayImage hLine, 160, 236, 1, 1, angle, 1
  18.     DisplayImage hLine, 480, 236, 1, 1, -angle, 3
  19.     _Display
  20.     _Limit 30
  21.  
  22. Function TextToImage& (text$, font&, fc&, bfc&, mode As _Byte)
  23.     'text$ is the text that we wish to transform into an image.
  24.     'font& is the handle of the font we want to use.
  25.     'fc& is the color of the font we want to use.
  26.     'bfc& is the background color of the font.
  27.  
  28.     'Mode 1 is print forwards
  29.     'Mode 2 is print backwards
  30.     'Mode 3 is print from top to bottom
  31.     'Mode 4 is print from bottom up
  32.     'Mode 0 got lost somewhere, but it's OK.  We check to see if our mode is < 1 or > 4 and compensate automatically if it is to make it one (default).
  33.  
  34.     If mode < 1 Or mode > 4 Then mode = 1
  35.     dc& = _DefaultColor: bgc& = _BackgroundColor
  36.     D = _Dest
  37.     F = _Font
  38.     T2Idown = CsrLin: T2Iright = Pos(0)
  39.     If font& <> 0 Then _Font font&
  40.     If mode < 3 Then
  41.         'print the text lengthwise
  42.         w& = _PrintWidth(text$): h& = _FontHeight
  43.     Else
  44.         'print the text vertically
  45.         For i = 1 To Len(text$)
  46.             If w& < _PrintWidth(Mid$(text$, i, 1)) Then w& = _PrintWidth(Mid$(text$, i, 1))
  47.         Next
  48.         h& = _FontHeight * (Len(text$))
  49.     End If
  50.  
  51.     TextToImage_temp& = _NewImage(w&, h&, 32)
  52.     TextToImage = TextToImage_temp&
  53.     _Dest TextToImage_temp&
  54.     If font& <> 0 Then _Font font&
  55.     Color fc&, bfc&
  56.  
  57.     Select Case mode
  58.         Case 1
  59.             'Print text forward
  60.             _PrintString (0, 0), text$
  61.         Case 2
  62.             'Print text backwards
  63.             temp$ = ""
  64.             For i = 0 To Len(text$) - 1
  65.                 temp$ = temp$ + Mid$(text$, Len(text$) - i, 1)
  66.             Next
  67.             _PrintString (0, 0), temp$
  68.         Case 3
  69.             'Print text upwards
  70.             'first lets reverse the text, so it's easy to place
  71.             temp$ = ""
  72.             For i = 0 To Len(text$) - 1
  73.                 temp$ = temp$ + Mid$(text$, Len(text$) - i, 1)
  74.             Next
  75.             'then put it where it belongs
  76.             For i = 1 To Len(text$)
  77.                 fx = (w& - _PrintWidth(Mid$(temp$, i, 1))) / 2 + .99 'This is to center any non-monospaced letters so they look better
  78.                 _PrintString (fx, _FontHeight * (i - 1)), Mid$(temp$, i, 1)
  79.             Next
  80.         Case 4
  81.             'Print text downwards
  82.             For i = 1 To Len(text$)
  83.                 fx = (w& - _PrintWidth(Mid$(text$, i, 1))) / 2 + .99 'This is to center any non-monospaced letters so they look better
  84.                 _PrintString (fx, _FontHeight * (i - 1)), Mid$(text$, i, 1)
  85.             Next
  86.     End Select
  87.     _Dest D
  88.     Color dc&, bgc&
  89.     _Font F
  90.     Locate T2Idown, T2Iright
  91.  
  92. Sub DisplayImage (Image As Long, x As Integer, y As Integer, xscale As Single, _
  93.                   yscale As Single, angle As Single, mode As _Byte)
  94.     'Image is the image handle which we use to reference our image.
  95.     'x,y is the X/Y coordinates where we want the image to be at on the screen.
  96.     'angle is the angle which we wish to rotate the image.
  97.     'mode determines HOW we place the image at point X,Y.
  98.     'Mode 0 we center the image at point X,Y
  99.     'Mode 1 we place the Top Left corner of oour image at point X,Y
  100.     'Mode 2 is Bottom Left
  101.     'Mode 3 is Top Right
  102.     'Mode 4 is Bottom Right
  103.  
  104.  
  105.     Dim px(3) As Integer, py(3) As Integer, w As Integer, h As Integer
  106.     Dim sinr As Single, cosr As Single, i As _Byte
  107.     w = _Width(Image): h = _Height(Image)
  108.     Select Case mode
  109.         Case 0 'center
  110.             px(0) = -w \ 2: py(0) = -h \ 2: px(3) = w \ 2: py(3) = -h \ 2
  111.             px(1) = -w \ 2: py(1) = h \ 2: px(2) = w \ 2: py(2) = h \ 2
  112.         Case 1 'top left
  113.             px(0) = 0: py(0) = 0: px(3) = w: py(3) = 0
  114.             px(1) = 0: py(1) = h: px(2) = w: py(2) = h
  115.         Case 2 'bottom left
  116.             px(0) = 0: py(0) = -h: px(3) = w: py(3) = -h
  117.             px(1) = 0: py(1) = 0: px(2) = w: py(2) = 0
  118.         Case 3 'top right
  119.             px(0) = -w: py(0) = 0: px(3) = 0: py(3) = 0
  120.             px(1) = -w: py(1) = h: px(2) = 0: py(2) = h
  121.         Case 4 'bottom right
  122.             px(0) = -w: py(0) = -h: px(3) = 0: py(3) = -h
  123.             px(1) = -w: py(1) = 0: px(2) = 0: py(2) = 0
  124.     End Select
  125.     sinr = Sin(angle / 57.2957795131): cosr = Cos(angle / 57.2957795131)
  126.     For i = 0 To 3
  127.         x2 = xscale * (px(i) * cosr + sinr * py(i)) + x: y2 = yscale * (py(i) * cosr - px(i) * sinr) + y
  128.         px(i) = x2: py(i) = y2
  129.     Next
  130.     _MapTriangle (0, 0)-(0, h - 1)-(w - 1, h - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  131.     _MapTriangle (0, 0)-(w - 1, 0)-(w - 1, h - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  132.  

Putimage just puts an image into a square of the screen.  It doesn't handle rotations...
Rotozoom handles angles and rotations, but it only works from the center point of where you position it.

DisplayImage works with whichever *corner* you want for the point where you place your image.  In this case, this allows us to create a pretty looking set of double doors which we can push or pull open with the arrow keys.  These doors crack in the middle, move smoothly, and basically look and act like we'd expect them to look -- and each with just a single line of code.

Now, I could've did this little demo with even fewer lines, if I'd just went with a single image for the wall/door and then rotated it 90 degrees between horizontal and vertical, but I was also wanting to keep things simple and not make the math look a little confusing for our angles being used.

24 lines for the main program and then an additional line for a graphic $INCLUDE library reference.  It'd be hard to get much shorter and simpler than that!  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!