展示二维码有两种方法
1)在没有选中任何文本的情况下,将鼠标移至想要产生二维码的文本上方,按鼠标中键将自动选中文本并展示二维码
2)手动选中文本,按鼠标中键展示二维码
取消已展示二维码有两种方法
1)在没有选中任何文本的情况下,并且鼠标下方也没有任何文本(即鼠标在空白位置),按鼠标中键取消已展示的二维码
2)在二维码图案上的任意位置点击鼠标右键取消已展示的二维码
达不到二维码展示条件时,将恢复使用鼠标中键的原本功能
源码示例:
tmp=
(
展示二维码有两种方法
1)在没有选中任何文本的情况下,将鼠标移至想要产生二维码的文本上方,按鼠标中键将自动选中文本并展示二维码
2)手动选中文本,按鼠标中键展示二维码


取消已展示二维码有两种方法
1)在没有选中任何文本的情况下,并且鼠标下方也没有任何文本(即鼠标在空白位置),按鼠标中键取消已展示的二维码
2)在二维码图案上的任意位置点击鼠标右键取消已展示的二维码


达不到二维码展示条件时,将恢复使用鼠标中键的原本功能
)
msgbox,64,提示,%tmp%,60

;本脚本只能使用U32或U64版本AHK运行
#SingleInstance, Force
SetBatchLines, -1 ;可有效提升超长内容二维码的生成速度
OnMessage(0x0201, "WM_LBUTTONDOWN") ;监听鼠标点击事件
OnMessage(0x0205, "WM_RBUTTONUP") ;监听鼠标右键弹起事件

MButton::
;clipsaved:=Clipboard ;如果有必要可以将原剪贴板数据先储存起来,等二维码窗口展示后还原
Clipboard := ""
Sleep 200
Send ^c
ClipWait,0.2,0 ;判断用户当前是否已选中文本
if (ErrorLevel==1) {  ;未选中
  Click, 2
  Sleep 100
  Send ^c
  ClipWait,0.2,0 ;判断双击是否成功选中文本
  if (ErrorLevel==1) ;未选中
    销毁窗口并按键("{MButton}")
}
if (Trim(Clipboard," `t`n`r")=="") ;排除字符串头尾的不可见字符
  销毁窗口并按键("{MButton}")

/*
原版生成的二维码内容中若有汉字字符,则扫描二维码后的内容会变成乱码,已用UTF-8编码解决
if RegExMatch(Clipboard, "[^\x{00}-\x{ff}]") ;非Ascii字符
{
  销毁窗口并按键()
  msgbox 不支持汉字字符
  exit
}
*/

ToolTip,二维码内容:{%Clipboard%}
SetTimer,RemoveToolTip,-5000
GUI pic: Destroy
GUI pic: +AlwaysOnTop +Hwndhwnd -Caption +ToolWindow +Border
GUI pic: Add,Picture,x0 y0 w200 h-1,% 生成QR码(文本转UTF8(Clipboard),10)
GUI pic: Show,w200 h200
Return

RemoveToolTip:
ToolTip
return

WM_LBUTTONDOWN() {  ;鼠标左键点击
  PostMessage,0xA1,2 ;可以让窗口跟随鼠标移动
}

WM_RBUTTONUP() {  ; 鼠标右键弹起
  销毁窗口并按键()
}

销毁窗口并按键(button:="") {
  GUI,pic:Destroy
  send % button
  exit
}


文本转UTF8(str) {
  ; 引用来源https://blog.csdn.net/qq_40890233/article/details/88082611
  tmp:=StrSplit(str)
  for k,v in tmp
  {
    c:=Asc(v)
    if ((c>=0x0001)&&(c<=0x007F)) ;当前字符为Aacii字符
      out.=Chr(c)
    else if (c>0x07FF) ;当前字符为16位Unicode字符
    {
      out.=Chr(0xE0|((c>>12)&0x0F))
      out.=Chr(0x80|((c>>6)&0x3F))
      out.=Chr(0x80|((c>>0)&0x3F))
    }
    Else ;当前字符为8位Unicode字符
    {
      out.=Chr(0xC0|((c>>6)&0x1F))
      out.=Chr(0x80|((c>>0)&0x3F))
    }
  }
  return out
}

生成QR码(str, PixelSize:=1) {
  ;引用来源https://www.autohotkey.com/boards/viewtopic.php?t=5538
  ;PixelSize参数为二维码内部格子的大小,1为1像素大小,此参数决定二维码图片的大小(清晰度)
  ;可以生成以下几种一维、二维码类型,这里只使用QR码
  ;BARCODER_GENERATE_CODE_39()
  ;BARCODER_GENERATE_CODE_ITF()
  ;BARCODER_GENERATE_CODE_128B()
  ;BARCODER_GENERATE_QR_CODE()
  MATRIX_TO_PRINT := BARCODER_GENERATE_QR_CODE(str)
  if (MATRIX_TO_PRINT = 1) ;出现的几率最多,所以单独判断,实际上在本示例脚本不会出现此种情况
  {
    ;Msgbox, 0x10, Error, The input message is blank. Please input a message to succesfully generate a QR Code image.
    Msgbox, 0x10, 错误, 输入内容为空,请输入内容以成功生成QR码图像。
    exit
  }
  If MATRIX_TO_PRINT between 1 and 7
  {
    ;Msgbox, 0x10, Error, ERROR CODE: %MATRIX_TO_PRINT% `n`nERROR CODE TABLE:`n`n1 - Input message is blank.`n2 - The Choosen Code Mode cannot encode all the characters in the input message.`n3 - Choosen Code Mode does not correspond to one of the currently indexed code modes (Automatic, numeric, alphanumeric or byte).`n4 - The choosen forced QR Matrix version (size) cannot encode the entire input message using the choosen ECL Code_Mode. Try forcing a higher version or choosing automated version selection (parameter value 0).`n5 - The input message is exceeding the QR Code standards maximum length for the choosen ECL and Code Mode.`n6 - Choosen Error Correction Level does not correspond to one of the standard ECLs (L, M, Q and H).`n7 - Forced version does not correspond to one of the QR Code standards versions.
    Msgbox, 0x10, 错误,错误代码:%MATRIX_TO_PRINT%`r`n错误代码表:`r`n1-输入内容为空`r`n2-所选择代码模式无法对输入内容中的所有字符进行编码`r`n3-所选择代码模式不符合当前的索引代码模式(自动、数字、字母数字或字节)`r`n4-所选择的强制QR矩阵version(size)不能使用所选择的ECL代码_模式编码整个输入消息,尝试强制执行较高版本或者将参数留空(参数默认值为0)version (size)5-输入信息超过所选 ECL 和代码模式的 QR 码标准最大长度`r`n6-选择的错误修正级别不对应于标准ECL(L、M、Q和H)之一`r`n7-强制版本不符合QR码标准版本之一
    exit
  }
  ; Start gdi+
  If !pToken := Gdip_Startup() {
    ;MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
    MsgBox, 48,Gdiplus启动失败,请确保你的系统上有Gdiplus
    ExitApp
  }
  ; Adding 8 pixels to the width and height here as a "quiet zone" for the image. This serves to improve the printed code readability. QR Code specs require the quiet zones to surround the whole image and to be at least 4 modules wide (4 on each side = 8 total width added to the image). Don't forget to increase this number accordingly if you plan to change the pixel size of each module.
  ;增加8像素的宽度和高度的白色填充区作为“静止区”的图像,这有助于提高QR码的可读性,QR 码规格要求静止区围绕整个图像,并至少有4个模块宽(每边4 = 8总宽度添加到图像),如果你计划更改QR码的图像大小,请不要忘记相应地修改这个数字。
  pBitmap := Gdip_CreateBitmap((MATRIX_TO_PRINT.MaxIndex() + 8) * PixelSize, (MATRIX_TO_PRINT.MaxIndex() + 8) * PixelSize) 
  G := Gdip_GraphicsFromImage(pBitmap)
  Gdip_SetSmoothingMode(pBitmap, 3)
  pBrush := Gdip_BrushCreateSolid(0xFFFFFFFF)
  Gdip_FillRectangle(G, pBrush, 0, 0, (MATRIX_TO_PRINT.MaxIndex() + 8) * PixelSize, (MATRIX_TO_PRINT.MaxIndex() + 8) * PixelSize) ;同上
  Gdip_DeleteBrush(pBrush)
  ;以下为使用Gdi+在二维矩阵中按数据逐个格子填充黑色或白色色块
  Loop % MATRIX_TO_PRINT.MaxIndex() ; Acess the Rows of the Matrix ;矩阵行
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_TO_PRINT[1].MaxIndex() ; Access the modules (Columns of the Rows). ;行中的每个块(或者称呼为列)
    {
      CURRENT_COLUMN := A_Index
      If (MATRIX_TO_PRINT[CURRENT_ROW, A_Index] = 1)
      {
        ;Gdip_SetPixel(pBitmap, A_Index + 3, CURRENT_ROW + 3, 0xFF000000) ; Adding 3 to the current column and row to skip the quiet zones.
        Loop %PixelSize%
        {
          CURRENT_REDIMENSION_ROW := A_Index
          Loop %PixelSize%
            Gdip_SetPixel(pBitmap, (CURRENT_COLUMN * PixelSize) + (3*PixelSize) - 1 + A_Index, (CURRENT_ROW * PixelSize) + (3*PixelSize) - 1 + CURRENT_REDIMENSION_ROW, 0xFF000000)
        }
      }
      If (MATRIX_TO_PRINT[CURRENT_ROW, A_Index] = 0) ; White pixels need some more attention too when doing multi pixelwide images. ;在处理多像素范围的图像时,白色像素也需要更多的关注。
      {
        Loop %PixelSize%
        {
          CURRENT_REDIMENSION_ROW := A_Index
          Loop %PixelSize%
            Gdip_SetPixel(pBitmap, (CURRENT_COLUMN * PixelSize) + (3*PixelSize) - 1 + A_Index, (CURRENT_ROW * PixelSize) + (3*PixelSize) -1 + CURRENT_REDIMENSION_ROW, 0xFFFFFFFF)
        }
      }
    }
  }
  StringReplace, FILE_NAME_TO_USE, str, `" ; We can't use all the characters that byte mode can encode in the name of the file. So we are replacing them here (if they exist).
  ;FILE_PATH_AND_NAME := A_ScriptDir . "\" . SubStr(RegExReplace(FILE_NAME_TO_USE, "[\t\r\n\\\/\`:\`?\`*\`|\`>\`<]"), 1) ; Same as above.
  FILE_PATH_AND_NAME := A_Temp . "\tmp.png" ;原代码是将图片以二维码内容命名后存储到脚本同目录,这里改为放置到系统临时文件夹,如果需要批量生成QR码图片,可以更改此处逻辑
  ;IfExist,%FILE_PATH_AND_NAME% ;检查文件是否已经存在,如果目标二维码文件已被其他程序打开,则新生成的二维码文件会由于无法覆盖原文件而出错
    ;MsgBox,4144,Error,%FILE_PATH_AND_NAME%`nalready exists - try again
  Gdip_SaveBitmapToFile(pBitmap, FILE_PATH_AND_NAME) ;保存位图到目标文件
  Gdip_DisposeImage(pBitmap)
  Gdip_DeleteGraphics(G)
  Gdip_Shutdown(pToken)
  Return FILE_PATH_AND_NAME ;返回二维码图片的地址用于GUI图片控件的显示
}

; 须在脚本的尾部引入以下两个库"
; #Include BARCODER.ahk  ; https://github.com/Ixiko/AHK-libs-and-classes-collection/blob/master/libs/a-f/BARCODER.ahk
; #Include Gdip_All.ahk

; Gdip standard library v1.45 by tic (Tariq Porter) 07/09/11
; Modifed by Rseding91 using fincs 64 bit compatible Gdip library 5/1/2013
; Supports: Basic, _L ANSi, _L Unicode x86 and _L Unicode x64
;
;#####################################################################################
;#####################################################################################
; STATUS ENUMERATION
; Return values for functions specified to have status enumerated return type
;#####################################################################################
;
; Ok =						= 0
; GenericError				= 1
; InvalidParameter			= 2
; OutOfMemory				= 3
; ObjectBusy				= 4
; InsufficientBuffer		= 5
; NotImplemented			= 6
; Win32Error				= 7
; WrongState				= 8
; Aborted					= 9
; FileNotFound				= 10
; ValueOverflow				= 11
; AccessDenied				= 12
; UnknownImageFormat		= 13
; FontFamilyNotFound		= 14
; FontStyleNotFound			= 15
; NotTrueTypeFont			= 16
; UnsupportedGdiplusVersion	= 17
; GdiplusNotInitialized		= 18
; PropertyNotFound			= 19
; PropertyNotSupported		= 20
; ProfileNotFound			= 21
;
;#####################################################################################
;#####################################################################################
; FUNCTIONS
;#####################################################################################
;
; UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
; BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
; StretchBlt(dDC, dx, dy, dw, dh, sDC, sx, sy, sw, sh, Raster="")
; SetImage(hwnd, hBitmap)
; Gdip_BitmapFromScreen(Screen=0, Raster="")
; CreateRectF(ByRef RectF, x, y, w, h)
; CreateSizeF(ByRef SizeF, w, h)
; CreateDIBSection
;
;#####################################################################################

; Function:     			UpdateLayeredWindow
; Description:  			Updates a layered window with the handle to the DC of a gdi bitmap
; 
; hwnd        				Handle of the layered window to update
; hdc           			Handle to the DC of the GDI bitmap to update the window with
; Layeredx      			x position to place the window
; Layeredy      			y position to place the window
; Layeredw      			Width of the window
; Layeredh      			Height of the window
; Alpha         			Default = 255 : The transparency (0-255) to set the window transparency
;
; return      				If the function succeeds, the return value is nonzero
;
; notes						If x or y omitted, then layered window will use its current coordinates
;							If w or h omitted then current width and height will be used

UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if ((x != "") && (y != ""))
    VarSetCapacity(pt, 8), NumPut(x, pt, 0, "UInt"), NumPut(y, pt, 4, "UInt")

  if (w = "") ||(h = "")
    WinGetPos,,, w, h, ahk_id %hwnd%
   
  return DllCall("UpdateLayeredWindow"
          , Ptr, hwnd
          , Ptr, 0
          , Ptr, ((x = "") && (y = "")) ? 0 : &pt
          , "int64*", w|h<<32
          , Ptr, hdc
          , "int64*", 0
          , "uint", 0
          , "UInt*", Alpha<<16|1<<24
          , "uint", 2)
}

;#####################################################################################

; Function				BitBlt
; Description			The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle 
;						of pixels from the specified source device context into a destination device context.
;
; dDC					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of the area to copy
; dh					height of the area to copy
; sDC					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used, which copies the source directly to the destination rectangle
;
; BLACKNESS				= 0x00000042
; NOTSRCERASE			= 0x001100A6
; NOTSRCCOPY			= 0x00330008
; SRCERASE				= 0x00440328
; DSTINVERT				= 0x00550009
; PATINVERT				= 0x005A0049
; SRCINVERT				= 0x00660046
; SRCAND				= 0x008800C6
; MERGEPAINT			= 0x00BB0226
; MERGECOPY				= 0x00C000CA
; SRCCOPY				= 0x00CC0020
; SRCPAINT				= 0x00EE0086
; PATCOPY				= 0x00F00021
; PATPAINT				= 0x00FB0A09
; WHITENESS				= 0x00FF0062
; CAPTUREBLT			= 0x40000000
; NOMIRRORBITMAP		= 0x80000000

BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdi32\BitBlt"
          , Ptr, dDC
          , "int", dx
          , "int", dy
          , "int", dw
          , "int", dh
          , Ptr, sDC
          , "int", sx
          , "int", sy
          , "uint", Raster ? Raster : 0x00CC0020)
}

;#####################################################################################

; Function				StretchBlt
; Description			The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle, 
;						stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary.
;						The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
;
; ddc					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination rectangle
; dh					height of destination rectangle
; sdc					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used. It uses the same raster operations as BitBlt		

StretchBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, sw, sh, Raster="")
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdi32\StretchBlt"
          , Ptr, ddc
          , "int", dx
          , "int", dy
          , "int", dw
          , "int", dh
          , Ptr, sdc
          , "int", sx
          , "int", sy
          , "int", sw
          , "int", sh
          , "uint", Raster ? Raster : 0x00CC0020)
}

;#####################################################################################

; Function				SetStretchBltMode
; Description			The SetStretchBltMode function sets the bitmap stretching mode in the specified device context
;
; hdc					handle to the DC
; iStretchMode			The stretching mode, describing how the target will be stretched
;
; return				If the function succeeds, the return value is the previous stretching mode. If it fails it will return 0
;
; STRETCH_ANDSCANS 		= 0x01
; STRETCH_ORSCANS 		= 0x02
; STRETCH_DELETESCANS 	= 0x03
; STRETCH_HALFTONE 		= 0x04

SetStretchBltMode(hdc, iStretchMode=4)
{
  return DllCall("gdi32\SetStretchBltMode"
          , A_PtrSize ? "UPtr" : "UInt", hdc
          , "int", iStretchMode)
}

;#####################################################################################

; Function				SetImage
; Description			Associates a new image with a static control
;
; hwnd					handle of the control to update
; hBitmap				a gdi bitmap to associate the static control with
;
; return				If the function succeeds, the return value is nonzero

SetImage(hwnd, hBitmap)
{
  SendMessage, 0x172, 0x0, hBitmap,, ahk_id %hwnd%
  E := ErrorLevel
  DeleteObject(E)
  return E
}

;#####################################################################################

; Function				SetSysColorToControl
; Description			Sets a solid colour to a control
;
; hwnd					handle of the control to update
; SysColor				A system colour to set to the control
;
; return				If the function succeeds, the return value is zero
;
; notes					A control must have the 0xE style set to it so it is recognised as a bitmap
;						By default SysColor=15 is used which is COLOR_3DFACE. This is the standard background for a control
;
; COLOR_3DDKSHADOW				= 21
; COLOR_3DFACE					= 15
; COLOR_3DHIGHLIGHT				= 20
; COLOR_3DHILIGHT				= 20
; COLOR_3DLIGHT					= 22
; COLOR_3DSHADOW				= 16
; COLOR_ACTIVEBORDER			= 10
; COLOR_ACTIVECAPTION			= 2
; COLOR_APPWORKSPACE			= 12
; COLOR_BACKGROUND				= 1
; COLOR_BTNFACE					= 15
; COLOR_BTNHIGHLIGHT			= 20
; COLOR_BTNHILIGHT				= 20
; COLOR_BTNSHADOW				= 16
; COLOR_BTNTEXT					= 18
; COLOR_CAPTIONTEXT				= 9
; COLOR_DESKTOP					= 1
; COLOR_GRADIENTACTIVECAPTION	= 27
; COLOR_GRADIENTINACTIVECAPTION	= 28
; COLOR_GRAYTEXT				= 17
; COLOR_HIGHLIGHT				= 13
; COLOR_HIGHLIGHTTEXT			= 14
; COLOR_HOTLIGHT				= 26
; COLOR_INACTIVEBORDER			= 11
; COLOR_INACTIVECAPTION			= 3
; COLOR_INACTIVECAPTIONTEXT		= 19
; COLOR_INFOBK					= 24
; COLOR_INFOTEXT				= 23
; COLOR_MENU					= 4
; COLOR_MENUHILIGHT				= 29
; COLOR_MENUBAR					= 30
; COLOR_MENUTEXT				= 7
; COLOR_SCROLLBAR				= 0
; COLOR_WINDOW					= 5
; COLOR_WINDOWFRAME				= 6
; COLOR_WINDOWTEXT				= 8

SetSysColorToControl(hwnd, SysColor=15)
{
   WinGetPos,,, w, h, ahk_id %hwnd%
   bc := DllCall("GetSysColor", "Int", SysColor, "UInt")
   pBrushClear := Gdip_BrushCreateSolid(0xff000000 | (bc >> 16 | bc & 0xff00 | (bc & 0xff) << 16))
   pBitmap := Gdip_CreateBitmap(w, h), G := Gdip_GraphicsFromImage(pBitmap)
   Gdip_FillRectangle(G, pBrushClear, 0, 0, w, h)
   hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
   SetImage(hwnd, hBitmap)
   Gdip_DeleteBrush(pBrushClear)
   Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap), DeleteObject(hBitmap)
   return 0
}

;#####################################################################################

; Function				Gdip_BitmapFromScreen
; Description			Gets a gdi+ bitmap from the screen
;
; Screen				0 = All screens
;						Any numerical value = Just that screen
;						x|y|w|h = Take specific coordinates with a width and height
; Raster				raster operation code
;
; return      			If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1:		one or more of x,y,w,h not passed properly
;
; notes					If no raster operation is specified, then SRCCOPY is used to the returned bitmap

Gdip_BitmapFromScreen(Screen=0, Raster="")
{
  if (Screen = 0)
  {
    Sysget, x, 76
    Sysget, y, 77	
    Sysget, w, 78
    Sysget, h, 79
  }
  else if (SubStr(Screen, 1, 5) = "hwnd:")
  {
    Screen := SubStr(Screen, 6)
    if !WinExist( "ahk_id " Screen)
      return -2
    WinGetPos,,, w, h, ahk_id %Screen%
    x := y := 0
    hhdc := GetDCEx(Screen, 3)
  }
  else if (Screen&1 != "")
  {
    Sysget, M, Monitor, %Screen%
    x := MLeft, y := MTop, w := MRight-MLeft, h := MBottom-MTop
  }
  else
  {
    StringSplit, S, Screen, |
    x := S1, y := S2, w := S3, h := S4
  }

  if (x = "") || (y = "") || (w = "") || (h = "")
    return -1

  chdc := CreateCompatibleDC(), hbm := CreateDIBSection(w, h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC()
  BitBlt(chdc, 0, 0, w, h, hhdc, x, y, Raster)
  ReleaseDC(hhdc)
  
  pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
  SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc)
  return pBitmap
}

;#####################################################################################

; Function				Gdip_BitmapFromHWND
; Description			Uses PrintWindow to get a handle to the specified window and return a bitmap from it
;
; hwnd					handle to the window to get a bitmap from
;
; return				If the function succeeds, the return value is a pointer to a gdi+ bitmap
;
; notes					Window must not be not minimised in order to get a handle to it's client area

Gdip_BitmapFromHWND(hwnd)
{
  WinGetPos,,, Width, Height, ahk_id %hwnd%
  hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
  PrintWindow(hwnd, hdc)
  pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
  SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
  return pBitmap
}

;#####################################################################################

; Function    			CreateRectF
; Description			Creates a RectF object, containing a the coordinates and dimensions of a rectangle
;
; RectF       			Name to call the RectF object
; x            			x-coordinate of the upper left corner of the rectangle
; y            			y-coordinate of the upper left corner of the rectangle
; w            			Width of the rectangle
; h            			Height of the rectangle
;
; return      			No return value

CreateRectF(ByRef RectF, x, y, w, h)
{
   VarSetCapacity(RectF, 16)
   NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float"), NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float")
}

;#####################################################################################

; Function    			CreateRect
; Description			Creates a Rect object, containing a the coordinates and dimensions of a rectangle
;
; RectF       			Name to call the RectF object
; x            			x-coordinate of the upper left corner of the rectangle
; y            			y-coordinate of the upper left corner of the rectangle
; w            			Width of the rectangle
; h            			Height of the rectangle
;
; return      			No return value

CreateRect(ByRef Rect, x, y, w, h)
{
  VarSetCapacity(Rect, 16)
  NumPut(x, Rect, 0, "uint"), NumPut(y, Rect, 4, "uint"), NumPut(w, Rect, 8, "uint"), NumPut(h, Rect, 12, "uint")
}
;#####################################################################################

; Function		    	CreateSizeF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF         		Name to call the SizeF object
; w            			w-value for the SizeF object
; h            			h-value for the SizeF object
;
; return      			No Return value

CreateSizeF(ByRef SizeF, w, h)
{
   VarSetCapacity(SizeF, 8)
   NumPut(w, SizeF, 0, "float"), NumPut(h, SizeF, 4, "float")     
}
;#####################################################################################

; Function		    	CreatePointF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF         		Name to call the SizeF object
; w            			w-value for the SizeF object
; h            			h-value for the SizeF object
;
; return      			No Return value

CreatePointF(ByRef PointF, x, y)
{
   VarSetCapacity(PointF, 8)
   NumPut(x, PointF, 0, "float"), NumPut(y, PointF, 4, "float")     
}
;#####################################################################################

; Function				CreateDIBSection
; Description			The CreateDIBSection function creates a DIB (Device Independent Bitmap) that applications can write to directly
;
; w						width of the bitmap to create
; h						height of the bitmap to create
; hdc					a handle to the device context to use the palette from
; bpp					bits per pixel (32 = ARGB)
; ppvBits				A pointer to a variable that receives a pointer to the location of the DIB bit values
;
; return				returns a DIB. A gdi bitmap
;
; notes					ppvBits will receive the location of the pixels in the DIB

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  hdc2 := hdc ? hdc : GetDC()
  VarSetCapacity(bi, 40, 0)
  
  NumPut(w, bi, 4, "uint")
  , NumPut(h, bi, 8, "uint")
  , NumPut(40, bi, 0, "uint")
  , NumPut(1, bi, 12, "ushort")
  , NumPut(0, bi, 16, "uInt")
  , NumPut(bpp, bi, 14, "ushort")
  
  hbm := DllCall("CreateDIBSection"
          , Ptr, hdc2
          , Ptr, &bi
          , "uint", 0
          , A_PtrSize ? "UPtr*" : "uint*", ppvBits
          , Ptr, 0
          , "uint", 0, Ptr)

  if !hdc
    ReleaseDC(hdc2)
  return hbm
}

;#####################################################################################

; Function				PrintWindow
; Description			The PrintWindow function copies a visual window into the specified device context (DC), typically a printer DC
;
; hwnd					A handle to the window that will be copied
; hdc					A handle to the device context
; Flags					Drawing options
;
; return				If the function succeeds, it returns a nonzero value
;
; PW_CLIENTONLY			= 1

PrintWindow(hwnd, hdc, Flags=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("PrintWindow", Ptr, hwnd, Ptr, hdc, "uint", Flags)
}

;#####################################################################################

; Function				DestroyIcon
; Description			Destroys an icon and frees any memory the icon occupied
;
; hIcon					Handle to the icon to be destroyed. The icon must not be in use
;
; return				If the function succeeds, the return value is nonzero

DestroyIcon(hIcon)
{
  return DllCall("DestroyIcon", A_PtrSize ? "UPtr" : "UInt", hIcon)
}

;#####################################################################################

PaintDesktop(hdc)
{
  return DllCall("PaintDesktop", A_PtrSize ? "UPtr" : "UInt", hdc)
}

;#####################################################################################

CreateCompatibleBitmap(hdc, w, h)
{
  return DllCall("gdi32\CreateCompatibleBitmap", A_PtrSize ? "UPtr" : "UInt", hdc, "int", w, "int", h)
}

;#####################################################################################

; Function				CreateCompatibleDC
; Description			This function creates a memory device context (DC) compatible with the specified device
;
; hdc					Handle to an existing device context					
;
; return				returns the handle to a device context or 0 on failure
;
; notes					If this handle is 0 (by default), the function creates a memory device context compatible with the application's current screen

CreateCompatibleDC(hdc=0)
{
   return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

;#####################################################################################

; Function				SelectObject
; Description			The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type
;
; hdc					Handle to a DC
; hgdiobj				A handle to the object to be selected into the DC
;
; return				If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced
;
; notes					The specified object must have been created by using one of the following functions
;						Bitmap - CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection (A single bitmap cannot be selected into more than one DC at the same time)
;						Brush - CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush
;						Font - CreateFont, CreateFontIndirect
;						Pen - CreatePen, CreatePenIndirect
;						Region - CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect
;
; notes					If the selected object is a region and the function succeeds, the return value is one of the following value
;
; SIMPLEREGION			= 2 Region consists of a single rectangle
; COMPLEXREGION			= 3 Region consists of more than one rectangle
; NULLREGION			= 1 Region is empty

SelectObject(hdc, hgdiobj)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj)
}

;#####################################################################################

; Function				DeleteObject
; Description			This function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object
;						After the object is deleted, the specified handle is no longer valid
;
; hObject				Handle to a logical pen, brush, font, bitmap, region, or palette to delete
;
; return				Nonzero indicates success. Zero indicates that the specified handle is not valid or that the handle is currently selected into a device context

DeleteObject(hObject)
{
   return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject)
}

;#####################################################################################

; Function				GetDC
; Description			This function retrieves a handle to a display device context (DC) for the client area of the specified window.
;						The display device context can be used in subsequent graphics display interface (GDI) functions to draw in the client area of the window. 
;
; hwnd					Handle to the window whose device context is to be retrieved. If this value is NULL, GetDC retrieves the device context for the entire screen					
;
; return				The handle the device context for the specified window's client area indicates success. NULL indicates failure

GetDC(hwnd=0)
{
  return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd)
}

;#####################################################################################

; DCX_CACHE = 0x2
; DCX_CLIPCHILDREN = 0x8
; DCX_CLIPSIBLINGS = 0x10
; DCX_EXCLUDERGN = 0x40
; DCX_EXCLUDEUPDATE = 0x100
; DCX_INTERSECTRGN = 0x80
; DCX_INTERSECTUPDATE = 0x200
; DCX_LOCKWINDOWUPDATE = 0x400
; DCX_NORECOMPUTE = 0x100000
; DCX_NORESETATTRS = 0x4
; DCX_PARENTCLIP = 0x20
; DCX_VALIDATE = 0x200000
; DCX_WINDOW = 0x1

