vertical text

Posted by Zimmy on Tue 11 Feb 2020 08:19 AM — 20 posts, 79,399 views.

#0
I'd like to be able to draw vertical text (rotated clockwise 90 degrees.) I've been able to do 180 degrees; but when I try 90, I get a distorted streak. Here is my attempt:


    WindowRectOp(win.."held", 2, 0, 0, voy.dimensions.window.x, voy.dimensions.font[1], col.window.transparent)
    WindowText(win.."held", "f", text, 0, 0, 0, 0, voy.colours.objects.held, false)
    WindowImageFromWindow(win, "held", win.."held")

    -- copied from the documentation:
    local angle = 90
    local radians = math.rad (angle)
    local sine   = math.sin (radians)
    local cosine = math.cos (radians)

    -- rotate clockwise
    WindowTransformImage(win, "held", voy.dimensions.font[1], voy.dimensions.window.y, 3, cosine,  sine,  -sine,  cosine)


I've also tried:
WindowTransformImage(win, "held", voy.dimensions.font[1], voy.dimensions.window.y, 3, 0, 1, -1, 0)


As well as different arbitrary coordinates. There must be something I don't understand.

[EDIT] I've been playing around with it further and found that reflections and stretching work fine. However, I can't get it to do any sort of sheering or rotation (copying examples from the documentation). When I try, I just end up with a reflection and/or stretch.
Amended on Wed 12 Feb 2020 06:05 AM by Zimmy
Australia Forum Administrator #1
Can you make up a complete example please? With all the miniwindow creation, etc?

Trying your code, and after making a "win" miniwindow I get:


Run-time error
World: smaug2
Immediate execution
[string "Immediate"]:7: attempt to index global 'voy' (a nil value)
stack traceback:
        [string "Immediate"]:7: in main chunk


I don't want to guess what all your variables are.

So, make "win" and "winheld" and "voy", and show what font you are using, and so on.

In other words, some code I can copy and paste and reproduce the issue.
#2
Sorry about that, hopefully this is more helpful.


WIN_X, WIN_Y = 300, 300
WindowCreate("MAIN_WINDOW", 0, 0, WIN_X, WIN_Y, miniwin.pos_center_all, 0, ColourNameToRGB("black")) -- main window
WindowCreate("TEXT_WINDOW", 0, 0, 0, 0, miniwin.pos_center_all, 0, ColourNameToRGB("purple")) -- workspace for rotating text
WindowFont("TEXT_WINDOW", "font", "Dina", 15, false, false, false, false)
TXT_Y = WindowFontInfo("TEXT_WINDOW", "font", 1)
WindowResize("TEXT_WINDOW", WIN_X, TXT_Y, ColourNameToRGB("purple"))
WindowText("TEXT_WINDOW", "font", "text here", 0, 0, WIN_X, WIN_Y, ColourNameToRGB("white"), false)
WindowImageFromWindow("MAIN_WINDOW", "text", "TEXT_WINDOW")
-- rotation angle in degrees
angle = 90
-- angle converted to radians
radians = math.rad (angle)
-- calculate sine and cosine
sine   = math.sin (radians)
cosine = math.cos (radians)
-- rotate clockwise
WindowTransformImage("MAIN_WINDOW", "text", TXT_Y, WIN_X, miniwin.image_copy, cosine,  sine,  -sine,  cosine)
WindowShow("MAIN_WINDOW", true)


Thanks!
Amended on Wed 12 Feb 2020 06:08 AM by Zimmy
USA Global Moderator #3
I just want to point out that "cosine, sine, -sine, cosine" is not what the doc says to do for clockwise rotation.
#4
Fiendish said:

I just want to point out that "cosine, sine, -sine, cosine" is not what the doc says to do for clockwise rotation.


Oh, it says: "cosine, -sine, sine, cosine," I did counter-clockwise on accident. I don't believe this is the source of my issue though.
Australia Forum Administrator #5
Seems OK to me.



