jchd

How to convert screen capture to lower color depth?

17 posts in this topic

I need to grab selective parts of screenshots (24-bit RGB) and convert them on the fly to either 15-bit RGB (5-5-5) or 16-bit (5-6-5). I need to favor the least CPU intensive way to perform the conversion in order to introduce as little latency as possible.

Context: screenshot will be taken at irregular intervals but often in rapid succession. I want to store them in an SQLite DB and keep the storage as small as possible. Some degradation in color accuracy is no problem. If needed I could even select 8-bit gray level images but I'm afraid this would require more computation than converting to 15-bit RGB.

Anyway, once I get the screenshot captured (I have the working code for that and for saving it) what sequence of operations (_WinAPI_*, _GDIplus_*, ???) should I invoke to perform the bitmap conversion?

I don't ask you code it for me, just tell me the sketch it needs.

I've tried a RGB to 16-bit graylevel routine found somewhere on the forum (uses _GDIplus_ functions) but it seems a bit of a CPU hog and it produces bitmaps containing 3 (identical) graylevel planes, so the space saving is essentially zero.

TIA for your guidance.


This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

As far as I know GDI+ library doesn't offer a good way to reduce color depth. There are a alot of examples to convert an image to greyscale but when you save it it will be automatically a 24-bit image except when you save it as GIF but it will be dithered automatically.

Best way is to use an additional DLL, e.g. Image Magick, FreeImage.

For a code how to convert an image to greyscale (ASM code) you can have a look to AutoIt Windows Screenshooter v1.43 Build 2012-02-16 Final.

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Hi UEZ,

Thanks for the confirmation I'm not (yet) completely dumb by not finding my way doing it.

Yes, I've seen the 8-bit --> 24-bit expansion when saving a greyscale image. I've also seen that it uses GIF dithering by not computing the best palette. I don't much care about GIFs since I don't want palettized formats. JPG, PNG can do it nicely.

It was my hope that you would get attracted by the post title and answer by posting one of the marvelous graphics tricks you master. Since you yourself know of no good way without resorting to some external lib or in-line asm, I guess I'll have to go this route.

Isn't it kind of surprising that within the huge number of image functions Windows carries, low- to high-level, there is no way to perform such a simplistic operation easily. I confess that I'm at complete loss when it comes to code for Win graphics with the number of objects and overlapping concepts that mess uses. I really need to find a good tutorial on Win graphics using Win calls. If you know some, please post them on occasion!

Thanks for advices.

Good night, neighbour.

Edited by jchd

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Bonjour jchd,

I did not investigate much time in this kind of issues because I didn't need it but I digged a litte bit around and it seems to be possible to control also the dithering method with GDI+ -> Bitmap.ConvertFormat method

The problem is that I don't know C/C++ very well and I need to investigate some time to understand it but it seems to be possible to control dithering in combination with GDIp.au3.

Here a small code example how to create bitmap in different formats.

#include <ScreenCapture.au3>

Global Const $GDIP_PXF32CMYK = 0x0000200F
Global Const $GDIP_PXFMAX = 0x00000010

_GDIPlus_Startup()
$iW = 300
$iH = 300
$hHBitmap = _ScreenCapture_Capture("", 0, 0, $iW, $iH) ;24 bit screen capture
$hBitmap_screen = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap) ;convert bitmap format
$hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH, 0, $GDIP_PXF04INDEXED)
$hGfxCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap)
_GDIPlus_GraphicsDrawImage($hGfxCtxt, $hBitmap_screen, 0, 0)
_GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "Test.png")
_WinAPI_DeleteObject($hHBitmap)
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_BitmapDispose($hBitmap_screen)
_GDIPlus_GraphicsDispose($hGfxCtxt)
_GDIPlus_Shutdown()
Exit

; #FUNCTION# ====================================================================================================================
; Name...........: _GDIPlus_BitmapCreateFromScan0
; Description ...: Creates a Bitmap object based on an array of bytes along with size and format information
; Syntax.........: _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight[, $iStride = 0[, $iPixelFormat = 0x0026200A[, $pScan0 = 0]]])
; Parameters ....: $iWidth      - The bitmap width, in pixels
;                  $iHeight     - The bitmap height, in pixels
;                  $iStride     - Integer that specifies the byte offset between the beginning of one scan line and the next. This
;                  +is usually (but not necessarily) the number of bytes in the pixel format (for example, 2 for 16 bits per pixel)
;                  +multiplied by the width of the bitmap. The value passed to this parameter must be a multiple of four
;                  $iPixelFormat - Specifies the format of the pixel data. Can be one of the following:
;                  |$GDIP_PXF01INDEXED   - 1 bpp, indexed
;                  |$GDIP_PXF04INDEXED   - 4 bpp, indexed
;                  |$GDIP_PXF08INDEXED   - 8 bpp, indexed
;                  |$GDIP_PXF16GRAYSCALE - 16 bpp, grayscale
;                  |$GDIP_PXF16RGB555    - 16 bpp; 5 bits for each RGB
;                  |$GDIP_PXF16RGB565    - 16 bpp; 5 bits red, 6 bits green, and 5 bits blue
;                  |$GDIP_PXF16ARGB1555  - 16 bpp; 1 bit for alpha and 5 bits for each RGB component
;                  |$GDIP_PXF24RGB       - 24 bpp; 8 bits for each RGB
;                  |$GDIP_PXF32RGB       - 32 bpp; 8 bits for each RGB. No alpha.
;                  |$GDIP_PXF32ARGB      - 32 bpp; 8 bits for each RGB and alpha
;                  |$GDIP_PXF32PARGB     - 32 bpp; 8 bits for each RGB and alpha, pre-mulitiplied
;                  $pScan0      - Pointer to an array of bytes that contains the pixel data. The caller is responsible for
;                  +allocating and freeing the block of memory pointed to by this parameter.
; Return values .: Success      - Returns a handle to a new Bitmap object
;                  Failure      - 0 and either:
;                  |@error and @extended are set if DllCall failed
;                  |$GDIP_STATUS contains a non zero value specifying the error code
; Remarks .......: After you are done with the object, call _GDIPlus_ImageDispose to release the object resources
; Related .......: _GDIPlus_ImageDispose
; Link ..........; @@MsdnLink@@ GdipCreateBitmapFromScan0
; Example .......; Yes
; ===============================================================================================================================
Func _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight, $iStride = 0, $iPixelFormat = 0x0026200A, $pScan0 = 0)
    Local $aResult = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $iWidth, "int", $iHeight, "int", $iStride, "int", $iPixelFormat, "ptr", $pScan0, "int*", 0)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aResult[6]
EndFunc   ;==>_GDIPlus_BitmapCreateFromScan0

Maybe somebody can help me to convert the MSN code to AutoIt.

Edit: the $PixelFormatIndexed* constants are already included in default GDIPlusConstants.au3 as $GDIP_PXF*.

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

Here another way to create dithered lower color bitmaps ->

Br,

UEZ


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

What is the purpose of the screenshots? Do you need them in a good quality or as small as possible whereas the image is still recognizable?

What dimension you are taking the screenshots?

Br,

UEZ


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

It will be for "manuals" of control applications driving one or more measurement instruments over HP-IB bus (using the VISA standard UDF).

Each control part is a series of radios, check boxes and combos (depends on the measure taken) and will embed images of driven instrument(s) front panel (oscilloscope, frequency analyser, RF generator, other more specialized intruments) taken by webcams.

There will be a large number of such screenshots, varying in size from 1/5 display to 2/3 or full display. Sometimes, the measure implies a sequence of settings sent to the instrument(s) and I need a sequence of shots showing operation, rates range from 0 (manual shot) to (say) 10 Hz. The SQLite DB will store shots and surrounding text in SQL views. All this operates under the control of a master sequencer AutoIt application, which lanches the selected measurement application, drives its settings, takes the screenshots and stores them within distinct SQL tables.

I dont need superb 48-bit per pixel, but it needs to be visually accurate (especially for some instruments front panel display like oscilloscopes). I was expecting to store as full color (RGB555 or 565) which gives good enough results. Ah yes, for some measures, the instrument itself creates virtual pictures available over the HP-IB bus. These are part of the controlling app GUI as well.

As you may have seen in the dev chat thread, tests show that 24-bit JPG compresses much better than 555 or 565 ZLW TIFF, so I'll stick with JPG.


This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

I'm confused. You do not need to use GDI+ and third-party DLL. All you need is CreateDIBSection() function. For example:

#Include <APIConstants.au3>
#Include <WinAPIEx.au3>

$hDesktop = _WinAPI_GetDesktopWindow()
$hDC = _WinAPI_GetDC($hDesktop)
$hMemDC = _WinAPI_CreateCompatibleDC($hDC)
$hBitmap = _WinAPI_CreateDIB(400, 400, 16)
$hMemSv = _WinAPI_SelectObject($hMemDC, $hBitmap)
_WinAPI_BitBlt($hMemDC, 0, 0, 400, 400, $hDC, 0, 0, $SRCCOPY)
_WinAPI_ReleaseDC($hDesktop, $hDC)
_WinAPI_SelectObject($hMemDC, $hMemSv)
_WinAPI_DeleteDC($hMemDC)

$hForm = GUICreate('MyGUI', 400, 400)
GUISetState()

$hDC = _WinAPI_GetDC($hForm)
_WinAPI_DrawBitmap($hDC, 0, 0, $hBitmap)
_WinAPI_ReleaseDC($hForm, $hDC)

Do
Until GUIGetMsg() = -3
Edited by Yashied

Share this post


Link to post
Share on other sites

Ah, that sounds simpler. Sorry for showing lack of understanding but how do you go saving the converted bitmap to file and which format (encoder) will make it smaller.


This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Here an alpha version to create indexed bitmaps (2 - 256 color aka 1, 4, 8 bits) with dithering option. The color quantization is self-developed and thus not perfect.
Currently the indexed bitmaps will be saved as 24-bit images but the color count is always between 2-256 colors as selected.
I'm currently searching for a way how to reduce depth without touching the color table.

When it works properly I will convert it to FreeBasic for better performance respectively will update the _GDIPlus_BitmapApplyFilter_Indexed function which currently used the GDI+ generated color table which is not good.

;v0.2 coded by UEZ build 2017-05-31
#include <Array.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>


Global $sFile = FileOpenDialog("Select an image", "", "Images (*.jpg;*.png;*.bmp)")
If @error Then Exit
_GDIPlus_Startup()
Global $hImage_Test = _GDIPlus_ImageLoadFromFile($sFile)
Global $aDim = _GDIPlus_ImageGetDimension($hImage_Test)
Global $fTimer = TimerInit()
Global $hBitmap_FSD = _GDIPlus_BitmapCreateIndexedColor($hImage_Test)
ConsoleWrite("Runtime: " & Round(TimerDiff($fTimer) / 1000, 2) & " seconds." & @CRLF)
Global $hGUI = GUICreate("GDI+ Test", $aDim[0], $aDim[1])
GUISetBkColor(0xFFFFFF)
GUISetState()

Global $hGfx = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsDrawImageRect($hGfx, $hBitmap_FSD, 0, 0, $aDim[0], $aDim[1])


;~ ShellExecute($sFile)
;~ _GDIPlus_ImageSaveToFile($hBitmap_FSD, @ScriptDir & "\Indexed.png")
;~ ShellExecute(@ScriptDir & "\Indexed.png")


Do
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            _GDIPlus_GraphicsDispose($hGfx)
            _GDIPlus_ImageDispose($hImage_Test)
            _GDIPlus_BitmapDispose($hBitmap_FSD)
            _GDIPlus_Shutdown()
            GUIDelete()
            Exit
    EndSwitch
Until False