GetDCEx(hwnd, flags=0, hrgnClip=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
    return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags)
}

;#####################################################################################

; Function				ReleaseDC
; Description			This function releases a device context (DC), freeing it for use by other applications. The effect of ReleaseDC depends on the type of device context
;
; hdc					Handle to the device context to be released
; hwnd					Handle to the window whose device context is to be released
;
; return				1 = released
;						0 = not released
;
; notes					The application must call the ReleaseDC function for each call to the GetWindowDC function and for each call to the GetDC function that retrieves a common device context
;						An application cannot use the ReleaseDC function to release a device context that was created by calling the CreateDC function; instead, it must use the DeleteDC function. 

ReleaseDC(hdc, hwnd=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc)
}

;#####################################################################################

; Function				DeleteDC
; Description			The DeleteDC function deletes the specified device context (DC)
;
; hdc					A handle to the device context
;
; return				If the function succeeds, the return value is nonzero
;
; notes					An application must not delete a DC whose handle was obtained by calling the GetDC function. Instead, it must call the ReleaseDC function to free the DC

DeleteDC(hdc)
{
   return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}
;#####################################################################################

; Function				Gdip_LibraryVersion
; Description			Get the current library version
;
; return				the library version
;
; notes					This is useful for non compiled programs to ensure that a person doesn't run an old version when testing your scripts

Gdip_LibraryVersion()
{
  return 1.45
}

;#####################################################################################

; Function:    			Gdip_BitmapFromBRA
; Description: 			Gets a pointer to a gdi+ bitmap from a BRA file
;
; BRAFromMemIn			The variable for a BRA file read to memory
; File					The name of the file, or its number that you would like (This depends on alternate parameter)
; Alternate				Changes whether the File parameter is the file name or its number
;
; return      			If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1 = The BRA variable is empty
;						-2 = The BRA has an incorrect header
;						-3 = The BRA has information missing
;						-4 = Could not find file inside the BRA

Gdip_BitmapFromBRA(ByRef BRAFromMemIn, File, Alternate=0)
{
  Static FName = "ObjRelease"
  
  if !BRAFromMemIn
    return -1
  Loop, Parse, BRAFromMemIn, `n
  {
    if (A_Index = 1)
    {
      StringSplit, Header, A_LoopField, |
      if (Header0 != 4 || Header2 != "BRA!")
        return -2
    }
    else if (A_Index = 2)
    {
      StringSplit, Info, A_LoopField, |
      if (Info0 != 3)
        return -3
    }
    else
      break
  }
  if !Alternate
    StringReplace, File, File, \, \\, All
  RegExMatch(BRAFromMemIn, "mi`n)^" (Alternate ? File "\|.+?\|(\d+)\|(\d+)" : "\d+\|" File "\|(\d+)\|(\d+)") "$", FileInfo)
  if !FileInfo
    return -4
  
  hData := DllCall("GlobalAlloc", "uint", 2, Ptr, FileInfo2, Ptr)
  pData := DllCall("GlobalLock", Ptr, hData, Ptr)
  DllCall("RtlMoveMemory", Ptr, pData, Ptr, &BRAFromMemIn+Info2+FileInfo1, Ptr, FileInfo2)
  DllCall("GlobalUnlock", Ptr, hData)
  DllCall("ole32\CreateStreamOnHGlobal", Ptr, hData, "int", 1, A_PtrSize ? "UPtr*" : "UInt*", pStream)
  DllCall("gdiplus\GdipCreateBitmapFromStream", Ptr, pStream, A_PtrSize ? "UPtr*" : "UInt*", pBitmap)
  If (A_PtrSize)
    %FName%(pStream)
  Else
    DllCall(NumGet(NumGet(1*pStream)+8), "uint", pStream)
  return pBitmap
}

;#####################################################################################

; Function				Gdip_DrawRectangle
; Description			This function uses a pen to draw the outline of a rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipDrawRectangle", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_DrawRoundedRectangle
; Description			This function uses a pen to draw the outline of a rounded rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawRoundedRectangle(pGraphics, pPen, x, y, w, h, r)
{
  Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
  Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
  Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
  Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
  E := Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
  Gdip_ResetClip(pGraphics)
  Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
  Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
  Gdip_DrawEllipse(pGraphics, pPen, x, y, 2*r, 2*r)
  Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y, 2*r, 2*r)
  Gdip_DrawEllipse(pGraphics, pPen, x, y+h-(2*r), 2*r, 2*r)
  Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
  Gdip_ResetClip(pGraphics)
  return E
}

;#####################################################################################

; Function				Gdip_DrawEllipse
; Description			This function uses a pen to draw the outline of an ellipse into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle the ellipse will be drawn into
; y						y-coordinate of the top left of the rectangle the ellipse will be drawn into
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawEllipse(pGraphics, pPen, x, y, w, h)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipDrawEllipse", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_DrawBezier
; Description			This function uses a pen to draw the outline of a bezier (a weighted curve) into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the bezier
; y1					y-coordinate of the start of the bezier
; x2					x-coordinate of the first arc of the bezier
; y2					y-coordinate of the first arc of the bezier
; x3					x-coordinate of the second arc of the bezier
; y3					y-coordinate of the second arc of the bezier
; x4					x-coordinate of the end of the bezier
; y4					y-coordinate of the end of the bezier
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawBezier(pGraphics, pPen, x1, y1, x2, y2, x3, y3, x4, y4)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipDrawBezier"
          , Ptr, pgraphics
          , Ptr, pPen
          , "float", x1
          , "float", y1
          , "float", x2
          , "float", y2
          , "float", x3
          , "float", y3
          , "float", x4
          , "float", y4)
}

;#####################################################################################

; Function				Gdip_DrawArc
; Description			This function uses a pen to draw the outline of an arc into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the arc
; y						y-coordinate of the start of the arc
; w						width of the arc
; h						height of the arc
; StartAngle			specifies the angle between the x-axis and the starting point of the arc
; SweepAngle			specifies the angle between the starting and ending points of the arc
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawArc(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipDrawArc"
          , Ptr, pGraphics
          , Ptr, pPen
          , "float", x
          , "float", y
          , "float", w
          , "float", h
          , "float", StartAngle
          , "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_DrawPie
; Description			This function uses a pen to draw the outline of a pie into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the pie
; y						y-coordinate of the start of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawPie(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipDrawPie", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_DrawLine
; Description			This function uses a pen to draw a line into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the line
; y1					y-coordinate of the start of the line
; x2					x-coordinate of the end of the line
; y2					y-coordinate of the end of the line
;
; return				status enumeration. 0 = success		

Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipDrawLine"
          , Ptr, pGraphics
          , Ptr, pPen
          , "float", x1
          , "float", y1
          , "float", x2
          , "float", y2)
}

;#####################################################################################

; Function				Gdip_DrawLines
; Description			This function uses a pen to draw a series of joined lines into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success				

Gdip_DrawLines(pGraphics, pPen, Points)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  StringSplit, Points, Points, |
  VarSetCapacity(PointF, 8*Points0)   
  Loop, %Points0%
  {
    StringSplit, Coord, Points%A_Index%, `,
    NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
  }
  return DllCall("gdiplus\GdipDrawLines", Ptr, pGraphics, Ptr, pPen, Ptr, &PointF, "int", Points0)
}

;#####################################################################################

; Function				Gdip_FillRectangle
; Description			This function uses a brush to fill a rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success

Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipFillRectangle"
          , Ptr, pGraphics
          , Ptr, pBrush
          , "float", x
          , "float", y
          , "float", w
          , "float", h)
}

;#####################################################################################

; Function				Gdip_FillRoundedRectangle
; Description			This function uses a brush to fill a rounded rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success

Gdip_FillRoundedRectangle(pGraphics, pBrush, x, y, w, h, r)
{
  Region := Gdip_GetClipRegion(pGraphics)
  Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
  Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
  Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
  Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
  E := Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
  Gdip_SetClipRegion(pGraphics, Region, 0)
  Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
  Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
  Gdip_FillEllipse(pGraphics, pBrush, x, y, 2*r, 2*r)
  Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y, 2*r, 2*r)
  Gdip_FillEllipse(pGraphics, pBrush, x, y+h-(2*r), 2*r, 2*r)
  Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
  Gdip_SetClipRegion(pGraphics, Region, 0)
  Gdip_DeleteRegion(Region)
  return E
}

;#####################################################################################

; Function				Gdip_FillPolygon
; Description			This function uses a brush to fill a polygon in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success
;
; notes					Alternate will fill the polygon as a whole, wheras winding will fill each new "segment"
; Alternate 			= 0
; Winding 				= 1

Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  StringSplit, Points, Points, |
  VarSetCapacity(PointF, 8*Points0)   
  Loop, %Points0%
  {
    StringSplit, Coord, Points%A_Index%, `,
    NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
  }   
  return DllCall("gdiplus\GdipFillPolygon", Ptr, pGraphics, Ptr, pBrush, Ptr, &PointF, "int", Points0, "int", FillMode)
}

;#####################################################################################

; Function				Gdip_FillPie
; Description			This function uses a brush to fill a pie in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the pie
; y						y-coordinate of the top left of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success

Gdip_FillPie(pGraphics, pBrush, x, y, w, h, StartAngle, SweepAngle)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipFillPie"
          , Ptr, pGraphics
          , Ptr, pBrush
          , "float", x
          , "float", y
          , "float", w
          , "float", h
          , "float", StartAngle
          , "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_FillEllipse
; Description			This function uses a brush to fill an ellipse in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the ellipse
; y						y-coordinate of the top left of the ellipse
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success

Gdip_FillEllipse(pGraphics, pBrush, x, y, w, h)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipFillEllipse", Ptr, pGraphics, Ptr, pBrush, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_FillRegion
; Description			This function uses a brush to fill a region in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Region
;
; return				status enumeration. 0 = success
;
; notes					You can create a region Gdip_CreateRegion() and then add to this

Gdip_FillRegion(pGraphics, pBrush, Region)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipFillRegion", Ptr, pGraphics, Ptr, pBrush, Ptr, Region)
}

;#####################################################################################

; Function				Gdip_FillPath
; Description			This function uses a brush to fill a path in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Path
;
; return				status enumeration. 0 = success

Gdip_FillPath(pGraphics, pBrush, Path)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipFillPath", Ptr, pGraphics, Ptr, pBrush, Ptr, Path)
}

;#####################################################################################

; Function				Gdip_DrawImagePointsRect
; Description			This function draws a bitmap into the Graphics of another bitmap and skews it
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; Points				Points passed as x1,y1|x2,y2|x3,y3 (3 points: top left, top right, bottom left) describing the drawing of the bitmap
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter

Gdip_DrawImagePointsRect(pGraphics, pBitmap, Points, sx="", sy="", sw="", sh="", Matrix=1)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  StringSplit, Points, Points, |
  VarSetCapacity(PointF, 8*Points0)   
  Loop, %Points0%
  {
    StringSplit, Coord, Points%A_Index%, `,
    NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
  }

  if (Matrix&1 = "")
    ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
  else if (Matrix != 1)
    ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")
    
  if (sx = "" && sy = "" && sw = "" && sh = "")
  {
    sx := 0, sy := 0
    sw := Gdip_GetImageWidth(pBitmap)
    sh := Gdip_GetImageHeight(pBitmap)
  }

  E := DllCall("gdiplus\GdipDrawImagePointsRect"
        , Ptr, pGraphics
        , Ptr, pBitmap
        , Ptr, &PointF
        , "int", Points0
        , "float", sx
        , "float", sy
        , "float", sw
        , "float", sh
        , "int", 2
        , Ptr, ImageAttr
        , Ptr, 0
        , Ptr, 0)
  if ImageAttr
    Gdip_DisposeImageAttributes(ImageAttr)
  return E
}

;#####################################################################################

; Function				Gdip_DrawImage
; Description			This function draws a bitmap into the Graphics of another bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination image
; dh					height of destination image
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source image
; sh					height of source image
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Gdip_DrawImage performs faster
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter. For example:
;						MatrixBright=
;						(
;						1.5		|0		|0		|0		|0
;						0		|1.5	|0		|0		|0
;						0		|0		|1.5	|0		|0
;						0		|0		|0		|1		|0
;						0.05	|0.05	|0.05	|0		|1
;						)
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|0|0|0|0|1

Gdip_DrawImage(pGraphics, pBitmap, dx="", dy="", dw="", dh="", sx="", sy="", sw="", sh="", Matrix=1)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if (Matrix&1 = "")
    ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
  else if (Matrix != 1)
    ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")

  if (sx = "" && sy = "" && sw = "" && sh = "")
  {
    if (dx = "" && dy = "" && dw = "" && dh = "")
    {
      sx := dx := 0, sy := dy := 0
      sw := dw := Gdip_GetImageWidth(pBitmap)
      sh := dh := Gdip_GetImageHeight(pBitmap)
    }
    else
    {
      sx := sy := 0
      sw := Gdip_GetImageWidth(pBitmap)
      sh := Gdip_GetImageHeight(pBitmap)
    }
  }

  E := DllCall("gdiplus\GdipDrawImageRectRect"
        , Ptr, pGraphics
        , Ptr, pBitmap
        , "float", dx
        , "float", dy
        , "float", dw
        , "float", dh
        , "float", sx
        , "float", sy
        , "float", sw
        , "float", sh
        , "int", 2
        , Ptr, ImageAttr
        , Ptr, 0
        , Ptr, 0)
  if ImageAttr
    Gdip_DisposeImageAttributes(ImageAttr)
  return E
}

;#####################################################################################

; Function				Gdip_SetImageAttributesColorMatrix
; Description			This function creates an image matrix ready for drawing
;
; Matrix				a matrix used to alter image attributes when drawing
;						passed with any delimeter
;
; return				returns an image matrix on sucess or 0 if it fails
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|0|0|0|0|1

Gdip_SetImageAttributesColorMatrix(Matrix)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  VarSetCapacity(ColourMatrix, 100, 0)
  Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", "", 1), "[^\d-\.]+", "|")
  StringSplit, Matrix, Matrix, |
  Loop, 25
  {
    Matrix := (Matrix%A_Index% != "") ? Matrix%A_Index% : Mod(A_Index-1, 6) ? 0 : 1
    NumPut(Matrix, ColourMatrix, (A_Index-1)*4, "float")
  }
  DllCall("gdiplus\GdipCreateImageAttributes", A_PtrSize ? "UPtr*" : "uint*", ImageAttr)
  DllCall("gdiplus\GdipSetImageAttributesColorMatrix", Ptr, ImageAttr, "int", 1, "int", 1, Ptr, &ColourMatrix, Ptr, 0, "int", 0)
  return ImageAttr
}

;#####################################################################################

; Function				Gdip_GraphicsFromImage
; Description			This function gets the graphics for a bitmap used for drawing functions
;
; pBitmap				Pointer to a bitmap to get the pointer to its graphics
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					a bitmap can be drawn into the graphics of another bitmap

Gdip_GraphicsFromImage(pBitmap)
{
  DllCall("gdiplus\GdipGetImageGraphicsContext", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
  return pGraphics
}

;#####################################################################################

; Function				Gdip_GraphicsFromHDC
; Description			This function gets the graphics from the handle to a device context
;
; hdc					This is the handle to the device context
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					You can draw a bitmap into the graphics of another bitmap

Gdip_GraphicsFromHDC(hdc)
{
    DllCall("gdiplus\GdipCreateFromHDC", A_PtrSize ? "UPtr" : "UInt", hdc, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
    return pGraphics
}

;#####################################################################################

; Function				Gdip_GetDC
; Description			This function gets the device context of the passed Graphics
;
; hdc					This is the handle to the device context
;
; return				returns the device context for the graphics of a bitmap

Gdip_GetDC(pGraphics)
{
  DllCall("gdiplus\GdipGetDC", A_PtrSize ? "UPtr" : "UInt", pGraphics, A_PtrSize ? "UPtr*" : "UInt*", hdc)
  return hdc
}

;#####################################################################################

; Function				Gdip_ReleaseDC
; Description			This function releases a device context from use for further use
;
; pGraphics				Pointer to the graphics of a bitmap
; hdc					This is the handle to the device context
;
; return				status enumeration. 0 = success

Gdip_ReleaseDC(pGraphics, hdc)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipReleaseDC", Ptr, pGraphics, Ptr, hdc)
}

;#####################################################################################

; Function				Gdip_GraphicsClear
; Description			Clears the graphics of a bitmap ready for further drawing
;
; pGraphics				Pointer to the graphics of a bitmap
; ARGB					The colour to clear the graphics to
;
; return				status enumeration. 0 = success
;
; notes					By default this will make the background invisible
;						Using clipping regions you can clear a particular area on the graphics rather than clearing the entire graphics

Gdip_GraphicsClear(pGraphics, ARGB=0x00ffffff)
{
    return DllCall("gdiplus\GdipGraphicsClear", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", ARGB)
}

;#####################################################################################

; Function				Gdip_BlurBitmap
; Description			Gives a pointer to a blurred bitmap from a pointer to a bitmap
;
; pBitmap				Pointer to a bitmap to be blurred
; Blur					The Amount to blur a bitmap by from 1 (least blur) to 100 (most blur)
;
; return				If the function succeeds, the return value is a pointer to the new blurred bitmap
;						-1 = The blur parameter is outside the range 1-100
;
; notes					This function will not dispose of the original bitmap

Gdip_BlurBitmap(pBitmap, Blur)
{
  if (Blur > 100) || (Blur < 1)
    return -1	
  
  sWidth := Gdip_GetImageWidth(pBitmap), sHeight := Gdip_GetImageHeight(pBitmap)
  dWidth := sWidth//Blur, dHeight := sHeight//Blur

  pBitmap1 := Gdip_CreateBitmap(dWidth, dHeight)
  G1 := Gdip_GraphicsFromImage(pBitmap1)
  Gdip_SetInterpolationMode(G1, 7)
  Gdip_DrawImage(G1, pBitmap, 0, 0, dWidth, dHeight, 0, 0, sWidth, sHeight)

  Gdip_DeleteGraphics(G1)

  pBitmap2 := Gdip_CreateBitmap(sWidth, sHeight)
  G2 := Gdip_GraphicsFromImage(pBitmap2)
  Gdip_SetInterpolationMode(G2, 7)
  Gdip_DrawImage(G2, pBitmap1, 0, 0, sWidth, sHeight, 0, 0, dWidth, dHeight)

  Gdip_DeleteGraphics(G2)
  Gdip_DisposeImage(pBitmap1)
  return pBitmap2
}

;#####################################################################################

; Function:     		Gdip_SaveBitmapToFile
; Description:  		Saves a bitmap to a file in any supported format onto disk
;   
; pBitmap				Pointer to a bitmap
; sOutput      			The name of the file that the bitmap will be saved to. Supported extensions are: .BMP,.DIB,.RLE,.JPG,.JPEG,.JPE,.JFIF,.GIF,.TIF,.TIFF,.PNG
; Quality      			If saving as jpg (.JPG,.JPEG,.JPE,.JFIF) then quality can be 1-100 with default at maximum quality
;
; return      			If the function succeeds, the return value is zero, otherwise:
;						-1 = Extension supplied is not a supported file format
;						-2 = Could not get a list of encoders on system
;						-3 = Could not find matching encoder for specified file format
;						-4 = Could not get WideChar name of output file
;						-5 = Could not save file to disk
;
; notes					This function will use the extension supplied from the sOutput parameter to determine the output format

Gdip_SaveBitmapToFile(pBitmap, sOutput, Quality=75)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  SplitPath, sOutput,,, Extension
  if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
    return -1
  Extension := "." Extension

  DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount, "uint*", nSize)
  VarSetCapacity(ci, nSize)
  DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, Ptr, &ci)
  if !(nCount && nSize)
    return -2
  
  If (A_IsUnicode){
    StrGet_Name := "StrGet"
    Loop, %nCount%
    {
      sString := %StrGet_Name%(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
      if !InStr(sString, "*" Extension)
        continue
      
      pCodec := &ci+idx
      break
    }
  } else {
    Loop, %nCount%
    {
      Location := NumGet(ci, 76*(A_Index-1)+44)
      nSize := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "uint", 0, "int",  0, "uint", 0, "uint", 0)
      VarSetCapacity(sString, nSize)
      DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "str", sString, "int", nSize, "uint", 0, "uint", 0)
      if !InStr(sString, "*" Extension)
        continue
      
      pCodec := &ci+76*(A_Index-1)
      break
    }
  }
  
  if !pCodec
    return -3

  if (Quality != 75)
  {
    Quality := (Quality < 0) ? 0 : (Quality > 100) ? 100 : Quality
    if Extension in .JPG,.JPEG,.JPE,.JFIF
    {
      DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, "uint*", nSize)
      VarSetCapacity(EncoderParameters, nSize, 0)
      DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, "uint", nSize, Ptr, &EncoderParameters)
      Loop, % NumGet(EncoderParameters, "UInt")      ;%
      {
        elem := (24+(A_PtrSize ? A_PtrSize : 4))*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
        if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
        {
          p := elem+&EncoderParameters-pad-4
          NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
          break
        }
      }      
    }
  }

  if (!A_IsUnicode)
  {
    nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sOutput, "int", -1, Ptr, 0, "int", 0)
    VarSetCapacity(wOutput, nSize*2)
    DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sOutput, "int", -1, Ptr, &wOutput, "int", nSize)
    VarSetCapacity(wOutput, -1)
    if !VarSetCapacity(wOutput)
      return -4
    E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, &wOutput, Ptr, pCodec, "uint", p ? p : 0)
  }
  else
    E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, &sOutput, Ptr, pCodec, "uint", p ? p : 0)
  return E ? -5 : 0
}

;#####################################################################################

; Function				Gdip_GetPixel
; Description			Gets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				Returns the ARGB value of the pixel

Gdip_GetPixel(pBitmap, x, y)
{
  DllCall("gdiplus\GdipBitmapGetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "uint*", ARGB)
  return ARGB
}

;#####################################################################################

; Function				Gdip_SetPixel
; Description			Sets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				status enumeration. 0 = success

Gdip_SetPixel(pBitmap, x, y, ARGB)
{
   return DllCall("gdiplus\GdipBitmapSetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "int", ARGB)
}

;#####################################################################################

; Function				Gdip_GetImageWidth
; Description			Gives the width of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the width in pixels of the supplied bitmap

Gdip_GetImageWidth(pBitmap)
{
   DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width)
   return Width
}

;#####################################################################################

; Function				Gdip_GetImageHeight
; Description			Gives the height of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the height in pixels of the supplied bitmap

Gdip_GetImageHeight(pBitmap)
{
   DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height)
   return Height
}

;#####################################################################################

; Function				Gdip_GetDimensions
; Description			Gives the width and height of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height

Gdip_GetImageDimensions(pBitmap, ByRef Width, ByRef Height)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  DllCall("gdiplus\GdipGetImageWidth", Ptr, pBitmap, "uint*", Width)
  DllCall("gdiplus\GdipGetImageHeight", Ptr, pBitmap, "uint*", Height)
}

;#####################################################################################

Gdip_GetDimensions(pBitmap, ByRef Width, ByRef Height)
{
  Gdip_GetImageDimensions(pBitmap, Width, Height)
}

;#####################################################################################

Gdip_GetImagePixelFormat(pBitmap)
{
  DllCall("gdiplus\GdipGetImagePixelFormat", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", Format)
  return Format
}

;#####################################################################################

; Function				Gdip_GetDpiX
; Description			Gives the horizontal dots per inch of the graphics of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height

Gdip_GetDpiX(pGraphics)
{
  DllCall("gdiplus\GdipGetDpiX", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpix)
  return Round(dpix)
}

;#####################################################################################

Gdip_GetDpiY(pGraphics)
{
  DllCall("gdiplus\GdipGetDpiY", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpiy)
  return Round(dpiy)
}

;#####################################################################################

Gdip_GetImageHorizontalResolution(pBitmap)
{
  DllCall("gdiplus\GdipGetImageHorizontalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpix)
  return Round(dpix)
}

;#####################################################################################

Gdip_GetImageVerticalResolution(pBitmap)
{
  DllCall("gdiplus\GdipGetImageVerticalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpiy)
  return Round(dpiy)
}

;#####################################################################################

Gdip_BitmapSetResolution(pBitmap, dpix, dpiy)
{
  return DllCall("gdiplus\GdipBitmapSetResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float", dpix, "float", dpiy)
}

;#####################################################################################

Gdip_CreateBitmapFromFile(sFile, IconNumber=1, IconSize="")
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  , PtrA := A_PtrSize ? "UPtr*" : "UInt*"
  
  SplitPath, sFile,,, ext
  if ext in exe,dll
  {
    Sizes := IconSize ? IconSize : 256 "|" 128 "|" 64 "|" 48 "|" 32 "|" 16
    BufSize := 16 + (2*(A_PtrSize ? A_PtrSize : 4))
    
    VarSetCapacity(buf, BufSize, 0)
    Loop, Parse, Sizes, |
    {
      DllCall("PrivateExtractIcons", "str", sFile, "int", IconNumber-1, "int", A_LoopField, "int", A_LoopField, PtrA, hIcon, PtrA, 0, "uint", 1, "uint", 0)
      
      if !hIcon
        continue

      if !DllCall("GetIconInfo", Ptr, hIcon, Ptr, &buf)
      {
        DestroyIcon(hIcon)
        continue
      }
      
      hbmMask  := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4))
      hbmColor := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4) + (A_PtrSize ? A_PtrSize : 4))
      if !(hbmColor && DllCall("GetObject", Ptr, hbmColor, "int", BufSize, Ptr, &buf))
      {
        DestroyIcon(hIcon)
        continue
      }
      break
    }
    if !hIcon
      return -1

    Width := NumGet(buf, 4, "int"), Height := NumGet(buf, 8, "int")
    hbm := CreateDIBSection(Width, -Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
    if !DllCall("DrawIconEx", Ptr, hdc, "int", 0, "int", 0, Ptr, hIcon, "uint", Width, "uint", Height, "uint", 0, Ptr, 0, "uint", 3)
    {
      DestroyIcon(hIcon)
      return -2
    }
    
    VarSetCapacity(dib, 104)
    DllCall("GetObject", Ptr, hbm, "int", A_PtrSize = 8 ? 104 : 84, Ptr, &dib) ; sizeof(DIBSECTION) = 76+2*(A_PtrSize=8?4:0)+2*A_PtrSize
    Stride := NumGet(dib, 12, "Int"), Bits := NumGet(dib, 20 + (A_PtrSize = 8 ? 4 : 0)) ; padding
    DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", Stride, "int", 0x26200A, Ptr, Bits, PtrA, pBitmapOld)
    pBitmap := Gdip_CreateBitmap(Width, Height)
    G := Gdip_GraphicsFromImage(pBitmap)
    , Gdip_DrawImage(G, pBitmapOld, 0, 0, Width, Height, 0, 0, Width, Height)
    SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
    Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmapOld)
    DestroyIcon(hIcon)
  }
  else
  {
    if (!A_IsUnicode)
    {
      VarSetCapacity(wFile, 1024)
      DllCall("kernel32\MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sFile, "int", -1, Ptr, &wFile, "int", 512)
      DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &wFile, PtrA, pBitmap)
    }
    else
      DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &sFile, PtrA, pBitmap)
  }
  
  return pBitmap
}

;#####################################################################################

Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
  return pBitmap
}

;#####################################################################################

Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff)
{
  DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background)
  return hbm
}

;#####################################################################################

Gdip_CreateBitmapFromHICON(hIcon)
{
  DllCall("gdiplus\GdipCreateBitmapFromHICON", A_PtrSize ? "UPtr" : "UInt", hIcon, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
  return pBitmap
}

;#####################################################################################

Gdip_CreateHICONFromBitmap(pBitmap)
{
  DllCall("gdiplus\GdipCreateHICONFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hIcon)
  return hIcon
}

;#####################################################################################

Gdip_CreateBitmap(Width, Height, Format=0x26200A)
{
    DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", 0, "int", Format, A_PtrSize ? "UPtr" : "UInt", 0, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
    Return pBitmap
}

;#####################################################################################

Gdip_CreateBitmapFromClipboard()
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if !DllCall("OpenClipboard", Ptr, 0)
    return -1
  if !DllCall("IsClipboardFormatAvailable", "uint", 8)
    return -2
  if !hBitmap := DllCall("GetClipboardData", "uint", 2, Ptr)
    return -3
  if !pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap)
    return -4
  if !DllCall("CloseClipboard")
    return -5
  DeleteObject(hBitmap)
  return pBitmap
}

;#####################################################################################

Gdip_SetBitmapToClipboard(pBitmap)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
  
  hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
  DllCall("GetObject", Ptr, hBitmap, "int", VarSetCapacity(oi, A_PtrSize = 8 ? 96 : 84, 0), Ptr, &oi)
  hdib := DllCall("GlobalAlloc", "uint", 2, Ptr, off1+NumGet(oi, off1, "UInt")-4, Ptr)
  pdib := DllCall("GlobalLock", Ptr, hdib, Ptr)
  DllCall("RtlMoveMemory", Ptr, pdib, "uint", &oi+off2, Ptr, 40)
  DllCall("RtlMoveMemory", Ptr, pdib+40, Ptr, NumGet(oi, off2 - (A_PtrSize ? A_PtrSize : 4)), Ptr, NumGet(oi, off1, "UInt"))
  DllCall("GlobalUnlock", Ptr, hdib)
  DllCall("DeleteObject", Ptr, hBitmap)
  DllCall("OpenClipboard", Ptr, 0)
  DllCall("EmptyClipboard")
  DllCall("SetClipboardData", "uint", 8, Ptr, hdib)
  DllCall("CloseClipboard")
}

;#####################################################################################

Gdip_CloneBitmapArea(pBitmap, x, y, w, h, Format=0x26200A)
{
  DllCall("gdiplus\GdipCloneBitmapArea"
          , "float", x
          , "float", y
          , "float", w
          , "float", h
          , "int", Format
          , A_PtrSize ? "UPtr" : "UInt", pBitmap
          , A_PtrSize ? "UPtr*" : "UInt*", pBitmapDest)
  return pBitmapDest
}

;#####################################################################################
; Create resources
;#####################################################################################

Gdip_CreatePen(ARGB, w)
{
   DllCall("gdiplus\GdipCreatePen1", "UInt", ARGB, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
   return pPen
}

;#####################################################################################

Gdip_CreatePenFromBrush(pBrush, w)
{
  DllCall("gdiplus\GdipCreatePen2", A_PtrSize ? "UPtr" : "UInt", pBrush, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
  return pPen
}

;#####################################################################################

Gdip_BrushCreateSolid(ARGB=0xff000000)
{
  DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
  return pBrush
}

;#####################################################################################

; HatchStyleHorizontal = 0
; HatchStyleVertical = 1
; HatchStyleForwardDiagonal = 2
; HatchStyleBackwardDiagonal = 3
; HatchStyleCross = 4
; HatchStyleDiagonalCross = 5
; HatchStyle05Percent = 6
; HatchStyle10Percent = 7
; HatchStyle20Percent = 8
; HatchStyle25Percent = 9
; HatchStyle30Percent = 10
; HatchStyle40Percent = 11
; HatchStyle50Percent = 12
; HatchStyle60Percent = 13
; HatchStyle70Percent = 14
; HatchStyle75Percent = 15
; HatchStyle80Percent = 16
; HatchStyle90Percent = 17
; HatchStyleLightDownwardDiagonal = 18
; HatchStyleLightUpwardDiagonal = 19
; HatchStyleDarkDownwardDiagonal = 20
; HatchStyleDarkUpwardDiagonal = 21
; HatchStyleWideDownwardDiagonal = 22
; HatchStyleWideUpwardDiagonal = 23
; HatchStyleLightVertical = 24
; HatchStyleLightHorizontal = 25
; HatchStyleNarrowVertical = 26
; HatchStyleNarrowHorizontal = 27
; HatchStyleDarkVertical = 28
; HatchStyleDarkHorizontal = 29
; HatchStyleDashedDownwardDiagonal = 30
; HatchStyleDashedUpwardDiagonal = 31
; HatchStyleDashedHorizontal = 32
; HatchStyleDashedVertical = 33
; HatchStyleSmallConfetti = 34
; HatchStyleLargeConfetti = 35
; HatchStyleZigZag = 36
; HatchStyleWave = 37
; HatchStyleDiagonalBrick = 38
; HatchStyleHorizontalBrick = 39
; HatchStyleWeave = 40
; HatchStylePlaid = 41
; HatchStyleDivot = 42
; HatchStyleDottedGrid = 43
; HatchStyleDottedDiamond = 44
; HatchStyleShingle = 45
; HatchStyleTrellis = 46
; HatchStyleSphere = 47
; HatchStyleSmallGrid = 48
; HatchStyleSmallCheckerBoard = 49
; HatchStyleLargeCheckerBoard = 50
; HatchStyleOutlinedDiamond = 51
; HatchStyleSolidDiamond = 52
; HatchStyleTotal = 53
Gdip_BrushCreateHatch(ARGBfront, ARGBback, HatchStyle=0)
{
  DllCall("gdiplus\GdipCreateHatchBrush", "int", HatchStyle, "UInt", ARGBfront, "UInt", ARGBback, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
  return pBrush
}

;#####################################################################################

Gdip_CreateTextureBrush(pBitmap, WrapMode=1, x=0, y=0, w="", h="")
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  , PtrA := A_PtrSize ? "UPtr*" : "UInt*"
  
  if !(w && h)
    DllCall("gdiplus\GdipCreateTexture", Ptr, pBitmap, "int", WrapMode, PtrA, pBrush)
  else
    DllCall("gdiplus\GdipCreateTexture2", Ptr, pBitmap, "int", WrapMode, "float", x, "float", y, "float", w, "float", h, PtrA, pBrush)
  return pBrush
}

;#####################################################################################

; WrapModeTile = 0
; WrapModeTileFlipX = 1
; WrapModeTileFlipY = 2
; WrapModeTileFlipXY = 3
; WrapModeClamp = 4
Gdip_CreateLineBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode=1)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  CreatePointF(PointF1, x1, y1), CreatePointF(PointF2, x2, y2)
  DllCall("gdiplus\GdipCreateLineBrush", Ptr, &PointF1, Ptr, &PointF2, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
  return LGpBrush
}

;#####################################################################################

; LinearGradientModeHorizontal = 0
; LinearGradientModeVertical = 1
; LinearGradientModeForwardDiagonal = 2
; LinearGradientModeBackwardDiagonal = 3
Gdip_CreateLineBrushFromRect(x, y, w, h, ARGB1, ARGB2, LinearGradientMode=1, WrapMode=1)
{
  CreateRectF(RectF, x, y, w, h)
  DllCall("gdiplus\GdipCreateLineBrushFromRect", A_PtrSize ? "UPtr" : "UInt", &RectF, "int", ARGB1, "int", ARGB2, "int", LinearGradientMode, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
  return LGpBrush
}

;#####################################################################################

Gdip_CloneBrush(pBrush)
{
  DllCall("gdiplus\GdipCloneBrush", A_PtrSize ? "UPtr" : "UInt", pBrush, A_PtrSize ? "UPtr*" : "UInt*", pBrushClone)
  return pBrushClone
}

;#####################################################################################
; Delete resources
;#####################################################################################

Gdip_DeletePen(pPen)
{
   return DllCall("gdiplus\GdipDeletePen", A_PtrSize ? "UPtr" : "UInt", pPen)
}

;#####################################################################################

Gdip_DeleteBrush(pBrush)
{
   return DllCall("gdiplus\GdipDeleteBrush", A_PtrSize ? "UPtr" : "UInt", pBrush)
}

;#####################################################################################

Gdip_DisposeImage(pBitmap)
{
   return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap)
}

;#####################################################################################

Gdip_DeleteGraphics(pGraphics)
{
   return DllCall("gdiplus\GdipDeleteGraphics", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

;#####################################################################################

Gdip_DisposeImageAttributes(ImageAttr)
{
  return DllCall("gdiplus\GdipDisposeImageAttributes", A_PtrSize ? "UPtr" : "UInt", ImageAttr)
}

;#####################################################################################

Gdip_DeleteFont(hFont)
{
   return DllCall("gdiplus\GdipDeleteFont", A_PtrSize ? "UPtr" : "UInt", hFont)
}

;#####################################################################################

Gdip_DeleteStringFormat(hFormat)
{
   return DllCall("gdiplus\GdipDeleteStringFormat", A_PtrSize ? "UPtr" : "UInt", hFormat)
}

;#####################################################################################

Gdip_DeleteFontFamily(hFamily)
{
   return DllCall("gdiplus\GdipDeleteFontFamily", A_PtrSize ? "UPtr" : "UInt", hFamily)
}

;#####################################################################################

Gdip_DeleteMatrix(Matrix)
{
   return DllCall("gdiplus\GdipDeleteMatrix", A_PtrSize ? "UPtr" : "UInt", Matrix)
}

;#####################################################################################
; Text functions
;#####################################################################################

Gdip_TextToGraphics(pGraphics, Text, Options, Font="Arial", Width="", Height="", Measure=0)
{
  IWidth := Width, IHeight:= Height
  
  RegExMatch(Options, "i)X([\-\d\.]+)(p*)", xpos)
  RegExMatch(Options, "i)Y([\-\d\.]+)(p*)", ypos)
  RegExMatch(Options, "i)W([\-\d\.]+)(p*)", Width)
  RegExMatch(Options, "i)H([\-\d\.]+)(p*)", Height)
  RegExMatch(Options, "i)C(?!(entre|enter))([a-f\d]+)", Colour)
  RegExMatch(Options, "i)Top|Up|Bottom|Down|vCentre|vCenter", vPos)
  RegExMatch(Options, "i)NoWrap", NoWrap)
  RegExMatch(Options, "i)R(\d)", Rendering)
  RegExMatch(Options, "i)S(\d+)(p*)", Size)

  if !Gdip_DeleteBrush(Gdip_CloneBrush(Colour2))
    PassBrush := 1, pBrush := Colour2
  
  if !(IWidth && IHeight) && (xpos2 || ypos2 || Width2 || Height2 || Size2)
    return -1

  Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
  Loop, Parse, Styles, |
  {
    if RegExMatch(Options, "\b" A_loopField)
    Style |= (A_LoopField != "StrikeOut") ? (A_Index-1) : 8
  }
  
  Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
  Loop, Parse, Alignments, |
  {
    if RegExMatch(Options, "\b" A_loopField)
      Align |= A_Index//2.1      ; 0|0|1|1|2|2
  }

  xpos := (xpos1 != "") ? xpos2 ? IWidth*(xpos1/100) : xpos1 : 0
  ypos := (ypos1 != "") ? ypos2 ? IHeight*(ypos1/100) : ypos1 : 0
  Width := Width1 ? Width2 ? IWidth*(Width1/100) : Width1 : IWidth
  Height := Height1 ? Height2 ? IHeight*(Height1/100) : Height1 : IHeight
  if !PassBrush
    Colour := "0x" (Colour2 ? Colour2 : "ff000000")
  Rendering := ((Rendering1 >= 0) && (Rendering1 <= 5)) ? Rendering1 : 4
  Size := (Size1 > 0) ? Size2 ? IHeight*(Size1/100) : Size1 : 12

  hFamily := Gdip_FontFamilyCreate(Font)
  hFont := Gdip_FontCreate(hFamily, Size, Style)
  FormatStyle := NoWrap ? 0x4000 | 0x1000 : 0x4000
  hFormat := Gdip_StringFormatCreate(FormatStyle)
  pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour)
  if !(hFamily && hFont && hFormat && pBrush && pGraphics)
    return !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0
   
  CreateRectF(RC, xpos, ypos, Width, Height)
  Gdip_SetStringFormatAlign(hFormat, Align)
  Gdip_SetTextRenderingHint(pGraphics, Rendering)
  ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)

  if vPos
  {
    StringSplit, ReturnRC, ReturnRC, |
    
    if (vPos = "vCentre") || (vPos = "vCenter")
      ypos += (Height-ReturnRC4)//2
    else if (vPos = "Top") || (vPos = "Up")
      ypos := 0
    else if (vPos = "Bottom") || (vPos = "Down")
      ypos := Height-ReturnRC4
    
    CreateRectF(RC, xpos, ypos, Width, ReturnRC4)
    ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
  }

  if !Measure
    E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, RC)

  if !PassBrush
    Gdip_DeleteBrush(pBrush)
  Gdip_DeleteStringFormat(hFormat)   
  Gdip_DeleteFont(hFont)
  Gdip_DeleteFontFamily(hFamily)
  return E ? E : ReturnRC
}

;#####################################################################################

Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, ByRef RectF)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if (!A_IsUnicode)
  {
    nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, 0, "int", 0)
    VarSetCapacity(wString, nSize*2)
    DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
  }
  
  return DllCall("gdiplus\GdipDrawString"
          , Ptr, pGraphics
          , Ptr, A_IsUnicode ? &sString : &wString
          , "int", -1
          , Ptr, hFont
          , Ptr, &RectF
          , Ptr, hFormat
          , Ptr, pBrush)
}

;#####################################################################################

Gdip_MeasureString(pGraphics, sString, hFont, hFormat, ByRef RectF)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  VarSetCapacity(RC, 16)
  if !A_IsUnicode
  {
    nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, "uint", 0, "int", 0)
    VarSetCapacity(wString, nSize*2)   
    DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
  }
  
  DllCall("gdiplus\GdipMeasureString"
          , Ptr, pGraphics
          , Ptr, A_IsUnicode ? &sString : &wString
          , "int", -1
          , Ptr, hFont
          , Ptr, &RectF
          , Ptr, hFormat
          , Ptr, &RC
          , "uint*", Chars
          , "uint*", Lines)
  
  return &RC ? NumGet(RC, 0, "float") "|" NumGet(RC, 4, "float") "|" NumGet(RC, 8, "float") "|" NumGet(RC, 12, "float") "|" Chars "|" Lines : 0
}

; Near = 0
; Center = 1
; Far = 2
Gdip_SetStringFormatAlign(hFormat, Align)
{
   return DllCall("gdiplus\GdipSetStringFormatAlign", A_PtrSize ? "UPtr" : "UInt", hFormat, "int", Align)
}

; StringFormatFlagsDirectionRightToLeft    = 0x00000001
; StringFormatFlagsDirectionVertical       = 0x00000002
; StringFormatFlagsNoFitBlackBox           = 0x00000004
; StringFormatFlagsDisplayFormatControl    = 0x00000020
; StringFormatFlagsNoFontFallback          = 0x00000400
; StringFormatFlagsMeasureTrailingSpaces   = 0x00000800
; StringFormatFlagsNoWrap                  = 0x00001000
; StringFormatFlagsLineLimit               = 0x00002000
; StringFormatFlagsNoClip                  = 0x00004000 
Gdip_StringFormatCreate(Format=0, Lang=0)
{
   DllCall("gdiplus\GdipCreateStringFormat", "int", Format, "int", Lang, A_PtrSize ? "UPtr*" : "UInt*", hFormat)
   return hFormat
}

; Regular = 0
; Bold = 1
; Italic = 2
; BoldItalic = 3
; Underline = 4
; Strikeout = 8
Gdip_FontCreate(hFamily, Size, Style=0)
{
   DllCall("gdiplus\GdipCreateFont", A_PtrSize ? "UPtr" : "UInt", hFamily, "float", Size, "int", Style, "int", 0, A_PtrSize ? "UPtr*" : "UInt*", hFont)
   return hFont
}

Gdip_FontFamilyCreate(Font)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if (!A_IsUnicode)
  {
    nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, "uint", 0, "int", 0)
    VarSetCapacity(wFont, nSize*2)
    DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, Ptr, &wFont, "int", nSize)
  }
  
  DllCall("gdiplus\GdipCreateFontFamilyFromName"
          , Ptr, A_IsUnicode ? &Font : &wFont
          , "uint", 0
          , A_PtrSize ? "UPtr*" : "UInt*", hFamily)
  
  return hFamily
}

;#####################################################################################
; Matrix functions
;#####################################################################################

Gdip_CreateAffineMatrix(m11, m12, m21, m22, x, y)
{
   DllCall("gdiplus\GdipCreateMatrix2", "float", m11, "float", m12, "float", m21, "float", m22, "float", x, "float", y, A_PtrSize ? "UPtr*" : "UInt*", Matrix)
   return Matrix
}

Gdip_CreateMatrix()
{
   DllCall("gdiplus\GdipCreateMatrix", A_PtrSize ? "UPtr*" : "UInt*", Matrix)
   return Matrix
}

;#####################################################################################
; GraphicsPath functions
;#####################################################################################

; Alternate = 0
; Winding = 1
Gdip_CreatePath(BrushMode=0)
{
  DllCall("gdiplus\GdipCreatePath", "int", BrushMode, A_PtrSize ? "UPtr*" : "UInt*", Path)
  return Path
}

Gdip_AddPathEllipse(Path, x, y, w, h)
{
  return DllCall("gdiplus\GdipAddPathEllipse", A_PtrSize ? "UPtr" : "UInt", Path, "float", x, "float", y, "float", w, "float", h)
}

Gdip_AddPathPolygon(Path, Points)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  StringSplit, Points, Points, |
  VarSetCapacity(PointF, 8*Points0)   
  Loop, %Points0%
  {
    StringSplit, Coord, Points%A_Index%, `,
    NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
  }   

  return DllCall("gdiplus\GdipAddPathPolygon", Ptr, Path, Ptr, &PointF, "int", Points0)
}