Template:version
Please help us by advising the version of MUSHclient you are using. Use the Help menu -> About MUSHclient.
#6
https://imgur.com/a/Wwv3BiU

Must be a compatibility issue? I run mush on wine64.
Australia Forum Administrator #7

Yes, I tested on Wine under Ubuntu and got much less satisfactory results.

Looks like a Wine bug. The code uses SetWorldTransform, and I found a couple of reports of rotation not working in Wine:

I’m not sure what to suggest. You could pre-render the image, unless, of course, the text changes all the time.

For a fairly small image you might be able to do the rotation in Lua, although it might be slow, a pixel at a time (see example below).

There is a Lua image library, maybe that could help:

If you want to do the image occasionally (for example, if it is the name of the MUD you are playing) you can install ImageMagick and get that to make a rotated PNG for you.


Example of rotating in Lua

This code does a 90 degree rotation by copying a pixel at a time. The “200 - x” and “200 - y” below are to get the flipping right.


WIN_X, WIN_Y = 300, 300
main_window = "MAIN_WINDOW"
text_window = "TEXT_WINDOW"
font = "font"


WindowCreate(main_window, 0, 0, WIN_X, WIN_Y, miniwin.pos_center_all, 0, ColourNameToRGB("black")) -- main window
WindowCreate(text_window, 0, 0, 0, 0, miniwin.pos_center_all, 0, ColourNameToRGB("purple")) -- workspace for rotating text
WindowFont(text_window, font, "Dina", 15, false, false, false, false)
TXT_Y = WindowFontInfo(text_window, font, 1)
WindowResize(text_window, WIN_X, TXT_Y, ColourNameToRGB("purple"))
WindowText(text_window, font, "text here", 0, 0, WIN_X, WIN_Y, ColourNameToRGB("white"), false)

-- rotate counter clockwise
for x = 1, 200 do
  for y = 1, 25 do
    local pixel = WindowGetPixel (text_window, 200 - x, 25 - y)
    WindowSetPixel (main_window, 200 - y,  x, pixel)
  end -- for y
end -- for x

WindowShow(main_window, true)

The rotation took 0.015 seconds on my PC using Wine under Ubuntu.

Amended on Wed 12 Feb 2020 09:49 PM by Nick Gammon
Australia Forum Administrator #8

Rotating the entire image took 0.141 seconds.

-- rotate counter clockwise
for x = 0, WIN_X - 1 do
  for y = 0, WIN_Y - 1  do
    local pixel = WindowGetPixel (text_window, WIN_X - x, WIN_Y - y)
    WindowSetPixel (main_window, WIN_X - y,  x, pixel)
  end -- for y
end -- for x
#9
Nick Gammon said:

Rotating the entire image took 0.141 seconds.


Okay so I think what would be best, is if when the plugin loads, I make an image of each letter in the font and size I want to use. Then rotate each image, before-hand, using the same method.
Australia Forum Administrator #10
If you got the exact dimensions for the text (which is achievable) then just doing one big rotation might be just as good. Doing lots of copies of each letter, and getting them spaced the right distance apart, would be time-consuming too.

After all, you only need to copy the non-black pixels in the source, which is what my first example tried to do, although I didn't calculate the exact size that the text took.
#11
The text changes and can not be per-determined so I need a dynamic solution. That's why I was thinking I would create images of each rotated letter on install. That way I could later produce vertical text by drawing the images under each-other to create words.
USA Global Moderator #12
Please tell me that you're doing an April fool's day gag where the player's output is all rotated sideways.
Australia Forum Administrator #13
It might be the labels on tabbed windows.
#14
Nick Gammon said:

It might be the labels on tabbed windows.

I already have a bunch of mini-windows and a small laptop screen. So real estate is quite valuable! Just trying to fit things in the few remaining empty-spaces I have. Plus the miniwindow this is for is already very densely packed. Basically, just want to display the weapon I have held in each hand. Thought it would look neat to have the text vertically along the edge of the mini-window respective to the hand it was held in.