Func _GDIPlus_BitmapCreateIndexedColor($hImage, $iAmountColors = 16, $iDitherStrength = 5, $bDither = True) ;v0.2
    Local $aDim = _GDIPlus_ImageGetDimension($hImage)
    If @error Then Return SetError(1, 0, 0)
    Local $iARGB, $iX, $iY, $iW = $aDim[0], $iH = $aDim[1], $r, $g, $b
    ConsoleWrite("Dimension: " & $iW & " x " & $iH & @CRLF)

    Local $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    Local $tPixel = DllStructCreate("uint argb[" & $iW * $iH & "]", $tBitmapData.Scan0)

    Local $hBitmap_Indexed = _GDIPlus_BitmapCreateFromScan0($iW, $iH, $GDIP_PXF32RGB)
    Local $tBitmapData_Indexed = _GDIPlus_BitmapLockBits($hBitmap_Indexed, 0, 0, $iW, $iH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    Local $tPixels_Indexed = DllStructCreate("uint argb[" & $iW * $iH & "]", $tBitmapData_Indexed.Scan0)
    $iAmountColors = ($iAmountColors < 2)  ? 2 : ($iAmountColors > 256) ? 256 : $iAmountColors
    Local $aColorTable[$iAmountColors][5]
    Local $n = 0, $k = 0, $c = 0, $cR = 0, $cG = 0, $cB = 0, $cGrey = 0
    Local $aColors[$iW * $iH][4], $aRed[$iW * $iH], $aGreen[$iW * $iH], $aBlue[$iW * $iH], $aGrey[$iW * $iH]
    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW
        For $iX = 0 To $iW - 1
            $iARGB = $tPixel.argb(($iRowOffset + $iX))
            $r = BitShift(BitAND($iARGB, 0x00FF0000), 16)
            $g = BitShift(BitAND($iARGB, 0x0000FF00), 8)
            $b = BitAND($iARGB, 0x000000FF)
            $aColors[$k][0] = BitAND($iARGB, 0xFFFFFF)
            $aColors[$k][1] = $r
            $aColors[$k][2] = $g
            $aColors[$k][3] = $b
            $k += 1
        Next
    Next
    $aColors = ArrayUnique($aColors)

    ConsoleWrite("Colors: " & UBound($aColors) & @CRLF)

#Region create color table
    For $iY = 0 To UBound($aColors) - 1
        If $aColors[$iY][1] > $aColors[$iY][2] And $aColors[$iY][1] > $aColors[$iY][3] Then
            $aRed[$cR] = $aColors[$iY][0]
            $cR += 1
        EndIf
        If $aColors[$iY][2] > $aColors[$iY][1] And $aColors[$iY][2] > $aColors[$iY][3] Then
            $aGreen[$cG] = $aColors[$iY][0]
            $cG += 1
        EndIf
        If $aColors[$iY][3] > $aColors[$iY][1] And $aColors[$iY][3] > $aColors[$iY][2] Then
            $aBlue[$cB] = $aColors[$iY][0]
            $cB += 1
        EndIf
        If $aColors[$iY][1] = $aColors[$iY][2] And $aColors[$iY][2] = $aColors[$iY][3] Then
            $aGrey[$cGrey] = $aColors[$iY][0]
            $cGrey += 1
        EndIf
    Next
    ReDim $aRed[$cR]
    ReDim $aGreen[$cG]
    ReDim $aBlue[$cB]
    ReDim $aGrey[$cGrey]

    Local $iUBRed = UBound($aRed)
    Local $iUBGreen = UBound($aGreen)
    Local $iUBBlue = UBound($aBlue)
    Local $iUBGrey = UBound($aGrey)
    Local $iSumColor = $iUBRed + $iUBGreen + $iUBBlue + $iUBGrey
    ConsoleWrite("Destination colors: " & $iAmountColors & @CRLF)

;~  _ArraySort($aRed, 0)
;~  _ArraySort($aGreen, 1)
    _ArraySort($aBlue, 0)
    _ArraySort($aGrey, 0)

    Local $aTable[$iAmountColors], $d = 0, $i, $j, $k = 0, $iDir = 0, $p = 1
    $j = Ceiling($iAmountColors * $iUBRed / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    $k += $j
    ConsoleWrite("Red container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBRed Then
        If $j = 1 Then
            $aTable[$d] = $aRed[$iUBRed - 1]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To UBound($aRed) - 1 Step UBound($aRed) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aRed[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Ceiling($iAmountColors * $iUBGreen / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    $k += $j
    ConsoleWrite("Green container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBGreen Then
        If $j = 1 Then
            $aTable[$d] = $aGreen[$iUBGreen - 1]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To UBound($aGreen) - 1 Step UBound($aGreen) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aGreen[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Ceiling($iAmountColors * $iUBBlue / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    $k += $j
    ConsoleWrite("Blue container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBBlue Then
        If $j = 1 Then
            $aTable[$d] = $aBlue[0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = UBound($aBlue) - 1 To 0 Step -UBound($aBlue) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aBlue[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Ceiling($iAmountColors * $iUBGrey / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    If $iAmountColors - $k - $j Then $j += $iAmountColors - $k - $j
    If $j > $iAmountColors - 1 Then $d = $iAmountColors
    ConsoleWrite("Grey container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBGrey Then
        If $j = 1 Then
            $aTable[$d] = $aGrey[0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = UBound($aGrey) - 1 To 0 Step -UBound($aGrey) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aGrey[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf
#EndRegion

    Local $currentPixel, $iR, $iG, $iB, $NearestColor, $errorR, $errorG, $errorB, $f

    $iDitherStrength = ($iDitherStrength < 3) ? 3 : $iDitherStrength
    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW + 1
        For $iX = 0 To $iW - 1
            $currentPixel = $tPixel.argb(($iRowOffset + $iX))
            $iR = BitAND(BitShift($currentPixel, 16), 0xFF)
            $iG = BitAND(BitShift($currentPixel, 8), 0xFF)
            $iB = BitAND($currentPixel, 0xFF)
            $NearestColor = FindNearestColor($currentPixel, $aTable)
            $tPixels_Indexed.argb(($iRowOffset + $iX)) = $NearestColor
            If $bDither Then
                $errorR = $iR - BitAND(BitShift($NearestColor, 16), 0xFF)
                $errorG = $iG - BitAND(BitShift($NearestColor, 8), 0xFF)
                $errorB = $iB - BitAND($NearestColor, 0xFF)
                If ($iX + 1) < ($iW - 1) Then
                    $c = $tPixel.argb($iY * $iW + ($iX + 1)) ;right
                    $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF),  BitShift(($errorR * 7), $iDitherStrength))
                    $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF),   BitShift(($errorG * 7), $iDitherStrength))
                    $iB = PlusTruncate(BitAND($c, 0xFF),                BitShift(($errorB * 7), $iDitherStrength))
                    $tPixel.argb(($iY * $iW) + ($iX + 1)) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                EndIf
                If ($iY + 1) < ($iH - 1) Then
                    If ($iX - 1) > 0 Then
                        $c = $tPixel.argb(($iY + 1) * $iW + $iX - 1) ;left and down
                        $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF),  BitShift(($errorR * 3), $iDitherStrength))
                        $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF),   BitShift(($errorG * 3), $iDitherStrength))
                        $iB = PlusTruncate(BitAND($c, 0xFF),                BitShift(($errorB * 3), $iDitherStrength))
                        $tPixel.argb(($iY + 1) * $iW + $iX - 1) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                    EndIf
                    $c = $tPixel.argb(($iY + 1) * $iW + $iX) ;down
                    $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF),  BitShift(($errorR * 5), $iDitherStrength))
                    $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF),   BitShift(($errorG * 5), $iDitherStrength))
                    $iB = PlusTruncate(BitAND($c, 0xFF),                BitShift(($errorB * 5), $iDitherStrength))
                    $tPixel.argb(($iY + 1) * $iW + $iX) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                    If ($iX + 1) < ($iW - 1) Then
                        $c = $tPixel.argb(($iY + 1) * $iW + $iX + 1) ;right and down
                        $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF),  BitShift($errorR, $iDitherStrength))
                        $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF),   BitShift($errorG, $iDitherStrength))
                        $iB = PlusTruncate(BitAND($c, 0xFF),                BitShift($errorB, $iDitherStrength))
                        $tPixel.argb(($iY + 1) * $iW + $iX + 1) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                    EndIf
                EndIf
            EndIf
        Next
    Next


    _GDIPlus_BitmapUnlockBits($hBitmap_Indexed, $tBitmapData_Indexed)
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    Return $hBitmap_Indexed
EndFunc   ;==>_GDIPlus_BitmapCreateIndexedColor

Func FindNearestColor($iColor, $aColorTable)
    Local $minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 1, $distanceSquared, $c, $bestIndex = 0, $Rdiff, $Gdiff, $Bdiff, $i
    For $i = 0 To UBound($aColorTable) - 1
        $c = $aColorTable[$i]
        $Rdiff = BitAND(BitShift($iColor, 16), 0xFF) - BitAND(BitShift($c, 16), 0xFF)
        $Gdiff = BitAND(BitShift($iColor, 8), 0xFF) - BitAND(BitShift($c, 8), 0xFF)
        $Bdiff = BitAND($iColor, 0xFF) - BitAND($c, 0xFF)
        $distanceSquared = $Rdiff * $Rdiff + $Gdiff * $Gdiff + $Bdiff * $Bdiff
        If $distanceSquared < $minDistanceSquared Then
            $minDistanceSquared = $distanceSquared
            $bestIndex = $i
        EndIf
    Next
    Return $aColorTable[$bestIndex]
EndFunc   ;==>FindNearestColor

Func PlusTruncate($a, $b)
    Return ($a + $b) < 0 ? 0 : ($a + $b) > 255 ? 255 : ($a + $b)
EndFunc

Func ArrayUnique($aArray, $iBase = 0, $oBase = 0, $bCaseSensitive = False)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique because of only 1 element
    Local $dim = UBound($aArray, 2), $y, $sChk
    If $dim Then ;2D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0][0] Then Return SetError(4, 0, 0)
        Local $oD = ObjCreate('Scripting.Dictionary')
        If @error Then Return SetError(5, 0, 0)
        Local $x, $k = $oBase, $l, $s, $aTmp, $flag, $sSep = Chr(01)
        Local $aUnique[UBound($aArray)][$dim]
        If Not $oBase Then $flag = 2
        For $y = $iBase To UBound($aArray) - 1
            For $x = 0 To $dim - 1
                $s &= $aArray[$y][$x] & $sSep
            Next
            If $bCaseSensitive Then
                $sChk = $s
            Else
                $sChk = StringLower($s)
            EndIf
            If Not $oD.Exists($sChk) And StringLen($s) > 3 Then
                $oD.Add($sChk, $y)
                $aTmp = StringSplit(StringTrimRight($s, 1), $sSep, 2)
                For $l = 0 To $dim - 1
                    If $l Then
                        $aUnique[$k][$l] = Int($aTmp[$l])
                    Else
                        $aUnique[$k][$l] = ($aTmp[$l])
                    EndIf
                Next
                $k += 1
            EndIf
            $s = ""
        Next
        $oD.RemoveAll
        $oD = ""
        If $k > 0 Then
            If $oBase Then $aUnique[0][0] = $k - 1
            ReDim $aUnique[$k][$dim]
        Else
            ReDim $aUnique[1][$dim]
        EndIf
    Else ;1D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0] Then Return SetError(4, 0, 0)
        Local $sData = '', $sSep = ChrW(160), $flag
        For $y = $iBase To UBound($aArray) - 1
            If Not IsDeclared($aArray[$y] & '$') Then
                Assign($aArray[$y] & '$', 0, 1)
                $sData &= $aArray[$y] & $sSep
            EndIf
        Next
        If Not $oBase Then $flag = 2
        Local $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep, $flag)
    EndIf
    Return $aUnique
EndFunc   ;==>ArrayUnique

 

Don't test it with higher resolution bitmaps. The higher the resolution the more time it will take to generate. Same for the amount of colors. The higher the destination color amount the higher it will take to generate.

 

Edit: v0.3 now with option to save bitmap as 1, 4 or 8 bit. 

;v0.3 coded by UEZ build 2017-05-31
#AutoIt3Wrapper_UseX64=n
#AutoIt3Wrapper_Version=b
#include <Array.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>


Global $sFile = FileOpenDialog("Select an image", "", "Images (*.jpg;*.png;*.bmp)")
If @error Then Exit
_GDIPlus_Startup()
Global $hImage_Test = _GDIPlus_ImageLoadFromFile($sFile)
Global $aDim = _GDIPlus_ImageGetDimension($hImage_Test)
Global $iColors = 16
Global $fTimer = TimerInit()
Global $hBitmap_FSD = _GDIPlus_BitmapCreateIndexedColor($hImage_Test, $iColors)
ConsoleWrite("Runtime: " & Round(TimerDiff($fTimer) / 1000, 2) & " seconds." & @CRLF)