Gdip_DeletePath(Path)
{
  return DllCall("gdiplus\GdipDeletePath", A_PtrSize ? "UPtr" : "UInt", Path)
}

;#####################################################################################
; Quality functions
;#####################################################################################

; SystemDefault = 0
; SingleBitPerPixelGridFit = 1
; SingleBitPerPixel = 2
; AntiAliasGridFit = 3
; AntiAlias = 4
Gdip_SetTextRenderingHint(pGraphics, RenderingHint)
{
  return DllCall("gdiplus\GdipSetTextRenderingHint", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", RenderingHint)
}

; Default = 0
; LowQuality = 1
; HighQuality = 2
; Bilinear = 3
; Bicubic = 4
; NearestNeighbor = 5
; HighQualityBilinear = 6
; HighQualityBicubic = 7
Gdip_SetInterpolationMode(pGraphics, InterpolationMode)
{
   return DllCall("gdiplus\GdipSetInterpolationMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", InterpolationMode)
}

; Default = 0
; HighSpeed = 1
; HighQuality = 2
; None = 3
; AntiAlias = 4
Gdip_SetSmoothingMode(pGraphics, SmoothingMode)
{
   return DllCall("gdiplus\GdipSetSmoothingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", SmoothingMode)
}

; CompositingModeSourceOver = 0 (blended)
; CompositingModeSourceCopy = 1 (overwrite)
Gdip_SetCompositingMode(pGraphics, CompositingMode=0)
{
   return DllCall("gdiplus\GdipSetCompositingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", CompositingMode)
}

;#####################################################################################
; Extra functions
;#####################################################################################

Gdip_Startup()
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
    DllCall("LoadLibrary", "str", "gdiplus")
  VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
  DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0)
  return pToken
}

Gdip_Shutdown(pToken)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  DllCall("gdiplus\GdiplusShutdown", Ptr, pToken)
  if hModule := DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
    DllCall("FreeLibrary", Ptr, hModule)
  return 0
}

; Prepend = 0; The new operation is applied before the old operation.
; Append = 1; The new operation is applied after the old operation.
Gdip_RotateWorldTransform(pGraphics, Angle, MatrixOrder=0)
{
  return DllCall("gdiplus\GdipRotateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", Angle, "int", MatrixOrder)
}

