Skip to content

Internal Documentation

Edison Hua edited this page Jan 4, 2026 · 55 revisions

This page documents the internal structure.

Control Flow

  1. Calling tr := TextRender() calls __New() and sets the pending_flush flag to true.

  2. When Draw() is first called, it will Flush() the graphics buffer.

  3. Flush() calls UpdateMemory()

  4. Since the graphics buffer does not exist, FreeMemory() returns and LoadMemory() creates the graphics buffer.

  5. The user can call FreeMemory() at any time to destroy the graphics buffer and free up RAM. The graphics buffer will be recreated automatically when needed.

State

this.Hash() - A 32-bit CRC32 hash of the bitmap pixels. Slow.

this.status - A 32-bit integer generated after every change in the canvas state. Avoid checking this value and use OnEvent("CanvasChange", callback) instead.

this.pending_flush - True if Draw() has been called at least once. False after a rendering action takes place.

To determine if the bitmap has been changed, use Hash() which returns a CRC32 hash of the underlying pixels. As this is very slow, it is recommended to use the status property instead, which is a 32-bit random number that is generated after every edit of the canvas. Objects that are drawn off screen will change this.status despite making no visible changes.

Finally pending_flush contains the publishing state of the window. After every render, if the canvas needs to be flushed and pending_flush is set to true. The next call to Draw() will clean the internal canvas by calling Flush().

Window Styles

To set the window title, window style, window extended style and window parent call new.

tr := new TextRender(title, style, styleEx, parent) ; AutoHotkey v1 only
  • Default window title is the empty string.
  • Default window style is 0x80000000 which is WS_POPUP.
  • Default window extended style is 0x80088, a combination of WS_EX_TOPMOST, WS_EX_TOOLWINDOW, and WS_EX_LAYERED.
  • Default window parent is A_ScriptHwnd.

As the new keyword only exists on AutoHotkey v1, the object must be manually instantiated on AutoHotkey v2. The following works in one line because __New() returns this.

tr := {base: TextRender.prototype}.__New(title, style, styleEx, parent) ; AutoHotkey v2 only

Pixel Access

this.ptr - A pointer to the bitmap pixels from CreateDIBSection.

this.size - The size of the pointer to the bitmap pixels.

Graphics

this.hwnd - A window handle that uniquely identifies the window.

this.hdc - A handle to a GDI device context.

this.hbm - A handle to a device independent bitmap. Use CopyToHBitmap() for a separate copy.

this.obm - The mysterious stock bitmap.

this.gfx - A GDI+ graphics handle.

Window ⊆ Bitmap ⊆ Canvas

image

Bitmap = Virtual Screen (Pixel Array, hdc, hbm, ptr)

Canvas = Infinite Graphics (GDI+ pointer)

Window = Visible Area (Can be seen with Window Spy, hwnd)

The bitmap is exactly the size of the user's screen (or the virtual screen consisting of all monitors combined). It is fully backed by memory. Normally many of these pixels are transparent as the render is usually smaller than the user's entire screen. The bitmap is also known as the back buffer, since it buffers the drawings in backing memory. The window is what the user can interact with on screen. The window is sometimes called the front buffer as it normally lives in the VRAM corresponding to your display. It is faster to edit the back buffer repeatedly and send the image to the front buffer once, than it is to constantly edit the front buffer. There are multiple reasons why including (1) the CPU can access the RAM faster than the VRAM and (2) giving applications access to the user's screen is generally a security risk. The canvas is infinite. It extends the bitmap and contains coordinates that lie outside of the screen. When TextRender receives instructions to draw off the screen, it simply doesn't change any pixels. However, it is important to remember this operation, as the screen size can change for example, and what was formerly off the screen can now be rendered on the screen. You can think of the canvas as containing visible in-bounds pixels and invisible out-of-bounds pixels from -∞ to ∞.

Window ⊆ Bitmap ⊆ Canvas

If a graphics element is rendered partially on screen, the part that lies within the bitmap is rendered by the graphics engine, and the part that lies outside the bitmap will be ignored. In the example above, the right side of the red rectangle will be discarded. Only the blue portion of the visible window is kept. Therefore, the pixels of the bitmap are a real subset of an infinite canvas. The window is the visible subset of the bitmap that has been edited.

Why is the bitmap the size of the virtual screen? Shouldn't the graphics buffer be equal to the window size? Constantly resizing the pixel array is a CPU intensive operation that reduces speed. For fastest performance, the screen is double buffered, therefore the graphics buffer must be the same size as the screen!

FreeMemory() can be called anytime to discard the graphics buffer. When Draw() or Render() is subsequently called, the graphics buffer will be reinitialized, and all past actions will be repainted from this.layers.

Bitmap Coordinates

The back buffer or backing memory. Previous names in the documentation were graphics buffer and internal canvas.

this.BitmapLeft

this.BitmapTop

this.BitmapRight

this.BitmapBottom

this.BitmapWidth

this.BitmapHeight

Stride should be manually calculated as 4 (# of color channels) × this.BitmapWidth.

Window Coordinates

The visible window area.

this.WindowLeft

this.WindowTop

this.WindowRight

this.WindowBottom

this.WindowWidth

this.WindowHeight

Canvas Coordinates

Describes the drawn image, regardless if it is off screen and cannot be rendered.

this.t - Time to show text on screen. If zero, then it will show indefinitely.

this.x

this.y

this.x2

this.y2

this.w

this.h

this.chars

this.words

this.lines

Other

this.events - Used by OnEvent().

this.layers - A history of all Draw() calls. Refreshed after Render().

this.data - The first parameter of Draw().

this.styles - The second and third parameters of Draw().

this.friend1 - An instance of TextRender() used by EventShowCoordinates.

this.friend2 - An instance of TextRender() used by EventCopyText.

TextRender class

TextRender.gdiplus() - Get the current number of gdiplus instances.

TextRender.gdiplusStartup() - Increase the number of gdiplus instances by 1.

TextRender.gdiplusShutdown() - Decrease the number of gdiplus instances by 1.

TextRender window private data

Call GetWindowLongPtr at offset 0 to get a pointer (address) to the associated AutoHotkey object.

Clone this wiki locally