Global $iW = $aDim[0], $iH = $aDim[1]
Global $hGUI = GUICreate("GDI+ Test", $iW, $iH)
GUISetBkColor(0xFFFFFF)
GUISetState()


Global $hGfx = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsDrawImageRect($hGfx, $hBitmap_FSD, 0, 0, $iW, $iH)

;~ ShellExecute($sFile)
;~ _GDIPlus_ImageSaveToFile($hBitmap_FSD, @ScriptDir & "\Indexed.png")
;~ ShellExecute(@ScriptDir & "\Indexed.png")


Do
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            _GDIPlus_GraphicsDispose($hGfx)
            _GDIPlus_ImageDispose($hImage_Test)
            _GDIPlus_BitmapDispose($hBitmap_FSD)
            _GDIPlus_Shutdown()
            GUIDelete()
            Exit
    EndSwitch
Until False

Func _GDIPlus_BitmapCreateIndexedColor($hImage, $iAmountColors = 16, $iDitherStrength = 5, $bDither = True, $bConvert = True) ;v0.3
    Local $aDim = _GDIPlus_ImageGetDimension($hImage)
    If @error Then Return SetError(1, 0, 0)
    Local $iARGB, $iX, $iY, $iW = $aDim[0], $iH = $aDim[1], $r, $g, $b
    ConsoleWrite("Dimension: " & $iW & " x " & $iH & @CRLF)

    Local $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    Local $tPixel = DllStructCreate("uint argb[" & $iW * $iH & "]", $tBitmapData.Scan0)

    Local $hBitmap_Indexed = _GDIPlus_BitmapCreateFromScan0($iW, $iH, $GDIP_PXF32RGB)
    Local $tBitmapData_Indexed = _GDIPlus_BitmapLockBits($hBitmap_Indexed, 0, 0, $iW, $iH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    Local $tPixels_Indexed = DllStructCreate("uint argb[" & $iW * $iH & "]", $tBitmapData_Indexed.Scan0)
    $iAmountColors = ($iAmountColors < 2) ? 2 : ($iAmountColors > 256) ? 256 : $iAmountColors
    Local $aColorTable[$iAmountColors][5]
    Local $n = 0, $k = 0, $c = 0, $cR = 0, $cG = 0, $cB = 0, $cGrey = 0
    Local $aColors[$iW * $iH][4], $aRed[$iW * $iH], $aGreen[$iW * $iH], $aBlue[$iW * $iH], $aGrey[$iW * $iH]
    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW
        For $iX = 0 To $iW - 1
            $iARGB = $tPixel.argb(($iRowOffset + $iX))
            $r = BitShift(BitAND($iARGB, 0x00FF0000), 16)
            $g = BitShift(BitAND($iARGB, 0x0000FF00), 8)
            $b = BitAND($iARGB, 0x000000FF)
            $aColors[$k][0] = BitAND($iARGB, 0xFFFFFF)
            $aColors[$k][1] = $r
            $aColors[$k][2] = $g
            $aColors[$k][3] = $b
            $k += 1
        Next
    Next
    $aColors = ArrayUnique($aColors)

    ConsoleWrite("Unique colors: " & UBound($aColors) & @CRLF)

    #Region create color table
    For $iY = 0 To UBound($aColors) - 1
        If $aColors[$iY][1] > $aColors[$iY][2] And $aColors[$iY][1] > $aColors[$iY][3] Then
            $aRed[$cR] = $aColors[$iY][0]
            $cR += 1
        EndIf
        If $aColors[$iY][2] > $aColors[$iY][1] And $aColors[$iY][2] > $aColors[$iY][3] Then
            $aGreen[$cG] = $aColors[$iY][0]
            $cG += 1
        EndIf
        If $aColors[$iY][3] > $aColors[$iY][1] And $aColors[$iY][3] > $aColors[$iY][2] Then
            $aBlue[$cB] = $aColors[$iY][0]
            $cB += 1
        EndIf
        If $aColors[$iY][1] = $aColors[$iY][2] And $aColors[$iY][2] = $aColors[$iY][3] Then
            $aGrey[$cGrey] = $aColors[$iY][0]
            $cGrey += 1
        EndIf
    Next
    ReDim $aRed[$cR]
    ReDim $aGreen[$cG]
    ReDim $aBlue[$cB]
    ReDim $aGrey[$cGrey]

    Local $iUBRed = UBound($aRed)
    Local $iUBGreen = UBound($aGreen)
    Local $iUBBlue = UBound($aBlue)
    Local $iUBGrey = UBound($aGrey)
    Local $iSumColor = $iUBRed + $iUBGreen + $iUBBlue + $iUBGrey
    ConsoleWrite("Destination colors: " & $iAmountColors & @CRLF)

;~  _ArraySort($aRed, 0)
;~  _ArraySort($aGreen, 1)
    _ArraySort($aBlue, 0)
    _ArraySort($aGrey, 0)

    Local $aTable[$iAmountColors], $d = 0, $i, $j, $k = 0, $iDir = 0, $p = 1
    $j = Ceiling($iAmountColors * $iUBRed / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    $k += $j
    ConsoleWrite("Red container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBRed Then
        If $j = 1 Then
            $aTable[$d] = $aRed[$iUBRed - 1]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To UBound($aRed) - 1 Step UBound($aRed) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aRed[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Ceiling($iAmountColors * $iUBGreen / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    $k += $j
    ConsoleWrite("Green container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBGreen Then
        If $j = 1 Then
            $aTable[$d] = $aGreen[$iUBGreen - 1]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To UBound($aGreen) - 1 Step UBound($aGreen) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aGreen[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Ceiling($iAmountColors * $iUBBlue / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    $k += $j
    ConsoleWrite("Blue container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBBlue Then
        If $j = 1 Then
            $aTable[$d] = $aBlue[0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = UBound($aBlue) - 1 To 0 Step -UBound($aBlue) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aBlue[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Ceiling($iAmountColors * $iUBGrey / $iSumColor) - 1
    $j = $j < 1 ? 1 : $j
    If $iAmountColors - $k - $j Then $j += $iAmountColors - $k - $j
    If $j > $iAmountColors - 1 Then $d = $iAmountColors
    ConsoleWrite("Grey container: " & $j & @CRLF)
    If $d < $iAmountColors And $iUBGrey Then
        If $j = 1 Then
            $aTable[$d] = $aGrey[0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = UBound($aGrey) - 1 To 0 Step -UBound($aGrey) / ($j * $p)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aGrey[$i]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf
    #EndRegion create color table

    Local $currentPixel, $iR, $iG, $iB, $NearestColor, $errorR, $errorG, $errorB, $f

    $iDitherStrength = ($iDitherStrength < 3) ? 3 : $iDitherStrength
    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW + 1
        For $iX = 0 To $iW - 1
            $currentPixel = $tPixel.argb(($iRowOffset + $iX))
            $iR = BitAND(BitShift($currentPixel, 16), 0xFF)
            $iG = BitAND(BitShift($currentPixel, 8), 0xFF)
            $iB = BitAND($currentPixel, 0xFF)
            $NearestColor = FindNearestColor($currentPixel, $aTable)
            $tPixels_Indexed.argb(($iRowOffset + $iX)) = $NearestColor
            If $bDither Then
                $errorR = $iR - BitAND(BitShift($NearestColor, 16), 0xFF)
                $errorG = $iG - BitAND(BitShift($NearestColor, 8), 0xFF)
                $errorB = $iB - BitAND($NearestColor, 0xFF)
                If ($iX + 1) < ($iW - 1) Then
                    $c = $tPixel.argb($iY * $iW + ($iX + 1)) ;right
                    $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF), BitShift(($errorR * 7), $iDitherStrength))
                    $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF), BitShift(($errorG * 7), $iDitherStrength))
                    $iB = PlusTruncate(BitAND($c, 0xFF), BitShift(($errorB * 7), $iDitherStrength))
                    $tPixel.argb(($iY * $iW) + ($iX + 1)) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                EndIf
                If ($iY + 1) < ($iH - 1) Then
                    If ($iX - 1) > 0 Then
                        $c = $tPixel.argb(($iY + 1) * $iW + $iX - 1) ;left and down
                        $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF), BitShift(($errorR * 3), $iDitherStrength))
                        $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF), BitShift(($errorG * 3), $iDitherStrength))
                        $iB = PlusTruncate(BitAND($c, 0xFF), BitShift(($errorB * 3), $iDitherStrength))
                        $tPixel.argb(($iY + 1) * $iW + $iX - 1) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                    EndIf
                    $c = $tPixel.argb(($iY + 1) * $iW + $iX) ;down
                    $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF), BitShift(($errorR * 5), $iDitherStrength))
                    $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF), BitShift(($errorG * 5), $iDitherStrength))
                    $iB = PlusTruncate(BitAND($c, 0xFF), BitShift(($errorB * 5), $iDitherStrength))
                    $tPixel.argb(($iY + 1) * $iW + $iX) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                    If ($iX + 1) < ($iW - 1) Then
                        $c = $tPixel.argb(($iY + 1) * $iW + $iX + 1) ;right and down
                        $iR = PlusTruncate(BitAND(BitShift($c, 16), 0xFF), BitShift($errorR, $iDitherStrength))
                        $iG = PlusTruncate(BitAND(BitShift($c, 8), 0xFF), BitShift($errorG, $iDitherStrength))
                        $iB = PlusTruncate(BitAND($c, 0xFF), BitShift($errorB, $iDitherStrength))
                        $tPixel.argb(($iY + 1) * $iW + $iX + 1) = BitShift($iR, -16) + BitShift($iG, -8) + $iB
                    EndIf
                EndIf
            EndIf
        Next
    Next


    _GDIPlus_BitmapUnlockBits($hBitmap_Indexed, $tBitmapData_Indexed)
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)

    If Not $bConvert Then Return $hBitmap_Indexed

    ;
    Local $tColorTable = DllStructCreate("dword colors[" & $iAmountColors & "]")
    For $i = 0 To UBound($aTable) - 1
        $tColorTable.colors(($i + 1)) = $aTable[$i]
    Next
    Local $iBpP = 8
    Switch $iAmountColors
        Case 2
            $iBpP = 1
        Case 3 To 16
            $iBpP = 4
    EndSwitch

    Local Const $hBitmap_Result = __WinAPI_CreateDIBExt($hBitmap_Indexed, $iW, $iH, $iBpP, $tColorTable, $iAmountColors)

    Return $hBitmap_Result
EndFunc   ;==>_GDIPlus_BitmapCreateIndexedColor

Func FindNearestColor($iColor, $aColorTable)
    Local $minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 1, $distanceSquared, $c, $bestIndex = 0, $Rdiff, $Gdiff, $Bdiff, $i
    For $i = 0 To UBound($aColorTable) - 1
        $c = $aColorTable[$i]
        $Rdiff = BitAND(BitShift($iColor, 16), 0xFF) - BitAND(BitShift($c, 16), 0xFF)
        $Gdiff = BitAND(BitShift($iColor, 8), 0xFF) - BitAND(BitShift($c, 8), 0xFF)
        $Bdiff = BitAND($iColor, 0xFF) - BitAND($c, 0xFF)
        $distanceSquared = $Rdiff * $Rdiff + $Gdiff * $Gdiff + $Bdiff * $Bdiff
        If $distanceSquared < $minDistanceSquared Then
            $minDistanceSquared = $distanceSquared
            $bestIndex = $i
        EndIf
    Next
    Return $aColorTable[$bestIndex]
EndFunc   ;==>FindNearestColor

Func PlusTruncate($a, $b)
    Return ($a + $b) < 0 ? 0 : ($a + $b) > 255 ? 255 : ($a + $b)
EndFunc   ;==>PlusTruncate