Gdip_ScaleWorldTransform(pGraphics, x, y, MatrixOrder=0)
{
  return DllCall("gdiplus\GdipScaleWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder)
}

Gdip_TranslateWorldTransform(pGraphics, x, y, MatrixOrder=0)
{
  return DllCall("gdiplus\GdipTranslateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder)
}

Gdip_ResetWorldTransform(pGraphics)
{
  return DllCall("gdiplus\GdipResetWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

Gdip_GetRotatedTranslation(Width, Height, Angle, ByRef xTranslation, ByRef yTranslation)
{
  pi := 3.14159, TAngle := Angle*(pi/180)	

  Bound := (Angle >= 0) ? Mod(Angle, 360) : 360-Mod(-Angle, -360)
  if ((Bound >= 0) && (Bound <= 90))
    xTranslation := Height*Sin(TAngle), yTranslation := 0
  else if ((Bound > 90) && (Bound <= 180))
    xTranslation := (Height*Sin(TAngle))-(Width*Cos(TAngle)), yTranslation := -Height*Cos(TAngle)
  else if ((Bound > 180) && (Bound <= 270))
    xTranslation := -(Width*Cos(TAngle)), yTranslation := -(Height*Cos(TAngle))-(Width*Sin(TAngle))
  else if ((Bound > 270) && (Bound <= 360))
    xTranslation := 0, yTranslation := -Width*Sin(TAngle)
}

Gdip_GetRotatedDimensions(Width, Height, Angle, ByRef RWidth, ByRef RHeight)
{
  pi := 3.14159, TAngle := Angle*(pi/180)
  if !(Width && Height)
    return -1
  RWidth := Ceil(Abs(Width*Cos(TAngle))+Abs(Height*Sin(TAngle)))
  RHeight := Ceil(Abs(Width*Sin(TAngle))+Abs(Height*Cos(Tangle)))
}

; RotateNoneFlipNone   = 0
; Rotate90FlipNone     = 1
; Rotate180FlipNone    = 2
; Rotate270FlipNone    = 3
; RotateNoneFlipX      = 4
; Rotate90FlipX        = 5
; Rotate180FlipX       = 6
; Rotate270FlipX       = 7
; RotateNoneFlipY      = Rotate180FlipX
; Rotate90FlipY        = Rotate270FlipX
; Rotate180FlipY       = RotateNoneFlipX
; Rotate270FlipY       = Rotate90FlipX
; RotateNoneFlipXY     = Rotate180FlipNone
; Rotate90FlipXY       = Rotate270FlipNone
; Rotate180FlipXY      = RotateNoneFlipNone
; Rotate270FlipXY      = Rotate90FlipNone 

Gdip_ImageRotateFlip(pBitmap, RotateFlipType=1)
{
  return DllCall("gdiplus\GdipImageRotateFlip", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", RotateFlipType)
}

; Replace = 0
; Intersect = 1
; Union = 2
; Xor = 3
; Exclude = 4
; Complement = 5
Gdip_SetClipRect(pGraphics, x, y, w, h, CombineMode=0)
{
   return DllCall("gdiplus\GdipSetClipRect",  A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "float", w, "float", h, "int", CombineMode)
}

Gdip_SetClipPath(pGraphics, Path, CombineMode=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  return DllCall("gdiplus\GdipSetClipPath", Ptr, pGraphics, Ptr, Path, "int", CombineMode)
}

Gdip_ResetClip(pGraphics)
{
   return DllCall("gdiplus\GdipResetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

Gdip_GetClipRegion(pGraphics)
{
  Region := Gdip_CreateRegion()
  DllCall("gdiplus\GdipGetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics, A_PtrSize ? "UPtr*" : "UInt*", Region)
  return Region
}

Gdip_SetClipRegion(pGraphics, Region, CombineMode=0)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("gdiplus\GdipSetClipRegion", Ptr, pGraphics, Ptr, Region, "int", CombineMode)
}

Gdip_CreateRegion()
{
  DllCall("gdiplus\GdipCreateRegion", A_PtrSize ? "UPtr*" : "UInt*", Region)
  return Region
}

Gdip_DeleteRegion(Region)
{
  return DllCall("gdiplus\GdipDeleteRegion", A_PtrSize ? "UPtr" : "UInt", Region)
}

;#####################################################################################
; BitmapLockBits
;#####################################################################################

Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode = 3, PixelFormat = 0x26200a)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  CreateRect(Rect, x, y, w, h)
  VarSetCapacity(BitmapData, 16+2*(A_PtrSize ? A_PtrSize : 4), 0)
  E := DllCall("Gdiplus\GdipBitmapLockBits", Ptr, pBitmap, Ptr, &Rect, "uint", LockMode, "int", PixelFormat, Ptr, &BitmapData)
  Stride := NumGet(BitmapData, 8, "Int")
  Scan0 := NumGet(BitmapData, 16, Ptr)
  return E
}

;#####################################################################################

Gdip_UnlockBits(pBitmap, ByRef BitmapData)
{
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  return DllCall("Gdiplus\GdipBitmapUnlockBits", Ptr, pBitmap, Ptr, &BitmapData)
}

;#####################################################################################

Gdip_SetLockBitPixel(ARGB, Scan0, x, y, Stride)
{
  Numput(ARGB, Scan0+0, (x*4)+(y*Stride), "UInt")
}

;#####################################################################################

Gdip_GetLockBitPixel(Scan0, x, y, Stride)
{
  return NumGet(Scan0+0, (x*4)+(y*Stride), "UInt")
}

;#####################################################################################

Gdip_PixelateBitmap(pBitmap, ByRef pBitmapOut, BlockSize)
{
  static PixelateBitmap
  
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  
  if (!PixelateBitmap)
  {
    if A_PtrSize != 8 ; x86 machine code
    MCode_PixelateBitmap =
    (LTrim Join
    558BEC83EC3C8B4514538B5D1C99F7FB56578BC88955EC894DD885C90F8E830200008B451099F7FB8365DC008365E000894DC88955F08945E833FF897DD4
    397DE80F8E160100008BCB0FAFCB894DCC33C08945F88945FC89451C8945143BD87E608B45088D50028BC82BCA8BF02BF2418945F48B45E02955F4894DC4
    8D0CB80FAFCB03CA895DD08BD1895DE40FB64416030145140FB60201451C8B45C40FB604100145FC8B45F40FB604020145F883C204FF4DE475D6034D18FF
    4DD075C98B4DCC8B451499F7F98945148B451C99F7F989451C8B45FC99F7F98945FC8B45F899F7F98945F885DB7E648B450C8D50028BC82BCA83C103894D
    C48BC82BCA41894DF48B4DD48945E48B45E02955E48D0C880FAFCB03CA895DD08BD18BF38A45148B7DC48804178A451C8B7DF488028A45FC8804178A45F8
    8B7DE488043A83C2044E75DA034D18FF4DD075CE8B4DCC8B7DD447897DD43B7DE80F8CF2FEFFFF837DF0000F842C01000033C08945F88945FC89451C8945
    148945E43BD87E65837DF0007E578B4DDC034DE48B75E80FAF4D180FAFF38B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945CC0F
    B6440E030145140FB60101451C0FB6440F010145FC8B45F40FB604010145F883C104FF4DCC75D8FF45E4395DE47C9B8B4DF00FAFCB85C9740B8B451499F7
    F9894514EB048365140033F63BCE740B8B451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB
    038975F88975E43BDE7E5A837DF0007E4C8B4DDC034DE48B75E80FAF4D180FAFF38B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955CC8A55
    1488540E038A551C88118A55FC88540F018A55F888140183C104FF4DCC75DFFF45E4395DE47CA68B45180145E0015DDCFF4DC80F8594FDFFFF8B451099F7
    FB8955F08945E885C00F8E450100008B45EC0FAFC38365DC008945D48B45E88945CC33C08945F88945FC89451C8945148945103945EC7E6085DB7E518B4D
    D88B45080FAFCB034D108D50020FAF4D18034DDC8BF08BF88945F403CA2BF22BFA2955F4895DC80FB6440E030145140FB60101451C0FB6440F010145FC8B
    45F40FB604080145F883C104FF4DC875D8FF45108B45103B45EC7CA08B4DD485C9740B8B451499F7F9894514EB048365140033F63BCE740B8B451C99F7F9
    89451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975103975EC7E5585DB7E468B4DD88B450C
    0FAFCB034D108D50020FAF4D18034DDC8BF08BF803CA2BF22BFA2BC2895DC88A551488540E038A551C88118A55FC88540F018A55F888140183C104FF4DC8
    75DFFF45108B45103B45EC7CAB8BC3C1E0020145DCFF4DCC0F85CEFEFFFF8B4DEC33C08945F88945FC89451C8945148945103BC87E6C3945F07E5C8B4DD8
    8B75E80FAFCB034D100FAFF30FAF4D188B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945C80FB6440E030145140FB60101451C0F
    B6440F010145FC8B45F40FB604010145F883C104FF4DC875D833C0FF45108B4DEC394D107C940FAF4DF03BC874068B451499F7F933F68945143BCE740B8B
    451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975083975EC7E63EB0233F639
    75F07E4F8B4DD88B75E80FAFCB034D080FAFF30FAF4D188B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955108A551488540E038A551C8811
    8A55FC88540F018A55F888140883C104FF4D1075DFFF45088B45083B45EC7C9F5F5E33C05BC9C21800
    )
    else ; x64 machine code
    MCode_PixelateBitmap =
    (LTrim Join
    4489442418488954241048894C24085355565741544155415641574883EC28418BC1448B8C24980000004C8BDA99488BD941F7F9448BD0448BFA8954240C
    448994248800000085C00F8E9D020000418BC04533E4458BF299448924244C8954241041F7F933C9898C24980000008BEA89542404448BE889442408EB05
    4C8B5C24784585ED0F8E1A010000458BF1418BFD48897C2418450FAFF14533D233F633ED4533E44533ED4585C97E5B4C63BC2490000000418D040A410FAF
    C148984C8D441802498BD9498BD04D8BD90FB642010FB64AFF4403E80FB60203E90FB64AFE4883C2044403E003F149FFCB75DE4D03C748FFCB75D0488B7C
    24188B8C24980000004C8B5C2478418BC59941F7FE448BE8418BC49941F7FE448BE08BC59941F7FE8BE88BC69941F7FE8BF04585C97E4048639C24900000
    004103CA4D8BC1410FAFC94863C94A8D541902488BCA498BC144886901448821408869FF408871FE4883C10448FFC875E84803D349FFC875DA8B8C249800
    0000488B5C24704C8B5C24784183C20448FFCF48897C24180F850AFFFFFF8B6C2404448B2424448B6C24084C8B74241085ED0F840A01000033FF33DB4533
    DB4533D24533C04585C97E53488B74247085ED7E42438D0C04418BC50FAF8C2490000000410FAFC18D04814863C8488D5431028BCD0FB642014403D00FB6
    024883C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC17CB28BCD410FAFC985C9740A418BC299F7F98BF0EB0233F685C9740B418BC3
    99F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585C97E4D4C8B74247885ED7E3841
    8D0C14418BC50FAF8C2490000000410FAFC18D04814863C84A8D4431028BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2413BD17CBD
    4C8B7424108B8C2498000000038C2490000000488B5C24704503E149FFCE44892424898C24980000004C897424100F859EFDFFFF448B7C240C448B842480
    000000418BC09941F7F98BE8448BEA89942498000000896C240C85C00F8E3B010000448BAC2488000000418BCF448BF5410FAFC9898C248000000033FF33
    ED33F64533DB4533D24533C04585FF7E524585C97E40418BC5410FAFC14103C00FAF84249000000003C74898488D541802498BD90FB642014403D00FB602
    4883C2044403D80FB642FB03F00FB642FA03E848FFCB75DE488B5C247041FFC0453BC77CAE85C9740B418BC299F7F9448BE0EB034533E485C9740A418BC3
    99F7F98BD8EB0233DB85C9740A8BC699F7F9448BD8EB034533DB85C9740A8BC599F7F9448BD0EB034533D24533C04585FF7E4E488B4C24784585C97E3541
    8BC5410FAFC14103C00FAF84249000000003C74898488D540802498BC144886201881A44885AFF448852FE4883C20448FFC875E941FFC0453BC77CBE8B8C
    2480000000488B5C2470418BC1C1E00203F849FFCE0F85ECFEFFFF448BAC24980000008B6C240C448BA4248800000033FF33DB4533DB4533D24533C04585
    FF7E5A488B7424704585ED7E48418BCC8BC5410FAFC94103C80FAF8C2490000000410FAFC18D04814863C8488D543102418BCD0FB642014403D00FB60248
    83C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC77CAB418BCF410FAFCD85C9740A418BC299F7F98BF0EB0233F685C9740B418BC399
    F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585FF7E4E4585ED7E42418BCC8BC541
    0FAFC903CA0FAF8C2490000000410FAFC18D04814863C8488B442478488D440102418BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2
    413BD77CB233C04883C428415F415E415D415C5F5E5D5BC3
    )
    
    VarSetCapacity(PixelateBitmap, StrLen(MCode_PixelateBitmap)//2)
    Loop % StrLen(MCode_PixelateBitmap)//2		;%
      NumPut("0x" SubStr(MCode_PixelateBitmap, (2*A_Index)-1, 2), PixelateBitmap, A_Index-1, "UChar")
    DllCall("VirtualProtect", Ptr, &PixelateBitmap, Ptr, VarSetCapacity(PixelateBitmap), "uint", 0x40, A_PtrSize ? "UPtr*" : "UInt*", 0)
  }

  Gdip_GetImageDimensions(pBitmap, Width, Height)
  
  if (Width != Gdip_GetImageWidth(pBitmapOut) || Height != Gdip_GetImageHeight(pBitmapOut))
    return -1
  if (BlockSize > Width || BlockSize > Height)
    return -2

  E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, Stride1, Scan01, BitmapData1)
  E2 := Gdip_LockBits(pBitmapOut, 0, 0, Width, Height, Stride2, Scan02, BitmapData2)
  if (E1 || E2)
    return -3

  E := DllCall(&PixelateBitmap, Ptr, Scan01, Ptr, Scan02, "int", Width, "int", Height, "int", Stride1, "int", BlockSize)
  
  Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapOut, BitmapData2)
  return 0
}

;#####################################################################################

Gdip_ToARGB(A, R, G, B)
{
  return (A << 24) | (R << 16) | (G << 8) | B
}

;#####################################################################################

Gdip_FromARGB(ARGB, ByRef A, ByRef R, ByRef G, ByRef B)
{
  A := (0xff000000 & ARGB) >> 24
  R := (0x00ff0000 & ARGB) >> 16
  G := (0x0000ff00 & ARGB) >> 8
  B := 0x000000ff & ARGB
}

;#####################################################################################

Gdip_AFromARGB(ARGB)
{
  return (0xff000000 & ARGB) >> 24
}

;#####################################################################################

Gdip_RFromARGB(ARGB)
{
  return (0x00ff0000 & ARGB) >> 16
}

;#####################################################################################

Gdip_GFromARGB(ARGB)
{
  return (0x0000ff00 & ARGB) >> 8
}

;#####################################################################################

Gdip_BFromARGB(ARGB)
{
  return 0x000000ff & ARGB
}

;#####################################################################################

StrGetB(Address, Length=-1, Encoding=0)
{
  ; Flexible parameter handling:
  if Length is not integer
  Encoding := Length,  Length := -1

  ; Check for obvious errors.
  if (Address+0 < 1024)
    return

  ; Ensure 'Encoding' contains a numeric identifier.
  if Encoding = UTF-16
    Encoding = 1200
  else if Encoding = UTF-8
    Encoding = 65001
  else if SubStr(Encoding,1,2)="CP"
    Encoding := SubStr(Encoding,3)

  if !Encoding ; "" or 0
  {
    ; No conversion necessary, but we might not want the whole string.
    if (Length == -1)
      Length := DllCall("lstrlen", "uint", Address)
    VarSetCapacity(String, Length)
    DllCall("lstrcpyn", "str", String, "uint", Address, "int", Length + 1)
  }
  else if Encoding = 1200 ; UTF-16
  {
    char_count := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "uint", 0, "uint", 0, "uint", 0, "uint", 0)
    VarSetCapacity(String, char_count)
    DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "str", String, "int", char_count, "uint", 0, "uint", 0)
  }
  else if Encoding is integer
  {
    ; Convert from target encoding to UTF-16 then to the active code page.
    char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", 0, "int", 0)
    VarSetCapacity(String, char_count * 2)
    char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", &String, "int", char_count * 2)
    String := StrGetB(&String, char_count, 1200)
  }
  
  return String
}


;############################################################################################################
; Barcode Generator Library v1.02 by Giordanno Sperotto. 
; Updated: 27 December 2014.
; General Notes for this library (Refer to functions GENERATE_QR_CODE(), GENERATE_CODE_39(), GENERATE_CODE_ITF() and GENERATE_CODE_128B()
; for specific operating instructions).
; 
; Barcode Generator Library v1.01 by Giordanno Sperotto is an AutoHotkey (AHK) library designed to help generate Barcodes for
; AHK applications.
;
; Barcodes are usually used as a mean to store logistical data in the physical bodys of products and packages, so as to better allow 
; managing of lot numbers, expiring dates and other specific aspects of these products. QR Codes, a type of Two-Dimensional (2D)
; Barcodes, have also been used a marketing tool to quickly guide customers to website stores and ease up the sales of their products. 
; The use of barcodes, however, is not restricited to such cases and careful thought over how to use a quick and cheap way of 
; printing and reading data from physical objects should definitely sparkle some new interesting ideas for a programmer.
;
; Best wishes.
;
; List of Main functions:
; GENERATE_QR_CODE() -> Call this function to retrieve a table object (2D) that represents the dark and light pixels of a QR Code
; Matrix by their respective addresses.
; GENERATE_CODE_39() -> Call this functions to retrieve an object that represents the dark and light pixels of a CODE39
; row by their respective addresses (Note: Even tough the code is 1D, the image to be printed is actually 2D, so stack as many copies 
; of the single line over each other for the height of the image you wish to create).
; GENERATE_CODE_ITF() -> Call this function to retrieve an object that represents the monochrome dark and light pixels of an Interleaved 
; Code 2 of 5 row. (Note: Even tough the code is 1D, the image to be printed is actually 2D, so stack as many copies 
; of the single line over each other for the height of the image you wish to create).
; GENERATE_CODE_128B() -> Call this functions to retrieve an object that represents the dark and light pixels of a CODE128B
; row by their respective addresses (Note: Even tough the code is 1D, the image to be printed is actually 2D, so stack as many copies 
; of the single line over each other for the height of the image you wish to create).
;##############################################################################################################


;##############################################################################################################
; QR Code Functions v1.02
; General Notes from the author (refer to the function "GENERATE_QR_CODE()" for an explanation on how to create a QR Code Matrix):
;
; QR Codes (a type of 2D barcode) are a great tool to print to and recover data from physical objects. An advancement compared
; to regular 1D barcodes, they can contain more data (Up to 7,089 numeric characters for v40), encode bigger sets of characters, are  
; more reliable than the previous and can be read quite quick with current market level hardware/software. Most 2D readers currently available 
; will ship bundled with reader software and will simply autotype the decoded message to the selected field of your application. Also, there is 
; probably an application to read QR Codes available for use with your very own cell phone as of today.
;
; For logistical purposes, you can simply print a QR Code on a label and stick it to an object you wish to track, and you (or another person) will
; be able to recover the data stored inside the image at a later time. This ensures a whole new world of possibilities for industrial and commercial
; processes.
;
; If you are seeking out how QR Codes work, the full form of implementing them is described in the document for ISO 18004. The code for this 
; implementation has also been commented whenever i felt like something needed an explanation, but this is NOT a tutorial.Thonky has an 
; online tutorial for generating QR Codes, which has greatly helped this implementation. You can check it out on http://www.thonky.com/qr-code-tutorial/
;
; Thanks to tic (Tariq Porter) for his GDI+ Library (http://www.autohotkey.com/forum/viewtopic.php?t=32238), which has been used through the
; development of this library to display the images of the QR Matrixes.
;
; Thanks to Infogulch for the functions Bin() and Dec() (http://www.autohotkey.com/board/topic/49990-binary-and-decimal-conversion/).
;
; Thanks to AlphaBravo for the functions GENERATE_CODE_ITF(), Add_Check_Sum() and GENERATE_CODE_128B()
; (http://ahkscript.org/boards/viewtopic.php?f=6&t=5538&p=32299#p32299)
; (https://autohotkey.com/boards/viewtopic.php?p=93223#p93223)
;
; "QR Code" is a registered trademark of DENSO-WAVE (http://www.qrcode.com/en/faq.html) and the specifications of the standard are
; publicly available. You are free to use QR Codes on commercial applications and no contracts/fees are required, but you may have to
; credit Denso-Wave if you explicitly refer to the name "QR Code".
;
; Best wishes.

;CHANGELOG:
;11 December 2014: First version is up. Supports CODE39 and QR Code barcode generation. Examples added to the post.
;23 December 2014: Added Interleaved 2 of 5 Functions by AlphaBravo. Changed the lib and function names to allow MyLib_MyFunc syntax compatibility.
;25 December 2014: Fixed a typo in the code generating incorrect version 11-M matrixes.
;27 December 2014: Ran a test on each of the 40 QR Code versions, using each of the 4 ECLs for every version. All 160 matrixes generated were found to be readable. Changed Version to 1.01 to avoid confusion with non-working version 11 scripts.
; 23 June 2016: Added Code128B generating functions designed by AlphaBravo.
;##############################################################################################################



;##################################################################################################################################################################
; GENERATE_QR_CODE() v1.01 by Giordanno Sperotto. 
; How to generate QR Codes using this library:
; This function takes four parameters. 
;
; The First parameter is the message to encode. Please make sure that the message contains only ASCII characters (for this 
; version of the library).
;
; The Second parameter is the Code Mode to be applied, which can be 0 (Automatic choosing - Default and recommended), 1 (Numeric, can only encode sequences of
; numeric characters, with no commas, dots or any other symbols), 2 (AlphaNumeric, can only encode sequences of numeric characters, UPPERCASE letters A-Z and symbols 
; $, %, *, +, -, ., /, :, and \.), and finally 3 (Byte, which can encode any type of ASCII character, but usually requires a higher version QR Code for the same length of the string compared to the other two.
; The function will abort execution and display a message if you try to Force the encoding of a message in a Code Mode that cannot contain all the characters entered.
;
; The Third parameter is the CHOOSEN_ERROR_CORRECTION_LEVEL. QR Code matrixes are able to output the correct message even if they are damaged (to some extent and excluding some critical 
; areas). As the choosen Error Correction Level increases, this power improves, but the output matrix becomes bigger, requiring a bigger version to encode the same ammount of data. Available Error 
; Correction Levels are 1 (Level L, can recover the full data if only up to 7% of the codewords are damaged but will create the smallest matrix outputs, 2 (Level M, the default level, can recover the full data 
; if up to 15% of the codewords are damaged), 3 (Level Q, can recover the full data if up to 25% of the codewords are damaged) and 4 (Level H, can recover the full data if up to 30% of the codewords are 
; damaged - This level has the lowest available space for message codewords in each version).
;
; The Fourth parameter is the CHOOSEN_VERSION. It defaults to 0 (Automatic, Best) which will use the smallest version that can hold the entire message. Although it is possible to use a bigger version
; to encode a small message, this is not a good idea, as it will increase the resources required to generate and decode the matrix, aswell as increase the possibility of a false reading (The latter should 
; only be of real concern if the reader hardware/software is scanning the message for other types of 2D barcodes aswell, as QR Codes have a very low false positive possibility (lower than 1D barcodes AFAIK), 
; but don't really check for the existence of a submatrix that conforms to the other available standards (in example: they may contain a valid small Data Matrix.)). Either way, the lowest version that can encode
; the entire message is the recomended version here, and the default value for this parameter (0) will find that version for you. The function will abort execution and display a message if you try to Force the 
; encoding of a message in a QR Code Version that cannot contain all the characters entered.
;
; HOW TO INTERPRET THE RESULTS:
; If the call is succesfull, the function returns a 2D object, which should be interpreted as MATRIX_TO_PRINT[ROW ADDRESS, COLUMN ADDRESS], where each possible values for Row and Column addresses will reffer to a single module
; (black or white square) inside the matrix. You can thus easily access the individual modules of the output matrix one by one with a loop inside a loop, both set to iterate up to the MaxIndex() of the object. 
; After this, you can use any type of image library you like to create the images, save to disk, print or display in a GUI. I recommend you to use GDI+ Library by Tic. 
; (http://www.autohotkey.com/board/topic/29449-gdi-standard-library-145-by-tic). Do not forget to include reasonable sized "quiet zones" (light colored areas) surrouding the output QR Code image for better readability.
; Example:
;
; Loop % Object.MaxIndex()
; {
; 	ROW := A_Index
;	Loop % Object[1].MaxIndex()
;	{
;		COLUMN := A_Index
;		CURRENT_VALUE := Object[ROW, COLUMN]
;		If (CURRENT_VALUE = 1)
;		{
;			msgbox % "The pixel in row " . ROW . " and column " . COLUMN . " is dark.
;		}
;		Else
;			msgbox % "The pixel in row " . ROW . " and column " . COLUMN . " is light.
;	}
;}
;
; Other possible return values (Error values)
; 1 - Input message is blank.
; 2 - Forced Code Mode cannot encode all the characters in the input message.
; 3 - Choosen Code Mode does not correspond to one of the currently indexed code modes (Automatic, numeric, alphanumeric or byte).
; 4 - The choosen forced version cannot encode the entire input message using the choosen ECL Code_Mode. Try forcing a higher version or choosing automated version selection (parameter value 0).
; 5 - The input message is exceeding the QR Code standards maximum length for the choosen ECL and Code Mode.
; 6 - Choosen Error Correction Level does not correspond to one of the standard ECLs (L, M, Q and H).
; 7 - Forced version does not correspond to one of the QR Code standards versions.
;###################################################################################################################################################################
BARCODER_GENERATE_QR_CODE(MESSAGE_TO_ENCODE, CHOOSEN_CODE_MODE := 0, CHOOSEN_ERROR_CORRECTION_LEVEL := 2, CHOOSEN_VERSION := 0)
{
  Global
  MATRIX_TO_PRINT := ""
  CREATE_LOG_AND_ANTILOG_TABLES()
  LIST_NUMBER_OF_GROUPS_AND_BLOCKS() ; Necessary for processing the later step of interleaving of CodeWords (CW) and ErrorCorrectionWords (ECW).
  GENERATE_ALPHANUMERIC_TABLE()
  GENERATE_VERSION_CAPACITY_CUBE()
  
  If (MESSAGE_TO_ENCODE = "")
  {
    Return 1 ; Input message is blank.
  }
  
  ; The best Code Mode is the one that succesfully encodes the entire message, but outputs the smaller QR Matrix. Numeric should be used whenever possible, AlphaNumeric is the second best choice and Byte mode should be selected only if the message cannot be encoded using the other two. Numeric can only hold numbers (sequences of the characters 0-9), AlphaNumeric can only hold numbers, uppercase letters A-Z and the symbols $, %, *, +, -, ., /, :, and \. Byte Mode can hold the entire ASCII table and Kanji mode can hold Japanese/Chinese/Korean characters.
  if (CHOOSEN_CODE_MODE = 0)
  {
    if MESSAGE_TO_ENCODE is number
    {
      CHOOSEN_CODE_MODE := 1
    }
    Else If (RegExMatch(MESSAGE_TO_ENCODE, "[^A-Z0-9`$`%`*`+`-`.`/`:`\ ]") = 0)
    {
      CHOOSEN_CODE_MODE := 2
    }
    Else
    {
      CHOOSEN_CODE_MODE := 3
    }
  }

  If !((CHOOSEN_VERSION >= 0) AND (CHOOSEN_VERSION <= 40))
  {
    ; Msgbox, 0x10, Error, Forced Version does not correspond to one of the QR Code standards versions. Please choose a version from 1 to 40. You can also specify 0 to let the function choose the best one for you.
    Return 7 ; Forced version does not correspond to one of the QR Code standards versions.
  }

  ; The best version to use is the smallest that sucessfully encodes the entire message.
  If (CHOOSEN_VERSION = 0)
  {
    Loop 40
    {
      If (StrLen(MESSAGE_TO_ENCODE) <= CAPACITY_CUBE[A_Index, CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_CODE_MODE])
      {
        CHOOSEN_VERSION := A_Index
        Break
      }
    }
    CODE_MODE_TO_USE := ""
  }
  
  If (CHOOSEN_VERSION = 0) ; If the conditionals above failed to find a suitable version...
  {
    Return 5 ; The input message is exceeding the QR Code standards maximum length for the choosen ECL and Code Mode.
  }
  
  
    if ((CHOOSEN_CODE_MODE != 1) AND (CHOOSEN_CODE_MODE != 2) AND (CHOOSEN_CODE_MODE != 3) AND (CHOOSEN_CODE_MODE != 0))
  {
    ;msgbox, 0x10, Error,  Please select an appropriate code mode for the message.
    Return 3 ; Choosen Code Mode does not correspond to one of the currently indexed code modes (Automatic, numeric, alphanumeric or byte).
  }
  
    if ((CHOOSEN_ERROR_CORRECTION_LEVEL != 1) AND (CHOOSEN_ERROR_CORRECTION_LEVEL != 2) AND (CHOOSEN_ERROR_CORRECTION_LEVEL != 3) AND (CHOOSEN_ERROR_CORRECTION_LEVEL != 4))
  {
    ;msgbox, 0x10, Error,  Please select an appropriate code mode for the message.
    Return 6 ; Choosen Error Correction Level does not correspond to one of the standard ECLs (L, M, Q and H).
  }
  
  If !(StrLen(MESSAGE_TO_ENCODE) <= CAPACITY_CUBE[CHOOSEN_VERSION, CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_CODE_MODE])
  {
    Return 4 ; The choosen forced version cannot encode the entire input message using the choosen ECL Code_Mode.
  }

  if (CHOOSEN_CODE_MODE = 1)
  {
    if MESSAGE_TO_ENCODE is not number
    {
      ;msgbox, 0x10, Error, Numeric code mode can only encode the characters 0-9. Please correct the Message to Encode or change the selected Code Mode if you wish to encode other characters.
      Return 2 ; Forced Code Mode cannot encode all the characters in the input message.
    }
    Numeric_Mode := "0001" ; Numeric type
    If (CHOOSEN_VERSION <= 9)
    {
      Char_Count := SubStr("0000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -9, 10) ; 10 bits with the size of the string are req for V1-9 Numeric.
    }
    If (CHOOSEN_VERSION >= 10) AND (CHOOSEN_VERSION <= 26)
    {
      Char_Count := SubStr("000000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -11, 12) ; 12 bits with the size of the string are req for V10-26 Numeric.
    }
    If (CHOOSEN_VERSION >= 27) AND (CHOOSEN_VERSION <= 40)
    {
      Char_Count := SubStr("00000000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -13, 14) ; 14 bits with the size of the string are req for V27-40 Numeric.
    }
    MESSAGE_CODE :=  Numeric_Mode . Char_Count ; We will add the codewords in the function call bellow, so "Message_Code" is not really complete here.
    MESSAGE_STRING := CONVERT_TO_NUMERIC_ENCODING(MESSAGE_TO_ENCODE)
  }

  if (CHOOSEN_CODE_MODE = 2)
  {
    If !(RegExMatch(MESSAGE_TO_ENCODE, "[^A-Z0-9`$`%`*`+`-`.`/`:`\ ]") = 0)
    {
      ;msgbox, 0x10, Error, AlphaNumeric Code Mode can only encode numeric characters, letters from A to Z (uppercase only), whitespaces and the symbols `$, `%, `*, `+, `-, `., `/ and `:. Please correct the Message to Encode or change the selected Code Mode if you wish to encode any other characters.
      Return 2 ; Forced Code Mode cannot encode all the characters in the input message.
    }
    ALPHANUMERIC_MODE := "0010"
    If (CHOOSEN_VERSION <= 9)
    {
      Char_Count := SubStr("000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -8, 9) ; 9 bits with the size of the string are req for V1-9 AlphaNumeric.
    }
    If (CHOOSEN_VERSION >= 10) AND (CHOOSEN_VERSION <= 26)
    {
      Char_Count := SubStr("00000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -10, 11) ; 11 bits with the size of the string are req for V10-26 AlphaNumeric.
    }
    If (CHOOSEN_VERSION >= 27) AND (CHOOSEN_VERSION <= 40)
    {
      Char_Count := SubStr("0000000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -12, 13) ; 13 bits with the size of the string are req for V27-40 AlphaNumeric.
    }
    MESSAGE_CODE :=  ALPHANUMERIC_MODE . Char_Count ; We will add the codewords in the function call bellow, so "Message_Code" is not really complete here.
    MESSAGE_STRING := CONVERT_TO_ALPHANUMERIC_ENCODING(MESSAGE_TO_ENCODE)
  }

  if (CHOOSEN_CODE_MODE = 3)
  {
    BYTE_MODE := "0100"
    If (CHOOSEN_VERSION <= 9)
    {
      Char_Count := SubStr("00000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -7, 8) ; 8 bits with the size of the string are req for V1-9 Byte Mode.
    }
    If (CHOOSEN_VERSION >= 10) AND (CHOOSEN_VERSION <= 40)
    {
      Char_Count := SubStr("0000000000000000" . Bin(StrLen(MESSAGE_TO_ENCODE)), -15, 16) ; 16 bits with the size of the string are req for V10-40 Byte Mode.
    }
    MESSAGE_CODE :=  BYTE_MODE . Char_Count ; We will add the codewords in the function call bellow, so "Message_Code" is not really complete here.
    MESSAGE_STRING := CONVERT_TO_BYTE_ENCODING(MESSAGE_TO_ENCODE)
  }

  ;To add the terminator, we have to know how many data codewords the version and ECL require. the V2H, in example, requires 16 data codewords, which equals 128 bits. If the string length up to now is less than that, we have to add the terminator, which is composed of up to 4 zeroed bits. The limit would than be 128, which means that if the string thus far is 127 bits, our terminator would be "0", if it were 126, "00", and so on up to "0000" which is the maximum for the terminator, in case the string were equal to or less than 124 bits. Once again: each version and ECL requires a different number of codewords to fill it's capacity, and we are creating a list for selecting these here.

  TOTAL_CODEWORDS := Object()
  TOTAL_CODEWORDS[1] := [19,34,55,80,108,136,156,194,232,274,324,370,428,461,523,589,647,721,795,861,932,1006,1094,1174,1276,1370,1468,1531,1631,1735,1843,1955,2071,2191,2306,2434,2566,2702,2812,2956] ; ECL L
  TOTAL_CODEWORDS[2] := [16,28,44,64,86,108,124,154,182,216,254,290,334,365,415,453,507,563,627,669,714,782,860,914,1000,1062,1128,1193,1267,1373,1455,1541,1631,1725,1812,1914,1992,2102,2216,2334] ; ECL M
  TOTAL_CODEWORDS[3] := [13,22,34,48,62,76,88,110,132,154,180,206,244,261,295,325,367,397,445,485,512,568,614,664,718,754,808,871,911,985,1033,1115,1171,1231,1286,1354,1426,1502,1582,1666] ; ECL Q
  TOTAL_CODEWORDS[4] := [9,16,26,36,46,60,66,86,100,122,140,158,180,197,223,253,283,313,341,385,406,442,464,514,538,596,628,661,701,745,793,845,901,961,986,1054,1096,1142,1222,1276] ; ECL H


;128 bits are required for a V2H Mode + Char_Count + String + Terminator + Pads. The terminator contains at most 4 zeros (limited also to the max string size of 128 bits). The pads are sequences of  the bytes "11101100" and "00010001" appended in alternating mode right after the terminator.
;125
  If StrLen(MESSAGE_CODE . MESSAGE_STRING) < (TOTAL_CODEWORDS[CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_VERSION] * 8) - 3
  {
    MESSAGE_UP_TO_TERMINATOR := MESSAGE_CODE . MESSAGE_STRING . "0000"
  }
  Else if StrLen(MESSAGE_CODE . MESSAGE_STRING) < (TOTAL_CODEWORDS[CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_VERSION] * 8) - 2
  {
    MESSAGE_UP_TO_TERMINATOR := MESSAGE_CODE . MESSAGE_STRING . "000"
  }
  Else if StrLen(MESSAGE_CODE . MESSAGE_STRING) < (TOTAL_CODEWORDS[CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_VERSION] * 8) - 1
  {
    MESSAGE_UP_TO_TERMINATOR := MESSAGE_CODE . MESSAGE_STRING . "00"
  }
  Else if StrLen(MESSAGE_CODE . MESSAGE_STRING) < (TOTAL_CODEWORDS[CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_VERSION] * 8)
  {
    MESSAGE_UP_TO_TERMINATOR := MESSAGE_CODE . MESSAGE_STRING . "0"
  }
  Else 
  {
    MESSAGE_UP_TO_TERMINATOR := MESSAGE_CODE . MESSAGE_STRING
  }

  ; We now see how many more zeroes we have to add till we get a multiple of 8 in the total bit length and add these after the teminator.
  MESSAGE_PAD_MULTIPLE_8 := MESSAGE_UP_TO_TERMINATOR ; note that MESSAGE_PAD_MULTIPLE_8 is only completed (in its namesake) below.

  If !(Mod(StrLen(MESSAGE_UP_TO_TERMINATOR), 8) = 0) ; If the number of bits in the string we have up till now is not a multiple of 8...
  {
    Loop % 8 - Mod(StrLen(MESSAGE_UP_TO_TERMINATOR), 8) ; We add zeros till the number of bits is a multiple of 8.
    {
      MESSAGE_PAD_MULTIPLE_8 := MESSAGE_PAD_MULTIPLE_8 . "0"
    }
  }
  NUMBER_OF_RIGHT_BYTE_PADS_TO_ADD := ((TOTAL_CODEWORDS[CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_VERSION] * 8) - StrLen(MESSAGE_PAD_MULTIPLE_8)) / 8

  FINAL_MESSAGE_RAW_DATA_BITS := MESSAGE_PAD_MULTIPLE_8 ; Again, note that FINAL_MESSAGE_RAW_DATA_BITS is only completed (in its namesake) below. We are including the right pad bytes to make the the message equal to the full capacity of the version.

  ; Now that we have the message, terminator and zero padded bits, we add the byte padds, which are alternating sequences of the bytes 236 and 17 up to the filled capacity of  cordewords for the version and ECL.
  Loop % NUMBER_OF_RIGHT_BYTE_PADS_TO_ADD
  {
    if (Mod(A_Index, 2) = 1)
      FINAL_MESSAGE_RAW_DATA_BITS := FINAL_MESSAGE_RAW_DATA_BITS . "11101100" ; 236
    else
      FINAL_MESSAGE_RAW_DATA_BITS := FINAL_MESSAGE_RAW_DATA_BITS . "00010001" ; 17
  }

  GROUPS_AND_BLOCKS_INFO := ""
  GROUPS_AND_BLOCKS_INFO := BLOCKS_AND_GROUPS[CHOOSEN_ERROR_CORRECTION_LEVEL, CHOOSEN_VERSION]
  StringSplit, GROUPS_AND_BLOCKS_INFO_, GROUPS_AND_BLOCKS_INFO , |

  MESSAGE_BITS_TO_SPLIT := FINAL_MESSAGE_RAW_DATA_BITS

  ; For each block, we will generate an ECW set.

  NUMBER_OF_BITS_PER_BLOCK := ""
  NUMBER_OF_BITS_PER_BLOCK := GROUPS_AND_BLOCKS_INFO_3 * 8
  Loop % GROUPS_AND_BLOCKS_INFO_1 ; Blocks in Group 1
  {
    BLOCK_%A_Index%_OF_GROUP_1 := SubStr(MESSAGE_BITS_TO_SPLIT, 1, NUMBER_OF_BITS_PER_BLOCK)
    StringTrimLeft, MESSAGE_BITS_TO_SPLIT, MESSAGE_BITS_TO_SPLIT, %NUMBER_OF_BITS_PER_BLOCK%
    GENERATE_ERROR_CORRECTION_CODEWORDS(BLOCK_%A_Index%_OF_GROUP_1, CHOOSEN_ERROR_CORRECTION_LEVEL, 1, A_Index, CHOOSEN_VERSION) 
  }

  NUMBER_OF_BITS_PER_BLOCK := ""
  NUMBER_OF_BITS_PER_BLOCK := GROUPS_AND_BLOCKS_INFO_4 * 8
  Loop % GROUPS_AND_BLOCKS_INFO_2 ; Blocks in Group 2
  {
    BLOCK_%A_Index%_OF_GROUP_2 := SubStr(MESSAGE_BITS_TO_SPLIT, 1, NUMBER_OF_BITS_PER_BLOCK)
    StringTrimLeft, MESSAGE_BITS_TO_SPLIT, MESSAGE_BITS_TO_SPLIT, %NUMBER_OF_BITS_PER_BLOCK%
    GENERATE_ERROR_CORRECTION_CODEWORDS(BLOCK_%A_Index%_OF_GROUP_2, CHOOSEN_ERROR_CORRECTION_LEVEL, 2, A_Index, CHOOSEN_VERSION) 
  }

  ; So basically, we now have each of the bytes of the final message allocated in their corresponding groups and blocks. To access the first byte of the first group of the first block, we would than use the first byte of BLOCK_1_OF_GROUP_1. To access the first byte of the first ECW string (the first one related to the first block of the first group of CW) we would than use: SubStr("00000000" . Bin(ERROR_CORRECTION_CODEWORDS_1_1[1]), -7, 8). Time to proceed the interleaving.

  ; First, we interleave the CW Bytes.
  HIGHEST_NUMBER_OF_BLOCKS_PER_GROUP := ""
  % ((GROUPS_AND_BLOCKS_INFO_3 > GROUPS_AND_BLOCKS_INFO_4) ? (HIGHEST_NUMBER_OF_BLOCKS_PER_GROUP := GROUPS_AND_BLOCKS_INFO_3) : (HIGHEST_NUMBER_OF_BLOCKS_PER_GROUP := GROUPS_AND_BLOCKS_INFO_4))

  Loop % HIGHEST_NUMBER_OF_BLOCKS_PER_GROUP
  {
    CURRENT_COLUMN := A_Index
    Loop % (GROUPS_AND_BLOCKS_INFO_1 + GROUPS_AND_BLOCKS_INFO_2)
    {
      If (A_Index <= GROUPS_AND_BLOCKS_INFO_1)
      {
        INTERLEAVED_MESSAGE_BITS := INTERLEAVED_MESSAGE_BITS . SubStr(BLOCK_%A_Index%_OF_GROUP_1, CURRENT_COLUMN * 8 - 7, 8)
      }
      If (A_Index > GROUPS_AND_BLOCKS_INFO_1)
      {
        CURRENT_BLOCK_NUMBER := A_Index - GROUPS_AND_BLOCKS_INFO_1
        INTERLEAVED_MESSAGE_BITS := INTERLEAVED_MESSAGE_BITS . SubStr(BLOCK_%CURRENT_BLOCK_NUMBER%_OF_GROUP_2, CURRENT_COLUMN * 8 - 7, 8)
      }
    }
  }
  CURRENT_COLUMN := ""


  ; And than, we interleave the ECW bytes.
  Loop % NUMBER_OF_ECW
  {
    CURRENT_COLUMN := A_Index
    Loop % (GROUPS_AND_BLOCKS_INFO_1 + GROUPS_AND_BLOCKS_INFO_2)
    {
      If (A_Index <= GROUPS_AND_BLOCKS_INFO_1)
      {
        INTERLEAVED_ECW_BITS := INTERLEAVED_ECW_BITS . SubStr("00000000" . Bin(ERROR_CORRECTION_CODEWORDS_1_%A_Index%[CURRENT_COLUMN]), -7, 8)
      }
      If (A_Index > GROUPS_AND_BLOCKS_INFO_1)
      {
        CURRENT_BLOCK_NUMBER := A_Index - GROUPS_AND_BLOCKS_INFO_1
        INTERLEAVED_ECW_BITS := INTERLEAVED_ECW_BITS . SubStr("00000000" . Bin(ERROR_CORRECTION_CODEWORDS_2_%CURRENT_BLOCK_NUMBER%[CURRENT_COLUMN]), -7, 8)
      }
    }
  }

  CURRENT_COLUMN := ""

  FINAL_MESSAGE := INTERLEAVED_MESSAGE_BITS . INTERLEAVED_ECW_BITS ; This string contains the full unencoded structured message, without any error correction code.

  MATRIX_TO_PRINT := GENERATE_MATRIX(FINAL_MESSAGE, CHOOSEN_VERSION, CHOOSEN_ERROR_CORRECTION_LEVEL)


  ; We are finally done. All that is left before printing is to insert the Version and Format information.The first string goes of the Format information goes below.
  MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, 0, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 1] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -1, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 2] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -2, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 3] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -3, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 4] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -4, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 5] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -5, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 6] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -6, 1), MATRIX_TO_PRINT[9, MATRIX_DIMENSIONS - 7] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -7, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS - 6, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -8, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS - 5, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -9, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS - 4, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -10, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS - 3, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -11, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS - 2, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -12, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS - 1, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -13, 1), MATRIX_TO_PRINT[MATRIX_DIMENSIONS, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -14, 1)

  ; And now the second string with the Format Info.
  MATRIX_TO_PRINT[1, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, 0, 1), MATRIX_TO_PRINT[2, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -1, 1), MATRIX_TO_PRINT[3, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -2, 1), MATRIX_TO_PRINT[4, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -3, 1), MATRIX_TO_PRINT[5, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -4, 1), MATRIX_TO_PRINT[6, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -5, 1), MATRIX_TO_PRINT[8, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -6, 1), MATRIX_TO_PRINT[9, 9] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -7, 1), MATRIX_TO_PRINT[9, 8] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -8, 1), MATRIX_TO_PRINT[9, 6] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -9, 1), MATRIX_TO_PRINT[9, 5] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -10, 1), MATRIX_TO_PRINT[9, 4] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -11, 1), MATRIX_TO_PRINT[9, 3] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -12, 1), MATRIX_TO_PRINT[9, 2] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -13, 1), MATRIX_TO_PRINT[9, 1] := SubStr(CHOOSEN_MASK_CODE_FOR_V2, -14, 1)

  ; Version information strings exist only on QR matrixes versions 7 and above.
  If (CHOOSEN_VERSION >= 7)
  {
    VERSION_INFORMATION_STRING := ""
    %  ((CHOOSEN_VERSION = 7) ? (VERSION_INFORMATION_STRING := "000111110010010100") : ("")), ((CHOOSEN_VERSION = 8) ? (VERSION_INFORMATION_STRING := "001000010110111100") : ("")), ((CHOOSEN_VERSION = 9) ? (VERSION_INFORMATION_STRING := "001001101010011001") : ("")), ((CHOOSEN_VERSION = 10) ? (VERSION_INFORMATION_STRING := "001010010011010011") : ("")), ((CHOOSEN_VERSION = 11) ? (VERSION_INFORMATION_STRING := "001011101111110110") : ("")), ((CHOOSEN_VERSION = 12) ? (VERSION_INFORMATION_STRING := "001100011101100010") : ("")), ((CHOOSEN_VERSION = 13) ? (VERSION_INFORMATION_STRING := "001101100001000111") : ("")), ((CHOOSEN_VERSION = 14) ? (VERSION_INFORMATION_STRING := "001110011000001101") : ("")), ((CHOOSEN_VERSION = 15) ? (VERSION_INFORMATION_STRING := "001111100100101000") : ("")), ((CHOOSEN_VERSION = 16) ? (VERSION_INFORMATION_STRING := "010000101101111000") : ("")), ((CHOOSEN_VERSION = 17) ? (VERSION_INFORMATION_STRING := "010001010001011101") : ("")), ((CHOOSEN_VERSION = 18) ? (VERSION_INFORMATION_STRING := "010010101000010111") : ("")), ((CHOOSEN_VERSION = 19) ? (VERSION_INFORMATION_STRING := "010011010100110010") : ("")), ((CHOOSEN_VERSION = 20) ? (VERSION_INFORMATION_STRING := "010100100110100110") : (""))
    % ((CHOOSEN_VERSION = 21) ? (VERSION_INFORMATION_STRING := "010101011010000011") : ("")), ((CHOOSEN_VERSION = 22) ? (VERSION_INFORMATION_STRING := "010110100011001001") : ("")), ((CHOOSEN_VERSION = 23) ? (VERSION_INFORMATION_STRING := "010111011111101100") : ("")), ((CHOOSEN_VERSION = 24) ? (VERSION_INFORMATION_STRING := "011000111011000100") : ("")), ((CHOOSEN_VERSION = 25) ? (VERSION_INFORMATION_STRING := "011001000111100001") : ("")), ((CHOOSEN_VERSION = 26) ? (VERSION_INFORMATION_STRING := "011010111110101011") : ("")), ((CHOOSEN_VERSION = 27) ? (VERSION_INFORMATION_STRING := "011011000010001110") : ("")), ((CHOOSEN_VERSION = 28) ? (VERSION_INFORMATION_STRING := "011100110000011010") : ("")), ((CHOOSEN_VERSION = 29) ? (VERSION_INFORMATION_STRING := "011101001100111111") : ("")), ((CHOOSEN_VERSION = 30) ? (VERSION_INFORMATION_STRING := "011110110101110101") : ("")), ((CHOOSEN_VERSION = 31) ? (VERSION_INFORMATION_STRING := "011111001001010000") : ("")), ((CHOOSEN_VERSION = 32) ? (VERSION_INFORMATION_STRING := "100000100111010101") : ("")), ((CHOOSEN_VERSION = 33) ? (VERSION_INFORMATION_STRING := "100001011011110000") : ("")), ((CHOOSEN_VERSION = 34) ? (VERSION_INFORMATION_STRING := "100010100010111010") : ("")), ((CHOOSEN_VERSION = 35) ? (VERSION_INFORMATION_STRING := "100011011110011111") : ("")), ((CHOOSEN_VERSION = 36) ? (VERSION_INFORMATION_STRING := "100100101100001011") : ("")), ((CHOOSEN_VERSION = 37) ? (VERSION_INFORMATION_STRING := "100101010000101110") : ("")), ((CHOOSEN_VERSION = 38) ? (VERSION_INFORMATION_STRING := "100110101001100100") : ("")), ((CHOOSEN_VERSION = 39) ? (VERSION_INFORMATION_STRING := "100111010101000001") : ("")), ((CHOOSEN_VERSION = 40) ? (VERSION_INFORMATION_STRING := "101000110001101001") : (""))

    Loop 6
    {
      CURRENT_COLUMN := A_Index
      Loop 3
      {
        CURRENT_ROW := A_Index
        MATRIX_TO_PRINT[MATRIX_DIMENSIONS - (11 - A_Index), CURRENT_COLUMN] := SubStr(VERSION_INFORMATION_STRING, 1 - (A_Index + (CURRENT_COLUMN * 3 - 2) - 1), 1)
        MATRIX_TO_PRINT[CURRENT_COLUMN, MATRIX_DIMENSIONS - (11 - A_Index)] := SubStr(VERSION_INFORMATION_STRING, 1 - (A_Index + (CURRENT_COLUMN * 3 - 2) - 1), 1)
      }
    }
  }

  ; Time to get rid of the global trash.

  Loop % GROUPS_AND_BLOCKS_INFO_1
  {
    BLOCK_%A_Index%_OF_GROUP_1 := ""
    ERROR_CORRECTION_CODEWORDS_1_%A_Index% := ""
  }

  Loop % GROUPS_AND_BLOCKS_INFO_2
  {
    BLOCK_%A_Index%_OF_GROUP_2 := ""
    ERROR_CORRECTION_CODEWORDS_2_%A_Index% := ""
  }

  Loop % ALIGNMENT_INDIVIDUAL_COORDINATES_0
  {
    ALIGNMENT_INDIVIDUAL_COORDINATES_%A_Index% := ""
  }

  CHOOSEN_MASK_CODE_FOR_V2 := "", ERROR_CORRECTION_CODEWORDS := "", CURRENT_COLUMN := "",  CURRENT_GROUP := "", CURRENT_REDIMENSION_ROW := "",  ERROR_CORRECTION_CODEWORDS_BITS := "", FINAL_MESSAGE := "", FINAL_MESSAGE_RAW_DATA_BITS := "", G := "", LIST_OF_PENALTY_VALUES := "", Loop_Times := "", MATRIX_MASK_ONE := "", MATRIX_MASK_TWO := "", MATRIX_MASK_THREE := "", MATRIX_MASK_FOUR := "", MATRIX_MASK_FIVE := "", MATRIX_MASK_SIX := "", MATRIX_MASK_SEVEN := "", MATRIX_MASK_EIGHT := "", MATRIX_UNMASKED := "", MESSAGE_PAD_MULTIPLE_8 := "", MESSAGE_STRING := "", MESSAGE_TO_ENCODE := "", MESSAGE_UP_TO_TERMINATOR := "", NUMBER_OF_BITS_TO_USE := "", NUMBER_OF_RIGHT_BYTE_PADS_TO_ADD := "", Numeric_Mode := "", Options := "", pBitmap := "", pBrush := "", PENALTY_VALUES_IN_ORDER0 := "", PENALTY_VALUES_IN_ORDER1 := "", PENALTY_VALUES_IN_ORDER2 := "", PENALTY_VALUES_IN_ORDER3 := "", PENALTY_VALUES_IN_ORDER4 := "", PENALTY_VALUES_IN_ORDER5 := "", PENALTY_VALUES_IN_ORDER6 := "", PENALTY_VALUES_IN_ORDER7 := "", PENALTY_VALUES_IN_ORDER8 := "", pToken := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_1 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_2 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_3 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_4 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_5 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_6 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_7 := "", TOTAL_PENALTY_FOR_MASKED_MATRIX_8 := "", MESSAGE_CODE := "", NUMBER_OF_ECW := "", CHOOSEN_ERROR_CORRECTION_LEVEL := "", CHOOSEN_CODE_MODE := "", CURRENT_ROW := "", CURRENT_ROW_OF_FUNCTION_PATTERN := "", CURRENT_WRITING_DIRECTION := "", FILE_PATH_AND_NAME := "", FUNCTION_ALIGN_PATTERN_CURRENT_ROW := "", FUNCTION_ALIGNMENT_PATTERN := "", FUNCTION_FINDER_PATTERN := "", GROUPS_AND_BLOCKS_INFO := "", GROUPS_AND_BLOCKS_INFO_0 := "", GROUPS_AND_BLOCKS_INFO_1 := "", GROUPS_AND_BLOCKS_INFO_2 := "", GROUPS_AND_BLOCKS_INFO_3 := "", GROUPS_AND_BLOCKS_INFO_4 := "", CURRENT_BLOCK_NUMBER := "", COMPOSITE_VERSION := "", Column_Count := "", CHOOSEN_VERSION := "", Char_Count := "", INTERLEAVED_ECW_BITS := "", INTERLEAVED_MESSAGE_BITS := "", LIST_OF_PENALTY_VALUES := "", MATRIX_DIMENSIONS := "", LOWER_MULTIPLE_OF_5 := "", NEAREST_MULTIPLE_OF_TEN := "", NOT_WRITE := "", NUMBER_OF_BITS_PER_BLOCK := "", NUMBER_OF_DARK_MODULES := "", NUMBER_OF_LIGHT_MODULES := "", PENALTY_SUM := "", PERCENT_OF_MODULES := "", PREVIOUS_BIT := "", RESULT_ONE := "", RESULT_TWO := "", Row_Count := "", SEQUENCE_TO_CHECK := "", START_BIT := "", TOTAL_CODEWORDS := "", TOTAL_MODULES := "", VERSION_INFORMATION_STRING := "", UPPER_MULTIPLE_OF_5 := "", INDIVIDUAL_ALIGNMENT_COORDS_0 := "", INDIVIDUAL_ALIGNMENT_COORDS_1 := "", INDIVIDUAL_ALIGNMENT_COORDS_2 := "", HIGHEST_NUMBER_OF_BLOCKS_PER_GROUP := "", COUNT_CONTINUOUS := "", CURRENT_ENTRY := "", ALIGNMENT_LOCATIONS := "", ALIGNMENT_INDIVIDUAL_COORDINATES_0 := "", COUNTER := "", FILE_NAME_TO_USE := "", BYTE_MODE := "", ALPHANUMERIC_MODE := "", Bits_Of_Last_Group := ""
  
  Return MATRIX_TO_PRINT
}
Return

; To encode alphanumeric characters, the table below is required.
GENERATE_ALPHANUMERIC_TABLE()
{
  Global
  ALPHA_TABLE := object()
  ALPHA_TABLE[0] := 0, ALPHA_TABLE[1] := 1, ALPHA_TABLE[2] := 2, ALPHA_TABLE[3] := 3, ALPHA_TABLE[4] := 4, ALPHA_TABLE[5] := 5, ALPHA_TABLE[6] := 6, ALPHA_TABLE[7] := 7, ALPHA_TABLE[8] := 8, ALPHA_TABLE[9] := 9, ALPHA_TABLE["A"] := 10,  ALPHA_TABLE["B"] := 11, ALPHA_TABLE["C"] := 12, ALPHA_TABLE["D"] := 13, ALPHA_TABLE["E"] := 14, ALPHA_TABLE["F"] := 15, ALPHA_TABLE["G"] := 16, ALPHA_TABLE["H"] := 17, ALPHA_TABLE["I"] := 18, ALPHA_TABLE["J"] := 19, ALPHA_TABLE["K"] := 20, ALPHA_TABLE["L"] := 21, ALPHA_TABLE["M"] := 22, ALPHA_TABLE["N"] := 23, ALPHA_TABLE["O"] := 24, ALPHA_TABLE["P"] := 25, ALPHA_TABLE["Q"] := 26, ALPHA_TABLE["R"] := 27, ALPHA_TABLE["S"] := 28, ALPHA_TABLE["T"] := 29, ALPHA_TABLE["U"] := 30, ALPHA_TABLE["V"] := 31, ALPHA_TABLE["W"] := 32, ALPHA_TABLE["X"] := 33, ALPHA_TABLE["Y"] := 34, ALPHA_TABLE["Z"] := 35, ALPHA_TABLE[A_Space] := 36, ALPHA_TABLE["`$"] := 37, ALPHA_TABLE["`%"] := 38, ALPHA_TABLE["`*"] := 39, ALPHA_TABLE["`+"] := 40, ALPHA_TABLE["`-"] := 41, ALPHA_TABLE["`."] := 42, ALPHA_TABLE["`/"] := 43, ALPHA_TABLE["`:"] := 44 
}

Return

;The function below generates a 3D object (CAPACITY_CUBE) to hold the capacity of the QR Matrix according to Version, ECL and Code Mode.
GENERATE_VERSION_CAPACITY_CUBE()
{
  global
  CAPACITY_CUBE := object()
  CAPACITY_STRING := "41	25	17	10	34	20	14	8	27	16	11	7	17	10	7	4	77	47	32	20	63	38	26	16	48	29	20	12	34	20	14	8	127	77	53	32	101	61	42	26	77	47	32	20	58	35	24	15	187	114	78	48	149	90	62	38	111	67	46	28	82	50	34	21	255	154	106	65	202	122	84	52	144	87	60	37	106	64	44	27	322	195	134	82	255	154	106	65	178	108	74	45	139	84	58	36	370	224	154	95	293	178	122	75	207	125	86	53	154	93	64	39	461	279	192	118	365	221	152	93	259	157	108	66	202	122	84	52	552	335	230	141	432	262	180	111	312	189	130	80	235	143	98	60	652	395	271	167	513	311	213	131	364	221	151	93	288	174	119	74	772	468	321	198	604	366	251	155	427	259	177	109	331	200	137	85	883	535	367	226	691	419	287	177	489	296	203	125	374	227	155	96	1022	619	425	262	796	483	331	204	580	352	241	149	427	259	177	109	1101	667	458	282	871	528	362	223	621	376	258	159	468	283	194	120	1250	758	520	320	991	600	412	254	703	426	292	180	530	321	220	136	1408	854	586	361	1082	656	450	277	775	470	322	198	602	365	250	154	1548	938	644	397	1212	734	504	310	876	531	364	224	674	408	280	173	1725	1046	718	442	1346	816	560	345	948	574	394	243	746	452	310	191	1903	1153	792	488	1500	909	624	384	1063	644	442	272	813	493	338	208	2061	1249	858	528	1600	970	666	410	1159	702	482	297	919	557	382	235	2232	1352	929	572	1708	1035	711	438	1224	742	509	314	969	587	403	248	2409	1460	1003	618	1872	1134	779	480	1358	823	565	348	1056	640	439	270	2620	1588	1091	672	2059	1248	857	528	1468	890	611	376	1108	672	461	284	2812	1704	1171	721	2188	1326	911	561	1588	963	661	407	1228	744	511	315	3057	1853	1273	784	2395	1451	997	614	1718	1041	715	440	1286	779	535	330	3283	1990	1367	842	2544	1542	1059	652	1804	1094	751	462	1425	864	593	365	3517	2132	1465	902	2701	1637	1125	692	1933	1172	805	496	1501	910	625	385	3669	2223	1528	940	2857	1732	1190	732	2085	1263	868	534	1581	958	658	405	3909	2369	1628	1002	3035	1839	1264	778	2181	1322	908	559	1677	1016	698	430	4158	2520	1732	1066	3289	1994	1370	843	2358	1429	982	604	1782	1080	742	457	4417	2677	1840	1132	3486	2113	1452	894	2473	1499	1030	634	1897	1150	790	486	4686	2840	1952	1201	3693	2238	1538	947	2670	1618	1112	684	2022	1226	842	518	4965	3009	2068	1273	3909	2369	1628	1002	2805	1700	1168	719	2157	1307	898	553	5253	3183	2188	1347	4134	2506	1722	1060	2949	1787	1228	756	2301	1394	958	590	5529	3351	2303	1417	4343	2632	1809	1113	3081	1867	1283	790	2361	1431	983	605	5836	3537	2431	1496	4588	2780	1911	1176	3244	1966	1351	832	2524	1530	1051	647	6153	3729	2563	1577	4775	2894	1989	1224	3417	2071	1423	876	2625	1591	1093	673	6479	3927	2699	1661	5039	3054	2099	1292	3599	2181	1499	923	2735	1658	1139	701	6743	4087	2809	1729	5313	3220	2213	1362	3791	2298	1579	972	2927	1774	1219	750	7089	4296	2953	1817	5596	3391	2331	1435	3993	2420	1663	1024	3057	1852	1273	784"
  StringSplit, CAPACITY_STRING_, CAPACITY_STRING, %A_Tab%
  Loop 40 ; There are 40 different QR Code versions (excluding Micro Qr Codes).
  {
    CURRENT_VERSION := A_Index
    Loop 4 ; There are 4 different Error Correction Levels (L, M, Q, and H, from lowest to highest. Low ECL = higher capacity, but high ECL = more reliable reading if the matrix code is damaged).
    {
      CURRENT_ECL := A_Index
      Loop 4 ; There are 4 different types of encoded data in QR Code. Numeric, AlphaNumeric, Byte and Kanji.
      {
        CURRENT_TYPE := A_Index
        CURRENT_ADDRESS := ((CURRENT_VERSION - 1) * 16) + ((CURRENT_ECL - 1) * 4) + CURRENT_TYPE
        CAPACITY_CUBE[CURRENT_VERSION, CURRENT_ECL, CURRENT_TYPE] := CAPACITY_STRING_%CURRENT_ADDRESS%
        CAPACITY_STRING_%CURRENT_ADDRESS% := ""
      }
    }
  }
  CURRENT_VERSION := "", CURRENT_ECL := "", CURRENT_TYPE := "", CURRENT_ADDRESS := "", CAPACITY_STRING_0 := "", CAPACITY_STRING := ""
}
Return

CONVERT_TO_NUMERIC_ENCODING(MESSAGE_TO_ENCODE)
{
  Global
  MESSAGE_STRING := ""
  Bits_Of_Last_Group := 10
  If (Mod(StrLen(MESSAGE_TO_ENCODE), 3) = 2) ; If the last group is 2 digits long, it will be encoded as a 7 bits long binary number.
  {
    Bits_Of_Last_Group := 7
  }
  If (Mod(StrLen(MESSAGE_TO_ENCODE), 3) = 1) ; If the last group is 1 digit long, it will be encoded as a 4 bits long binary number.
  {
    Bits_Of_Last_Group := 4
  }
  Loop_Times := Ceil(StrLen(MESSAGE_TO_ENCODE) / 3) ; Numeric messages are encoded 3 digits a time.
  Message_Left := MESSAGE_TO_ENCODE ; This will be cut 3 digit a time in the loop bellow (to form the binary string).
  NUMBER_OF_BITS_TO_USE := 10 ; Initially, we will make sure every group has a total of 10 bits (appending as many zeros as necessary).
  Loop % Loop_Times ; This conditional executes once for every 3 digits in the message (rounded up).
  {
    If (A_Index = Loop_Times) ; If this is the last group, check the ammount of bits to pad according to variable Bits_Of_Last_Group.
    {
      NUMBER_OF_BITS_TO_USE := Bits_Of_Last_Group
    }	
    Current_Group := SubStr(Message_Left, 1, 3)

    MESSAGE_STRING := MESSAGE_STRING . SubStr("0000000000" . Bin(Current_Group), 1 - NUMBER_OF_BITS_TO_USE, NUMBER_OF_BITS_TO_USE) ; Explaining this: We are padding as many zeros as the groups max size to the binary string in the line above, and than we are getting the last NUMBER_OF_BITS_TO_USE digits of the concatenated result. This ensures that all and only the required zeros are padded to the string, whichever size .
    Message_Left := SubStr(Message_Left, 4)
  }
  Return MESSAGE_STRING
}
Return

CONVERT_TO_ALPHANUMERIC_ENCODING(MESSAGE_TO_ENCODE) ; Numeric messages are encoded 3 bits at a time in a 10 digit binary number (except for the last group, which may be 10, 7 or 4, depending on its length)
{
  Global
  MESSAGE_STRING := ""
  Bits_Of_Last_Group := 11
  If (Mod(StrLen(MESSAGE_TO_ENCODE), 2) = 1) ; If the string length is an odd number, the last character shall be encoded using 6 bits.
  {
    Bits_Of_Last_Group := 6
  }
  Loop_Times := Ceil(StrLen(MESSAGE_TO_ENCODE) / 2) ; Numeric messages are encoded 3 digits a time.
  Message_Left := MESSAGE_TO_ENCODE ; This will be cut 3 digit a time in the loop bellow (to form the binary string).
  NUMBER_OF_BITS_TO_USE := 11 ; Initially, we will make sure every group has a total of 11 bits (appending as many zeros as necessary).
  Loop % Loop_Times ; This conditional executes once for every 3 digits in the message (rounded up).
  {
    If (A_Index = Loop_Times) ; If this is the last group, check the ammount of bits to pad according to variable Bits_Of_Last_Group.
    {
      NUMBER_OF_BITS_TO_USE := Bits_Of_Last_Group
    }
    Current_Group := SubStr(Message_Left, 1, 2)
    FIRST_CHAR := SubStr(Current_Group, 1, 1)
    SECOND_CHAR := SubStr(Current_Group, 2, 1)
    if ((A_Index != Loop_Times) OR ((Mod(StrLen(MESSAGE_TO_ENCODE), 2) = 0)))
    {
      MESSAGE_STRING := MESSAGE_STRING . SubStr("00000000000" . Bin((ALPHA_TABLE[FIRST_CHAR] * 45) + ALPHA_TABLE[SECOND_CHAR]), 1 - NUMBER_OF_BITS_TO_USE, NUMBER_OF_BITS_TO_USE) ; Explaining this: We are padding as many zeros as the groups max size to the binary string in the line above, and than we are getting the last NUMBER_OF_BITS_TO_USE digits of the concatenated result. This ensures that all and only the required zeros are padded to the string, whichever size .
      Message_Left := SubStr(Message_Left, 3)
    }
    If ((A_Index = Loop_Times) AND (Mod(StrLen(MESSAGE_TO_ENCODE), 2) = 1))
    {
      MESSAGE_STRING := MESSAGE_STRING . SubStr("00000000000" . Bin(ALPHA_TABLE[FIRST_CHAR]), 1 - NUMBER_OF_BITS_TO_USE, NUMBER_OF_BITS_TO_USE) ; Explaining this: We are padding as many zeros as the groups max size to the binary string in the line above, and than we are getting the last NUMBER_OF_BITS_TO_USE digits of the concatenated result. This ensures that all and only the required zeros are padded to the string, whichever size .
      Message_Left := SubStr(Message_Left, 3)
    }
  }
  Return MESSAGE_STRING
}
Return

CONVERT_TO_BYTE_ENCODING(MESSAGE_TO_ENCODE)
{
  Global
  MESSAGE_STRING := ""
  Loop_Times := StrLen(MESSAGE_TO_ENCODE) ; Byte messages are encoded 1 char at a time.
  Message_Left := MESSAGE_TO_ENCODE
  NUMBER_OF_BITS_TO_USE := 8 ; Initially, we will make sure every group has a total of 8 bits (appending as many zeros as necessary).
  Loop % Loop_Times
  {
  Current_Group := SubStr(Message_Left, 1, 1)

    MESSAGE_STRING := MESSAGE_STRING . SubStr("00000000" . Bin(Asc(Current_Group)), 1 - NUMBER_OF_BITS_TO_USE, NUMBER_OF_BITS_TO_USE) ; Explaining this: We are padding as many zeros as the groups max size to the binary string in the line above, and than we are getting the last NUMBER_OF_BITS_TO_USE digits of the concatenated result. This ensures that all and only the required zeros are padded to the string, whichever size .
    Message_Left := SubStr(Message_Left, 2)
  }
  Return MESSAGE_STRING
}
Return

GET_GENERATOR_POLYNOMIAL(CHOOSEN_VERSION, CHOOSEN_ERROR_CORRECTION_LEVEL) ; Here we calculate the generator polynomial required.
{
  Global
  ; The Generator polynomial for the Error Correction Words (ECW) is a result of the expression (X - a**0) * (X - a**1) * (X - a**2) ... (X - a**[n-1]) where n is equal do the number of codewords that must be generated.
  ; This means that if you were to need a generator for 2 ECW, you would simply get a simplified galois field abiding form of (X - a**0) * (X - a**1), which is actually (a**0 * x**2 + a**25 * x**1 + a**1 * x**0).
  ; A generator for 3 Error Correction Words is achieved simply by multiplying the generator for 2 ECW with (X-a**2). And so on for the next generators. More info on http://www.thonky.com/qr-code-tutorial/error-correction-coding/#step-7-understanding-the-generator-polynomial
    
  ; Although it is thereby possible to generate a calculator for any generator polynomials based on required ECW, coding such a routine seems to be a waste of processing power since you only got 13 possible generator polynomials anyways. The ISO 18004 also gives the possible generators in an annex table.
  
  COMPOSITE_VERSION := CHOOSEN_VERSION . "-" . CHOOSEN_ERROR_CORRECTION_LEVEL ; This has been assigned on an earlier call to GET_NUMBER_OF_GROUPS_AND_BLOCKS.
  if (COMPOSITE_VERSION = "1-1") ; 1-L. 7 ECW.
  {
    NUMBER_OF_ECW := 7
    Loop, 7
      GENERATOR_X%A_Index% := 8 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 87, GENERATOR_A3 := 229, GENERATOR_A4 := 146, GENERATOR_A5 := 149, GENERATOR_A6 := 238, GENERATOR_A7 := 102, GENERATOR_A8 := 21
    Return
  }
  
  If COMPOSITE_VERSION in 1-2,2-1 ; 1-M , 2-L. 10 ECW. ANY SPACES AND TABS AROUND THE COMMAS ARE SIGNIFICANT FOR IF VAR IN MATCHLIST !!
  {
    NUMBER_OF_ECW := 10
    Loop, 10
      GENERATOR_X%A_Index% := 11 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 251, GENERATOR_A3 := 67, GENERATOR_A4 := 46, GENERATOR_A5 := 61, GENERATOR_A6 := 118, GENERATOR_A7 := 70, GENERATOR_A8 := 64, GENERATOR_A9 := 94, GENERATOR_A10 := 32, GENERATOR_A11 := 45
    Return
  }
  
  If COMPOSITE_VERSION in 1-3 ; 1-Q. 13 ECW
  {
    NUMBER_OF_ECW := 13
    Loop, 13
      GENERATOR_X%A_Index% := 14 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 74, GENERATOR_A3 := 152, GENERATOR_A4 := 176, GENERATOR_A5 := 100, GENERATOR_A6 := 86, GENERATOR_A7 := 100, GENERATOR_A8 := 106, GENERATOR_A9 := 104, GENERATOR_A10 := 130, GENERATOR_A11 := 218, GENERATOR_A12 := 206, GENERATOR_A13 := 140, GENERATOR_A14 := 78
    Return
  }
  
  If COMPOSITE_VERSION in 3-1 ; 3-L. 15 ECW
  {
    NUMBER_OF_ECW := 15
    Loop, 15
      GENERATOR_X%A_Index% := 16 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 8, GENERATOR_A3 := 183, GENERATOR_A4 := 61, GENERATOR_A5 := 91, GENERATOR_A6 := 202, GENERATOR_A7 := 37, GENERATOR_A8 := 51, GENERATOR_A9 := 58, GENERATOR_A10 := 58, GENERATOR_A11 := 237, GENERATOR_A12 := 140, GENERATOR_A13 := 124, GENERATOR_A14 := 5, GENERATOR_A15 := 99, GENERATOR_A16 := 105
    Return
  }
  
  If COMPOSITE_VERSION in 2-2,4-4,6-2 ; 2-M, 4-H, 6-M. 16 ECW
  {
    NUMBER_OF_ECW := 16
    Loop, 16
      GENERATOR_X%A_Index% := 17 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 120, GENERATOR_A3 := 104, GENERATOR_A4 := 107, GENERATOR_A5 := 109, GENERATOR_A6 := 102, GENERATOR_A7 := 161, GENERATOR_A8 := 76, GENERATOR_A9 := 3, GENERATOR_A10 := 91, GENERATOR_A11 := 191, GENERATOR_A12 := 147, GENERATOR_A13 := 169, GENERATOR_A14 := 182, GENERATOR_A15 := 194, GENERATOR_A16 := 225, GENERATOR_A17 := 120
    Return
  }
  
  If COMPOSITE_VERSION in 1-4 ; 1-H. 17 ECW
  {
    NUMBER_OF_ECW := 17
    Loop, 17
      GENERATOR_X%A_Index% := 18 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 43, GENERATOR_A3 := 139, GENERATOR_A4 := 206, GENERATOR_A5 := 78, GENERATOR_A6 := 43, GENERATOR_A7 := 239, GENERATOR_A8 := 123, GENERATOR_A9 := 206, GENERATOR_A10 := 214, GENERATOR_A11 := 147, GENERATOR_A12 := 24, GENERATOR_A13 := 99, GENERATOR_A14 := 150, GENERATOR_A15 := 39, GENERATOR_A16 := 243, GENERATOR_A17 := 163, GENERATOR_A18 := 136
    Return
  }
  
  If COMPOSITE_VERSION in 3-3,4-2,5-3,6-1,7-2,7-3,10-1 ; 3-Q, 4-M, 5-Q, 6-L, 7-M, 7-Q, 10-L. 18 ECW
  {
    NUMBER_OF_ECW := 18
    Loop, 18
      GENERATOR_X%A_Index% := 19 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 215, GENERATOR_A3 := 234, GENERATOR_A4 := 158, GENERATOR_A5 := 94, GENERATOR_A6 := 184, GENERATOR_A7 := 97, GENERATOR_A8 := 118, GENERATOR_A9 := 170, GENERATOR_A10 := 79, GENERATOR_A11 := 187, GENERATOR_A12 :=152, GENERATOR_A13 := 148, GENERATOR_A14 := 252, GENERATOR_A15 := 179, GENERATOR_A16 := 5, GENERATOR_A17 := 98, GENERATOR_A18 := 96, GENERATOR_A19 := 153
    Return
  }
  
  If COMPOSITE_VERSION in 4-1,7-1,9-3,11-1,14-3 ; 4-L, 7-L, 9-Q, 11-L, 14-Q. 20 ECW
  {
    NUMBER_OF_ECW := 20
    Loop, 20
      GENERATOR_X%A_Index% := 21 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 17, GENERATOR_A3 := 60, GENERATOR_A4 := 79, GENERATOR_A5 := 50, GENERATOR_A6 := 61, GENERATOR_A7 := 163, GENERATOR_A8 := 26, GENERATOR_A9 := 187, GENERATOR_A10 := 202, GENERATOR_A11 := 180, GENERATOR_A12 := 221, GENERATOR_A13 := 225, GENERATOR_A14 := 83, GENERATOR_A15 := 239, GENERATOR_A16 := 156, GENERATOR_A17 := 164, GENERATOR_A18 := 212, GENERATOR_A19 := 212, GENERATOR_A20 := 188, GENERATOR_A21 := 190
    Return
  }
  
  If COMPOSITE_VERSION in 2-3,3-4,5-4,8-2,8-3,9-2,12-2,13-2,13-4,15-1 ; 2-Q, 3-H, 5-H, 8-M, 8-Q, 9-M, 12-M, 13-M, 13-H, 15-L. 22 ECW
  {
    NUMBER_OF_ECW := 22
    Loop, 22
      GENERATOR_X%A_Index% := 23 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 210, GENERATOR_A3 := 171, GENERATOR_A4 := 247, GENERATOR_A5 := 242, GENERATOR_A6 := 93, GENERATOR_A7 := 230, GENERATOR_A8 := 14, GENERATOR_A9 := 109, GENERATOR_A10 := 221, GENERATOR_A11 := 53, GENERATOR_A12 := 200, GENERATOR_A13 := 74, GENERATOR_A14 := 8, GENERATOR_A15 := 172, GENERATOR_A16 := 98, GENERATOR_A17 := 80, GENERATOR_A18 := 219, GENERATOR_A19 := 134, GENERATOR_A20 := 160, GENERATOR_A21 := 105, GENERATOR_A22 := 165, GENERATOR_A23 := 231
    Return
  }
  
  If COMPOSITE_VERSION in 5-2,6-3,8-1,9-4,10-3,11-4,12-1,13-3,14-2,14-4,15-2,15-4,16-1,16-3,22-4 ; 5-M, 6-Q, 8-L, 9-H, 10-Q, 11-H, 12-L, 13-Q, 14-M, 14-H, 15-M, 15-H, 16-L, 16-Q, 22-H. 24 ECW
  {
    NUMBER_OF_ECW := 24
    Loop, 24
      GENERATOR_X%A_Index% := 25 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 229, GENERATOR_A3 := 121, GENERATOR_A4 := 135, GENERATOR_A5 := 48, GENERATOR_A6 := 211, GENERATOR_A7 := 117, GENERATOR_A8 := 251, GENERATOR_A9 := 126, GENERATOR_A10 := 159, GENERATOR_A11 := 180, GENERATOR_A12 := 169, GENERATOR_A13 := 152, GENERATOR_A14 := 192, GENERATOR_A15 := 226, GENERATOR_A16 := 228, GENERATOR_A17 := 218, GENERATOR_A18 := 111, GENERATOR_A19 := 0, GENERATOR_A20 := 117, GENERATOR_A21 := 232, GENERATOR_A22 := 87, GENERATOR_A23 := 96, GENERATOR_A24 := 227, GENERATOR_A25 := 21
    Return
  }
  
  If COMPOSITE_VERSION in 3-2,4-3,5-1,7-4,8-4,10-2,12-3,13-1,18-2,19-2,19-3,19-4,20-2,21-2,25-1 ; 3-M, 4-Q, 5-L, 7-H, 8-H, 10-M, 12-Q, 13-L, 18-M, 19-M, 19-Q, 19-H, 20-M, 21-M, 25-L. 26 ECW
  {
    NUMBER_OF_ECW := 26
    Loop, 26
      GENERATOR_X%A_Index% := 27 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 173, GENERATOR_A3 := 125, GENERATOR_A4 := 158, GENERATOR_A5 := 2, GENERATOR_A6 := 103, GENERATOR_A7 := 182, GENERATOR_A8 := 118, GENERATOR_A9 := 17, GENERATOR_A10 := 145, GENERATOR_A11 := 201, GENERATOR_A12 := 111, GENERATOR_A13 := 28, GENERATOR_A14 := 165, GENERATOR_A15 := 53, GENERATOR_A16 := 161, GENERATOR_A17 := 21, GENERATOR_A18 := 245, GENERATOR_A19 := 142, GENERATOR_A20 := 13, GENERATOR_A21 := 102, GENERATOR_A22 := 48, GENERATOR_A23 := 227, GENERATOR_A24 := 153, GENERATOR_A25 := 145, GENERATOR_A26 := 218, GENERATOR_A27 := 70
    Return
  }
  
  If COMPOSITE_VERSION in 2-4,6-4,10-4,11-3,12-4,16-2,17-1,17-2,17-3,17-4,18-3,18-4,19-1,20-1,20-4,21-1,21-3,22-1,22-2,23-2,24-2,25-2,26-1,26-2,26-3,27-2,28-2,29-2,30-2,31-2,32-2,33-2,34-2,35-2,36-2,37-2,38-2,39-2,40-2 ; 2-H, 6-H, 10-H, 11-Q, 12-H, 16-M, 17-L, 17-M, 17-Q, 17-H, 18-Q, 18-H, 19-L, 20-L, 20-H, 21-L, 21-Q, 22-L, 22-M, 23-M, 24-M, 25-M, 26-L, 26-M, 26-Q, 27-2, 28-M, 29-M, 30-M, 31-M, 32-M, 33-2, 34-M, 35-M, 36-M, 37-M, 38-M, 39-M, 40-M 28 ECW
  {
    NUMBER_OF_ECW := 28
    Loop, 28
      GENERATOR_X%A_Index% := 29 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
    GENERATOR_A1 := 0, GENERATOR_A2 := 168, GENERATOR_A3 := 223, GENERATOR_A4 := 200, GENERATOR_A5 := 104, GENERATOR_A6 := 224, GENERATOR_A7 := 234, GENERATOR_A8 := 108, GENERATOR_A9 := 180, GENERATOR_A10 := 110, GENERATOR_A11 := 190, GENERATOR_A12 := 195, GENERATOR_A13 := 147, GENERATOR_A14 := 205, GENERATOR_A15 := 27, GENERATOR_A16 := 232, GENERATOR_A17 := 201, GENERATOR_A18 := 21, GENERATOR_A19 := 43, GENERATOR_A20 := 245, GENERATOR_A21 := 87, GENERATOR_A22 := 42, GENERATOR_A23 := 195, GENERATOR_A24 := 212, GENERATOR_A25 := 119, GENERATOR_A26 := 242, GENERATOR_A27 := 37, GENERATOR_A28 := 9, GENERATOR_A29 := 123
    Return
  }
  
  NUMBER_OF_ECW := 30 ; If the number of ECW is not any one of the above, that means we have 30 ECW  (the maximum).
  Loop, 30
    GENERATOR_X%A_Index% := 31 - A_Index + (StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8) - 1
  GENERATOR_A1 := 0, GENERATOR_A2 := 41, GENERATOR_A3 := 173, GENERATOR_A4 := 145, GENERATOR_A5 := 152, GENERATOR_A6 := 216, GENERATOR_A7 := 31, GENERATOR_A8 := 179, GENERATOR_A9 := 182, GENERATOR_A10 := 50, GENERATOR_A11 := 48, GENERATOR_A12 := 110, GENERATOR_A13 := 86, GENERATOR_A14 := 239, GENERATOR_A15 := 96, GENERATOR_A16 := 222, GENERATOR_A17 := 125, GENERATOR_A18 := 42, GENERATOR_A19 := 173, GENERATOR_A20 := 226, GENERATOR_A21 := 193, GENERATOR_A22 := 224, GENERATOR_A23 := 130, GENERATOR_A24 := 156, GENERATOR_A25 := 37, GENERATOR_A26 := 251, GENERATOR_A27 := 216, GENERATOR_A28 := 238, GENERATOR_A29 := 40, GENERATOR_A30 := 192, GENERATOR_A31 := 180
  Return
}
Return


LIST_NUMBER_OF_GROUPS_AND_BLOCKS()
{
  Global
  ; We are going to create 4 lines (one for each ECL) with 40 entries on number of: BLOCKS IN GROUP 1|BLOCKS IN GROUP 2, CW IN BLOCKS OF GROUP 1, CW IN BLOCKS OF GROUP 2.
  BLOCKS_AND_GROUPS := ""
  BLOCKS_AND_GROUPS := object()
  BLOCKS_AND_GROUPS[1] := ["1|0|19|0", "1|0|34|0", "1|0|55|0", "1|0|80|0", "1|0|108|0", "2|0|68|0", "2|0|78|0", "2|0|97|0", "2|0|116|0", "2|2|68|69", "4|0|81|0", "2|2|92|93", "4|0|107|0", "3|1|115|116", "5|1|87|88", "5|1|98|99", "1|5|107|108", "5|1|120|121", "3|4|113|114", "3|5|107|108", "4|4|116|117", "2|7|111|112", "4|5|121|122", "6|4|117|118", "8|4|106|107", "10|2|114|115", "8|4|122|123", "3|10|117|118", "7|7|116|117", "5|10|115|116", "13|3|115|116", "17|0|115|0", "17|1|115|116", "13|6|115|116", "12|7|121|122", "6|14|121|122", "17|4|122|123", "4|18|122|123", "20|4|117|118", "19|6|118|119"] ; The 40 values for ECL L.
  BLOCKS_AND_GROUPS[2] := ["1|0|16|0", "1|0|28|0", "1|0|44|0", "2|0|32|0", "2|0|43|0", "4|0|27|0", "4|0|31|0", "2|2|38|39", "3|2|36|37", "4|1|43|44", "1|4|50|51", "6|2|36|37", "8|1|37|38", "4|5|40|41", "5|5|41|42", "7|3|45|46", "10|1|46|47", "9|4|43|44", "3|11|44|45", "3|13|41|42", "17|0|42|0", "17|0|46|0", "4|14|47|48", "6|14|45|46", "8|13|47|48", "19|4|46|47", "22|3|45|46", "3|23|45|46", "21|7|45|46", "19|10|47|48", "2|29|46|47", "10|23|46|47", "14|21|46|47", "14|23|46|47", "12|26|47|48", "6|34|47|48", "29|14|46|47", "13|32|46|47", "40|7|47|48", "18|31|47|48"] ; The 40 values for ECL M.
  BLOCKS_AND_GROUPS[3] := ["1|0|13|0", "1|0|22|0", "2|0|17|0", "2|0|24|0", "2|2|15|16", "4|0|19|0", "2|4|14|15", "4|2|18|19", "4|4|16|17", "6|2|19|20", "4|4|22|23", "4|6|20|21", "8|4|20|21", "11|5|16|17", "5|7|24|25", "15|2|19|20", "1|15|22|23", "17|1|22|23", "17|4|21|22", "15|5|24|25", "17|6|22|23", "7|16|24|25", "11|14|24|25", "11|16|24|25", "7|22|24|25", "28|6|22|23", "8|26|23|24", "4|31|24|25", "1|37|23|24", "15|25|24|25", "42|1|24|25", "10|35|24|25", "29|19|24|25", "44|7|24|25", "39|14|24|25", "46|10|24|25", "49|10|24|25", "48|14|24|25", "43|22|24|25", "34|34|24|25"] ; The 40 values for ECL Q.
  BLOCKS_AND_GROUPS[4] := ["1|0|9|0", "1|0|16|0", "2|0|13|0", "4|0|9|0", "2|2|11|12", "4|0|15|0", "4|1|13|14", "4|2|14|15", "4|4|12|13", "6|2|15|16", "3|8|12|13", "7|4|14|15", "12|4|11|12", "11|5|12|13", "11|7|12|13", "3|13|15|16", "2|17|14|15", "2|19|14|15", "9|16|13|14", "15|10|15|16", "19|6|16|17", "34|0|13|0", "16|14|15|16", "30|2|16|17", "22|13|15|16", "33|4|16|17", "12|28|15|16", "11|31|15|16", "19|26|15|16", "23|25|15|16", "23|28|15|16", "19|35|15|16", "11|46|15|16", "59|1|16|17", "22|41|15|16", "2|64|15|16", "24|46|15|16", "42|32|15|16", "10|67|15|16", "20|61|15|16"] ; The 40 values for ECL H.
}
Return

GENERATE_ERROR_CORRECTION_CODEWORDS(FINAL_MESSAGE_RAW_DATA_BITS, CHOOSEN_ERROR_CORRECTION_LEVEL, GROUP, BLOCK, CHOOSEN_VERSION) ; The total number of ECW depends on version and ECL.

; Generator polynomial for 28 error correction code words as an example. Numbers are exponents: x28 + a168x27 + a223x26 + a200x25 + a104x24 + a224x23 + a234x22 + a108x21 + a180x20 + a110x19 + a190x18 + a195x17 + a147x16 + a205x15 + a27x14 + a232x13 + a201x12 + a21x11 + a43x10 + a245x9 + a87x8 + a42x7 + a195x6 + a212x5 + a119x4 + a242x3 + a37x2 + a9x + a123
{
  Global
  GET_GENERATOR_POLYNOMIAL(CHOOSEN_VERSION, CHOOSEN_ERROR_CORRECTION_LEVEL)
  MESSAGE_POLYNOMIAL := "", ERROR_CORRECTION_CODEWORDS := "" ; Free the ECW object list and the message polynomial.

  MESSAGE_POLYNOMIAL := FINAL_MESSAGE_RAW_DATA_BITS
  
  NUMBER_OF_MESSAGE_BYTES := StrLen(FINAL_MESSAGE_RAW_DATA_BITS) / 8

  Loop % NUMBER_OF_MESSAGE_BYTES ; We create an index with the value of the bytes of the message.
  {
    BYTE_%A_Index%_OF_MESSAGE_POLYNOMIAL := Dec(SubStr(MESSAGE_POLYNOMIAL, 1, 8))
    StringTrimLeft, MESSAGE_POLYNOMIAL, MESSAGE_POLYNOMIAL, 8
  }

  GENERATOR_FIRST := NUMBER_OF_ECW + 1 ; To be deducted on every step

  ; Looping routine starts here.

  Loop % NUMBER_OF_MESSAGE_BYTES
  {
    GET_GENERATOR_POLYNOMIAL(CHOOSEN_VERSION, CHOOSEN_ERROR_CORRECTION_LEVEL)
  
  
  ; 	Step a - Multiply the Generator Polynomial by the Lead Term of the Message Polynomial
    Loop % NUMBER_OF_ECW + NUMBER_OF_MESSAGE_BYTES ; below, we transform the bytes of the message into alpha exponent sums for the generator polynomial alphas. To do that, we transform the byte into it's alpha exponent value according to the table.
    {
      GENERATOR_A%A_Index% := Mod(GENERATOR_A%A_Index% + AntilogTable[BYTE_1_OF_MESSAGE_POLYNOMIAL], 255)
    }
  
  
    Loop % NUMBER_OF_ECW + NUMBER_OF_MESSAGE_BYTES
    {
      GENERATOR_A%A_Index% := LogTable[GENERATOR_A%A_Index%] ; This will revert the alpha notation to the X multiplier. End of step 1a.
    }
  
    ; Step b - XOR the result with the message polynomial (XOR with Zero for the lacking generator bytes and vice versa).


    NEW_MESSAGE_LIST := ""
    
    Loop % NUMBER_OF_ECW + NUMBER_OF_MESSAGE_BYTES
    {
      NEXT_ITERATION := A_Index + 1
      BYTE_%A_INDEX%_OF_MESSAGE_POLYNOMIAL := ((GENERATOR_A%NEXT_ITERATION% > 0) ? (GENERATOR_A%NEXT_ITERATION%) : (0)) ^ ((BYTE_%NEXT_ITERATION%_OF_MESSAGE_POLYNOMIAL > 0) ? (BYTE_%NEXT_ITERATION%_OF_MESSAGE_POLYNOMIAL) : (0))
      MESSAGE_X%A_Index% := (NUMBER_OF_ECW + NUMBER_OF_MESSAGE_BYTES) - A_Index + NUMBER_OF_ECW  ; Here we create the exponents of X in the Message Polynomial.
    }

    GENERATOR_FIRST--
    Loop % NUMBER_OF_ECW + NUMBER_OF_MESSAGE_BYTES
    {
      GENERATOR_X%A_Index% := GENERATOR_FIRST - A_Index + 15 
    }
  }

  ERROR_CORRECTION_CODEWORDS_%GROUP%_%BLOCK% := ""
  ERROR_CORRECTION_CODEWORDS_%GROUP%_%BLOCK% := object()
  Loop % NUMBER_OF_ECW + 1 ; Retrieve the Error Correction Codewords to return to the caller.
  {
    ERROR_CORRECTION_CODEWORDS_%GROUP%_%BLOCK%[A_Index] := BYTE_%A_INDEX%_OF_MESSAGE_POLYNOMIAL
  }

  Loop % NUMBER_OF_ECW + NUMBER_OF_MESSAGE_BYTES + 2 ; Free the message polynomial bytes for future use.
  {
    BYTE_%A_Index%_OF_MESSAGE_POLYNOMIAL := "", MESSAGE_X%A_Index% := "", GENERATOR_X%A_Index% := "", GENERATOR_A%A_Index% := ""
  }
  
  NUMBER_OF_MESSAGE_BYTES := "", GENERATOR_FIRST := "", NEXT_ITERATION := ""
}

Return


GENERATE_MATRIX(FINAL_MESSAGE, CHOOSEN_VERSION, CHOOSEN_ERROR_CORRECTION_LEVEL)
;Brief note:
; Here is the idea. Add all bits of CW + ECW, than apply masks and only than add the function patterns and the reserved areas (so that you can evaluate the mask) the last being all light color modules. 
{
  Global
  MATRIX_UNMASKED := object()
  
  MATRIX_DIMENSIONS := 17 + (CHOOSEN_VERSION * 4) ; First, we set the matrix dimensions. Since matrix dimensions start on 21 x 21 modules for V1 and increase by 4 modules on every subsequent version.
  
  CURRENT_ROW := 1, COUNTER := 0
  While (COUNTER < (MATRIX_DIMENSIONS**2 - MATRIX_DIMENSIONS)) ; We access each of the rows by the formula MATRIX_DIMENSIONS + 1 - CURRENT_ROW. Since the 7th column is entirely skipped before we enter it (because it belongs to a timing pattern that holds the exception regarding the writing algorithm)
  {
    CURRENT_COLUMN++
    If (CURRENT_ROW = MATRIX_DIMENSIONS + 1) ; If this condition is met, we have left the below loop by the means of exceeding the upper limits of the matrix.
    {
      CURRENT_ROW := MATRIX_DIMENSIONS, CURRENT_COLUMN++
    }
    If (CURRENT_ROW = 0) ; If this condition is met, we have left the below loop by the means of exceeding the bottom limits of the matrix.
    {
      CURRENT_ROW := 1, CURRENT_COLUMN++
    }
    If (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN = 7) ; If this condition is met, we have reached the vertical timing pattern, which holds an exception to the writing algorithm, so we have to skipp writing to that column entirely and move the message writing position as though.
    {
      CURRENT_COLUMN++
    }
    While ((CURRENT_ROW <= MATRIX_DIMENSIONS) AND (CURRENT_ROW > 0) AND (CURRENT_COLUMN <= MATRIX_DIMENSIONS)) ; And than we access each of the individual modules (or the columns of individual rows). We access the columns by the formula MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN
    {
      COUNTER++
      NOT_WRITE := 0
      
      ; Now to check if the current module address belongs to a function finder pattern.
      
      ; First, we check if the current module address reffers to a module located in the right-uppermost function finder pattern.
      
      If ((MATRIX_DIMENSIONS + 1 - CURRENT_ROW <= 9) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 9)) ; At least 9 modules from the top and 9 modules from the left limits of the matrixes: all module addresses that satisfy these conditions are in the left-uppermost function finder pattern.
      {
        NOT_WRITE := 1
      }
      
      If ((MATRIX_DIMENSIONS + 1 - CURRENT_ROW <= 9) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= MATRIX_DIMENSIONS - 7)) ; At least 9 modules from the top and 8 from the right limits of the matrixes: all module addresses that satisfy these conditions are in the right-uppermost function finder pattern.
      {
        NOT_WRITE := 1
      }
      
      If ((MATRIX_DIMENSIONS + 1 - CURRENT_ROW >= MATRIX_DIMENSIONS - 7) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 9)) ; At least 8 modules from the base and 9 modules from the left limits of the matrixes: all module addresses that satisfy these conditions are in the left-bottommost function finder pattern.
      {
        NOT_WRITE := 1
      }
      
      ; Now to check if the current module address belongs to a version information area (these exist only on version 7 or greater QR Matrixes)
      If (CHOOSEN_VERSION >= 7)
      {
        If ((MATRIX_DIMENSIONS + 1 - CURRENT_ROW <= 6) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= MATRIX_DIMENSIONS - 10)) ; Between 9 to 11 modules from the right and 1 to 6 modules from the top limits of the matrixes: all module addresses that satisfy these conditions are located in the top-rightmost version information area, which exists only for versions 7 and higher.
        {
          NOT_WRITE := 1
        }
        
        If ((MATRIX_DIMENSIONS + 1 - CURRENT_ROW >= MATRIX_DIMENSIONS - 10) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6)) ; Between 9 to the 11 modules from the bottom and 1 to 6 modules from the left limits of the matrixes: all module addresses that satisfy these conditions are located in the bottom-leftmost version information area, which exists only for versions 7 and higher.
        {
          NOT_WRITE := 1
        }
      }
      
      ; Now to check if the current module address belongs to a function ALIGNMENT pattern.
      
      ; We start off creating a string with the addresses for the upper-left modules of the alignment patterns of the choosen version.
      ; Version 1 Matrixes don't have any alignment patterns.
      ALIGNMENT_LOCATIONS := "" ; We start off erasing this variable just to make sure it starts off empty.
      % ((CHOOSEN_VERSION = 2) ? (ALIGNMENT_LOCATIONS := "17x17") : ("")), ((CHOOSEN_VERSION = 3) ? (ALIGNMENT_LOCATIONS := "21x21") : ("")), ((CHOOSEN_VERSION = 4) ? (ALIGNMENT_LOCATIONS := "25x25") : ("")), ((CHOOSEN_VERSION = 5) ? (ALIGNMENT_LOCATIONS := "29x29") : ("")), ((CHOOSEN_VERSION = 6) ? (ALIGNMENT_LOCATIONS := "33x33") : (""))
      % ((CHOOSEN_VERSION = 7) ? (ALIGNMENT_LOCATIONS := "5x21|21x5|21x21|21x37|37x21|37x37") : ("")), ((CHOOSEN_VERSION = 8) ? (ALIGNMENT_LOCATIONS := "5x23|23x5|23x23|23x41|41x23|41x41") : ("")), ((CHOOSEN_VERSION = 9) ? (ALIGNMENT_LOCATIONS := "5x25|25x5|25x25|25x45|45x25|45x45") : ("")), ((CHOOSEN_VERSION = 10) ? (ALIGNMENT_LOCATIONS := "5x27|27x5|27x27|27x49|49x27|49x49") : ("")), ((CHOOSEN_VERSION = 11) ? (ALIGNMENT_LOCATIONS := "5x29|29x5|29x29|29x53|53x29|53x53") : ("")), ((CHOOSEN_VERSION = 12) ? (ALIGNMENT_LOCATIONS := "5x31|31x5|31x31|31x57|57x31|57x57") : ("")), ((CHOOSEN_VERSION = 13) ? (ALIGNMENT_LOCATIONS := "5x33|33x5|33x33|33x61|61x33|61x61") : (""))
      % ((CHOOSEN_VERSION = 14) ? (ALIGNMENT_LOCATIONS := "5x25|5x45|25x5|25x25|25x45|25x65|45x5|45x25|45x45|45x65|65x25|65x45|65x65") : ("")), ((CHOOSEN_VERSION = 15) ? (ALIGNMENT_LOCATIONS := "5x25|5x47|25x5|25x25|25x47|25x69|47x5|47x25|47x47|47x69|69x25|69x47|69x69") : ("")), ((CHOOSEN_VERSION = 16) ? (ALIGNMENT_LOCATIONS := "5x25|5x49|25x5|25x25|25x49|25x73|49x5|49x25|49x49|49x73|73x25|73x49|73x73") : ("")), ((CHOOSEN_VERSION = 17) ? (ALIGNMENT_LOCATIONS := "5x29|5x53|29x5|29x29|29x53|29x77|53x5|53x29|53x53|53x77|77x29|77x53|77x77") : ("")), ((CHOOSEN_VERSION = 18) ? (ALIGNMENT_LOCATIONS := "5x29|5x55|29x5|29x29|29x55|29x81|55x5|55x29|55x55|55x81|81x29|81x55|81x81") : ("")), ((CHOOSEN_VERSION = 19) ? (ALIGNMENT_LOCATIONS := "5x29|5x57|29x5|29x29|29x57|29x85|57x5|57x29|57x57|57x85|85x29|85x57|85x85") : ("")), ((CHOOSEN_VERSION = 20) ? (ALIGNMENT_LOCATIONS := "5x33|5x61|33x5|33x33|33x61|33x89|61x5|61x33|61x61|61x89|89x33|89x61|89x89") : (""))
      % ((CHOOSEN_VERSION = 21) ? (ALIGNMENT_LOCATIONS := "5x27|5x49|5x71|27x5|27x27|27x49|27x71|27x93|49x5|49x27|49x49|49x71|49x93|71x5|71x27|71x49|71x71|71x93|93x27|93x49|93x71|93x93") : ("")), ((CHOOSEN_VERSION = 22) ? (ALIGNMENT_LOCATIONS := "5x25|5x49|5x73|25x5|25x25|25x49|25x73|25x97|49x5|49x25|49x49|49x73|49x97|73x5|73x25|73x49|73x73|73x97|97x25|97x49|97x73|97x97") : ("")), ((CHOOSEN_VERSION = 23) ? (ALIGNMENT_LOCATIONS := "5x29|5x53|5x77|29x5|29x29|29x53|29x77|29x101|53x5|53x29|53x53|53x77|53x101|77x5|77x29|77x53|77x77|77x101|101x29|101x53|101x77|101x101") : ("")), ((CHOOSEN_VERSION = 24) ? (ALIGNMENT_LOCATIONS := "5x27|5x53|5x79|27x5|27x27|27x53|27x79|27x105|53x5|53x27|53x53|53x79|53x105|79x5|79x27|79x53|79x79|79x105|105x27|105x53|105x79|105x105") : ("")), ((CHOOSEN_VERSION = 25) ? (ALIGNMENT_LOCATIONS := "5x31|5x57|5x83|31x5|31x31|31x57|31x83|31x109|57x5|57x31|57x57|57x83|57x109|83x5|83x31|83x57|83x83|83x109|109x31|109x57|109x83|109x109") : ("")), ((CHOOSEN_VERSION = 26) ? (ALIGNMENT_LOCATIONS := "5x29|5x57|5x85|29x5|29x29|29x57|29x85|29x113|57x5|57x29|57x57|57x85|57x113|85x5|85x29|85x57|85x85|85x113|113x29|113x57|113x85|113x113") : ("")), ((CHOOSEN_VERSION = 27) ? (ALIGNMENT_LOCATIONS := "5x33|5x61|5x89|33x5|33x33|33x61|33x89|33x117|61x5|61x33|61x61|61x89|61x117|89x5|89x33|89x61|89x89|89x117|117x33|117x61|117x89|117x117") : (""))
      % ((CHOOSEN_VERSION = 28) ? (ALIGNMENT_LOCATIONS := "5x25|5x49|5x73|5x97|25x5|25x25|25x49|25x73|25x97|25x121|49x5|49x25|49x49|49x73|49x97|49x121|73x5|73x25|73x49|73x73|73x97|73x121|97x5|97x25|97x49|97x73|97x97|97x121|121x25|121x49|121x73|121x97|121x121") : ("")), ((CHOOSEN_VERSION = 29) ? (ALIGNMENT_LOCATIONS := "5x29|5x53|5x77|5x101|29x5|29x29|29x53|29x77|29x101|29x125|53x5|53x29|53x53|53x77|53x101|53x125|77x5|77x29|77x53|77x77|77x101|77x125|101x5|101x29|101x53|101x77|101x101|101x125|125x29|125x53|125x77|125x101|125x125") : ("")), ((CHOOSEN_VERSION = 30) ? (ALIGNMENT_LOCATIONS := "5x25|5x51|5x77|5x103|25x5|25x25|25x51|25x77|25x103|25x129|51x5|51x25|51x51|51x77|51x103|51x129|77x5|77x25|77x51|77x77|77x103|77x129|103x5|103x25|103x51|103x77|103x103|103x129|129x25|129x51|129x77|129x103|129x129") : ("")), ((CHOOSEN_VERSION = 31) ? (ALIGNMENT_LOCATIONS := "5x29|5x55|5x81|5x107|29x5|29x29|29x55|29x81|29x107|29x133|55x5|55x29|55x55|55x81|55x107|55x133|81x5|81x29|81x55|81x81|81x107|81x133|107x5|107x29|107x55|107x81|107x107|107x133|133x29|133x55|133x81|133x107|133x133") : ("")), ((CHOOSEN_VERSION = 32) ? (ALIGNMENT_LOCATIONS := "5x33|5x59|5x85|5x111|33x5|33x33|33x59|33x85|33x111|33x137|59x5|59x33|59x59|59x85|59x111|59x137|85x5|85x33|85x59|85x85|85x111|85x137|111x5|111x33|111x59|111x85|111x111|111x137|137x33|137x59|137x85|137x111|137x137") : ("")), ((CHOOSEN_VERSION = 33) ? (ALIGNMENT_LOCATIONS := "5x29|5x57|5x85|5x113|29x5|29x29|29x57|29x85|29x113|29x141|57x5|57x29|57x57|57x85|57x113|57x141|85x5|85x29|85x57|85x85|85x113|85x141|113x5|113x29|113x57|113x85|113x113|113x141|141x29|141x57|141x85|141x113|141x141") : ("")), ((CHOOSEN_VERSION = 34) ? (ALIGNMENT_LOCATIONS := "5x33|5x61|5x89|5x117|33x5|33x33|33x61|33x89|33x117|33x145|61x5|61x33|61x61|61x89|61x117|61x145|89x5|89x33|89x61|89x89|89x117|89x145|117x5|117x33|117x61|117x89|117x117|117x145|145x33|145x61|145x89|145x117|145x145") : (""))
      % ((CHOOSEN_VERSION = 35) ? (ALIGNMENT_LOCATIONS := "5x29|5x53|5x77|5x101|5x125|29x5|29x29|29x53|29x77|29x101|29x125|29x149|53x5|53x29|53x53|53x77|53x101|53x125|53x149|77x5|77x29|77x53|77x77|77x101|77x125|77x149|101x5|101x29|101x53|101x77|101x101|101x125|101x149|125x5|125x29|125x53|125x77|125x101|125x125|125x149|149x29|149x53|149x77|149x101|149x125|149x149") : ("")), ((CHOOSEN_VERSION = 36) ? (ALIGNMENT_LOCATIONS := "5x23|5x49|5x75|5x101|5x127|23x5|23x23|23x49|23x75|23x101|23x127|23x153|49x5|49x23|49x49|49x75|49x101|49x127|49x153|75x5|75x23|75x49|75x75|75x101|75x127|75x153|101x5|101x23|101x49|101x75|101x101|101x127|101x153|127x5|127x23|127x49|127x75|127x101|127x127|127x153|153x23|153x49|153x75|153x101|153x127|153x153") : ("")), ((CHOOSEN_VERSION = 37) ? (ALIGNMENT_LOCATIONS := "5x27|5x53|5x79|5x105|5x131|27x5|27x27|27x53|27x79|27x105|27x131|27x157|53x5|53x27|53x53|53x79|53x105|53x131|53x157|79x5|79x27|79x53|79x79|79x105|79x131|79x157|105x5|105x27|105x53|105x79|105x105|105x131|105x157|131x5|131x27|131x53|131x79|131x105|131x131|131x157|157x27|157x53|157x79|157x105|157x131|157x157") : ("")), ((CHOOSEN_VERSION = 38) ? (ALIGNMENT_LOCATIONS := "5x31|5x57|5x83|5x109|5x135|31x5|31x31|31x57|31x83|31x109|31x135|31x161|57x5|57x31|57x57|57x83|57x109|57x135|57x161|83x5|83x31|83x57|83x83|83x109|83x135|83x161|109x5|109x31|109x57|109x83|109x109|109x135|109x161|135x5|135x31|135x57|135x83|135x109|135x135|135x161|161x31|161x57|161x83|161x109|161x135|161x161") : ("")), ((CHOOSEN_VERSION = 39) ? (ALIGNMENT_LOCATIONS := "5x25|5x53|5x81|5x109|5x137|25x5|25x25|25x53|25x81|25x109|25x137|25x165|53x5|53x25|53x53|53x81|53x109|53x137|53x165|81x5|81x25|81x53|81x81|81x109|81x137|81x165|109x5|109x25|109x53|109x81|109x109|109x137|109x165|137x5|137x25|137x53|137x81|137x109|137x137|137x165|165x25|165x53|165x81|165x109|165x137|165x165") : ("")), ((CHOOSEN_VERSION = 40) ? (ALIGNMENT_LOCATIONS := "5x29|5x57|5x85|5x113|5x141|29x5|29x29|29x57|29x85|29x113|29x141|29x169|57x5|57x29|57x57|57x85|57x113|57x141|57x169|85x5|85x29|85x57|85x85|85x113|85x141|85x169|113x5|113x29|113x57|113x85|113x113|113x141|113x169|141x5|141x29|141x57|141x85|141x113|141x141|141x169|169x29|169x57|169x85|169x113|169x141|169x169") : (""))
      
      ; Now, we separate the strings into the individual addresses
      ALIGNMENT_INDIVIDUAL_COORDINATES_ := ""
      StringSplit, ALIGNMENT_INDIVIDUAL_COORDINATES_, ALIGNMENT_LOCATIONS, "|"
      
      
      Loop % ALIGNMENT_INDIVIDUAL_COORDINATES_0
      {
        INDIVIDUAL_ALIGNMENT_COORDS_ := ""
        StringSplit, INDIVIDUAL_ALIGNMENT_COORDS_, ALIGNMENT_INDIVIDUAL_COORDINATES_%A_Index%, x
        If ((MATRIX_DIMENSIONS + 1 - CURRENT_ROW >= INDIVIDUAL_ALIGNMENT_COORDS_1) AND (MATRIX_DIMENSIONS + 1 - CURRENT_ROW <= INDIVIDUAL_ALIGNMENT_COORDS_1 + 4) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= INDIVIDUAL_ALIGNMENT_COORDS_2) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= INDIVIDUAL_ALIGNMENT_COORDS_2 + 4)) ; If these conditons are met, we are at an alignment patterns area.
        {
          NOT_WRITE := 1 
        }
      }
      
      
      ; Now to check if the current module address belongs to a timing pattern.
      ; The timing patterns are always located in the 7th row and the 7th column.
      if (MATRIX_DIMENSIONS + 1 - CURRENT_ROW = 7)
      {
        NOT_WRITE := 1
      }
      
      if (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN = 7)
      {
        NOT_WRITE := 1
      }
      
      ; CODE TO MOVE IF WE ARE NOT IN ANY FUNCTION PATTERN OR RESERVED AREA.
      ; Now things get a little tricky. QR Code matrixes are always odd numbered dimensioned. This means that the column we start off positiong the EC + ECW is always an even numbered columm. Whenever we are at an odd number column, the next address to visit is located leftwards 1 position. Whenever we are at an even number columns, the next address to visit is located rightwards 1 and uppwards 1, except if we reach the end of the column, where it will be leftwards 1 and modify the direction of the movements to be leftwards 1 at an even numbered columns and rightwards 1 downwards 1 at an odd column. 
      If (NOT_WRITE = 0)
      {
        StringLeft, BIT_TO_WRITE, FINAL_MESSAGE, 1
        StringTrimLeft, FINAL_MESSAGE, FINAL_MESSAGE, 1
        MATRIX_UNMASKED[MATRIX_DIMENSIONS + 1 - CURRENT_ROW, MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN] := BIT_TO_WRITE
      }
      
      If (((Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 1) OR (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 0)) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= 8))
        CURRENT_WRITING_DIRECTION := "UP"
      If (((Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 3) OR (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 2)) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= 8))
        CURRENT_WRITING_DIRECTION := "DOWN"
      If (((Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 1) OR (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 2)) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6))
        CURRENT_WRITING_DIRECTION := "DOWN"
      If (((Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 3) OR (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN, 4) = 0)) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6))
        CURRENT_WRITING_DIRECTION := "UP"
       
       
      If (CURRENT_WRITING_DIRECTION = "UP")
      {
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 1) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= 8)
        {
          CURRENT_COLUMN++
          Continue
        }
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 1) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6)
        {
          CURRENT_COLUMN--, CURRENT_ROW++
          Continue
        }
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 0) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= 8)
        {
          CURRENT_COLUMN--, CURRENT_ROW++
          Continue
        }
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 0) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6)
        {
          CURRENT_COLUMN++
          Continue
        }
      }
      
      If (CURRENT_WRITING_DIRECTION = "DOWN")
      {
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 1) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= 8)
        {
          CURRENT_COLUMN++
          Continue
        }
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 1) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6)
        {
          CURRENT_COLUMN--, CURRENT_ROW--
          Continue
        }
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 0) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN >= 8)
        {
          CURRENT_COLUMN --, CURRENT_ROW--
          Continue
        }
        If (Mod(MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN,2) = 0) AND (MATRIX_DIMENSIONS + 1 - CURRENT_COLUMN <= 6)
        {
          CURRENT_COLUMN++
          Continue
        }
      }
    }
  }

  ; Once all the CW+ECW bits have been added, we now apply the masks. Each mask goes on one copy of the unmasked matrix.
  
  ; First, make a copy of the unmasked version.
  MATRIX_MASK_ONE := Object(), MATRIX_MASK_TWO := Object(), MATRIX_MASK_THREE := Object(), MATRIX_MASK_FOUR := Object(), MATRIX_MASK_FIVE := Object(), MATRIX_MASK_SIX := Object(), MATRIX_MASK_SEVEN	:= Object(), MATRIX_MASK_EIGHT := Object()
  Loop % MATRIX_DIMENSIONS ; One for every row
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_DIMENSIONS ; One for every column
    {
      MATRIX_MASK_ONE[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_TWO[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_THREE[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_FOUR[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_FIVE[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_SIX[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_SEVEN[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
      MATRIX_MASK_EIGHT[CURRENT_ROW, A_Index] := MATRIX_UNMASKED[CURRENT_ROW, A_Index]
    }
  }
  
  
  ; And now we apply each mask to the copies we created previously.
  ; Note: since the specs define start row and column addresses as 0 (rather than at 1), the calculations must subtract 1. The matrix objects start at 1, so they won't be affected.
  Loop % MATRIX_DIMENSIONS ; One for every row
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_DIMENSIONS ; One for every column
    {
      If (Mod((CURRENT_ROW-1) + (A_Index-1), 2) = 0) ; Mask one: Switch if (row + column) mod 2 == 0	
      {
        MATRIX_MASK_ONE[CURRENT_ROW, A_Index] := !(MATRIX_MASK_ONE[CURRENT_ROW, A_Index])
      }
      If (Mod((CURRENT_ROW-1), 2) = 0) ; Mask two: Switch if (row) mod 2 == 0
      {
        MATRIX_MASK_TWO[CURRENT_ROW, A_Index] := !(MATRIX_MASK_TWO[CURRENT_ROW, A_Index])
      }
      If (Mod((A_Index-1), 3) = 0) ; Mask three: Switch if (column) mod 3 == 0
      {
        MATRIX_MASK_THREE[CURRENT_ROW, A_Index] := !(MATRIX_MASK_THREE[CURRENT_ROW, A_Index])
      }
      If (Mod((CURRENT_ROW-1) + (A_Index-1), 3) = 0) ; Mask Four: Switch if (row + column) mod 3 == 0
      {
        MATRIX_MASK_FOUR[CURRENT_ROW, A_Index] := !(MATRIX_MASK_FOUR[CURRENT_ROW, A_Index])
      }
      If (Mod(Floor((CURRENT_ROW-1) / 2) + Floor((A_Index-1) / 3), 2) = 0)	; Mask Five: Switch if ( floor(row / 2) + floor(column / 3) ) mod 2 == 0
      {
        MATRIX_MASK_FIVE[CURRENT_ROW, A_Index] := !(MATRIX_MASK_FIVE[CURRENT_ROW, A_Index])
      }
      If (Mod((CURRENT_ROW-1) * (A_Index-1), 2) + Mod((CURRENT_ROW-1) * (A_Index-1), 3) = 0) ; Mask Six: Switch if ((row * column) mod 2) + ((row * column) mod 3) == 0
      {
        MATRIX_MASK_SIX[CURRENT_ROW, A_Index] := !(MATRIX_MASK_SIX[CURRENT_ROW, A_Index])
      }
      If (Mod(Mod((CURRENT_ROW-1) * (A_Index-1), 2) + Mod((CURRENT_ROW-1) * (A_Index-1), 3), 2) = 0) ; Mask Seven: Switch if ( ((row * column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
      {
        MATRIX_MASK_SEVEN[CURRENT_ROW, A_Index] := !(MATRIX_MASK_SEVEN[CURRENT_ROW, A_Index])
      }
      If (Mod(Mod((CURRENT_ROW-1) + (A_Index-1), 2) + Mod((CURRENT_ROW-1) * (A_Index-1), 3), 2) = 0) ; Mask Eight: Switch if ( ((row + column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
      {
        MATRIX_MASK_EIGHT[CURRENT_ROW, A_Index] := !(MATRIX_MASK_EIGHT[CURRENT_ROW, A_Index])
      }
    }
  }
  
  
  ;Than, we apply the function and reserved (this one all-light) areas to the masked matrixes.
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_ONE, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_TWO, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_THREE, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_FOUR, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_FIVE, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_SIX, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_SEVEN, CHOOSEN_VERSION) 
  APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_MASK_EIGHT, CHOOSEN_VERSION) 
  
  
  ; Now that we have applyed the masks, we have to evaluate each one to choose the best (by readability). To do that, we must calculate the penalty scores for each of the masks, based on 4 specific criteria regarding readability.
  
  ;Calculating the penalty scores.
  TOTAL_PENALTY_FOR_MASKED_MATRIX_1 := CALCULATE_PENALTY(MATRIX_MASK_ONE)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_2 := CALCULATE_PENALTY(MATRIX_MASK_TWO)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_3 := CALCULATE_PENALTY(MATRIX_MASK_THREE)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_4 := CALCULATE_PENALTY(MATRIX_MASK_FOUR)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_5 := CALCULATE_PENALTY(MATRIX_MASK_FIVE)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_6 := CALCULATE_PENALTY(MATRIX_MASK_SIX)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_7 := CALCULATE_PENALTY(MATRIX_MASK_SEVEN)
  TOTAL_PENALTY_FOR_MASKED_MATRIX_8 := CALCULATE_PENALTY(MATRIX_MASK_EIGHT)
  
  ;Finding the lowest penalty masked matrix
  LIST_OF_PENALTY_VALUES := TOTAL_PENALTY_FOR_MASKED_MATRIX_1 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_2 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_3 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_4 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_5 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_6 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_7 . "|" . TOTAL_PENALTY_FOR_MASKED_MATRIX_8
  Sort, LIST_OF_PENALTY_VALUES, N D|
  StringSplit, PENALTY_VALUES_IN_ORDER, LIST_OF_PENALTY_VALUES, |
  
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_1 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_ONE) : (""))
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_2 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_TWO) : (""))
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_3 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_THREE) : (""))
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_4 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_FOUR) : (""))
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_5 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_FIVE) : (""))
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_6 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_SIX) : (""))
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_7 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_SEVEN) : (""))	
  % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_8 = PENALTY_VALUES_IN_ORDER1) ? (MATRIX_TO_PRINT := MATRIX_MASK_EIGHT) : (""))
  
  If (CHOOSEN_ERROR_CORRECTION_LEVEL = 1)
  {
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_1 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "111011111000100") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_2 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "111001011110011") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_3 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "111110110101010") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_4 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "111100010011101") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_5 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "110011000101111") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_6 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "110001100011000") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_7 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "110110001000001") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_8 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "110100101110110") : (""))
  }
  
  If (CHOOSEN_ERROR_CORRECTION_LEVEL = 2)
  {
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_1 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "101010000010010") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_2 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "101000100100101") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_3 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "101111001111100") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_4 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "101101101001011") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_5 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "100010111111001") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_6 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "100000011001110") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_7 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "100111110010111") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_8 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "100101010100000") : (""))
  }
  
  If (CHOOSEN_ERROR_CORRECTION_LEVEL = 3)
  {
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_1 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "011010101011111") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_2 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "011000001101000") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_3 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "011111100110001") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_4 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "011101000000110") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_5 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "010010010110100") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_6 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "010000110000011") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_7 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "010111011011010") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_8 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "010101111101101") : (""))
  }
  
  If (CHOOSEN_ERROR_CORRECTION_LEVEL = 4)
  {
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_1 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "001011010001001") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_2 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "001001110111110") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_3 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "001110011100111") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_4 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "001100111010000") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_5 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "000011101100010") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_6 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "000001001010101") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_7 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "000110100001100") : (""))
    % ((TOTAL_PENALTY_FOR_MASKED_MATRIX_8 = PENALTY_VALUES_IN_ORDER1) ? (CHOOSEN_MASK_CODE_FOR_V2 := "000100000111011") : (""))
  }
  
  Return MATRIX_TO_PRINT
}
Return

