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.

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