Func __WinAPI_CreateDIBExt($hBitmap_Indexed, $iWidth, $iHeight, $iBitsPerPel = 8, $tColorTable = 0, $iColorCount = 0)
    Local $tBITMAPINFO, $RGBQ[2], $hBitmap, $Colors
    Switch $iBitsPerPel
        Case 1
            $Colors = 2
        Case 4
            $Colors = 16
        Case 8
            $Colors = 256
        Case Else
            $Colors = 0
    EndSwitch
    If $Colors Then
        If Not IsDllStruct($tColorTable) Then
            Switch $iBitsPerPel
                Case 1
                    $RGBQ[0] = 0
                    $RGBQ[1] = 0xFFFFFF
                    $tColorTable = _WinAPI_CreateDIBColorTable($RGBQ)
                Case Else

            EndSwitch
        Else
            If $Colors > $iColorCount Then
                $Colors = $iColorCount
            EndIf
            If (Not $Colors) Or ((4 * $Colors) > DllStructGetSize($tColorTable)) Then
                Return SetError(1, 0, 0)
            EndIf
        EndIf
        $RGBQ = 'dword[' & $Colors & ']'
    Else
        $RGBQ = ''
    EndIf

    $tBITMAPINFO = DllStructCreate('dword biSize;long biWidth;long biHeight;ushort biPlanes;ushort biBitCount;dword biCompression;dword biSizeImage;long biXPelsPerMeter;long biYPelsPerMeter;dword biClrUsed;dword biClrImportant;' & $RGBQ)
    If @error Then
        Return SetError(2, 0, 0)
    EndIf
    DllStructSetData($tBITMAPINFO, 'biSize', 40)
    DllStructSetData($tBITMAPINFO, 'biWidth', $iWidth)
    DllStructSetData($tBITMAPINFO, 'biHeight', $iHeight)
    DllStructSetData($tBITMAPINFO, 'biPlanes', 1)
    DllStructSetData($tBITMAPINFO, 'biBitCount', $iBitsPerPel)
    DllStructSetData($tBITMAPINFO, 'biCompression', 0)
    DllStructSetData($tBITMAPINFO, 'biSizeImage', 0)
    DllStructSetData($tBITMAPINFO, 'biXPelsPerMeter', 0)
    DllStructSetData($tBITMAPINFO, 'biYPelsPerMeter', 0)
    DllStructSetData($tBITMAPINFO, 'biClrUsed', $Colors)
    DllStructSetData($tBITMAPINFO, 'biClrImportant', 0)
    If $Colors Then
        If IsDllStruct($tColorTable) Then
            _WinAPI_MoveMemory(DllStructGetPtr($tBITMAPINFO) + 40, DllStructGetPtr($tColorTable), 4 * $Colors)
        Else
            _WinAPI_ZeroMemory(DllStructGetPtr($tBITMAPINFO) + 40, 4 * $Colors)
        EndIf
    EndIf
    Local $pBits
    $hBitmap = _WinAPI_CreateDIBSection(0, $tBITMAPINFO, 0, $pBits)
    If @error Then
        Return SetError(3, 0, 0)
    EndIf

    Local Const $hDC_backbuffer = _WinAPI_CreateCompatibleDC(0)
    Local Const $DC_obj = _WinAPI_SelectObject($hDC_backbuffer, $hBitmap)
    Local Const $hCanvas = _GDIPlus_GraphicsCreateFromHDC($hDC_backbuffer)
    _GDIPlus_GraphicsSetInterpolationMode($hCanvas, 5)
    _GDIPlus_GraphicsDrawImageRect($hCanvas, $hBitmap_Indexed, 0, 0, $iWidth, $iHeight)

    Local Const $hBitmap_Result = _GDIPlus_BitmapCreateFromGdiDib($tBITMAPINFO, $pBits)

;~  _WinAPI_DeleteObject($hBitmap)
    _GDIPlus_GraphicsDispose($hCanvas)
    _WinAPI_SelectObject($hDC_backbuffer, $DC_obj)
    _WinAPI_DeleteDC($hDC_backbuffer)

    Return $hBitmap_Result
EndFunc   ;==>__WinAPI_CreateDIBExt

Func _GDIPlus_BitmapCreateFromGdiDib($tBITMAPINFO, $pBits)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipCreateBitmapFromGdiDib", "struct*", $tBITMAPINFO, "ptr", $pBits, "ptr*", 0)
    If @error Then Return SetError(@error, @extended, 0)
    If $aResult[0] Then Return SetError(10, $aResult[0], 0)
    Return $aResult[3]
EndFunc   ;==>_GDIPlus_BitmapCreateFromGdiDib

Func ArrayUnique($aArray, $iBase = 0, $oBase = 0, $bCaseSensitive = False)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique because of only 1 element
    Local $dim = UBound($aArray, 2), $y, $sChk
    If $dim Then ;2D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0][0] Then Return SetError(4, 0, 0)
        Local $oD = ObjCreate('Scripting.Dictionary')
        If @error Then Return SetError(5, 0, 0)
        Local $x, $k = $oBase, $l, $s, $aTmp, $flag, $sSep = Chr(01)
        Local $aUnique[UBound($aArray)][$dim]
        If Not $oBase Then $flag = 2
        For $y = $iBase To UBound($aArray) - 1
            For $x = 0 To $dim - 1
                $s &= $aArray[$y][$x] & $sSep
            Next
            If $bCaseSensitive Then
                $sChk = $s
            Else
                $sChk = StringLower($s)
            EndIf
            If Not $oD.Exists($sChk) And StringLen($s) > 3 Then
                $oD.Add($sChk, $y)
                $aTmp = StringSplit(StringTrimRight($s, 1), $sSep, 2)
                For $l = 0 To $dim - 1
                    If $l Then
                        $aUnique[$k][$l] = Int($aTmp[$l])
                    Else
                        $aUnique[$k][$l] = ($aTmp[$l])
                    EndIf
                Next
                $k += 1
            EndIf
            $s = ""
        Next
        $oD.RemoveAll
        $oD = ""
        If $k > 0 Then
            If $oBase Then $aUnique[0][0] = $k - 1
            ReDim $aUnique[$k][$dim]
        Else
            ReDim $aUnique[1][$dim]
        EndIf
    Else ;1D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0] Then Return SetError(4, 0, 0)
        Local $sData = '', $sSep = ChrW(160), $flag
        For $y = $iBase To UBound($aArray) - 1
            If Not IsDeclared($aArray[$y] & '$') Then
                Assign($aArray[$y] & '$', 0, 1)
                $sData &= $aArray[$y] & $sSep
            EndIf
        Next
        If Not $oBase Then $flag = 2
        Local $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep, $flag)
    EndIf
    Return $aUnique
EndFunc   ;==>ArrayUnique

Sometimes the amount of colors set differs between 24-bit and reduced bitmap format.

 

Edit2: v0.6

;v0.6 coded by UEZ build 2017-06-20
#AutoIt3Wrapper_UseX64=n
#AutoIt3Wrapper_Version=b
#include <Array.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>


Global $sFile = FileOpenDialog("Select an image", "", "Images (*.jpg;*.png;*.bmp)")
If @error Then Exit
_GDIPlus_Startup()
Global $hImage_Test = _GDIPlus_ImageLoadFromFile($sFile)
Global $aDim = _GDIPlus_ImageGetDimension($hImage_Test)
Global $iColors = 16
Global $fTimer = TimerInit()
Global $hBitmap_FSD = _GDIPlus_BitmapCreateIndexedColor($hImage_Test, $iColors)
ConsoleWrite("Runtime: " & Round(TimerDiff($fTimer) / 1000, 2) & " seconds." & @CRLF)

Global $iW = $aDim[0], $iH = $aDim[1]
Global $hGUI = GUICreate("GDI+ Test", $iW, $iH)
GUISetBkColor(0xFFFFFF)
GUISetState()


Global $hGfx = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsDrawImageRect($hGfx, $hBitmap_FSD, 0, 0, $iW, $iH)

ShellExecute($sFile)
_GDIPlus_ImageSaveToFile($hBitmap_FSD, @ScriptDir & "\Indexed.png")
ShellExecute(@ScriptDir & "\Indexed.png")


Do
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            _GDIPlus_GraphicsDispose($hGfx)
            _GDIPlus_ImageDispose($hImage_Test)
            _GDIPlus_BitmapDispose($hBitmap_FSD)
            _GDIPlus_Shutdown()
            GUIDelete()
            Exit
    EndSwitch
Until False