APPLY_FUNCTIONS_AND_TURN_RESERVED_AREAS_LIGHT(MATRIX_TO_APPLY, CHOOSEN_VERSION) ; No need for a global indicator.
{
  Global
  ; First, we create the model objects for the function alignment patterns and function finder patterns.
  FUNCTION_FINDER_PATTERN := Object(), FUNCTION_ALIGNMENT_PATTERN := Object()
  FUNCTION_FINDER_PATTERN[1] := [1,1,1,1,1,1,1], FUNCTION_FINDER_PATTERN[2] := [1,0,0,0,0,0,1], FUNCTION_FINDER_PATTERN[3] := [1,0,1,1,1,0,1], FUNCTION_FINDER_PATTERN[4] := [1,0,1,1,1,0,1], FUNCTION_FINDER_PATTERN[5] := [1,0,1,1,1,0,1], FUNCTION_FINDER_PATTERN[6] := [1,0,0,0,0,0,1], FUNCTION_FINDER_PATTERN[7] := [1,1,1,1,1,1,1]
  FUNCTION_ALIGNMENT_PATTERN[1] := [1, 1, 1, 1, 1], FUNCTION_ALIGNMENT_PATTERN[2] := [1, 0, 0, 0, 1], FUNCTION_ALIGNMENT_PATTERN[3] := [1, 0, 1, 0, 1], FUNCTION_ALIGNMENT_PATTERN[4] := [1, 0, 0, 0, 1], FUNCTION_ALIGNMENT_PATTERN[5] := [1, 1, 1, 1, 1]
  
  ;Next, we print those starting at their corresponding addresses in the matrixes, for all eight masks (This function is called once for every mask).
  ;First, we print the Function Finder Patterns.
  Loop 7
  {
    CURRENT_ROW_OF_FUNCTION_PATTERN := A_Index
    Loop 7
    {
      MATRIX_TO_APPLY[CURRENT_ROW_OF_FUNCTION_PATTERN, A_Index] := FUNCTION_FINDER_PATTERN[CURRENT_ROW_OF_FUNCTION_PATTERN, A_Index] ; Left-Uppermost Function Finder Pattern.
      MATRIX_TO_APPLY[CURRENT_ROW_OF_FUNCTION_PATTERN, MATRIX_DIMENSIONS - 7 + A_Index] := FUNCTION_FINDER_PATTERN[CURRENT_ROW_OF_FUNCTION_PATTERN, A_Index] ; Right-Uppermost Function Finder Pattern.
      MATRIX_TO_APPLY[MATRIX_DIMENSIONS - 7 + CURRENT_ROW_OF_FUNCTION_PATTERN, A_Index] := FUNCTION_FINDER_PATTERN[CURRENT_ROW_OF_FUNCTION_PATTERN, A_Index] ; Left-Bottommost Function Finder Pattern.
    }
  }
  
  ; Next, we print the Timing Patterns.
  Loop % MATRIX_DIMENSIONS ; MATRIX_DIMENSIONS is a global variable created on a function that is called before this one. It contains the matrix dimensions, both height and width.
  {
    If (A_Index > 8) AND (A_Index < MATRIX_DIMENSIONS - 7) AND (Mod(A_Index,2) = 1)
    {
      MATRIX_TO_APPLY[7, A_Index] := 1 ; The Horizontal Timing Pattern.
      MATRIX_TO_APPLY[A_Index, 7] := 1 ; The Vertical Timing Pattern.
    }
    If (A_Index > 8) AND (A_Index < MATRIX_DIMENSIONS - 7) AND (Mod(A_Index,2) = 0)
    {
      MATRIX_TO_APPLY[7, A_Index] := 0 ; The Horizontal Timing Pattern.
      MATRIX_TO_APPLY[A_Index, 7] := 0 ; The Vertical Timing Pattern.
    }
  }
  
  Loop % ALIGNMENT_INDIVIDUAL_COORDINATES_0 ; ALIGNMENT_INDIVIDUAL_COORDINATES_0 is a global variable created on a function that is called before this one. It contains the number of function alignment patterns for the choosen version.
  {
    INDIVIDUAL_ALIGNMENT_COORDS_ := ""
    StringSplit, INDIVIDUAL_ALIGNMENT_COORDS_, ALIGNMENT_INDIVIDUAL_COORDINATES_%A_Index%, x
    Loop 5 ; Function Finder Patterns are dimensioned 5 x 5 (width and height).
    {
      FUNCTION_ALIGN_PATTERN_CURRENT_ROW := A_Index
      Loop 5
      {
        MATRIX_TO_APPLY[INDIVIDUAL_ALIGNMENT_COORDS_1 + A_Index - 1, INDIVIDUAL_ALIGNMENT_COORDS_2 + FUNCTION_ALIGN_PATTERN_CURRENT_ROW - 1] := FUNCTION_ALIGNMENT_PATTERN[FUNCTION_ALIGN_PATTERN_CURRENT_ROW, A_Index]
      }
    }
  }
  
  ;Now, we turn the reserved areas light.
  Loop 8
  {
    ; reserved area for the left-uppermost function finder pattern.
    MATRIX_TO_APPLY[A_index, 8] := 0
    MATRIX_TO_APPLY[8, A_Index] := 0
    
    ; reserved area for the right-uppermost function finder pattern.
    MATRIX_TO_APPLY[A_Index, MATRIX_DIMENSIONS - 7] := 0
    MATRIX_TO_APPLY[8, MATRIX_DIMENSIONS + A_Index - 8] := 0
    
    ; reserved area for the left-bottommost function finder pattern.
    MATRIX_TO_APPLY[MATRIX_DIMENSIONS + A_Index - 8, 8] := 0
    MATRIX_TO_APPLY[MATRIX_DIMENSIONS - 7, A_Index] := 0
  }
  
  MATRIX_TO_APPLY[MATRIX_DIMENSIONS - 7, 9] := 1
  
}
Return

