Skip to content

Internal Documentation

Edison Hua edited this page Sep 29, 2022 · 51 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. It is fully backed by memory and corresponds to pixels. The bitmap is also known as the graphics buffer, since it buffers the drawings to the screen. The canvas is an infinite abstraction. It extends the bitmap and contains coordinates that lie outside of the bitmap. The window is what the user can interact with on screen.

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 graphics buffer. Formerly called the internal canvas.

this.BitmapLeft

this.BitmapTop

this.BitmapRight

this.BitmapBottom

this.BitmapWidth

this.BitmapHeight

Stride should be manually calculated as 4 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.