Func _GDIPlus_BitmapCreateIndexedColor($hImage, $iAmountColors = 16, $iDitherType = 4, $bDither = True)
    Local $aDim = _GDIPlus_ImageGetDimension($hImage)
    If @error Then Return SetError(1, 0, 0)
    Local $iRGB, $iX, $iY, $iW = $aDim[0], $iH = $aDim[1], $r, $g, $b
    ConsoleWrite("Dimension: " & $iW & " x " & $iH & @CRLF)

    Local $iAverageColor = _ASM_BitmapGetAverageColorValue($hImage), $iAA, $iAR, $iAG, $iAB
    $iAA = BitShift(BitAND($iAverageColor, 0xFF000000), 24)
    $iAR = BitShift(BitAND($iAverageColor, 0x00FF0000), 16)
    $iAG = BitShift(BitAND($iAverageColor, 0x0000FF00), 8)
    $iAB = BitAND($iAverageColor, 0x000000FF)
    ConsoleWrite("Average color: " & Hex($iAverageColor, 8) & @CRLF)

    Local $iW2 = 16, $iH2 = 16
    If $iAmountColors > 192 Then
        $iW2 = 24
        $iH2 = 24
    EndIf
    Local $hImage_Resized = _GDIPlus_ImageResize($hImage, $iW2, $iH2, 2)

    Local $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    Local $tPixel = DllStructCreate("uint argb[" & $iW * $iH & "]", $tBitmapData.Scan0)

    Local $hBitmap_Indexed = _GDIPlus_BitmapCreateFromScan0($iW, $iH, $GDIP_PXF32RGB)
    Local $tBitmapData_Indexed = _GDIPlus_BitmapLockBits($hBitmap_Indexed, 0, 0, $iW, $iH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    Local $tPixels_Indexed = DllStructCreate("uint argb[" & $iW * $iH & "]", $tBitmapData_Indexed.Scan0)

    $iAmountColors = ($iAmountColors < 2)  ? 2 : ($iAmountColors > 256) ? 256 : $iAmountColors
    Local $aColorTable[$iAmountColors][5]
    Local $n = 0, $k = 0, $c, $d, $e, $f, $cR = 0, $cG = 0, $cB = 0, $cGrey = 0
    Local $aColors[$iW * $iH][6], $aColors_tmp[$iW2 * $iH2][5], $aRed[$iW * $iH][3], $aGreen[$iW * $iH][3], $aBlue[$iW * $iH][3], $aGrey[$iW * $iH][3]



    Local $tBitmapData_Resized = _GDIPlus_BitmapLockBits($hImage_Resized, 0, 0, $iW2, $iH2, $GDIP_ILMREAD, $GDIP_PXF32RGB)
    Local $tPixel_Resized = DllStructCreate("uint argb[" & $iW2 * $iH2 & "]", $tBitmapData_Resized.Scan0)

    For $iY = 0 To $iH2 - 1
        $iRowOffset = $iY * $iW2 + 1
        For $iX = 0 To $iW2 - 1
            $iRGB = $tPixel_Resized.argb(($iRowOffset + $iX))
            $r = BitShift(BitAND($iRGB, 0x00FF0000), 16)
            $g = BitShift(BitAND($iRGB, 0x0000FF00), 8)
            $b = BitAND($iRGB, 0x000000FF)
            $aColors_tmp[$k][0] = BitAND($iRGB, 0xFFFFFF)
            $aColors_tmp[$k][1] = $r
            $aColors_tmp[$k][2] = $g
            $aColors_tmp[$k][3] = $b
            $k += 1
        Next
    Next
    _ArraySort($aColors_tmp)

    _GDIPlus_BitmapUnlockBits($hImage_Resized, $tBitmapData_Resized)
    _GDIPlus_ImageDispose($hImage_Resized)

    $c = 0
    $d = 1
    $e = 0
    $f = 1
    While True
        If $d < UBound($aColors_tmp) Then
            If $aColors_tmp[$c][0] = $aColors_tmp[$d][0] Then
                $d += 1
                $f += 1
            Else
                For $x = 0 To 3
                    $aColors[$e][$x] = $aColors_tmp[$c][$x]
                Next
                $aColors[$e][4] = $f
                $aColors[$e][5] = Round(Sqrt(($iAR - $aColors[$e][1]) * ($iAR - $aColors[$e][1]) + ($iAG - $aColors[$e][2]) * ($iAG - $aColors[$e][2]) + ($iAB - $aColors[$e][3]) * ($iAB - $aColors[$e][3])), 0)
                $e += 1
                $c = $d
                $d += 1
                $f = 1
            EndIf
        Else
            ExitLoop
        EndIf
    WEnd
    If $aColors_tmp[$c][0] = $aColors_tmp[$c - 1][0] Then
        $d += 1
        $f += 1
    Else
        For $x = 0 To 3
            $aColors[$e][$x] = $aColors_tmp[$c][$x]
        Next
        $aColors[$e][4] = $f
        $aColors[$e][5] = Round(Sqrt(($iAR - $aColors[$e][1]) * ($iAR - $aColors[$e][1]) + ($iAG - $aColors[$e][2]) * ($iAG - $aColors[$e][2]) + ($iAB - $aColors[$e][3]) * ($iAB - $aColors[$e][3])), 0)
    EndIf
    ReDim $aColors[$e + 1][6]

    ConsoleWrite("Colors: " & UBound($aColors) & @CRLF)
    _ArraySort($aColors, 0, 0, 0, 5)


#Region create color table
    For $iY = 0 To UBound($aColors) - 1
        If $aColors[$iY][1] > $aColors[$iY][2] And $aColors[$iY][1] > $aColors[$iY][3] Then
            $aRed[$cR][0] = $aColors[$iY][0]
            $aRed[$cR][1] = $aColors[$iY][4]
            $aRed[$cR][2] = $aColors[$iY][5]
            $cR += 1
        EndIf
        If $aColors[$iY][2] > $aColors[$iY][1] And $aColors[$iY][2] > $aColors[$iY][3] Then
            $aGreen[$cG][0] = $aColors[$iY][0]
            $aGreen[$cG][1] = $aColors[$iY][4]
            $aGreen[$cG][2] = $aColors[$iY][5]
            $cG += 1
        EndIf
        If $aColors[$iY][3] > $aColors[$iY][1] And $aColors[$iY][3] > $aColors[$iY][2] Then
            $aBlue[$cB][0] = $aColors[$iY][0]
            $aBlue[$cB][1] = $aColors[$iY][4]
            $aBlue[$cB][2] = $aColors[$iY][5]
            $cB += 1
        EndIf
        If $aColors[$iY][1] = $aColors[$iY][2] And $aColors[$iY][2] = $aColors[$iY][3] Then
            $aGrey[$cGrey][0] = $aColors[$iY][0]
            $aGrey[$cGrey][1] = $aColors[$iY][4]
            $aGrey[$cGrey][2] = $aColors[$iY][5]
            $cGrey += 1
        EndIf
    Next
    ReDim $aRed[$cR][3]
    ReDim $aGreen[$cG][3]
    ReDim $aBlue[$cB][3]
    ReDim $aGrey[$cGrey][3]
    Local $iUBRed = UBound($aRed)
    Local $iUBGreen = UBound($aGreen)
    Local $iUBBlue = UBound($aBlue)
    Local $iUBGrey = UBound($aGrey)
    Local $iSumColor = $iUBRed + $iUBGreen + $iUBBlue + $iUBGrey
    ConsoleWrite("Destination colors: " & $iAmountColors & @CRLF)
    Local $aColorCounts[4][2] = [[$iUBRed, "Red"], [$iUBGreen, "Green"], [$iUBBlue, "Blue"], [$iUBGrey, "Grey"]]
    _ArraySort($aColorCounts, 1, 0, 0, 0)

;~  Switch $aColorCounts[0][1]
;~      Case "Red"
;~          _ArraySort($aGreen, 1, 0, 0, 2)
;~          _ArraySort($aBlue, 1, 0, 0, 2)
;~          _ArraySort($aGrey, 1, 0, 0, 2)
;~      Case "Green"
;~          _ArraySort($aRed, 1, 0, 0, 2)
;~          _ArraySort($aBlue, 1, 0, 0, 2)
;~          _ArraySort($aGrey, 1, 0, 0, 2)
;~      Case "Blue"
;~          _ArraySort($aRed, 1, 0, 0, 2)
;~          _ArraySort($aGreen, 1, 0, 0, 2)
;~          _ArraySort($aGrey, 1, 0, 0, 2)
;~      Case Else
;~          _ArraySort($aRed, 1, 0, 0, 2)
;~          _ArraySort($aGreen, 1, 0, 0, 2)
;~          _ArraySort($aBlue, 1, 0, 0, 2)
;~  EndSwitch

;~  Switch $aColorCounts[0][1]
;~      Case "Red"
;~          _ArraySort($aRed, 1, 0, 0, 2)
;~      Case "Green"
;~          _ArraySort($aGreen, 1, 0, 0, 2)
;~      Case "Blue"
;~          _ArraySort($aBlue, 1, 0, 0, 2)
;~      Case Else
;~          _ArraySort($aGrey, 1, 0, 0, 2)
;~  EndSwitch

    _ArraySort($aRed, 1, 0, 0, 2)
    _ArraySort($aGreen, 1, 0, 0, 2)
    _ArraySort($aBlue, 1, 0, 0, 2)
    _ArraySort($aGrey, 1, 0, 0, 2)

;~  _ArrayDisplayMod($aRed)
;~  _ArrayDisplayMod($aGreen)
;~  _ArrayDisplayMod($aBlue)
;~  _ArrayDisplayMod($aGrey)

    Local $aTable[$iAmountColors], $d = 0, $i, $j, $k = 0, $iDir = 0, $p = $iAmountColors / 2
    $j = Floor($iAmountColors * $iUBRed / $iSumColor + 0.5)
    $k += $j
    ConsoleWrite("Red: " & $j & @CRLF)
    Local $rr = Int($j / ($iAmountColors * $p)) + 0.95

    If $d < $iAmountColors And UBound($aRed) Then
        If $j = 1 Then
            $aTable[$d] = $aRed[0][0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To $iUBRed - 1 Step $iUBRed / ($j * $rr)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aRed[$i][0]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Floor($iAmountColors * $iUBGreen / $iSumColor + 0.5)
    $k += $j
    ConsoleWrite("Green: " & $j & @CRLF)
    Local $gg = Int($j / ($iAmountColors * $p)) + 1
    If $d < $iAmountColors And UBound($aGreen) Then
        If $j = 1 Then
            $aTable[$d] = $aGreen[0][0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To $iUBGreen - 1 Step $iUBGreen / ($j * $gg)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aGreen[$i][0]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Floor($iAmountColors * $iUBBlue / $iSumColor + 0.5)
    $k += $j
    ConsoleWrite("Blue: " & $j & @CRLF)
    Local $bb = Int($j / ($iAmountColors * $p)) + 1
    If $d < $iAmountColors And UBound($aBlue) Then
        If $j = 1 Then
            $aTable[$d] = $aBlue[0][0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To $iUBBlue - 1 Step $iUBBlue / ($j * $bb)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aBlue[$i][0]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf

    $j = Floor($iAmountColors * $iUBGrey / $iSumColor + 0.5)
    If $iAmountColors - $k - $j Then $j += $iAmountColors - $k - $j
    ConsoleWrite("Grey: " & $j & @CRLF)
    Local $gr = Int($j / ($iAmountColors * $p)) + 1
    If $d < $iAmountColors And UBound($aGrey) Then
        If $j = 1 Then
            $aTable[$d] = $aGrey[0][0]
            $d += 1
        ElseIf $j > 1 Then
            For $i = 0 To $iUBGrey - 1 Step $iUBGrey / ($j * $gr)
                If $d < $iAmountColors Then
                    $aTable[$d] = $aGrey[$i][0]
                    $d += 1
                Else
                    ExitLoop
                EndIf
            Next
        EndIf
    EndIf
;~  _ArrayDisplayMod($aTable)
#EndRegion

    Local $currentPixel, $iR, $iG, $iB, $NearestColor, $errorR, $errorG, $errorB, $iRow, $iCol, _
          $aMatrix, $matrixWidth, $matrixHeight, $matrixStartX, $coefficient, $offsetX, $offsetY, $offsetIndex
    Local $aMatrixAtkinson[3][4] = [[0,     0,          1/8,            1/8], _
                                    [1/8,   1/8,        1/8,            0], _
                                    [0,     1/8,        0,              0]], _
          $aMatrixFS[2][3] = [[0,       0,          7/16], _
                              [3/16,    5/16,       1/16]], _
          $aMatrixJJN[3][5] = [[0,      0,          0,          7/48,       5/48], _
                               [3/48,   5/48,       7/48,       5/48,       3/48], _
                               [1/48,   3/48,       5/48,       3/48,       1/48]], _
          $aMatrixSierra2[2][5] = [[0,      0,          0,          4/16,       3/16], _
                                   [1/16,   2/16,       3/16,       2/16,       1/16]], _
          $aMatrixSierra3[3][5] = [[0,      0,          0,          5/32,       3/32], _
                                   [2/32,   4/32,       5/32,       4/32,       2/32], _
                                   [0,      2/32,       3/32,       2/32,       0]], _
          $aMatrixStucki[3][5] = [[0,       0,          0,          8/42,       4/42], _
                                  [2/42,    4/42,       8/42,       4/42,       2/42], _
                                  [1/42,    2/42,       4/42,       2/42,       1/42]], _
          $aMatrixBurkes[2][5] = [[0,       0,          0,          8/32,       4/32], _
                                  [2/32,    4/32,       8/32,       4/32,       2/32]]

    Switch $iDitherType
        Case 1  ;Floyd-Steinberg
            $matrixHeight = 1
            $matrixWidth = 2
            $matrixStartX = 1
            $aMatrix = $aMatrixFS
        Case 2  ;Burkes
            $matrixHeight = 1
            $matrixWidth = 4
            $matrixStartX = 2
            $aMatrix = $aMatrixBurkes
        Case 3  ;Jarvis, Judice, and Ninke
            $matrixHeight = 2
            $matrixWidth = 4
            $matrixStartX = 2
            $aMatrix = $aMatrixJJN
        Case 4  ;Stucki
            $matrixHeight = 2
            $matrixWidth = 4
            $matrixStartX = 2
            $aMatrix = $aMatrixStucki
        Case 5  ;Two-Row Sierra
            $matrixHeight = 1
            $matrixWidth = 4
            $matrixStartX = 2
            $aMatrix = $aMatrixStucki
        Case 6  ;Three-Row Sierra
            $matrixHeight = 2
            $matrixWidth = 4
            $matrixStartX = 2
            $aMatrix = $aMatrixStucki
        Case 7  ;Atkinson
            $matrixHeight = 2
            $matrixWidth = 3
            $matrixStartX = 1
            $aMatrix = $aMatrixAtkinson
    EndSwitch

    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW + 1
        For $iX = 0 To $iW - 1
            $currentPixel = $tPixel.argb(($iRowOffset + $iX))
            $iR = BitAND(BitShift($currentPixel, 16), 0xFF)
            $iG = BitAND(BitShift($currentPixel, 8), 0xFF)
            $iB = BitAND($currentPixel, 0xFF)
            $NearestColor = FindNearestColor($currentPixel, $aTable)
            $tPixels_Indexed.argb(($iRowOffset + $iX)) = $NearestColor
            If $bDither Then
                $errorR = $iR - BitAND(BitShift($NearestColor, 16), 0xFF)
                $errorG = $iG - BitAND(BitShift($NearestColor, 8), 0xFF)
                $errorB = $iB - BitAND($NearestColor, 0xFF)
                For $iRow = 0 to $matrixHeight
                    $offsetY = $iY + $iRow
                    For $iCol = 0 To $matrixWidth
                        $coefficient = $aMatrix[$iRow][$iCol]
                        $offsetX = $iX + ($iCol - $matrixStartX)
                        If ($coefficient <> 0 And $offsetX >= 0 And $offsetX < $iW And $offsetY >= 0 And $offsetY < $iH) Then
                            $offsetIndex = $offsetY * $iW + $offsetX
                            $c = $tPixel.argb(($offsetIndex))
                            $tPixel.argb(($offsetIndex)) = BitShift(PlusTruncate(BitAND(BitShift($c, 16), 0xFF), ($errorR * $coefficient)), -16) + _
                                                           BitShift(PlusTruncate(BitAND(BitShift($c, 8), 0xFF), ($errorG * $coefficient)), -8) + _
                                                                    PlusTruncate(BitAND($c, 0xFF), Int($errorB * $coefficient))
                        EndIf
                    Next
                Next
            EndIf
        Next
    Next

    _GDIPlus_BitmapUnlockBits($hBitmap_Indexed, $tBitmapData_Indexed)
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    Return $hBitmap_Indexed
EndFunc   ;==>_GDIPlus_BitmapCreateIndexedColor

Func FindNearestColor($iColor, $aColorTable)
    Local $minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 255 * 255 + 1, $distanceSquared, $c, $bestIndex = 0, $Adiff, $Rdiff, $Gdiff, $Bdiff, $i
    For $i = 0 To UBound($aColorTable) - 1
        $c = $aColorTable[$i]
        $Adiff = BitAND(BitShift($iColor, 24), 0xFF) - BitAND(BitShift($c, 24), 0xFF)
        $Rdiff = BitAND(BitShift($iColor, 16), 0xFF) - BitAND(BitShift($c, 16), 0xFF)
        $Gdiff = BitAND(BitShift($iColor, 8), 0xFF) - BitAND(BitShift($c, 8), 0xFF)
        $Bdiff = BitAND($iColor, 0xFF) - BitAND($c, 0xFF)
        $distanceSquared = $Adiff * $Adiff + $Rdiff * $Rdiff + $Gdiff * $Gdiff + $Bdiff * $Bdiff
        If $distanceSquared < $minDistanceSquared Then
            $minDistanceSquared = $distanceSquared
            $bestIndex = $i
        EndIf
    Next
    Return $aColorTable[$bestIndex]
EndFunc   ;==>FindNearestColor

Func PlusTruncate($a, $b)
    Return ($a + $b) < 0 ? 0 : ($a + $b) > 255 ? 255 : ($a + $b)
EndFunc

Func _ASM_BitmapGetAverageColorValue($hBitmap) ;coded by UEZ build 2016-02-08
    Local $aDim = _GDIPlus_ImageGetDimension($hBitmap)
    If @error Then Return SetError(0, 0, 0)
    Local Const $iPixels = $aDim[0] * $aDim[1]
    Local $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $aDim[0], $aDim[1], $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    Local $pScan = $tBitmapData.Scan0
    Local $iStride = $tBitmapData.Stride * 4
    Local $tPixelData = DllStructCreate("dword[" & Abs($iStride * $aDim[1]) & "]", $pScan)
    Local $tStruct_ARGB = DllStructCreate("uint64 Alpha;uint64 Red;uint64 Green;uint64 Blue")

    Local $tCodeBuffer = DllStructCreate("byte ASM[75]")
    $tCodeBuffer.ASM = "0x8B7424048B7C24088B4C240C8B0689C381E3FF000000015F1889C381E300FF0000C1EB08015F1089C381E30000FF00C1EB10015F0889C381E3000000FFC1EB18011F83C60483E90177C2C3" ;write opcodes into memory (struct) / length: 75
    Local $fTimer = TimerInit()
    Local $aRet = DllCall("user32.dll", "none", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), _
                          "ptr", DllStructGetPtr($tPixelData), _
                          "ptr", DllStructGetPtr($tStruct_ARGB), _
                          "uint", $iPixels, _
                          "int", Null)

    _GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData)

    Return BitShift($tStruct_ARGB.Alpha / $iPixels, -24) + BitShift($tStruct_ARGB.Red / $iPixels, -16) + BitShift($tStruct_ARGB.Green / $iPixels, -8) + Int($tStruct_ARGB.Blue / $iPixels)
EndFunc

Func _ArrayDisplayMod(Const ByRef $aArray, $sTitle = Default, $sArrayRange = Default, $iFlags = Default, $vUser_Separator = Default, $sHeader = Default, $iMax_ColWidth = Default, $iAlt_Color = Default, $hUser_Function = Default)

    ; Default values
    If $sTitle = Default Then $sTitle = "ArrayDisplay"
    If $sArrayRange = Default Then $sArrayRange = ""
    If $iFlags = Default Then $iFlags = 0
    If $vUser_Separator = Default Then $vUser_Separator = ""
    If $sHeader = Default Then $sHeader = ""
    If $iMax_ColWidth = Default Then $iMax_ColWidth = 350
    If $iAlt_Color = Default Then $iAlt_Color = 0
    If $hUser_Function = Default Then $hUser_Function = 0

    ; Check for transpose, column align, verbosity and button and "Row" column visibility
    Local $iTranspose = BitAND($iFlags, 1)
    Local $iColAlign = BitAND($iFlags, 6) ; 0 = Left (default); 2 = Right; 4 = Center
    Local $iVerbose = BitAND($iFlags, 8)
    Local $iButtonMargin = ((BitAND($iFlags, 32)) ? (0) : ((BitAND($iFlags, 16)) ? (20) : (40))) ; Flag 32 = 0; flag 16 = 20; neither flag = 40
    Local $iNoRow = BitAND($iFlags, 64)

    ; Check valid array
    Local $sMsg = "", $iRet = 1
    If IsArray($aArray) Then
        ; Dimension checking
        Local $iDimension = UBound($aArray, $UBOUND_DIMENSIONS), $iRowCount = UBound($aArray, $UBOUND_ROWS), $iColCount = UBound($aArray, $UBOUND_COLUMNS)
        If $iDimension > 2 Then
            $sMsg = "Larger than 2D array passed to function"
            $iRet = 2
        EndIf
    Else
        $sMsg = "No array variable passed to function"
    EndIf
    If $sMsg Then
        If $iVerbose And MsgBox($MB_SYSTEMMODAL + $MB_ICONERROR + $MB_YESNO, _
                "ArrayDisplay Error: " & $sTitle, $sMsg & @CRLF & @CRLF & "Exit the script?") = $IDYES Then
            Exit
        Else
            Return SetError($iRet, 0, "")
        EndIf
    EndIf

    ; Determine copy separator
    Local $iCW_ColWidth = Number($vUser_Separator)

    ; Separator handling
    Local $sAD_Separator = ChrW(0xFAB1)
    ; Set separator to use in this UDF and store existing one
    Local $sCurr_Separator = Opt("GUIDataSeparatorChar", $sAD_Separator)
    ; Set default user separator if required
    If $vUser_Separator = "" Then $vUser_Separator = $sCurr_Separator

    ; Declare variables
    Local $vTmp, $iRowLimit = 65525, $iColLimit = 250 ; Row = AutoIt 64k limit minus UDF controls; Column - arbitrary limit

    ; Set original dimensions for data display
    Local $iDataRow = $iRowCount
    Local $iDataCol = $iColCount

    ; Set display limits for dimensions - column value only set for 2D arrays
    Local $iItem_Start = 0, $iItem_End = $iRowCount - 1, $iSubItem_Start = 0, $iSubItem_End = (($iDimension = 2) ? ($iColCount - 1) : (0))
    ; Flag to determine if range set
    Local $bRange_Flag = False, $avRangeSplit
    ; Check for range settings
    If $sArrayRange Then
        ; Split into separate dimension sections
        Local $aArray_Range = StringRegExp($sArrayRange & "||", "(?U)(.*)\|", 3)
        ; Dimension 1
        If $aArray_Range[0] Then
            $avRangeSplit = StringSplit($aArray_Range[0], ":")
            If @error Then
                $iItem_End = Number($avRangeSplit[1])
            Else
                $iItem_Start = Number($avRangeSplit[1])
                $iItem_End = Number($avRangeSplit[2])
            EndIf
        EndIf
        ; Check row bounds
        If $iItem_Start > $iItem_End Then
            $vTmp = $iItem_Start
            $iItem_Start = $iItem_End
            $iItem_End = $vTmp
        EndIf
        If $iItem_Start < 0 Then $iItem_Start = 0
        If $iItem_End > $iRowCount - 1 Then $iItem_End = $iRowCount - 1
        ; Check if range set
        If $iItem_Start <> 0 Or $iItem_End <> $iRowCount - 1 Then $bRange_Flag = True
        ; Dimension 2
        If $iDimension = 2 And $aArray_Range[1] Then
            $avRangeSplit = StringSplit($aArray_Range[1], ":")
            If @error Then
                $iSubItem_End = Number($avRangeSplit[1])
            Else
                $iSubItem_Start = Number($avRangeSplit[1])
                $iSubItem_End = Number($avRangeSplit[2])
            EndIf
            ; Check column bounds
            If $iSubItem_Start > $iSubItem_End Then
                $vTmp = $iSubItem_Start
                $iSubItem_Start = $iSubItem_End
                $iSubItem_End = $vTmp
            EndIf
            If $iSubItem_Start < 0 Then $iSubItem_Start = 0
            If $iSubItem_End > $iColCount - 1 Then $iSubItem_End = $iColCount - 1
            ; Check if range set
            If $iSubItem_Start <> 0 Or $iSubItem_End <> $iColCount - 1 Then $bRange_Flag = True
        EndIf
    EndIf

    ; Create data display
    Local $sDisplayData = "[" & $iDataRow
    ; Check if rows will be truncated
    Local $bTruncated = False
    If $iTranspose Then
        If $iItem_End - $iItem_Start > $iColLimit Then
            $bTruncated = True
            $iItem_End = $iItem_Start + $iColLimit - 1
        EndIf
    Else
        If $iItem_End - $iItem_Start > $iRowLimit Then
            $bTruncated = True
            $iItem_End = $iItem_Start + $iRowLimit - 1
        EndIf
    EndIf
    If $bTruncated Then
        $sDisplayData &= "*]"
    Else
        $sDisplayData &= "]"
    EndIf
    If $iDimension = 2 Then
        $sDisplayData &= " [" & $iDataCol
        If $iTranspose Then
            If $iSubItem_End - $iSubItem_Start > $iRowLimit Then
                $bTruncated = True
                $iSubItem_End = $iSubItem_Start + $iRowLimit - 1
            EndIf
        Else
            If $iSubItem_End - $iSubItem_Start > $iColLimit Then
                $bTruncated = True
                $iSubItem_End = $iSubItem_Start + $iColLimit - 1
            EndIf
        EndIf
        If $bTruncated Then
            $sDisplayData &= "*]"
        Else
            $sDisplayData &= "]"
        EndIf
    EndIf
    ; Create tooltip data
    Local $sTipData = ""
    If $bTruncated Then $sTipData &= "Truncated"
    If $bRange_Flag Then
        If $sTipData Then $sTipData &= " - "
        $sTipData &= "Range set"
    EndIf
    If $iTranspose Then
        If $sTipData Then $sTipData &= " - "
        $sTipData &= "Transposed"
    EndIf

    ; Split custom header on separator
    Local $asHeader = StringSplit($sHeader, $sCurr_Separator, $STR_NOCOUNT) ; No count element
    If UBound($asHeader) = 0 Then Local $asHeader[1] = [""]
    $sHeader = "Row"
    Local $iIndex = $iSubItem_Start
    If $iTranspose Then
        ; All default headers
        For $j = $iItem_Start To $iItem_End
            $sHeader &= $sAD_Separator & "Col " & $j
        Next
    Else
        ; Create custom header with available items
        If $asHeader[0] Then
            ; Set as many as available
            For $iIndex = $iSubItem_Start To $iSubItem_End
                ; Check custom header available
                If $iIndex >= UBound($asHeader) Then ExitLoop
                $sHeader &= $sAD_Separator & $asHeader[$iIndex]
            Next
        EndIf
        ; Add default headers to fill to end
        For $j = $iIndex To $iSubItem_End
            $sHeader &= $sAD_Separator & "Col " & $j
        Next
    EndIf
    ; Remove "Row" header if not needed
    If $iNoRow Then $sHeader = StringTrimLeft($sHeader, 4)

    ; Display splash dialog if required
    If $iVerbose And ($iItem_End - $iItem_Start + 1) * ($iSubItem_End - $iSubItem_Start + 1) > 10000 Then
        SplashTextOn("ArrayDisplay", "Preparing display" & @CRLF & @CRLF & "Please be patient", 300, 100)
    EndIf

    ; Convert array into ListViewItem compatible lines
    Local $iBuffer = 4094 ; Max characters a ListView will display (Windows limitation)
    If $iTranspose Then
        ; Swap dimensions
        $vTmp = $iItem_Start
        $iItem_Start = $iSubItem_Start
        $iSubItem_Start = $vTmp
        $vTmp = $iItem_End
        $iItem_End = $iSubItem_End
        $iSubItem_End = $vTmp
    EndIf
    Local $avArrayText[$iItem_End - $iItem_Start + 1]
    For $i = $iItem_Start To $iItem_End
        ; Add row number if required
        If Not $iNoRow Then $avArrayText[$i - $iItem_Start] = "[" & $i & "]"
        For $j = $iSubItem_Start To $iSubItem_End
            If $iDimension = 1 Then
                If $iTranspose Then
                    Switch VarGetType($aArray[$j])
                        Case "Array"
                            $vTmp = "{Array}"
                        Case Else
                            $vTmp = $aArray[$j]
                    EndSwitch
                Else
                    Switch VarGetType($aArray[$i])
                        Case "Array"
                            $vTmp = "{Array}"
                        Case Else
                            $vTmp = $aArray[$i]
                    EndSwitch
                EndIf
            Else
                If $iTranspose Then
                    Switch VarGetType($aArray[$j][$i])
                        Case "Array"
                            $vTmp = "{Array}"
                        Case Else
                            $vTmp = $aArray[$j][$i]
                    EndSwitch
                Else
                    Switch VarGetType($aArray[$i][$j])
                        Case "Array"
                            $vTmp = "{Array}"
                        Case Else
                            $vTmp = $aArray[$i][$j]
                    EndSwitch
                EndIf
            EndIf
            ; Truncate if required so ListView will display
            If StringLen($vTmp) > $iBuffer Then $vTmp = StringLeft($vTmp, $iBuffer)
            $vTmp = Hex($vTmp, 8)
            $avArrayText[$i - $iItem_Start] &= $sAD_Separator & $vTmp
        Next
        ; Remove leading delimiter if no "Row" column
        If $iNoRow Then $avArrayText[$i - $iItem_Start] = StringTrimLeft($avArrayText[$i - $iItem_Start], 1)
    Next

    ; GUI Constants
    Local Const $_ARRAYCONSTANT_GUI_DOCKBOTTOM = 64
    Local Const $_ARRAYCONSTANT_GUI_DOCKBORDERS = 102
    Local Const $_ARRAYCONSTANT_GUI_DOCKHEIGHT = 512
    Local Const $_ARRAYCONSTANT_GUI_DOCKLEFT = 2
    Local Const $_ARRAYCONSTANT_GUI_DOCKRIGHT = 4
    Local Const $_ARRAYCONSTANT_GUI_DOCKHCENTER = 8
    Local Const $_ARRAYCONSTANT_GUI_EVENT_CLOSE = -3
    Local Const $_ARRAYCONSTANT_GUI_FOCUS = 256
    Local Const $_ARRAYCONSTANT_GUI_BKCOLOR_LV_ALTERNATE = 0xFE000000
    Local Const $_ARRAYCONSTANT_SS_CENTER = 0x1
    Local Const $_ARRAYCONSTANT_SS_CENTERIMAGE = 0x0200
    Local Const $_ARRAYCONSTANT_LVM_GETITEMCOUNT = (0x1000 + 4)
    Local Const $_ARRAYCONSTANT_LVM_GETITEMRECT = (0x1000 + 14)
    Local Const $_ARRAYCONSTANT_LVM_GETCOLUMNWIDTH = (0x1000 + 29)
    Local Const $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH = (0x1000 + 30)
    Local Const $_ARRAYCONSTANT_LVM_GETITEMSTATE = (0x1000 + 44)
    Local Const $_ARRAYCONSTANT_LVM_GETSELECTEDCOUNT = (0x1000 + 50)
    Local Const $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE = (0x1000 + 54)
    Local Const $_ARRAYCONSTANT_LVS_EX_GRIDLINES = 0x1
    Local Const $_ARRAYCONSTANT_LVIS_SELECTED = 0x2
    Local Const $_ARRAYCONSTANT_LVS_SHOWSELALWAYS = 0x8
    Local Const $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT = 0x20
    Local Const $_ARRAYCONSTANT_WS_EX_CLIENTEDGE = 0x0200
    Local Const $_ARRAYCONSTANT_WS_MAXIMIZEBOX = 0x00010000
    Local Const $_ARRAYCONSTANT_WS_MINIMIZEBOX = 0x00020000
    Local Const $_ARRAYCONSTANT_WS_SIZEBOX = 0x00040000
    Local Const $_ARRAYCONSTANT_WM_SETREDRAW = 11
    Local Const $_ARRAYCONSTANT_LVSCW_AUTOSIZE = -1

    ; Set coord mode 1
    Local $iCoordMode = Opt("GUICoordMode", 1)

    ; Create GUI
    Local $iOrgWidth = 210, $iHeight = 200, $iMinSize = 250
    Local $hGUI = GUICreate($sTitle, $iOrgWidth, $iHeight, Default, Default, BitOR($_ARRAYCONSTANT_WS_SIZEBOX, $_ARRAYCONSTANT_WS_MINIMIZEBOX, $_ARRAYCONSTANT_WS_MAXIMIZEBOX))
    Local $aiGUISize = WinGetClientSize($hGUI)
    Local $iButtonWidth_2 = $aiGUISize[0] / 2
    Local $iButtonWidth_3 = $aiGUISize[0] / 3
    ; Create ListView
    Local $idListView = GUICtrlCreateListView($sHeader, 0, 0, $aiGUISize[0], $aiGUISize[1] - $iButtonMargin, $_ARRAYCONSTANT_LVS_SHOWSELALWAYS)
    GUICtrlSetBkColor($idListView, $_ARRAYCONSTANT_GUI_BKCOLOR_LV_ALTERNATE)
    GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_LVS_EX_GRIDLINES, $_ARRAYCONSTANT_LVS_EX_GRIDLINES)
    GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT, $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT)
    GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_WS_EX_CLIENTEDGE, $_ARRAYCONSTANT_WS_EX_CLIENTEDGE)
    Local $idCopy_ID = 9999, $idCopy_Data = 99999, $idData_Label = 99999, $idUser_Func = 99999, $idExit_Script = 99999
    ; Check if any buttons required
    If $iButtonMargin Then
        ; Create Copy buttons
        $idCopy_ID = GUICtrlCreateButton("Copy Data && Hdr/Row", 0, $aiGUISize[1] - $iButtonMargin, $iButtonWidth_2, 20)
        $idCopy_Data = GUICtrlCreateButton("Copy Data Only", $iButtonWidth_2, $aiGUISize[1] - $iButtonMargin, $iButtonWidth_2, 20)
        ; Check if other buttons are required
        If $iButtonMargin = 40 Then
            Local $iButtonWidth_Var = $iButtonWidth_2
            Local $iOffset = $iButtonWidth_2
            If IsFunc($hUser_Function) Then
                ; Create UserFunc button if function passed
                $idUser_Func = GUICtrlCreateButton("Run User Func", $iButtonWidth_3, $aiGUISize[1] - 20, $iButtonWidth_3, 20)
                $iButtonWidth_Var = $iButtonWidth_3
                $iOffset = $iButtonWidth_3 * 2
            EndIf
            ; Create Exit button and data label
            $idExit_Script = GUICtrlCreateButton("Exit Script", $iOffset, $aiGUISize[1] - 20, $iButtonWidth_Var, 20)
            $idData_Label = GUICtrlCreateLabel($sDisplayData, 0, $aiGUISize[1] - 20, $iButtonWidth_Var, 18, BitOR($_ARRAYCONSTANT_SS_CENTER, $_ARRAYCONSTANT_SS_CENTERIMAGE))
            ; Change label colour and create tooltip if required
            Select
                Case $bTruncated Or $iTranspose Or $bRange_Flag
                    GUICtrlSetColor($idData_Label, 0xFF0000)
                    GUICtrlSetTip($idData_Label, $sTipData)
            EndSelect
        EndIf
    EndIf
    ; Set resizing
    GUICtrlSetResizing($idListView, $_ARRAYCONSTANT_GUI_DOCKBORDERS)
    GUICtrlSetResizing($idCopy_ID, $_ARRAYCONSTANT_GUI_DOCKLEFT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($idCopy_Data, $_ARRAYCONSTANT_GUI_DOCKRIGHT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($idData_Label, $_ARRAYCONSTANT_GUI_DOCKLEFT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($idUser_Func, $_ARRAYCONSTANT_GUI_DOCKHCENTER + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
    GUICtrlSetResizing($idExit_Script, $_ARRAYCONSTANT_GUI_DOCKRIGHT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)

    ; Start ListView update
    GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_WM_SETREDRAW, 0, 0)

    ; Fill listview
    Local $idItem
    For $i = 0 To UBound($avArrayText) - 1
        $idItem = GUICtrlCreateListViewItem($avArrayText[$i], $idListView)
        If $iAlt_Color Then
            GUICtrlSetBkColor($idItem, $iAlt_Color)
        EndIf
    Next

    ; Align columns if required - $iColAlign = 2 for Right and 4 for Center
    If $iColAlign Then
        Local Const $_ARRAYCONSTANT_LVCF_FMT = 0x01
        Local Const $_ARRAYCONSTANT_LVM_SETCOLUMNW = (0x1000 + 96)
        Local $tColumn = DllStructCreate("uint Mask;int Fmt;int CX;ptr Text;int TextMax;int SubItem;int Image;int Order;int cxMin;int cxDefault;int cxIdeal")
        DllStructSetData($tColumn, "Mask", $_ARRAYCONSTANT_LVCF_FMT)
        DllStructSetData($tColumn, "Fmt", $iColAlign / 2) ; Left = 0; Right = 1; Center = 2
        Local $pColumn = DllStructGetPtr($tColumn)
        ; Loop through columns
        For $i = 1 To $iSubItem_End - $iSubItem_Start + 1
            GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETCOLUMNW, $i, $pColumn)
        Next
    EndIf

    ; End ListView update
    GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_WM_SETREDRAW, 1, 0)

    ; Allow for borders with and without vertical scrollbar
    Local $iBorder = 45
    If UBound($avArrayText) > 20 Then
        $iBorder += 20
    EndIf
    ; Adjust dialog width
    Local $iWidth = $iBorder, $iColWidth = 0, $aiColWidth[$iSubItem_End - $iSubItem_Start + 2], $iMin_ColWidth = 55
    ; Get required column widths to fit items
    For $i = 0 To $iSubItem_End - $iSubItem_Start + 1
        GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH, $i, $_ARRAYCONSTANT_LVSCW_AUTOSIZE)
        $iColWidth = GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_GETCOLUMNWIDTH, $i, 0)
        ; Set minimum if required
        If $iColWidth < $iMin_ColWidth Then
            GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH, $i, $iMin_ColWidth)
            $iColWidth = $iMin_ColWidth
        EndIf
        ; Add to total width
        $iWidth += $iColWidth
        ; Store  value
        $aiColWidth[$i] = $iColWidth
    Next
    ; Reduce width if no "Row" colukm
    If $iNoRow Then $iWidth -= 55
    ; Now check max size
    If $iWidth > @DesktopWidth - 100 Then
        ; Apply max col width limit to reduce width
        $iWidth = $iBorder
        For $i = 0 To $iSubItem_End - $iSubItem_Start + 1
            If $aiColWidth[$i] > $iMax_ColWidth Then
                ; Reset width
                GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_SETCOLUMNWIDTH, $i, $iMax_ColWidth)
                $iWidth += $iMax_ColWidth
            Else
                ; Retain width
                $iWidth += $aiColWidth[$i]
            EndIf
        Next
    EndIf
    ; Check max/min width
    If $iWidth > @DesktopWidth - 100 Then
        $iWidth = @DesktopWidth - 100
    ElseIf $iWidth < $iMinSize Then
        $iWidth = $iMinSize
    EndIf

    ; Get row height
    Local $tRECT = DllStructCreate("struct; long Left;long Top;long Right;long Bottom; endstruct") ; $tagRECT
    DllCall("user32.dll", "struct*", "SendMessageW", "hwnd", GUICtrlGetHandle($idListView), "uint", $_ARRAYCONSTANT_LVM_GETITEMRECT, "wparam", 0, "struct*", $tRECT)
    ; Set required GUI height
    Local $aiWin_Pos = WinGetPos($hGUI)
    Local $aiLV_Pos = ControlGetPos($hGUI, "", $idListView)
    $iHeight = ((UBound($avArrayText) + 2) * (DllStructGetData($tRECT, "Bottom") - DllStructGetData($tRECT, "Top"))) + $aiWin_Pos[3] - $aiLV_Pos[3]
    ; Check min/max height
    If $iHeight > @DesktopHeight - 100 Then
        $iHeight = @DesktopHeight - 100
    ElseIf $iHeight < $iMinSize Then
        $iHeight = $iMinSize
    EndIf

    If $iVerbose Then SplashOff()

    ; Display and resize dialog
    GUISetState(@SW_HIDE, $hGUI)
    WinMove($hGUI, "", (@DesktopWidth - $iWidth) / 2, (@DesktopHeight - $iHeight) / 2, $iWidth, $iHeight)
    GUISetState(@SW_SHOW, $hGUI)

    ; Switch to GetMessage mode
    Local $iOnEventMode = Opt("GUIOnEventMode", 0), $iMsg

    While 1

        $iMsg = GUIGetMsg() ; Variable needed to check which "Copy" button was pressed
        Switch $iMsg
            Case $_ARRAYCONSTANT_GUI_EVENT_CLOSE
                ExitLoop

            Case $idCopy_ID, $idCopy_Data
                ; Count selected rows
                Local $iSel_Count = GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_GETSELECTEDCOUNT, 0, 0)
                ; Display splash dialog if required
                If $iVerbose And (Not $iSel_Count) And ($iItem_End - $iItem_Start) * ($iSubItem_End - $iSubItem_Start) > 10000 Then
                    SplashTextOn("ArrayDisplay", "Copying data" & @CRLF & @CRLF & "Please be patient", 300, 100)
                EndIf
                ; Generate clipboard text
                Local $sClip = "", $sItem, $aSplit
                ; Add items
                For $i = 0 To $iItem_End - $iItem_Start
                    ; Skip if copying selected rows and item not selected
                    If $iSel_Count And Not (GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_GETITEMSTATE, $i, $_ARRAYCONSTANT_LVIS_SELECTED)) Then
                        ContinueLoop
                    EndIf
                    $sItem = $avArrayText[$i]
                    If $iMsg = $idCopy_Data Then
                        ; Remove row ID if required
                        $sItem = StringRegExpReplace($sItem, "^\[\d+\].(.*)$", "$1")
                    EndIf
                    If $iCW_ColWidth Then
                        ; Expand columns
                        $aSplit = StringSplit($sItem, $sAD_Separator)
                        $sItem = ""
                        For $j = 1 To $aSplit[0]
                            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
                        Next
                    Else
                        ; Use defined separator
                        $sItem = StringReplace($sItem, $sAD_Separator, $vUser_Separator)
                    EndIf
                    $sClip &= $sItem & @CRLF
                Next
                ; Add header line if required
                If $iMsg = $idCopy_ID Then
                    If $iCW_ColWidth Then
                        $aSplit = StringSplit($sHeader, $sAD_Separator)
                        $sItem = ""
                        For $j = 1 To $aSplit[0]
                            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
                        Next
                    Else
                        $sItem = StringReplace($sHeader, $sAD_Separator, $vUser_Separator)
                    EndIf
                    $sClip = $sItem & @CRLF & $sClip
                EndIf
                ;Send to clipboard
                ClipPut($sClip)
                ; Remove splash if used
                SplashOff()
                ; Refocus ListView
                GUICtrlSetState($idListView, $_ARRAYCONSTANT_GUI_FOCUS)

            Case $idUser_Func
                ; Get selected indices
                Local $aiSelItems[$iRowLimit] = [0]
                For $i = 0 To GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_GETITEMCOUNT, 0, 0)
                    If GUICtrlSendMsg($idListView, $_ARRAYCONSTANT_LVM_GETITEMSTATE, $i, $_ARRAYCONSTANT_LVIS_SELECTED) Then
                        $aiSelItems[0] += 1
                        $aiSelItems[$aiSelItems[0]] = $i + $iItem_Start
                    EndIf
                Next
                ReDim $aiSelItems[$aiSelItems[0] + 1]
                ; Pass array and selection to user function
                $hUser_Function($aArray, $aiSelItems)
                GUICtrlSetState($idListView, $_ARRAYCONSTANT_GUI_FOCUS)

            Case $idExit_Script
                ; Clear up
                GUIDelete($hGUI)
                Exit
        EndSwitch
    WEnd

    ; Clear up
    GUIDelete($hGUI)
    Opt("GUICoordMode", $iCoordMode) ; Reset original Coord mode
    Opt("GUIOnEventMode", $iOnEventMode) ; Reset original GUI mode
    Opt("GUIDataSeparatorChar", $sCurr_Separator) ; Reset original separator

    Return 1