CALCULATE_PENALTY(MATRIX_TO_USE)
{
  Global
  ; Rule 1: Add 3 penalty scores for each row/column with 5 consective modules of same color. Add 1 more for each consecutive that exceeds the 5th.
  
  ; First check for horizontal sequences.
  PENALTY_SUM := 0 ; This is unnecessary, since the function is not global. Leaving it here just in case i change that (update: changed!).
  Loop % MATRIX_DIMENSIONS
  {
    Row_Count := A_Index
    PREVIOUS_BIT := 2 ; A value to simply make it impossible for 0 or 1 to match.
    COUNT_CONTINUOUS := 0 ; To guarantee we start off with no count for continuous bits.
    Loop % MATRIX_DIMENSIONS
    {
      CURRENT_BIT := MATRIX_TO_USE[Row_Count, A_Index]
      START_BIT := MATRIX_TO_USE[Row_Count, A_Index]
      If ((PREVIOUS_BIT = 2) OR !(PREVIOUS_BIT = CURRENT_BIT))
      {
        If (COUNT_CONTINUOUS > 3)
        {
          PENALTY_SUM += COUNT_CONTINUOUS - 4 + 3
        }
        COUNT_CONTINUOUS := 0
        PREVIOUS_BIT := CURRENT_BIT
        Continue
      }
      COUNT_CONTINUOUS++
      PREVIOUS_BIT := CURRENT_BIT
    }
    if (COUNT_CONTINUOUS > 3)
    {
      PENALTY_SUM += COUNT_CONTINUOUS - 4 + 3
    }
  }
  
  ;Next, check for vertical sequences. It is the same code, except we change the row and column adresses.
  Loop % MATRIX_DIMENSIONS
  {
    Column_Count := A_Index
    PREVIOUS_BIT := 2 ; A value to simply make it impossible for 0 or 1 to match.
    COUNT_CONTINUOUS := 0 ; To guarantee we start off with no count for continuous bits.
    Loop % MATRIX_DIMENSIONS
    {
      CURRENT_BIT := MATRIX_TO_USE[A_Index, Column_Count]
      START_BIT := MATRIX_TO_USE[A_Index, Column_Count]
      If ((PREVIOUS_BIT = 2) OR !(PREVIOUS_BIT = CURRENT_BIT))
      {
        If (COUNT_CONTINUOUS > 3)
        {
          PENALTY_SUM += COUNT_CONTINUOUS - 4 + 3
        }
        COUNT_CONTINUOUS := 0
        PREVIOUS_BIT := CURRENT_BIT
        Continue
      }
      COUNT_CONTINUOUS++
      PREVIOUS_BIT := CURRENT_BIT
    }
    if (COUNT_CONTINUOUS > 3)
    {
      PENALTY_SUM += COUNT_CONTINUOUS - 4 + 3
    }
  }
  
  ; Rule 2: Add 3 penalty scores for every overlapping 2x2 block of the same color. (i.e., a 3x3 = 2 overlaping 2x2 blocks.)
  
  Loop % MATRIX_DIMENSIONS ; We access each row
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_DIMENSIONS ; And than we access each block (or column of row).
    {
      If ((MATRIX_TO_USE[CURRENT_ROW, A_Index] = MATRIX_TO_USE[CURRENT_ROW, A_Index + 1]) AND (MATRIX_TO_USE[CURRENT_ROW, A_Index] = MATRIX_TO_USE[CURRENT_ROW + 1, A_Index]) AND (MATRIX_TO_USE[CURRENT_ROW, A_Index] = MATRIX_TO_USE[CURRENT_ROW + 1, A_Index + 1]))
      {
        PENALTY_SUM += 3
      }
    }
  }
  
  ; Rule 3: Add 40 (yes, fourty) penalty scores for each of the following patterns in vertical or horizontal position: 10111010000 and 00001011101.
  ; Reasoning here is that these patterns are similar to the finder function patterns and may cause trouble to the reader algorithms.
  
  
  ; First, we do an horizontal scan for the patterns.
  Loop % MATRIX_DIMENSIONS ; We access each row
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_DIMENSIONS - 10 ; And than we access each block (or column of row). Since thes sequences are 11 digits long and the matrix is 25 bits long, there is no need to scan the16th bit onwards in the same row.
    {
      SEQUENCE_TO_CHECK := MATRIX_TO_USE[CURRENT_ROW, A_Index] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 1] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 2] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 3] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 4] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 5] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 6] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 7] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 8] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 9] . MATRIX_TO_USE[CURRENT_ROW, A_Index + 10]
      if ((SEQUENCE_TO_CHECK = "10111010000") OR (SEQUENCE_TO_CHECK = "00001011101"))
      {
        PENALTY_SUM += 40
      }
    }
  }
  
  
  ; Next, we do a vertical scan for the patterns.
  Loop % MATRIX_DIMENSIONS - 10 ; We access each row. Since the sequences are 11 digits long and the matrix is 25 bits long, there is no need to scan the 16th bit onwards in the same column.
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_DIMENSIONS ; And than we access each block (or column of row).
    {
      SEQUENCE_TO_CHECK := MATRIX_TO_USE[CURRENT_ROW, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 1, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 2, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 3, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 4, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 5, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 6, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 7, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 8, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 9, A_Index] . MATRIX_TO_USE[CURRENT_ROW + 10, A_Index]
      if ((SEQUENCE_TO_CHECK = "10111010000") OR (SEQUENCE_TO_CHECK = "00001011101"))
      {
        PENALTY_SUM += 40
      }
    }
  }
  
  ; Rule 4: This rule is based on the ratio of light modules to dark modules. Basically, what we are trying to do here is to penalize matrixes with too many light or too many dark modules. The most desirable ratio for a good readability is around half for each.
  
  NUMBER_OF_LIGHT_MODULES := ""
  TOTAL_MODULES := MATRIX_DIMENSIONS * MATRIX_DIMENSIONS ; or 25 x 25.
  Loop % MATRIX_DIMENSIONS ; We access each row.
  {
    CURRENT_ROW := A_Index
    Loop % MATRIX_DIMENSIONS
    {
      if (MATRIX_TO_USE[CURRENT_ROW, A_Index] = 0)
      {
        NUMBER_OF_LIGHT_MODULES++
      }
      if (MATRIX_TO_USE[CURRENT_ROW, A_Index] = 1)
      {
        NUMBER_OF_DARK_MODULES++
      }
    }
  }
  PERCENT_OF_MODULES := NUMBER_OF_DARK_MODULES / TOTAL_MODULES
  
  ; Below we calculate the nearest multiples of 5 before and after the calculated percent of modules. Example, if PERCENT_OF_MODULES is 343, we should get 345 and 340.
  NEAREST_MULTIPLE_OF_TEN := round(PERCENT_OF_MODULES, -1)
  If (Mod(PERCENT_OF_MODULES,5) = 0)
  {
    UPPER_MULTIPLE_OF_5 := PERCENT_OF_MODULES
    LOWER_MULTIPLE_OF_5 := PERCENT_OF_MODULES
  }
  Else if (NEAREST_MULTIPLE_OF_TEN > PERCENT_OF_MODULES)
  {
    UPPER_MULTIPLE_OF_5 := NEAREST_MULTIPLE_OF_TEN
    LOWER_MULTIPLE_OF_5 := NEAREST_MULTIPLE_OF_TEN - 5
  }
  Else if (NEAREST_MULTIPLE_OF_TEN < PERCENT_OF_MODULES)
  {
    UPPER_MULTIPLE_OF_5 := NEAREST_MULTIPLE_OF_TEN + 5
    LOWER_MULTIPLE_OF_5 := NEAREST_MULTIPLE_OF_TEN
  }
  
  ; Now we subtract 50 from each value, take the absolute result and divide it by 5.
  RESULT_ONE := abs(UPPER_MULTIPLE_OF_5 - 50) / 5
  RESULT_TWO := abs(LOWER_MULTIPLE_OF_5 - 50) / 5
  
  ;Finaly we take the lowest value and multiply it by 10. This is the final penalty for rule 4.
  if (RESULT_ONE > RESULT_TWO)
  {
    PENALTY_SUM += RESULT_ONE * 10
  }
  Else if ((RESULT_ONE < RESULT_TWO) OR (RESULT_ONE = RESULT_TWO))
  {
    PENALTY_SUM += RESULT_TWO * 10
  }
  
  Return PENALTY_SUM ; PENALTY_SUM should now contain the overal score of the masked matrix provided.We are returning it now for comparison against the other 7. The lowest overal penalty shall decide the masked matrix to use.
}
Return




