jchd Posted March 29, 2012 Share Posted March 29, 2012 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 hereRegExp tutorial: enough to get startedPCRE 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) Link to comment Share on other sites More sharing options...
UEZ Posted March 29, 2012 Share Posted March 29, 2012 (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 March 29, 2012 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
jchd Posted March 29, 2012 Author Share Posted March 29, 2012 (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 March 29, 2012 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 hereRegExp tutorial: enough to get startedPCRE 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) Link to comment Share on other sites More sharing options...
UEZ Posted March 30, 2012 Share Posted March 30, 2012 (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 methodThe 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.expandcollapse popup#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_BitmapCreateFromScan0Maybe 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 March 30, 2012 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
UEZ Posted April 2, 2012 Share Posted April 2, 2012 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
UEZ Posted April 3, 2012 Share Posted April 3, 2012 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
jchd Posted April 3, 2012 Author Share Posted April 3, 2012 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 hereRegExp tutorial: enough to get startedPCRE 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) Link to comment Share on other sites More sharing options...
Yashied Posted April 3, 2012 Share Posted April 3, 2012 (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 April 3, 2012 by Yashied My UDFs: iKey | FTP Uploader | Battery Checker | Boot Manager | Font Viewer | UDF Keyword Manager | Run Dialog Replacement | USBProtect | 3D Axis | Calculator | Sleep | iSwitcher | TM | NetHelper | File Types Manager | Control Viewer | SynFolders | DLL Helper Animated Tray Icons UDF Library | Hotkeys UDF Library | Hotkeys Input Control UDF Library | Caret Shape UDF Library | Context Help UDF Library | Most Recently Used List UDF Library | Icons UDF Library | FTP UDF Library | Script Communications UDF Library | Color Chooser UDF Library | Color Picker Control UDF Library | IPHelper (Vista/7) UDF Library | WinAPI Extended UDF Library | WinAPIVhd UDF Library | Icon Chooser UDF Library | Copy UDF Library | Restart UDF Library | Event Log UDF Library | NotifyBox UDF Library | Pop-up Windows UDF Library | TVExplorer UDF Library | GuiHotKey UDF Library | GuiSysLink UDF Library | Package UDF Library | Skin UDF Library | AITray UDF Library | RDC UDF Library Appropriate path | Button text color | Gaussian random numbers | Header's styles (Vista/7) | ICON resource enumeration | Menu & INI | Tabbed string size | Tab's skin | Pop-up circular menu | Progress Bar without animation (Vista/7) | Registry export | Registry path jumping | Unique hardware ID | Windows alignment More... Link to comment Share on other sites More sharing options...
jchd Posted April 3, 2012 Author Share Posted April 3, 2012 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 hereRegExp tutorial: enough to get startedPCRE 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) Link to comment Share on other sites More sharing options...
UEZ Posted May 31, 2017 Share Posted May 31, 2017 (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. expandcollapse popup;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. expandcollapse popup;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 expandcollapse popup;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 September 26, 2017 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
Malkey Posted June 1, 2017 Share Posted June 1, 2017 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 expandcollapse popup#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 Link to comment Share on other sites More sharing options...
UEZ Posted June 1, 2017 Share Posted June 1, 2017 (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 June 1, 2017 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
jchd Posted June 1, 2017 Author Share Posted June 1, 2017 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. argumentum 1 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 hereRegExp tutorial: enough to get startedPCRE 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) Link to comment Share on other sites More sharing options...
UEZ Posted June 1, 2017 Share Posted June 1, 2017 Well, at least for me it was very informative to dig in this direction. ^^ I still investigating... argumentum 1 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
junkew Posted June 1, 2017 Share Posted June 1, 2017 With clr.au3 you can reach .net graphics and bitmap objects. Is .net library not offering this? Then example in vb .net or c# even in memory would be possible on clr.au3 example as danyfirex made compiled on the fly. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
UEZ Posted June 2, 2017 Share Posted June 2, 2017 (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 June 2, 2017 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
junkew Posted June 3, 2017 Share Posted June 3, 2017 2 threads FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now