EndFunc   ;==>_ArrayDisplay

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#11 ·  Posted

Using a modified example of the _GDIPlus_BitmapCloneArea() function in AutoIt help file, the following data was generated along with the accompanying graphic file.
PXF24RGB.png looks good.

Time Takex: 38.2686496880273msec   File: PXF01INDEXED.gif   Size: 4.953KB   Format: $GDIP_PXF01INDEXED
Time Takex: 15.0987536185511msec   File: PXF04INDEXED.gif   Size: 11.396KB   Format: $GDIP_PXF04INDEXED
Time Takex: 16.2310318492872msec   File: PXF08INDEXED.gif   Size: 15.387KB   Format: $GDIP_PXF08INDEXED
Time Takex: 25.9660286344743msec   File: PXF16RGB555.gif   Size: 27.737KB   Format: $GDIP_PXF16RGB555
Time Takex: 32.4496874235916msec   File: PXF16RGB565.gif   Size: 27.823KB   Format: $GDIP_PXF16RGB565
Time Takex: 32.9894137569843msec   File: PXF24RGB.gif   Size: 29.571KB   Format: $GDIP_PXF24RGB
Time Takex: 15.7586203773858msec   File: PXF32RGB.gif   Size: 29.571KB   Format: $GDIP_PXF32RGB
Time Takex: 17.7074308966158msec   File: PXF32ARGB.gif   Size: 29.571KB   Format: $GDIP_PXF32ARGB