Thanks for all the help!
Australia Forum Administrator #15
What you could do for the speed issue is cache the rotated text. So, when you want to draw a weapon, see if you already have a miniwindow (not shown) which contains the weapon name rotated. If not, you could make one. From then on, just copy from this rotated window onto the main window.
#16
Nick Gammon said:

What you could do for the speed issue is cache the rotated text. So, when you want to draw a weapon, see if you already have a miniwindow (not shown) which contains the weapon name rotated. If not, you could make one. From then on, just copy from this rotated window onto the main window.

Would there be a difference in performance between creating the cache from miniwindows containing rotated text vs images that are copies of a miniwindow containing the rotated text. Do I need a separate secret miniwindow for each new weapon or can I just have a new image for each weapon?
Australia Forum Administrator #17
I don't really understand the question. What is option A and what is option B?
#18
Nick Gammon said:

I don't really understand the question. What is option A and what is option B?


Option A: (what I thought you were suggesting) separate miniwindows for each rotated string.

Option B: using a single miniwindow to generate and copy images of each rotated string (clearing the contents after each time)

Anyways, here's what I have so far. I haven't coded in the cache mechanism yet. The only problem I'm having is with transparency.
[EDIT]Figured out the transparency. Edited the code.


--------------------------------------------------------------------------------
--   VERTICAL FONT
--------------------------------------------------------------------------------
function load_vertical_font(font, size, colour)
    rotated = {}
    WindowCreate(win.."upright", 0, 0, 0, 0, miniwin.pos_center_all, 0, ColourNameToRGB("black"))
    WindowCreate(win.."rotated", 0, 0, 0, 0, miniwin.pos_center_all, 0, ColourNameToRGB("black"))
    WindowFont(win.."upright", "vf", font, size, false, false, false, false)
    local alphabet = "-!?.'abcdefghijklmnopqrstucwxyz "
    alphabet:gsub(".", function(l)
        local w, h = WindowTextWidth(win.."upright", "vf", l), WindowFontInfo(win.."upright", "vf", 1)
        WindowResize(win.."upright", w, h, ColourNameToRGB("purple"))
        WindowRectOp(win.."upright", 2, 0, 0, w, h, ColourNameToRGB("purple"))
        WindowResize(win.."rotated", h, w, ColourNameToRGB("purple"))
        WindowText(win.."upright", "vf", l, 0, 0, w, h, colour, false)
        for x = 1, w do
            for y = 1, h do
                local pixel = WindowGetPixel(win.."upright", x, h - y)
                WindowSetPixel(win.."rotated", y, x, pixel)
                WindowImageFromWindow(win.."main", l, win.."rotated")
                rotated[l] = {x = h, y = w}
            end
        end
    end)
end

function print_vertical_text(text, x1, y1)
    text = string.lower(text)
    text:gsub("[-!?.'a-z ]", function(l)
        WindowDrawImage(win.."main", l, x1, y1, 0, 0, miniwin.image_transparent_copy)
        y1 = y1 + rotated[l].y
    end)
    WindowShow(win.."main", true)
end
--------------------------------------------------------------------------------
--   START EXECUTION HERE
--------------------------------------------------------------------------------
win = "win_"
WIN_X, WIN_Y = 300, 300
WindowCreate(win.."main", 0, 0, WIN_X, WIN_Y, miniwin.pos_center_all, 0, ColourNameToRGB("black"))
load_vertical_font("Dina", 14, ColourNameToRGB("white"))
print_vertical_text("Hello World!", 0, 0)
Amended on Thu 13 Feb 2020 06:28 AM by Zimmy
Australia Forum Administrator #19
Your technique seems perfectly acceptable.

Time taken to draw: 0.5 ms.


start = utils.timer ()
print_vertical_text("Hello World!", 0, 0)
print (string.format ("Time taken = %0.4f", utils.timer () - start))