CREATE_LOG_AND_ANTILOG_TABLES() ; The complete Log and Antilog tables for Galois Field 256 (bitwise mod 2, bytewise mod 285, as specified in the ISO for QR Codes). The steps of this calculum are explained in http://www.thonky.com/qr-code-tutorial/error-correction-coding/#step-5-generate-powers-of-2-using-bytewise-modulo-100011101.
{
  Global
  LogTable := Object(), AntiLogTable := Object()
  LogTable[0] := 1
  Loop 255
  {
    CURRENT_ENTRY := (((LogTable[A_index - 1]  * 2) > 255) ? ((LogTable[A_index - 1]  * 2) ^ 285) : ((LogTable[A_index - 1]  * 2)))
    LogTable[A_Index] := CURRENT_ENTRY
    AntilogTable[CURRENT_ENTRY] := A_Index
  }	
}
Return

GUICLOSE:
ExitApp
Return

;##############################################################################################################
; CODE 39 (or CODE 3 of 9) Functions v1.0 by Giordanno Sperotto.
; General Notes from the author (refer to the function "GENERATE_CODE_39()" for an explanation on how to create a CODE 39 Matrix):
; 
; Barcodes have been in use since at least the 1960s. They are a machine-readable representation of data that can be attached to
; a specific object in order to allow anyone with a scanner to later recall info for that specific object. An invaluable logistical tool, barcodes
; can greatly increase the production of workers inside an organization by dramatically reducing the time required for information to be 
; relayed between two specific users. Code 39 is a form of one-dimensional (1D) barcode symbology developed by David Allais and widely
; used in the market due to it's simplicity of implementation and ease availability of low cost scanners.
; 
; The current standard for CODE39 Barcodes is ANSI/AIM BC1/1995.
;
; Each character in Code39 Barcode is encoded as 5 (five) dark strips separated by 4 (four) light strips with 1 (one) light strip
; added between characters. These strips vary in width. The first character of a valid Code39 barcode is always * (asterisk) and 
; the last is also * (asterisk), both used to mark the beggining and the end of the barcode and both not being translated as output
; data when the code is read. The characters between these two compose the valid data string. Code39 can thereby be implemented
; as simply a text font translating each character to it's according set of strips and a small programmable routine to add the asterisks 
; in the beggining and the end of the string. This library also uses this simple approach. A modulo 43 checksum is optional on the standard, 
; but will not be readily available for this version. 
; Code39 can encode digits (0-9), UPPERCASE letters (A-Z), symbols -, +, $, %, .,/ and whitespaces, for a total of 43 possible
; characters.
; Do not forget to include reasonable sized "quiet zones" (light-colored areas) before the first and after the last bar of the code in the image.
;##############################################################################################################