Time Takex: 26.2558145852891msec   File: PXF01INDEXED.png   Size: 3.278KB   Format: $GDIP_PXF01INDEXED
Time Takex: 16.574548945149msec   File: PXF04INDEXED.png   Size: 8.393KB   Format: $GDIP_PXF04INDEXED
Time Takex: 18.0482312491887msec   File: PXF08INDEXED.png   Size: 12.986KB   Format: $GDIP_PXF08INDEXED
Time Takex: 18.9417379308679msec   File: PXF16RGB555.png   Size: 20.882KB   Format: $GDIP_PXF16RGB555
Time Takex: 17.2534329070059msec   File: PXF16RGB565.png   Size: 21.11KB   Format: $GDIP_PXF16RGB565
Time Takex: 15.8881184741562msec   File: PXF24RGB.png   Size: 18.176KB   Format: $GDIP_PXF24RGB <=== Looks good
Time Takex: 16.7559670247737msec   File: PXF32RGB.png   Size: 20.947KB   Format: $GDIP_PXF32RGB
Time Takex: 15.9406421777414msec   File: PXF32ARGB.png   Size: 20.933KB   Format: $GDIP_PXF32ARGB

Time Takex: 16.2735941608131msec   File: PXF01INDEXED.jpg   Size: 26.915KB   Format: $GDIP_PXF01INDEXED
Time Takex: 16.5863214994008msec   File: PXF04INDEXED.jpg   Size: 31.349KB   Format: $GDIP_PXF04INDEXED
Time Takex: 19.1412676324186msec   File: PXF08INDEXED.jpg   Size: 31.28KB   Format: $GDIP_PXF08INDEXED
Time Takex: 18.8584244700087msec   File: PXF16RGB555.jpg   Size: 31.187KB   Format: $GDIP_PXF16RGB555
Time Takex: 18.5493194558061msec   File: PXF16RGB565.jpg   Size: 31.286KB   Format: $GDIP_PXF16RGB565
Time Takex: 14.3416878220473msec   File: PXF24RGB.jpg   Size: 31.274KB   Format: $GDIP_PXF24RGB
Time Takex: 16.0559528373365msec   File: PXF32RGB.jpg   Size: 31.274KB   Format: $GDIP_PXF32RGB
Time Takex: 16.3855843563884msec   File: PXF32ARGB.jpg   Size: 31.274KB   Format: $GDIP_PXF32ARGB