;#######################################################################################################
;  GENERATE_CODE_39() v1.0 by Giordanno Sperotto.
; How to generate CODE39 lines using this library:
; This function takes a single parameter: The message you wish to encode. This parameter should be a string of characters, 
; composed of any combination of the following characters: Digits (0-9), UPPERCASE letters (A-Z), symbols -, +, $, %, .,/ and whitespaces.
;
; The function will complain and abort execution if you try to encode a message that contains any characters other than these.
;
; Although there is not limit for how many characters you can encode, do notice that the output image gets bigger quite fast as you try
; to encode a bigger message. Since most scanners have a practical scanning area limit, it is wise to limit the message size to no more
; than a few characters whenever possible.
;
; If you absolutely need to encode many characters, consider using QR Code instead.
;#########################################################################################################


BARCODER_GENERATE_CODE_39(MESSAGE_TO_ENCODE)
{
  Global
  If (MESSAGE_TO_ENCODE = "")
  {
    ;msgbox, 0x10, Error, Please insert a message to encode.
    Return 1
  }
  
  CREATE_CODE_39_CHARACTER_TABLE()
  MATRIX_TO_PRINT := ""
  If !(RegExMatch(MESSAGE_TO_ENCODE, "[^A-Z0-9`$`%`+`-`.`/ ]") = 0)
  {
    ;msgbox, 0x10, Error, The input string contains characters that cannot be encoded in a CODE39 Barcode. Please correct the message or choose another type of barcode. `n`nAvailable characters for encoding in CODE39 Barcodes: Numbers (0-9), UPPERCASE Letters (A-Z), Some symbols ($, `%, +, -, ., /) and whitespaces.
    Return 1
  }
  
  MATRIX_TO_PRINT := object()
  Loop, parse, MESSAGE_TO_ENCODE
  {
    VALUE_TO_SEARCH := A_LoopField
    % (A_LoopField = "+") ? (VALUE_TO_SEARCH := 43) : ("") ; Was getting an awkawd behavior when trying to straight translate CODE_39_TABLE[A_LoopField] when A_LoopField is "+". AHK v1.1.13.00.
    % (A_LoopField = "/") ? (VALUE_TO_SEARCH := 47) : ("") ; Was getting an awkawd behavior when trying to straight translate CODE_39_TABLE[A_LoopField] when A_LoopField is "/". AHK v1.1.13.00.
    LINE_TO_USE := LINE_TO_USE . CODE_39_TABLE[VALUE_TO_SEARCH] . ",0,"
    VALUE_TO_SEARCH := ""
  }
  StringTrimRight, LINE_TO_USE, LINE_TO_USE, 1
  LINE_TO_USE := "1,0,0,1,0,1,1,0,1,1,0,1," . "0," LINE_TO_USE . ",1,0,0,1,0,1,1,0,1,1,0,1" ; Asterisk + Message + Asterisk
  StringSplit, COLUMN_TO_USE_, LINE_TO_USE, `,
  Loop % COLUMN_TO_USE_0
  {
    MATRIX_TO_PRINT[A_Index] := COLUMN_TO_USE_%A_Index%
    COLUMN_TO_USE_%A_Index% := ""
  }
  
  ; Getting rid of the global trash.
  COLUMN_TO_USE_0 := "", LINE_TO_USE := ""
  
  Return MATRIX_TO_PRINT
}
Return

CREATE_CODE_39_CHARACTER_TABLE()
{
  Global
  CODE_39_TABLE := Object()
  CODE_39_TABLE[1] := "1,1,0,1,0,0,1,0,1,0,1,1", CODE_39_TABLE[2] := "1,0,1,1,0,0,1,0,1,0,1,1", CODE_39_TABLE[3] := "1,1,0,1,1,0,0,1,0,1,0,1",  CODE_39_TABLE[4] := "1,0,1,0,0,1,1,0,1,0,1,1", CODE_39_TABLE[5] := "1,1,0,1,0,0,1,1,0,1,0,1", CODE_39_TABLE[6] := "1,0,1,1,0,0,1,1,0,1,0,1", CODE_39_TABLE[7] := "1,0,1,0,0,1,0,1,1,0,1,1", CODE_39_TABLE[8] := "1,1,0,1,0,0,1,0,1,1,0,1", CODE_39_TABLE[9] := "1,0,1,1,0,0,1,0,1,1,0,1", CODE_39_TABLE[0] := "1,0,1,0,0,1,1,0,1,1,0,1", CODE_39_TABLE["A"] := "1,1,0,1,0,1,0,0,1,0,1,1", CODE_39_TABLE["B"] := "1,0,1,1,0,1,0,0,1,0,1,1", CODE_39_TABLE["C"] := "1,1,0,1,1,0,1,0,0,1,0,1", CODE_39_TABLE["D"] := "1,0,1,0,1,1,0,0,1,0,1,1", CODE_39_TABLE["E"] := "1,1,0,1,0,1,1,0,0,1,0,1", CODE_39_TABLE["F"] := "1,0,1,1,0,1,1,0,0,1,0,1", CODE_39_TABLE["G"] := "1,0,1,0,1,0,0,1,1,0,1,1", CODE_39_TABLE["H"] := "1,1,0,1,0,1,0,0,1,1,0,1", CODE_39_TABLE["I"] := "1,0,1,1,0,1,0,0,1,1,0,1", CODE_39_TABLE["J"] := "1,0,1,0,1,1,0,0,1,1,0,1", CODE_39_TABLE["K"] := "1,1,0,1,0,1,0,1,0,0,1,1", CODE_39_TABLE["L"] := "1,0,1,1,0,1,0,1,0,0,1,1", CODE_39_TABLE["M"] := "1,1,0,1,1,0,1,0,1,0,0,1", CODE_39_TABLE["N"] := "1,0,1,0,1,1,0,1,0,0,1,1", CODE_39_TABLE["O"] := "1,1,0,1,0,1,1,0,1,0,0,1", CODE_39_TABLE["P"] := "1,0,1,1,0,1,1,0,1,0,0,1", CODE_39_TABLE["Q"] := "1,0,1,0,1,0,1,1,0,0,1,1", CODE_39_TABLE["R"] := "1,1,0,1,0,1,0,1,1,0,0,1", CODE_39_TABLE["S"] := "1,0,1,1,0,1,0,1,1,0,0,1", CODE_39_TABLE["T"] := "1,0,1,0,1,1,0,1,1,0,0,1", CODE_39_TABLE["U"] := "1,1,0,0,1,0,1,0,1,0,1,1", CODE_39_TABLE["V"] := "1,0,0,1,1,0,1,0,1,0,1,1", CODE_39_TABLE["W"] := "1,1,0,0,1,1,0,1,0,1,0,1", CODE_39_TABLE["X"] := "1,0,0,1,0,1,1,0,1,0,1,1", CODE_39_TABLE["Y"] := "1,1,0,0,1,0,1,1,0,1,0,1", CODE_39_TABLE["Z"] := "1,0,0,1,1,0,1,1,0,1,0,1", CODE_39_TABLE[A_Space] := "1,0,0,1,1,0,1,0,1,1,0,1", CODE_39_TABLE["`-"] := "1,0,0,1,0,1,0,1,1,0,1,1", CODE_39_TABLE["`$"] := "1,0,0,1,0,0,1,0,0,1,0,1", CODE_39_TABLE["`%"] := "1,0,1,0,0,1,0,0,1,0,0,1", CODE_39_TABLE["`."] := "1,1,0,0,1,0,1,0,1,1,0,1", CODE_39_TABLE[47] := "1,0,0,1,0,0,1,0,1,0,0,1", CODE_39_TABLE[43] := "1,0,0,1,0,1,0,0,1,0,0,1"
}
Return


;##############################################################################################################
; INTERLEAVED 2 OF 5 (or CODE_ITF) Functions by AlphaBravo (http://ahkscript.org/boards/viewtopic.php?f=6&t=5538&p=32299#p32299)
; INTERLEAVED 2 OF 5 is a 1D Barcode capable of encoding numeric digits (0-9). Although the availability of encodable characters is smaller, 
; the output image is also smaller than some other types of 1D barcode, such as CODE 39. Consider using this type of barcode whenever the 
; upper limits in the dimensions of the output barcode are a critical factor and also whenever numeric digits are all you need to encode. Also, 
; read the note below before choosing this barcode format.
;
; !! NOTE !! 
;
; 1 - An INTERLEAVED 2 OF 5 image can only encode an even number of digits. Odd numebered strings will thus be
; encoded with a leading 0 padded to the left of the string.The code lines to deal with this issue have to be thus scripted by the user accoding
; to each case. 
; 2 - We've had a few problems with some scanners not reading an output ITF barcode whenever it was 4 chars long or shorter 
; than that. The output code was, however, checked to be 100% legit and was succesfully readen by other scanners. It is thereby recommended
; that you test your scanners whenever possible if you expect that encoding such a low number of chars is a possibility for your application.
; 3 - Like any other barcodes, don't forget to add "quiet zones" (light colored areas with reasonable widths) before the first and after the last bar
; of the code.
; 4 - Adding bearer bars to the top and bottom of the output ITF image has been recommended by some fonts to prevent partial readings on 
; long codes.
;##############################################################################################################


;#######################################################################################################
;  GENERATE_CODE_ITF() by AlphaBravo
; How to generate INTERLEAVED CODE 2 OF 5 lines using this library:
; This function takes two parameters: The message you wish to encode and an indicator for whether a checksum should be added 
; to the output code or not. The first parameter should be a string of numeric characters, composed of any combination of the digits 0-9.
; The second parameter is optional and contain a BOOL value (0 or 1) that determines whether to add a checksum to the code or not.
; Although optional in the standard, the checksum has a purpose: it is to make it harder for the scanner to output a misread.
;
; The function defaults to NOT add the checksum.
;
; Although there is no limit to how many characters you can encode using CODE_ITF, do notice that the output image gets bigger quite fast
; as you try to encode a bigger message (not as fast as CODE39, but much faster than a 2D barcode). Since most scanners have a practical
; scanning area limit, it is wise to limit the message size to no more than a few characters whenever possible.
;
; If you absolutely need to encode many characters, consider using a 2D Barcode, such as QR Code instead.
;#########################################################################################################

BARCODER_GENERATE_CODE_ITF(NUMBER_TO_ENCODE, Add_Check_Sum:=0)
{
    if NUMBER_TO_ENCODE is not digit
        return 1
    if Add_Check_Sum
        NUMBER_TO_ENCODE := Add_Check_Sum(NUMBER_TO_ENCODE)
    
    ; http://en.wikipedia.org/wiki/Interleaved_2_of_5
    Codes := "NNWWN,WNNNW,NWNNW,WWNNN,NNWNW,WNWNN,NWWNN,NNNWW,WNNWN,NWNWN"

    ; assign "Narrow/Wide" codes to numbers 0-9
    CODE_ITF_TABLE := [], MATRIX_TO_PRINT := [], BarWidth := []
    for k, v in StrSplit(Codes, ",")
        CODE_ITF_TABLE[A_Index-1] := v

    ; an odd number of digits is encoded by adding a "0" as first digit
    NUMBER_TO_ENCODE := RegExReplace(NUMBER_TO_ENCODE, "^(?=\d(\d\d)*$)", "0")
    
    ; assign bar/space width to number
    for k, v in StrSplit(NUMBER_TO_ENCODE)
    {
        Pair .= v
        if Mod(A_Index,2)
            continue
        Code1 := StrSplit(CODE_ITF_TABLE[SubStr(Pair,1,1)]) ,   Code2 := StrSplit(CODE_ITF_TABLE[SubStr(Pair, 0) ]) , Pair := ""    
        ; interleave bar/space widths, (W/N to bars) and (w/n to spaces)
        loop, 5                                     
            BarWidth.Insert(Code1[A_Index])     ,   BarWidth.Insert(RegExReplace(Code2[A_Index], "(.)", "$L1"))
    }

    for k, v in StrSplit("1010")        ; add prefix
        MATRIX_TO_PRINT.Insert(v)

    ; assign matrix
    for i, v in BarWidth {
        if (v == "W")
            loop, 3
                MATRIX_TO_PRINT.Insert("1")
        else if (v == "N")
            MATRIX_TO_PRINT.Insert("1")
        else if (v == "w")
            loop, 3
                MATRIX_TO_PRINT.Insert("0")
        else if (v == "n")
            MATRIX_TO_PRINT.Insert("0")
    }

    for k, v in StrSplit("11101")       ; add suffix
        MATRIX_TO_PRINT.Insert(v)
    return MATRIX_TO_PRINT
}

Return

Add_Check_Sum(num){ ; http://en.wikipedia.org/wiki/Universal_Product_Code#Check_digits
    for k, v in StrSplit(num)
        if mod(A_Index, 2)
            odd += v
        else
            even += v
    return num . 10-mod(odd*3+even, 10)
}

Return

;##############################################################################################################
; CODE128B Function by AlphaBravo (https://www.autohotkey.com/boards/viewtopic.php?p=93223#p93223)

; CODE128B Barcodes are capable of encoding numeric digits, letters, spaces and many symbols. Although the availability of encodable is limited, it has 
; many more options of characters than CODE39 and CODE_ITF. The output image is also a bit smaller than some other types of 1D barcode, such as CODE 39. 
; Consider using this type of barcode whenever the upper limits in the dimensions of the output barcode are a critical factor and also if your decoder is able to 
; decode this set with reasonable speed and high accuracy. Also, read the note below before choosing this barcode format.
;
; !! NOTE !! 
;
; 1 - Like any other barcodes, don't forget to add "quiet zones" (light colored areas with reasonable widths) before the first and after the last bar
; of the code.
; 2 - Code128B has twice the ammount of possible bar widths than Code39. Higher printing quality may be required with scanned image reading routines!
; Always test the full routine througly.
;##############################################################################################################

;#######################################################################################################
;  BARCODER_GENERATE_CODE_128B() by AlphaBravo
; How to generate CODE128B lines using this library:
; This function takes a single parameters: The message you wish to encode.
; The first parameter should be a string of the characters available for the Code128B set (letters, numbers and some symbols, see https://en.wikipedia.org/wiki/Code_128 
; for a complete table with the available characters - The table is for the B set !!)
; Although there is no limit to how many characters you can encode using CODE_128B, do notice that the output image gets bigger quite fast
; as you try to encode a bigger message (not as fast as CODE39, but much faster than a 2D barcode). Since most scanners have a practical
; scanning area limit, it is wise to limit the message size to no more than a few characters whenever possible or separate it into multiple barcodes.
;#########################################################################################################


BARCODER_GENERATE_CODE_128B(Str){
  data =
  (ltrim
  0	 	11011001100	212222
  1	!	11001101100	222122
  2	"	11001100110	222221
  3	#	10010011000	121223
  4	$	10010001100	121322
  5	`%	10001001100	131222
  6	&	10011001000	122213
  7	'	10011000100	122312
  8	(	10001100100	132212
  9	)	11001001000	221213
  10	*	11001000100	221312
  11	+	11000100100	231212
  12	,	10110011100	112232
  13	-	10011011100	122132
  14	.	10011001110	122231
  15	/	10111001100	113222
  16	0	10011101100	123122
  17	1	10011100110	123221
  18	2	11001110010	223211
  19	3	11001011100	221132
  20	4	11001001110	221231
  21	5	11011100100	213212
  22	6	11001110100	223112
  23	7	11101101110	312131
  24	8	11101001100	311222
  25	9	11100101100	321122
  26	:	11100100110	321221
  27	;	11101100100	312212
  28	<	11100110100	322112
  29	=	11100110010	322211
  30	>	11011011000	212123
  31	?	11011000110	212321
  32	@	11000110110	232121
  33	A	10100011000	111323
  34	B	10001011000	131123
  35	C	10001000110	131321
  36	D	10110001000	112313
  37	E	10001101000	132113
  38	F	10001100010	132311
  39	G	11010001000	211313
  40	H	11000101000	231113
  41	I	11000100010	231311
  42	J	10110111000	112133
  43	K	10110001110	112331
  44	L	10001101110	132131
  45	M	10111011000	113123
  46	N	10111000110	113321
  47	O	10001110110	133121
  48	P	11101110110	313121
  49	Q	11010001110	211331
  50	R	11000101110	231131
  51	S	11011101000	213113
  52	T	11011100010	213311
  53	U	11011101110	213131
  54	V	11101011000	311123
  55	W	11101000110	311321
  56	X	11100010110	331121
  57	Y	11101101000	312113
  58	Z	11101100010	312311
  59	[	11100011010	332111
  60	\	11101111010	314111
  61	]	11001000010	221411
  62	^	11110001010	431111
  63	_	10100110000	111224
  64	``	10100001100	111422
  65	a	10010110000	121124
  66	b	10010000110	121421
  67	c	10000101100	141122
  68	d	10000100110	141221
  69	e	10110010000	112214
  70	f	10110000100	112412
  71	g	10011010000	122114
  72	h	10011000010	122411
  73	i	10000110100	142112
  74	j	10000110010	142211
  75	k	11000010010	241211
  76	l	11001010000	221114
  77	m	11110111010	413111
  78	n	11000010100	241112
  79	o	10001111010	134111
  80	p	10100111100	111242
  81	q	10010111100	121142
  82	r	10010011110	121241
  83	s	10111100100	114212
  84	t	10011110100	124112
  85	u	10011110010	124211
  86	v	11110100100	411212
  87	w	11110010100	421112
  88	x	11110010010	421211
  89	y	11011011110	212141
  90	z	11011110110	214121
  91	{	11110110110	412121
  92	|	10101111000	111143
  93	}	10100011110	111341
  94	~	10001011110	131141
  95	Del	10111101000	114113
  96	FC3	10111100010	114311
  97	FC2	11110101000	411113
  98	Sh	11110100010	411311
  99	CdC	10111011110	113141
  100	CdB	10111101110	114131
  101	CdA	11101011110	311141
  102	FC1	11110101110	411131
  )	
  StartA 	:= "11010000100"	, CheckSumA	:= 103 , CodeA := "11101011110"
  StartB 	:= "11010010000"	, CheckSumB	:= 104 , CodeB := "10111101110"
  StartC 	:= "11010011100"	, CheckSumC	:= 105 , CodeC := "10111011110"
  Stop 	:= "1100011101011"  , Quiet 	:= "000000000000"
  MATRIX_TO_PRINT := [], Pattern := []
  ;-------------------------------------------
  loop, parse, data, `n, `r
    f := StrSplit(A_LoopField, "`t")
    , Pattern[f.1] := f.3
  ;-------------------------------------------
  if (Str~="[a-z]")								; lower case
    CheckSum := CheckSumB, Start := StartB
  else											; not lower case
    CheckSum := CheckSumA, Start := StartA
  if (Str~="^(\d\d)+$")							; Even number of digits
    CheckSum := CheckSumC, Start := StartC
  ;-------------------------------------------	
  STR_TO_PRINT := Quiet . Start
  loop, parse, str
  {		
    STR_TO_PRINT .= Pattern[value := (Start = StartC) ? SubStr(Str, 2*A_Index -1, 2) : Asc(A_LoopField) - 32]
    CheckSum += value * A_Index
  }
  ;-------------------------------------------
  CheckCode := mod(CheckSum, 103)	
  STR_TO_PRINT .= Pattern[CheckCode] . Stop . Quiet
  loop, parse, STR_TO_PRINT
    MATRIX_TO_PRINT.Push(A_LoopField)	
  return MATRIX_TO_PRINT
}

Return

;######################################################################################
;THIRD PARTY FUNCTIONS																														   #
;######################################################################################

;#####################################################################################
; Bin() and Dec() by Infogulch - http://www.autohotkey.com/board/topic/49990-binary-and-decimal-conversion/
; Transforms decimal numbers to and from binary format.
;#####################################################################################


Bin(x){
  while x
    r:=1&x r,x>>=1
  return r
}
Return

Dec(x){
  b:=StrLen(x),r:=0
  loop,parse,x
    r|=A_LoopField<<--b
  return r
}
Return

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。