Time Takex: 26.1601248494471msec   File: PXF01INDEXED.bmp   Size: 15.506KB   Format: $GDIP_PXF01INDEXED
Time Takex: 42.2631679037911msec   File: PXF04INDEXED.bmp   Size: 61.894KB   Format: $GDIP_PXF04INDEXED
Time Takex: 35.2144265105847msec   File: PXF08INDEXED.bmp   Size: 124.502KB   Format: $GDIP_PXF08INDEXED
Time Takex: 24.8660494628395msec   File: PXF16RGB555.bmp   Size: 247.158KB   Format: $GDIP_PXF16RGB555
Time Takex: 10.8385982812071msec   File: PXF16RGB565.bmp   Size: 247.17KB   Format: $GDIP_PXF16RGB565
Time Takex: 16.1202490951736msec   File: PXF24RGB.bmp   Size: 370.71KB   Format: $GDIP_PXF24RGB
Time Takex: 16.4091294648921msec   File: PXF32RGB.bmp   Size: 492.858KB   Format: $GDIP_PXF32RGB
Time Takex: 17.8333066690011msec   File: PXF32ARGB.bmp   Size: 492.858KB   Format: $GDIP_PXF32ARGB
#include <GDIPlus.au3>
#include <ScreenCapture.au3>

Local $aFormat[9] = ["$GDIP_PXF01INDEXED", "$GDIP_PXF04INDEXED", "$GDIP_PXF08INDEXED", "$GDIP_PXF16GRAYSCALE", _
                "$GDIP_PXF16RGB555", "$GDIP_PXF16RGB565", "$GDIP_PXF24RGB", "$GDIP_PXF32RGB", "$GDIP_PXF32ARGB"]
Local $aFileEXTs[4] = [".gif", ".png", ".jpg", ".bmp"]

For $j = 0 To UBound($aFileEXTs) - 1
    For $i = 0 To UBound($aFormat) - 1
        $sFileName = StringTrimLeft($aFormat[$i], 6) & $aFileEXTs[$j]

        Local $hTimer = TimerInit()
        Example($sFileName, $aFormat[$i])
        If FileExists($sFileName) Then _
                ConsoleWrite("Time Takex: " & TimerDiff($hTimer) & "msec" & _
                "   File: " & $sFileName & _
                "   Size: " & FileGetSize($sFileName) / 1000 & "KB" & _
                "   Format: " & $aFormat[$i] & @CRLF)
    Next
    ConsoleWrite(@CRLF)
Next

;Display graphic files
#cs
For $j = 0 To UBound($aFileEXTs) - 1
    For $i = 0 To UBound($aFormat) - 1
        $sFileName = StringTrimLeft($aFormat[$i], 6) & $aFileEXTs[$j]
        If FileExists($sFileName) Then ShellExecute($sFileName)
    Next
Next
#ce


Func Example($sFileName, $Format)
    Local $hBitmap, $hClone, $hImage, $iX, $iY

    ; Initialize GDI+ library
    _GDIPlus_Startup()

    ; Capture 32 bit bitmap
    $hBitmap = _ScreenCapture_Capture("", 50, 50, 400, 400, False)
    $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)

    ; Create 24 bit bitmap clone
    $iX = _GDIPlus_ImageGetWidth($hImage)
    $iY = _GDIPlus_ImageGetHeight($hImage)
    $hClone = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iX, $iY, Execute($Format))

    ; Save bitmap to file
    _GDIPlus_ImageSaveToFile($hClone, $sFileName)

    ; Clean up resources
    _GDIPlus_ImageDispose($hClone)
    _GDIPlus_ImageDispose($hImage)
    _WinAPI_DeleteObject($hBitmap)

    ; Shut down GDI+ library
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

 

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

The problem with the built-in function is that parameters are not available (e.g. you cannot select the destination color count) and the bandwith for the color spectrum is not fully used.

E.g. for 8 bit you can use up to 256 colors but GDI+ uses less. But the color quantization works very good.

Ok, the original question was a little bit different or at least to another direction.

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#13 ·  Posted

All,

Thanks for your efforts but I wouldn't want anyone to waste time on my initial question since it's no more an issue for me.

However the outcome could certainly benefit to the community.

1 person likes this

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites

#14 ·  Posted

Well, at least for me it was very informative to dig in this direction. ^^

 

I still investigating...

1 person likes this

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

@junkew where I can find / download clr.au3?

If you use native GDI+ function calls in a simple loop than AutoIt is nearly as fast as C# / FB / etc. also using GDI+ -> e.g. Rotating Earth Example. FB version has nearly the same FPS rate as AutoIt.

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now