├── ImagePut (for v1).ahk ├── ImagePut.ahk ├── LICENSE ├── README.md ├── demo.ahk ├── examples ├── Overlay Example.ahk ├── Paste Image From Clipboard to Explorer Window.ahk ├── Save Screenshot To Desktop.ahk └── Screen Clipping using the Snipping Tool.ahk ├── source ├── __Note__.txt ├── checkalpha.c ├── colorkey.c ├── cpuid.c ├── from_sprite.c ├── hex.c ├── imagesearch1.c ├── imagesearch2.c ├── imagesearch3.c ├── imagesearchall1.c ├── imagesearchall2.c ├── pixelsearch1.c ├── pixelsearch1x.c ├── pixelsearch1x2.c ├── pixelsearch1y.c ├── pixelsearch2.c ├── pixelsearch2x.c ├── pixelsearch3.c ├── pixelsearch3x.c ├── pixelsearch4.c ├── pixelsearch4x.c ├── pixelsearchall1.c ├── pixelsearchall1x.c ├── pixelsearchall2.c ├── pixelsearchall2x.c ├── pixelsearchall3.c ├── pixelsearchall3x.c ├── pixelsearchall4.c ├── pixelsearchall4x.c ├── setalpha.c └── transcolor.c └── test └── 0. Show All Inputs.ahk /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Edison Hua 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImagePut 2 | 3 | #### A core library for images in AutoHotkey 4 | 5 | ![image](https://github.com/iseahound/ImagePut/assets/9779668/65a563a6-3d95-4819-9ea6-64d5ab00d993) 6 | 7 | Imagine a scenario where you have a file but you're trying to get it uploaded into an API. Or you have a function that returns some strange image format, is there any way you can *just* get it to show its contents? If you pass anything, literally anything you believe is an image to `ImagePutWindow()` it will show up on screen. And then you can magically transform it to be compatible with that specific API. 8 | 9 | #### Note: If you don't want a large library you can copy paste the relevant functions 10 | 11 | ## So you want to convert an image? 12 | 13 | But you don't know how. That's okay because you can just do this: 14 | 15 | str := ImagePutBase64("cats.jpg") 16 | 17 | or this 18 | 19 | ImagePutClipboard("https://example.com/cats.jpg") 20 | 21 | or something like this: 22 | 23 | pStream := ImagePutStream([0, 0, A_ScreenWidth, A_ScreenHeight]) 24 | 25 | Working with images should be this easy. ImagePut has automatic type inference, meaning that it will guess whether the input is (1) a file (2) a website url or (3) a series of coordinates that map to the screen. This functionality enables the user to only memorize a single function for any possible input. For a full list of supported input types, click on the documentation link [here](https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions#input-types). For output types click [here](https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions#output-functions). 26 | 27 | Convert file formats. 28 | 29 | ; Saves a JPEG as a GIF. 30 | ImagePutFile("cats.jpg", "gif") 31 | 32 | Convert file formats and image types at the same time! 33 | 34 | ; Saves a JPEG as a base64 encoded GIF. 35 | str := ImagePutBase64("cats.jpg", "gif") 36 | 37 | There's also some weird functions like ```ImagePutCursor``` which lets you set anything as your cursor. Make sure you don't choose an extremely large image! 38 | 39 | Finally, there are several advanced features. The first is the ability to specify the [input type](https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions#input-types) directly. The second is [cropping](https://github.com/iseahound/ImagePut/wiki/Crop,-Scale,-&-Other-Flags#crop) and [scaling](https://github.com/iseahound/ImagePut/wiki/Crop,-Scale,-&-Other-Flags#scale) functionality. Third is use of ImageEqual() a function that can compare multiple inputs across different windows image data types! 40 | 41 | ; Declare input type as file. 42 | ImagePutWindow({file: "cats.jpg"}) 43 | 44 | ; Scale 2x and crop 10% from each edge. 45 | ImagePutWindow({file: "cats.jpg", scale: 2, crop:["-10%", "-10%", "-10%", "-10%"]}) 46 | 47 | ; Unknown image type declared as "image" to be cropped to 200x200 pixels. 48 | ImagePutWindow({image: "cats.jpg", crop: [0, 0, 200, 200]}) 49 | 50 | ; Compare a url to a file. 51 | MsgBox % ImageEqual("https://example.com/cats.jpg", "cats.jpg") 52 | 53 | ; Validate an image as an actual image. 54 | ImageEqual("cats.jpg") 55 | 56 | #### Features 57 | 58 | * Simple and effective 59 | * Decipher over [20+ image types](https://github.com/iseahound/ImagePut/wiki/Quick-Start#accepts) 60 | * Supports newer formats `avif`, `heic`, and `webp` 61 | * View and debug any image variable with `ImagePutWindow(image)` 62 | * Screen capture with fast [PixelSearch](https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch#pixelsearch) and [ImageSearch](https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch#imagesearch) 63 | * [Add](https://github.com/iseahound/ImagePut/wiki/Add-Image-to-AutoHotkey-GUI) animated GIFs to AutoHotkey GUIs 64 | * [Upload](https://github.com/iseahound/ImagePut/wiki/Uploading-Images-API-POST-Request) images using website apis 65 | * [Load](https://github.com/iseahound/ImagePut/wiki/Read-an-Image-From-Memory) images from blocks of memory 66 | 67 | #### Documentation 68 | 69 | * **Function List** - [Quick Start](https://github.com/iseahound/ImagePut/wiki/Quick-Start) 70 | * Chinese Documentation (中文) - [这里有一个中文版的使用教程](https://www.autoahk.com/archives/37246) 71 | * [Input Types & Output Functions](https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions) 72 | * [Crop, Scale, & Other Flags](https://github.com/iseahound/ImagePut/wiki/Crop,-Scale,-&-Other-Flags) 73 | * [PixelSearch & ImageSearch](https://github.com/iseahound/ImagePut/wiki/PixelSearch-and-ImageSearch) 74 | 75 | #### Engineering 76 | 77 | * [Engineering Challenges Q&A](https://github.com/iseahound/ImagePut/wiki/Engineering-Challenges-Q&A) - Click here to read some interesting stuff. 78 | * [Internal Documentation](https://github.com/iseahound/ImagePut/wiki/Internal-Documentation) - Understanding how ImagePut works. 79 | 80 | #### Design Philosophy 81 | 82 | * 100% Compatibility with [Gdip_All.ahk](https://github.com/buliasz/AHKv2-Gdip) 83 | * ImagePut is designed to be the fastest 84 | * ImagePut should serve as a reference implementation 85 | * Specific conversions between formats like PNG file to hIcon are not considered 86 | * Users should be able to replace uses of ImagePut with individual functions 87 | * Therefore users should be copy and paste individual functions 88 | * If you need help extracting a function please ask! 89 | 90 | #### Help and Support 91 | 92 | Feel free to ask for any help, questions, or post suggestions, etc. 93 | 94 | * v1 forum: https://www.autohotkey.com/boards/viewtopic.php?t=76301 95 | * v2 forum: https://www.autohotkey.com/boards/viewtopic.php?t=76633 96 | 97 | Extra appreciation: https://ko-fi.com/iseahound 98 | -------------------------------------------------------------------------------- /demo.ahk: -------------------------------------------------------------------------------- 1 | #include *i ImagePut%A_TrayMenu%.ahk 2 | #include *i ImagePut (for v%true%).ahk 3 | #singleinstance force 4 | 5 | ; Show image in window. 6 | hwnd := ImagePutWindow("https://picsum.photos/1000/200", "Thank you for trying ImagePut ? Right click to close") 7 | 8 | ; Save image to file. 9 | filepath := ImagePutFile("https://i.imgur.com/cCyb8bq.gif") 10 | 11 | ; Display images without borders. Right click image to close. 12 | ImageShow(filepath) -------------------------------------------------------------------------------- /examples/Overlay Example.ahk: -------------------------------------------------------------------------------- 1 | MsgBox "Press CapsLock to toggle the overlay. Press F1 to Play / Pause. Press F2 to advance to the next frame." 2 | 3 | #Requires AutoHotkey v2 4 | #Include ..\ImagePut.ahk 5 | 6 | ; You can use url, filepath, basically anything. 7 | image1 := "https://mathiasbynens.be/demo/animated-webp-supported.webp" 8 | image2 := "https://mathiasbynens.be/demo/animated-webp-supported.webp" 9 | 10 | ; Store hwnd in array for speed. 11 | hwnds := [] 12 | 13 | ; Detect Caps Lock key press and toggle actions 14 | CapsLock:: { 15 | static toggle := -1 16 | 17 | ; Make images appear if never created. 18 | if (toggle = -1) { 19 | 20 | width := ImageWidth(image1) 21 | height := ImageHeight(image2) 22 | 23 | hwnds.push(Show(image1, 0, A_ScreenHeight - height)) 24 | hwnds.push(Show(image2, A_ScreenWidth - Width, A_ScreenHeight - height)) 25 | 26 | ; Synchronize playback 27 | PlayAll() 28 | 29 | toggle := 1 30 | } 31 | 32 | toggle := !toggle 33 | 34 | if !toggle { 35 | ; PlayAll() ; Resume playback 36 | ShowAll() ; Show the animated GIF 37 | } else { 38 | HideAll() ; Hide the animated GIF 39 | ; PauseAll() ; Pause playback 40 | } 41 | } 42 | 43 | F1:: PlayPauseAll() 44 | F2:: NextFrameAll() 45 | 46 | Play(hwnd) => PostMessage(0x8001,,,, hwnd) 47 | Restart(hwnd) => PostMessage(0x8001, 1,,, hwnd) 48 | Pause(hwnd) => PostMessage(0x8002,,,, hwnd) 49 | Stop(hwnd) => PostMessage(0x8002, 1,,, hwnd) 50 | PlayPause(hwnd) => PostMessage(0x202,,,, hwnd) 51 | RestartStop(hwnd) => PostMessage(0x202, 1,,, hwnd) 52 | IsPlaying(hwnd) => DllCall("GetWindowLong", "ptr", hwnd, "int", 4*A_PtrSize, "ptr") 53 | Step(hwnd, n) => PostMessage(0x8000, n,,, hwnd) 54 | NextFrame(hwnd) => Step(hwnd, 1) 55 | PrevFrame(hwnd) => Step(hwnd, -1) 56 | 57 | Show(image, x?, y?, w?, h?) { 58 | WS_POPUP := 0x80000000 ; Required 59 | WS_VISIBLE := 0x10000000 ; show immediately 60 | WS_DISABLED := 0x8000000 ; Disables user interaction with the window. 61 | 62 | WS_EX_TOPMOST := 0x8 ; Always on top. 63 | WS_EX_TOOLWINDOW := 0x80 ; Hides from Alt+Tab menu. Removes small icon. 64 | WS_EX_LAYERED := 0x80000 ; For UpdateLayeredWindow. 65 | WS_EX_CLICKTHROUGH := 0x80020 ; Makes it click through 66 | 67 | ; Shows the image in a window (without a border) and returns a handle to a window. 68 | ; title - Window Title | string -> MyTitle 69 | ; pos - Window Coordinates | array -> [x,y,w,h] or [0,0] 70 | ; style - Window Style | uint -> WS_VISIBLE 71 | ; styleEx - Window Extended Style | uint -> WS_EX_LAYERED 72 | ; parent - Window Parent | ptr -> hwnd 73 | ; playback - Animate Window? | bool -> True 74 | ; cache - Cache Animation Frames? | bool -> False 75 | style := WS_POPUP | WS_VISIBLE | WS_DISABLED 76 | styleEx := WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_CLICKTHROUGH 77 | return ImageShow(image, "GifWindow", [x?, y?, w?, h?], style, styleEx,, False, True) 78 | } 79 | 80 | ShowAll() { 81 | for hwnd in hwnds 82 | try WinShow(hwnd) 83 | } 84 | 85 | HideAll() { 86 | for hwnd in hwnds 87 | try WinHide(hwnd) 88 | } 89 | 90 | CloseAll() { 91 | for hwnd in hwnds 92 | try WinClose(hwnd) 93 | } 94 | 95 | PauseAll() { 96 | for hwnd in hwnds 97 | Pause(hwnd) 98 | } 99 | 100 | PlayAll() { 101 | for hwnd in hwnds 102 | Play(hwnd) 103 | } 104 | 105 | PlayPauseAll() { 106 | for hwnd in hwnds 107 | PlayPause(hwnd) 108 | } 109 | 110 | NextFrameAll() { 111 | for hwnd in hwnds 112 | NextFrame(hwnd) 113 | } 114 | -------------------------------------------------------------------------------- /examples/Paste Image From Clipboard to Explorer Window.ahk: -------------------------------------------------------------------------------- 1 | MsgBox "Copy an image from a webpage OR copy the image link. When you navigate to an explorer window or desktop, press Ctrl + v. It's that simple! As a bonus, this converts PDFs and SVGs to png files—perfect for sending to the printer." 2 | 3 | #Requires AutoHotkey v2 4 | #include ..\ImagePut.ahk 5 | #HotIf WinActive("ahk_exe explorer.exe") 6 | ~^v:: { 7 | Sleep 50 ; Delay opening clipboard when Ctrl + v 8 | if DllCall("IsClipboardFormatAvailable", "uint", 2) && !DllCall("IsClipboardFormatAvailable", "uint", 15) 9 | ImagePutExplorer(ClipboardAll) 10 | else if ImagePut.isURL(A_Clipboard) 11 | ImagePutExplorer(A_Clipboard) 12 | } 13 | #HotIf 14 | 15 | ; To turn off auto conversion of formats with PDF urls 16 | ; ImagePutExplorer({render: 0, image: A_Clipboard}) -------------------------------------------------------------------------------- /examples/Save Screenshot To Desktop.ahk: -------------------------------------------------------------------------------- 1 | MsgBox "Save the current screen to desktop by pressing Win + Control + Shift + s" 2 | 3 | #Requires AutoHotkey v2 4 | #include ..\ImagePut.ahk 5 | #^+s:: ImagePutWindow({file: ImagePutFile("A", A_Desktop "\1"), scale: 2/3}, "Saved screenshot to desktop. Right click to close.") -------------------------------------------------------------------------------- /examples/Screen Clipping using the Snipping Tool.ahk: -------------------------------------------------------------------------------- 1 | MsgBox "Press Win + Shift + s to activate Snipping Tool normally. After you select a portion of your screen, you'll see a window pop up of your snip! Right click the window to close it. Press Win + Alt + s to snip and save to a file instead." 2 | 3 | #Requires AutoHotkey v2 4 | #include ..\ImagePut.ahk 5 | #!s:: 6 | $~#+s:: { 7 | (ThisHotkey ~= '~#\+s') || Send('#+s') ; Run("ms-screenclip:") doesn't work 8 | SavedClip := ClipboardAll() 9 | A_Clipboard := "" ; Start off blank for clipboard detection 10 | exe := ProcessExist("SnippingTool.exe") ? "ScreenClippingHost.exe" : "SnippingTool.exe" 11 | ProcessWait(exe) ; Wait for screen to turn dark 12 | ProcessWaitClose(exe) ; Wait for overlay to dissappear 13 | 14 | if !DllCall("IsClipboardFormatAvailable", "uint", 2) ; Check for CF_Bitmap 15 | return A_Clipboard := SavedClip ; Restore clipboard if user pressed Escape 16 | 17 | capture := ImagePutBuffer(ClipboardAll) 18 | if ThisHotkey ~= "!" 19 | if filepath := FileSelect('S') 20 | ImagePutFile(capture, filepath) 21 | hwnd := ImagePutWindow(capture) ; Show screenshot 22 | Hotkey "Esc", (h) => ( ; Press Escape to close the screenshot 23 | WinExist(hwnd) && WinClose(hwnd), 24 | Hotkey(h, "Off")), "On" ; Specify "On" to re-enable the hotkey 25 | } -------------------------------------------------------------------------------- /source/__Note__.txt: -------------------------------------------------------------------------------- 1 | Use this at the top of your code to get the x64 calling convention 2 | 3 | __attribute__((ms_abi)) // Tells GCC we want to compile for Windows. 4 | 5 | 6 | The following regex is useful for formatting machine code: 7 | 8 | ^(?:(?:1|2),x(?:86|64):)?(.{0,100})(.{0,100})(.{0,100})(.{0,100})(.{0,100})(.{0,100})(.{0,100})(.{0,100})(.{0,100})(?<10>.{0,100})(?<11>.{0,100})(?<12>.{0,100})$ 9 | 10 | "$1"`r`n. "$2"`r`n. "$3"`r`n. "$4"`r`n. "$5"`r`n. "$6"`r`n. "$7"`r`n. "$8"`r`n. "$9"`r`n. "${10}"`r`n. "${11}"`r`n. "${12}" 11 | 12 | Make sure to press #!v to paste it! -------------------------------------------------------------------------------- /source/checkalpha.c: -------------------------------------------------------------------------------- 1 | int checkalpha(unsigned int * start, unsigned int * end) { 2 | while (start < end) { 3 | if (*((char *) start + 3) > 0) 4 | return 1; 5 | start++; 6 | } 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /source/colorkey.c: -------------------------------------------------------------------------------- 1 | void colorkey(unsigned int * start, unsigned int * end, unsigned int key, unsigned int value) { 2 | while (start < end) { 3 | if (*start == key) 4 | *start = value; 5 | start++; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/cpuid.c: -------------------------------------------------------------------------------- 1 | void native_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { 2 | /* ecx is often an input as well as an output. */ 3 | asm volatile("cpuid" 4 | : "=a" (*eax), 5 | "=b" (*ebx), 6 | "=c" (*ecx), 7 | "=d" (*edx) 8 | : "0" (*eax), "2" (*ecx)); 9 | } -------------------------------------------------------------------------------- /source/from_sprite.c: -------------------------------------------------------------------------------- 1 | void from_sprite(unsigned int * start, unsigned int * end, unsigned int key) { 2 | while (start < end) { 3 | if (*start == key) 4 | *start = 0x00000000; 5 | start++; 6 | } 7 | } -------------------------------------------------------------------------------- /source/hex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void hex(char * hex, unsigned char * bin, size_t size, char * str, size_t length) { 4 | unsigned char * start = bin; 5 | unsigned char * end = bin + size; 6 | char high, low; 7 | char * s = str; 8 | 9 | while(start < end) { 10 | high = (*start >> 4) & 0xF; 11 | *s = *(hex + high); 12 | low = *start & 0xF; 13 | *(s + 1) = *(hex + low); 14 | start += 1; 15 | s += 2; 16 | } 17 | } -------------------------------------------------------------------------------- /source/imagesearch1.c: -------------------------------------------------------------------------------- 1 | unsigned int * imagesearch1(unsigned int * start, unsigned int width, unsigned int height, unsigned int * s, unsigned int w, unsigned int h, unsigned int x, unsigned int y) { 2 | // source: left, top, width, height, start, current, end refer to the haystack (main image) 3 | // target: x, y, w, h, s, c, e refer to the needle (search or template image) 4 | 5 | int trans = (*((unsigned char *) s + 3) == 0); // Check if top-left pixel is transparent 6 | 7 | unsigned int c1 = *(s); // ↓ Top-left pixel 8 | unsigned int c2 = *(s + x); // c1 — c2 9 | unsigned int c3 = *(s + y * w); // | | 10 | unsigned int c4 = *(s + x + y * w); // c3 — c4 ← Focused pixel 11 | 12 | unsigned int x_domain = width - w; // Avoid search of the narrow edge on the right-hand side 13 | unsigned int y_domain = height - h + 1; // Add 1 to search at least one row 14 | 15 | unsigned int * end = start + width * y_domain; // Remaining area must be greater than search height 16 | unsigned int * current = start; 17 | 18 | // Start off searching with pointers. 19 | while (current < end) { 20 | 21 | unsigned int offset = current - start; // Don't need to divide by 4 here. 22 | unsigned int left = offset % width; // Get x coordinate of source image 23 | unsigned int top = offset / width; // optimized away 24 | 25 | // Rank Reasoning 26 | // 1. The focused pixel has the most entropy and is least likely to match the source. 27 | // CANNOT BE TRANSPARENT. 28 | // 2. The top-left pixel may match the source. 29 | // 3. The x-domain check determines if the match is too far to the right. 30 | // Advancing the pointer by w - 1 makes little difference. 31 | if (c4 == *(current + x + y * width)) // Rank 1 - Focused Pixel 32 | if (trans || c1 == *(current)) // Rank 2 - Top-left Pixel 33 | if (left <= x_domain) // Rank 3 - X-Domain Check 34 | { 35 | // Subimage matching loop. 36 | unsigned int * c = s; 37 | for (int i = 0; i < h; i++) { 38 | unsigned int * p = current + width * i; 39 | unsigned int * e = c + w; 40 | while (c < e) { // Scan line-by-line 41 | if (*((unsigned char *) c + 3)) // Skip transparent pixels 42 | if (*c != *p) 43 | goto next; 44 | 45 | c++; // Iterate over the template image 46 | p++; // Reset pointer for each scanline 47 | } 48 | } 49 | return current; 50 | } 51 | 52 | next: 53 | current++; 54 | } 55 | 56 | return start + width * height; // real end 57 | } -------------------------------------------------------------------------------- /source/imagesearch2.c: -------------------------------------------------------------------------------- 1 | __attribute__((ms_abi)) // Tells GCC we want to compile for Windows. 2 | __attribute__ ((target ("sse4.2"))) // According to godbolt.org it doesn't do anything even with 3 | // __builtin_assume_aligned(ptr, 16) and -Ofast -msse4.2. 4 | // You might want to study SIMD instructions and if there is a good instruction to 5 | // take advantage of, go with inline assembly. 6 | unsigned int imagesearch2(unsigned int * restrict result, unsigned int * start, unsigned int width, unsigned int height, unsigned int * s, unsigned int w, unsigned int h, unsigned int coord_focus, unsigned short variation) { 7 | // width, height, start, current, end, color refer to the haystack (main image) 8 | // x, y, w, h, s, c, e, cf refer to the needle (search image) 9 | // The location of the focused pixel 10 | unsigned short x; 11 | unsigned short y; 12 | 13 | if ( coord_focus ) { 14 | x = coord_focus >> sizeof(x)*8; 15 | y = coord_focus & 0x0000ffff; 16 | goto focus_determined; 17 | } 18 | 19 | // Try to locate the focused pixel in the middle of the sprite 20 | unsigned int * c = s + w/4 + h/4 * w; 21 | unsigned int * last_pixel = s + h * w; 22 | while(c < last_pixel) { 23 | for (unsigned int * e = c + w / 2; c < e; c++) { 24 | if ( *((unsigned char *) c + 3) ) { 25 | unsigned int offset = (c - s); 26 | x = offset % w; 27 | y = offset / w; 28 | goto focus_determined; 29 | } 30 | } 31 | c += w / 2; 32 | } 33 | 34 | // If every pixel in the middle was transparent 35 | // try to find a non-transparent pixel on the left-hand side. 36 | 37 | // This falls into an infinite loop if somehow the sprite doesn't have any 38 | // non-transparent pixel in the leftmost quarter. 39 | c = s + (w/4-1) + (h-1) * w; 40 | while(-1) { 41 | for (unsigned int * e = c - w / 4; c > e; c--) { 42 | if ( *((unsigned char *) c + 3) ) { 43 | unsigned int offset = (c - s); 44 | x = offset % w; 45 | y = offset / w; 46 | goto focus_determined; 47 | } 48 | } 49 | c -= w - w / 4; 50 | } 51 | 52 | focus_determined:; 53 | 54 | // Stash the coordinates of the focused pixel 55 | *(result + 2) = x << sizeof(x)*8 | y; 56 | 57 | 58 | 59 | // Prepare the search. 60 | 61 | // The color on the focused pixel 62 | unsigned int cf = *(s + x + y * w); 63 | 64 | // We don't need to search in the narrow edge on the right-hand side 65 | unsigned int range_width = width - w; 66 | // Remaining area must be greater than search height 67 | unsigned int range_height = height - h; 68 | 69 | unsigned int * current = start; 70 | unsigned int * end = start + width * range_height + width; 71 | 72 | // Start the search. 73 | unsigned int left = 0; 74 | while ( current < end ) { 75 | 76 | unsigned int offset = current - start; 77 | //unsigned int left = offset % width; 78 | // TODO Remove the comment below 79 | // I believe div is worse than cmov'ing one constant number. 80 | // The code loses some readability though. 81 | unsigned int top = offset / width; // optimized away 82 | 83 | // Checks before subimage loop. 84 | 85 | // X-Range Check 86 | if ( left > range_width ) { 87 | current += w - 1; 88 | left = 0; 89 | continue; 90 | } 91 | // 92 | 93 | // Focused Pixel 94 | unsigned int * color; 95 | color = current + x + y * width; 96 | if ( variation == 0 ) { 97 | if ( cf != *color ) { 98 | current++; 99 | left++; 100 | goto next; 101 | } 102 | } else { 103 | for (int b = 0; b < 3; b++) { 104 | unsigned short diff = *((unsigned char *)&cf + b) - *((unsigned char *)color + b); 105 | if ( diff > variation && diff < (unsigned short)(0 - variation) ) { 106 | current++; 107 | left++; 108 | goto next; 109 | } 110 | } 111 | } 112 | 113 | // Subimage loop. 114 | c = s; 115 | color = current; 116 | for (int i = 0; i < h; i++) { 117 | unsigned int * e = c + w; 118 | while (c < e) { 119 | if ( *((unsigned char *) c + 3) ) { // Skip transparent pixels 120 | if ( variation == 0 ) { 121 | if ( *c != *color ) { 122 | current++; 123 | left++; 124 | goto next; 125 | } 126 | } else { 127 | for (int b = 0; b < 3; b++) { 128 | unsigned short diff = *((unsigned char *) c + b) - *((unsigned char *) color + b); 129 | if ( diff > variation && diff < (unsigned short)(0 - variation) ) { 130 | current++; 131 | left++; 132 | goto next; 133 | } 134 | } 135 | } 136 | } 137 | c++; // Iterate over the needle image 138 | color++; // Iterate over the haystack image 139 | } 140 | color = color - w + width; // Reset pointer for each scanline 141 | } 142 | 143 | // Found a matching image! 144 | *(result) = (current - start) % width; 145 | *(result + 1) = (current - start) / width; 146 | return 1; 147 | 148 | next: 149 | if ( left >= width ) { 150 | left = 0; 151 | } 152 | } 153 | 154 | // TODO Remove the comment below for clean code 155 | // 156 | // This is how I checked the input data, writing the first few pixels at s or start 157 | // and then ImagePutClipboard and check each pixel's color in GIMP. 158 | // 159 | // It seems loading with ({sprite: fpath}) gives you the right buffer but 160 | // loading with (fpath, transparent_color) gives a buffer with no transparent pixels 161 | // even if the file had pixels with the matching transparent_color. 162 | // 163 | //for ( unsigned int k = 0; k < w*h/2; k++ ) { 164 | // *(result+k) = *(s + k%32); 165 | //} 166 | //return (w*h)/2/2; 167 | // 168 | 169 | return 0; 170 | } 171 | -------------------------------------------------------------------------------- /source/imagesearch3.c: -------------------------------------------------------------------------------- 1 | unsigned int imagesearch(unsigned int ** result, unsigned int capacity, unsigned int * start, unsigned int width, unsigned int height, unsigned int * s, unsigned int w, unsigned int h) { 2 | // width, height, start, current, end refer to the haystack (main image) 3 | // x, y, w, h, s, c, e refer to the needle (search image) 4 | unsigned int count = 0; 5 | unsigned int * current = start; 6 | unsigned int * end = start + width * (height - h); // Remaining area must be greater than search height 7 | 8 | int range_x = width - w; 9 | int range_y = height - h; // optimized away 10 | 11 | int x, y, offset; 12 | unsigned int * c; 13 | unsigned int * e; 14 | unsigned int * p; 15 | 16 | // Start off searching with pointers. 17 | while (current < end) { 18 | 19 | // Check if the current pixel matches the first pixel of subimage. 20 | if (*current == *s || *((unsigned char *) s + 3) == 0) { // just continue if search image is transparent 21 | 22 | // Convert current pointer to (x, y). 23 | offset = current - start; 24 | x = offset % width; 25 | y = offset / width; // optimized away 26 | 27 | // Check if (x, y) exceeds bounds. 28 | if (x > range_x) // range_y check is done above 29 | goto next; 30 | 31 | // Subimage loop. 32 | c = s; 33 | for (int i = 0; i < h; i++) { 34 | p = current + width * i; 35 | e = c + w; 36 | while (c < e) { 37 | if (*((unsigned char *) c + 3)) { // skip transparent pixels in search image 38 | if (*c != *p) 39 | goto next; 40 | } 41 | c++; // Here simply incrementing will interate the entire image 42 | p++; // Will be reset each run 43 | } 44 | } 45 | 46 | // Found matching image! 47 | if (count < capacity) 48 | *(result + count) = current; 49 | count++; 50 | } 51 | next: 52 | current++; 53 | } 54 | return count; 55 | } -------------------------------------------------------------------------------- /source/imagesearchall1.c: -------------------------------------------------------------------------------- 1 | unsigned int imagesearchall1(unsigned int ** result, unsigned int capacity, unsigned int * start, unsigned int width, unsigned int height, unsigned int * s, unsigned int w, unsigned int h) { 2 | // width, height, start, current, end refer to the haystack (main image) 3 | // x, y, w, h, s, c, e refer to the needle (search image) 4 | unsigned int count = 0; 5 | unsigned int * current = start; 6 | unsigned int * end = start + width * (height - h); // Remaining area must be greater than search height 7 | 8 | int range_x = width - w; 9 | int range_y = height - h; // optimized away 10 | 11 | int x, y, offset; 12 | unsigned int * c; 13 | unsigned int * e; 14 | unsigned int * p; 15 | 16 | // Start off searching with pointers. 17 | while (current < end) { 18 | 19 | // Check if the current pixel matches the first pixel of subimage. 20 | if (*current == *s || *((unsigned char *) s + 3) == 0) { // just continue if search image is transparent 21 | 22 | // Convert current pointer to (x, y). 23 | offset = current - start; 24 | x = offset % width; 25 | y = offset / width; // optimized away 26 | 27 | // Check if (x, y) exceeds bounds. 28 | if (x > range_x) // range_y check is done above 29 | goto next; 30 | 31 | // Subimage loop. 32 | c = s; 33 | for (int i = 0; i < h; i++) { 34 | p = current + width * i; 35 | e = c + w; 36 | while (c < e) { 37 | if (*((unsigned char *) c + 3)) { // skip transparent pixels in search image 38 | if (*c != *p) 39 | goto next; 40 | } 41 | c++; // Here simply incrementing will interate the entire image 42 | p++; // Will be reset each run 43 | } 44 | } 45 | 46 | // Found matching image! 47 | if (count < capacity) 48 | *(result + count) = current; 49 | count++; 50 | } 51 | next: 52 | current++; 53 | } 54 | return count; 55 | } -------------------------------------------------------------------------------- /source/imagesearchall2.c: -------------------------------------------------------------------------------- 1 | __attribute__((ms_abi)) // Tells GCC we want to compile for Windows. 2 | __attribute__ ((target ("sse4.2"))) // According to godbolt.org it doesn't do anything even with 3 | // __builtin_assume_aligned(ptr, 16) and -Ofast -msse4.2. 4 | // You might want to study SIMD instructions and if there is a good instruction to 5 | // take advantage of, go with inline assembly. 6 | unsigned int imagesearchall2(unsigned int * restrict result, unsigned int capacity, unsigned int * start, unsigned int width, unsigned int height, unsigned int * s, unsigned int w, unsigned int h, unsigned int coord_focus, unsigned short variation ) { 7 | // width, height, start, current, end, color refer to the haystack (main image) 8 | // x, y, w, h, s, c, e, cf refer to the needle (search image) 9 | unsigned int count = 0; 10 | 11 | // The location of the focused pixel 12 | unsigned short x; 13 | unsigned short y; 14 | 15 | if ( coord_focus ) { 16 | x = coord_focus >> sizeof(x)*8; 17 | y = coord_focus & 0x0000ffff; 18 | goto focus_determined; 19 | } 20 | 21 | // Try to locate the focused pixel in the middle of the sprite 22 | unsigned int * c = s + w/4 + h/4 * w; 23 | unsigned int * last_pixel = s + h * w; 24 | while(c < last_pixel) { 25 | for (unsigned int * e = c + w / 2; c < e; c++) { 26 | if ( *((unsigned char *) c + 3) ) { 27 | unsigned int offset = (c - s); 28 | x = offset % w; 29 | y = offset / w; 30 | goto focus_determined; 31 | } 32 | } 33 | c += w / 2; 34 | } 35 | 36 | // If every pixel in the middle was transparent 37 | // try to find a non-transparent pixel on the left-hand side. 38 | 39 | // This falls into an infinite loop if somehow the sprite doesn't have any 40 | // non-transparent pixel in the leftmost quarter. 41 | c = s + (w/4-1) + (h-1) * w; 42 | while(-1) { 43 | for (unsigned int * e = c - w / 4; c > e; c--) { 44 | if ( *((unsigned char *) c + 3) ) { 45 | unsigned int offset = (c - s); 46 | x = offset % w; 47 | y = offset / w; 48 | goto focus_determined; 49 | } 50 | } 51 | c -= w - w / 4; 52 | } 53 | 54 | focus_determined:; 55 | 56 | 57 | 58 | // Prepare the search. 59 | 60 | // The color on the focused pixel 61 | unsigned int cf = *(s + x + y * w); 62 | 63 | // We don't need to search in the narrow edge on the right-hand side 64 | unsigned int range_width = width - w; 65 | // Remaining area must be greater than search height 66 | unsigned int range_height = height - h; 67 | 68 | unsigned int * current = start; 69 | unsigned int * end = start + width * range_height + width; 70 | 71 | // Start the search. 72 | unsigned int left = 0; 73 | while ( current < end ) { 74 | 75 | unsigned int offset = current - start; 76 | //unsigned int left = offset % width; 77 | // TODO Remove the comment below 78 | // I believe div is worse than cmov'ing one constant number. 79 | // The code loses some readability though. 80 | unsigned int top = offset / width; // optimized away 81 | 82 | // Checks before subimage loop. 83 | 84 | // X-Range Check 85 | if ( left > range_width ) { 86 | current += w - 1; 87 | left = 0; 88 | continue; 89 | } 90 | // 91 | 92 | // Focused Pixel 93 | unsigned int * color; 94 | color = current + x + y * width; 95 | if ( variation == 0 ) { 96 | if ( cf != *color ) { 97 | current++; 98 | left++; 99 | goto next; 100 | } 101 | } else { 102 | for (int b = 0; b < 3; b++) { 103 | unsigned short diff = *((unsigned char *)&cf + b) - *((unsigned char *)color + b); 104 | if ( diff > variation && diff < (unsigned short)(0 - variation) ) { 105 | current++; 106 | left++; 107 | goto next; 108 | } 109 | } 110 | } 111 | 112 | // Subimage loop. 113 | unsigned int * c = s; 114 | color = current; 115 | for (int i = 0; i < h; i++) { 116 | unsigned int * e = c + w; 117 | while (c < e) { 118 | if ( *((unsigned char *) c + 3) ) { // Skip transparent pixels 119 | if ( variation == 0 ) { 120 | if ( *c != *color ) { 121 | current++; 122 | left++; 123 | goto next; 124 | } 125 | } else { 126 | for (int b = 0; b < 3; b++) { 127 | unsigned short diff = *((unsigned char *) c + b) - *((unsigned char *) color + b); 128 | if ( diff > variation && diff < (unsigned short)(0 - variation) ) { 129 | current++; 130 | left++; 131 | goto next; 132 | } 133 | } 134 | } 135 | } 136 | c++; // Iterate over the needle image 137 | color++; // Iterate over the haystack image 138 | } 139 | color = color - w + width; // Reset pointer for each scanline 140 | } 141 | 142 | // Found a matching image! 143 | if ( count < capacity ) { 144 | *(result + count * 2) = (current - start) % width; 145 | *(result + count * 2 + 1) = (current - start) / width; 146 | } 147 | count++; 148 | 149 | // XXX Let us not deal with overlapped matches 150 | current += w; 151 | left += w; 152 | 153 | next: 154 | if ( left >= width ) { 155 | left = 0; 156 | } 157 | } 158 | 159 | // Stash the coordinates of the focused pixel 160 | *(result + count * 2) = x << sizeof(x)*8 | y; 161 | 162 | // TODO Remove the comment below for clean code 163 | // 164 | // This is how I checked the input data, writing the first few pixels at s or start 165 | // and then ImagePutClipboard and check each pixel's color in GIMP. 166 | // 167 | // It seems loading with ({sprite: fpath}) gives you the right buffer but 168 | // loading with (fpath, transparent_color) gives a buffer with no transparent pixels 169 | // even if the file had pixels with the matching transparent_color. 170 | // 171 | //for ( unsigned int k = 0; k < w*h/2; k++ ) { 172 | // *(result+k) = *(s + k%32); 173 | //} 174 | //return (w*h)/2/2; 175 | // 176 | 177 | return count; 178 | } 179 | -------------------------------------------------------------------------------- /source/pixelsearch1.c: -------------------------------------------------------------------------------- 1 | // 1x baseline. Searches for a single pixel. 2 | unsigned int * pixelsearch1(unsigned int * start, unsigned int * end, unsigned int color) { 3 | while (start < end) { 4 | if (*start == color) 5 | return start; 6 | start++; 7 | } 8 | return start; // start == end if no match. 9 | } -------------------------------------------------------------------------------- /source/pixelsearch1x.c: -------------------------------------------------------------------------------- 1 | // 2x faster. Uses 128-bit registers and searches 4 pixels at once. 2 | #include 3 | 4 | unsigned int * pixelsearch1x(unsigned int * start, unsigned int * end, unsigned int color) { 5 | 6 | // Create a vector of four copies of the target color. 7 | __m128i vcolor = _mm_set1_epi32(color); 8 | 9 | // Loop over start pointer with a step of four unsigned integers. 10 | while (start < end - 3) { 11 | 12 | // Load four unsigned integers from start into a vector. 13 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 14 | 15 | // Compare vstart and vcolor for equality. 16 | __m128i vcmp = _mm_cmpeq_epi32(vstart, vcolor); 17 | 18 | // Create a mask from each byte (using the most significant bit) in vcmp. 19 | int mask = _mm_movemask_epi8(vcmp); 20 | 21 | // If the mask is nonzero, there is at least one match. 22 | if (mask != 0) 23 | break; 24 | 25 | // Increment start by four unsigned integers. 26 | start += 4; 27 | } 28 | 29 | // Clean up any remaining elements. 30 | while (start < end) { 31 | if (*start == color) 32 | return start; 33 | start++; 34 | } 35 | 36 | return start; // start == end if no match. 37 | } -------------------------------------------------------------------------------- /source/pixelsearch1x2.c: -------------------------------------------------------------------------------- 1 | // 2.5x faster. Uses loop unrolling to search 8 pixels at once. 2 | #include 3 | 4 | unsigned int * pixelsearch1x2(unsigned int * start, unsigned int * end, unsigned int color) { 5 | 6 | // Create a vector of four copies of the target color. 7 | __m128i vcolor = _mm_set1_epi32(color); 8 | 9 | // Loop over start pointer with a step of four unsigned integers. 10 | while (start < end - 7) { 11 | 12 | // Load four unsigned integers from start into a vector. 13 | __m128i vstart1 = _mm_loadu_si128((__m128i *) start); 14 | __m128i vstart2 = _mm_loadu_si128((__m128i *) start + 4); 15 | 16 | // Compare vstart and vcolor for equality. 17 | __m128i vcmp1 = _mm_cmpeq_epi32(vstart1, vcolor); 18 | __m128i vcmp2 = _mm_cmpeq_epi32(vstart2, vcolor); 19 | 20 | // Create a mask from each byte (using the most significant bit) in vcmp. 21 | int mask1 = _mm_movemask_epi8(vcmp1); 22 | int mask2 = _mm_movemask_epi8(vcmp2); 23 | 24 | // If the mask is nonzero, there is at least one match. 25 | if (mask1 != 0 || mask2 != 0) 26 | break; 27 | 28 | // Increment start by four unsigned integers. 29 | start += 8; 30 | } 31 | 32 | // Clean up any remaining elements. 33 | while (start < end) { 34 | if (*start == color) 35 | return start; 36 | start++; 37 | } 38 | 39 | return start; // start == end if no match. 40 | } -------------------------------------------------------------------------------- /source/pixelsearch1y.c: -------------------------------------------------------------------------------- 1 | // 2.75x faster. Uses 256-bit registers and searches 8 pixels at once. 2 | #include 3 | 4 | unsigned int * pixelsearch1y(unsigned int * start, unsigned int * end, unsigned int color) { 5 | 6 | // Create a vector of eight copies of the target color. 7 | __m256i vcolor = _mm256_set1_epi32(color); 8 | 9 | // Loop over start pointer with a step of eight unsigned integers. 10 | while (start < end - 7) { 11 | 12 | // Load eight unsigned integers from start into a vector. 13 | __m256i vstart = _mm256_loadu_si256((__m256i *) start); 14 | 15 | // Compare vstart and vcolor for equality. 16 | __m256i vcmp = _mm256_cmpeq_epi32(vstart, vcolor); 17 | 18 | // Create a mask from each byte (using the most significant bit) in vcmp. 19 | int mask = _mm256_movemask_epi8(vcmp); 20 | 21 | // If the mask is nonzero, there is at least one match. 22 | if (mask != 0) 23 | break; 24 | 25 | // Increment start by eight unsigned integers. 26 | start += 8; 27 | } 28 | 29 | // Clean up any remaining elements. 30 | while (start < end) { 31 | if (*start == color) 32 | return start; 33 | start++; 34 | } 35 | 36 | return start; // start == end if no match. 37 | } -------------------------------------------------------------------------------- /source/pixelsearch2.c: -------------------------------------------------------------------------------- 1 | // 0.55x slower. Searches for a range of pixels. 2 | unsigned int * pixelsearch2(unsigned int * start, unsigned int * end, unsigned char rh, unsigned char rl, unsigned char gh, unsigned char gl, unsigned char bh, unsigned char bl) { 3 | unsigned char r, g, b; 4 | while (start < end) { 5 | r = *((unsigned char *) start + 2); 6 | g = *((unsigned char *) start + 1); 7 | b = *((unsigned char *) start + 0); 8 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) 9 | return start; 10 | start++; 11 | } 12 | return start; // start == end if no match. 13 | } -------------------------------------------------------------------------------- /source/pixelsearch2x.c: -------------------------------------------------------------------------------- 1 | // 1.5x faster. Uses 128-bit registers and searches 4 pixels at once. 2 | #include 3 | 4 | #define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) 5 | #define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) 6 | #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) 7 | #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) 8 | 9 | unsigned int * pixelsearch2x(unsigned int * start, unsigned int * end, unsigned char rh, unsigned char rl, unsigned char gh, unsigned char gl, unsigned char bh, unsigned char bl) { 10 | 11 | // Comparison mask for unsigned integers. 12 | __m128i vmask = _mm_set1_epi32(0xFFFFFFFF); 13 | 14 | // Reconstruct ARGB from individual color channels. 15 | unsigned int h = (0xFF << 24 | rh << 16 | gh << 8 | bh << 0); 16 | unsigned int l = (0x00 << 24 | rl << 16 | gl << 8 | bl << 0); 17 | 18 | // Create a vector of four copies of the target color. 19 | __m128i vh = _mm_set1_epi32(h); 20 | __m128i vl = _mm_set1_epi32(l); 21 | 22 | // Loop over start pointer with a step of four unsigned integers. 23 | while (start < end - 3) { 24 | 25 | // Load four unsigned integers from start into a vector. 26 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 27 | 28 | // Compare vstart <= vh and vstart >= vl. Note these are macros. 29 | __m128i v2 = _mm_cmple_epu8(vstart, vh); 30 | __m128i v3 = _mm_cmpge_epu8(vstart, vl); 31 | __m128i v1234 = _mm_and_si128(v2, v3); 32 | 33 | // Check if any of the four unsigned integers matched. 34 | __m128i vcmp = _mm_cmpeq_epi32(v1234, vmask); 35 | 36 | // Create a mask from each byte (using the most significant bit) in vcmp. 37 | int mask = _mm_movemask_epi8(vcmp); 38 | 39 | // If the mask is nonzero, there is at least one match. 40 | if (mask != 0) 41 | break; 42 | 43 | // Increment start by four unsigned integers. 44 | start += 4; 45 | } 46 | 47 | // Clean up any remaining elements. 48 | unsigned char r, g, b; 49 | while (start < end) { 50 | r = *((unsigned char *) start + 2); 51 | g = *((unsigned char *) start + 1); 52 | b = *((unsigned char *) start + 0); 53 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) 54 | return start; 55 | start++; 56 | } 57 | 58 | return start; // start == end if no match. 59 | } -------------------------------------------------------------------------------- /source/pixelsearch3.c: -------------------------------------------------------------------------------- 1 | // 0.5x slower. Searches for a single pixel from a list. 2 | unsigned int * pixelsearch3(unsigned int * start, unsigned int * end, unsigned int * colors, unsigned int length) { 3 | while (start < end) { 4 | for (int i = 0; i < length; i++) 5 | if (*start == colors[i]) 6 | return start; 7 | start++; 8 | } 9 | return start; // start == end if no match. 10 | } -------------------------------------------------------------------------------- /source/pixelsearch3x.c: -------------------------------------------------------------------------------- 1 | // 2.75x faster. Uses 128-bit registers, checks 3 pixels, searching 12 pixels at once. 2 | #include 3 | 4 | unsigned int * pixelsearch3x(unsigned int * start, unsigned int * end, unsigned int * colors, unsigned int length) { 5 | 6 | // Save the starting pointer position. 7 | unsigned int * current = start; 8 | 9 | // Offset of pointer to stored colors. 10 | int i = 0; 11 | 12 | // Number of colors left. 13 | int n; 14 | 15 | // Somehow there are diminishing returns past running 3 checks at once. 16 | int check; 17 | 18 | // Determine how many matches to be run in sequence. 19 | enter: 20 | n = length - i; 21 | if (n == 0) 22 | return end; // Somehow goto exit creates a bigger binary in mcodeforgcc. 23 | if (n == 1) { 24 | check = 1; 25 | goto init_1; 26 | } 27 | if (n == 2) { 28 | check = 2; 29 | goto init_2; 30 | } 31 | if (n > 2) { 32 | check = 3; 33 | goto init_3; // Segue into next label. 34 | } 35 | 36 | // Create a vector of four copies of the target color. 37 | init_3: 38 | __m128i vcolor3 = _mm_set1_epi32(*(colors + i + 2)); 39 | init_2: 40 | __m128i vcolor2 = _mm_set1_epi32(*(colors + i + 1)); 41 | init_1: 42 | __m128i vcolor1 = _mm_set1_epi32(*(colors + i + 0)); 43 | 44 | // Restore starting pointer for each run. 45 | start = current; 46 | 47 | // Loop over start pointer with a step of four unsigned integers. 48 | if (n == 1) 49 | goto check_1; 50 | if (n == 2) 51 | goto check_2; 52 | if (n > 2) 53 | goto check_3; // Segue into next label. 54 | 55 | check_3: 56 | while (start < end - 3) { 57 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 58 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor1, vstart)); 59 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor2, vstart)); 60 | int mask3 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor3, vstart)); 61 | if (mask1 != 0 || mask2 != 0 || mask3 != 0) 62 | goto exit; 63 | start += 4; 64 | } 65 | i += 3; 66 | goto enter; 67 | 68 | check_2: 69 | while (start < end - 3) { 70 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 71 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor1, vstart)); 72 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor2, vstart)); 73 | if (mask1 != 0 || mask2 != 0) 74 | goto exit; 75 | start += 4; 76 | } 77 | i += 2; 78 | goto enter; 79 | 80 | check_1: 81 | while (start < end - 3) { 82 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 83 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor1, vstart)); 84 | if (mask1 != 0) 85 | goto exit; 86 | start += 4; 87 | } 88 | i += 1; 89 | goto enter; 90 | 91 | // Clean up any remaining elements. 92 | exit: 93 | while (start < end) { 94 | for (int j = 0; j < check; j++) 95 | if (*start == colors[i + j]) 96 | return start; 97 | start++; 98 | } 99 | 100 | return start; // start == end if no match. 101 | } -------------------------------------------------------------------------------- /source/pixelsearch4.c: -------------------------------------------------------------------------------- 1 | // 0.5x slower. Searches for a range of pixels from a list. 2 | unsigned int * pixelsearch4(unsigned int * start, unsigned int * end, unsigned int * high, unsigned int * low, unsigned int length) { 3 | unsigned char r, g, b, rh, gh, bh, rl, gl, bl; 4 | while (start < end) { 5 | 6 | r = *((unsigned char *) start + 2); 7 | g = *((unsigned char *) start + 1); 8 | b = *((unsigned char *) start + 0); 9 | 10 | for (int i = 0; i < length; i++) { 11 | 12 | rh = *((unsigned char *) high + 4*i + 2); 13 | gh = *((unsigned char *) high + 4*i + 1); 14 | bh = *((unsigned char *) high + 4*i + 0); 15 | rl = *((unsigned char *) low + 4*i + 2); 16 | gl = *((unsigned char *) low + 4*i + 1); 17 | bl = *((unsigned char *) low + 4*i + 0); 18 | 19 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) 20 | return start; 21 | } 22 | start++; 23 | } 24 | return start; // start == end if no match. 25 | } -------------------------------------------------------------------------------- /source/pixelsearch4x.c: -------------------------------------------------------------------------------- 1 | // 2x faster. Uses 128-bit registers, checks 3 pixel ranges, searching 12 pixels at once. 2 | #include 3 | 4 | #define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) 5 | #define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) 6 | #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) 7 | #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) 8 | 9 | unsigned int * pixelsearch4x(unsigned int * start, unsigned int * end, unsigned int * high, unsigned int * low, unsigned int length) { 10 | 11 | // Save the starting pointer position. 12 | unsigned int * current = start; 13 | 14 | // Offset of pointer to stored colors. 15 | int i = 0; 16 | 17 | // Number of colors left. 18 | int n; 19 | 20 | // Somehow there are diminishing returns past running 3 checks at once. 21 | int check; 22 | 23 | // Comparison mask for unsigned integers. 24 | __m128i vmask = _mm_set1_epi32(0xFFFFFFFF); 25 | 26 | // Determine how many matches to be run in sequence. 27 | enter: 28 | n = length - i; 29 | if (n == 0) 30 | return end; // Somehow goto exit creates a bigger binary in mcodeforgcc. 31 | if (n == 1) { 32 | check = 1; 33 | goto init_1; 34 | } 35 | if (n == 2) { 36 | check = 2; 37 | goto init_2; 38 | } 39 | if (n > 2) { 40 | check = 3; 41 | goto init_3; // Segue into next label. 42 | } 43 | 44 | // Create a vector of four copies of the target color. 45 | init_3: 46 | __m128i vh3 = _mm_set1_epi32(*(high + i + 2)); 47 | __m128i vl3 = _mm_set1_epi32(*(low + i + 2)); 48 | init_2: 49 | __m128i vh2 = _mm_set1_epi32(*(high + i + 1)); 50 | __m128i vl2 = _mm_set1_epi32(*(low + i + 1)); 51 | init_1: 52 | __m128i vh1 = _mm_set1_epi32(*(high + i + 0)); 53 | __m128i vl1 = _mm_set1_epi32(*(low + i + 0)); 54 | 55 | // Restore starting pointer for each run. 56 | start = current; 57 | 58 | // Loop over start pointer with a step of four unsigned integers. 59 | if (n == 1) 60 | goto check_1; 61 | if (n == 2) 62 | goto check_2; 63 | if (n > 2) 64 | goto check_3; // Segue into next label. 65 | 66 | check_3: 67 | while (start < end - 3) { 68 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 69 | 70 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 71 | _mm_and_si128( 72 | _mm_cmple_epu8(vstart, vh1), 73 | _mm_cmpge_epu8(vstart, vl1)))); 74 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 75 | _mm_and_si128( 76 | _mm_cmple_epu8(vstart, vh2), 77 | _mm_cmpge_epu8(vstart, vl2)))); 78 | int mask3 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 79 | _mm_and_si128( 80 | _mm_cmple_epu8(vstart, vh3), 81 | _mm_cmpge_epu8(vstart, vl3)))); 82 | 83 | if (mask1 != 0 || mask2 != 0 || mask3 != 0) 84 | goto exit; 85 | 86 | start += 4; 87 | } 88 | i += 3; 89 | goto enter; 90 | 91 | check_2: 92 | while (start < end - 3) { 93 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 94 | 95 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 96 | _mm_and_si128( 97 | _mm_cmple_epu8(vstart, vh1), 98 | _mm_cmpge_epu8(vstart, vl1)))); 99 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 100 | _mm_and_si128( 101 | _mm_cmple_epu8(vstart, vh2), 102 | _mm_cmpge_epu8(vstart, vl2)))); 103 | 104 | if (mask1 != 0 || mask2 != 0) 105 | goto exit; 106 | 107 | start += 4; 108 | } 109 | i += 2; 110 | goto enter; 111 | 112 | check_1: 113 | while (start < end - 3) { 114 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 115 | 116 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 117 | _mm_and_si128( 118 | _mm_cmple_epu8(vstart, vh1), 119 | _mm_cmpge_epu8(vstart, vl1)))); 120 | 121 | if (mask1 != 0) 122 | goto exit; 123 | 124 | start += 4; 125 | } 126 | i += 1; 127 | goto enter; 128 | 129 | // Clean up any remaining elements. 130 | exit: 131 | unsigned char r, g, b, rh, gh, bh, rl, gl, bl; 132 | while (start < end) { 133 | 134 | r = *((unsigned char *) start + 2); 135 | g = *((unsigned char *) start + 1); 136 | b = *((unsigned char *) start + 0); 137 | 138 | for (int j = 0; j < check; j++) { 139 | 140 | rh = *((unsigned char *) high + 4*(i + j) + 2); 141 | gh = *((unsigned char *) high + 4*(i + j) + 1); 142 | bh = *((unsigned char *) high + 4*(i + j) + 0); 143 | rl = *((unsigned char *) low + 4*(i + j) + 2); 144 | gl = *((unsigned char *) low + 4*(i + j) + 1); 145 | bl = *((unsigned char *) low + 4*(i + j) + 0); 146 | 147 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) 148 | return start; 149 | } 150 | start++; 151 | } 152 | 153 | return start; // start == end if no match. 154 | } -------------------------------------------------------------------------------- /source/pixelsearchall1.c: -------------------------------------------------------------------------------- 1 | unsigned int pixelsearchall1(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned int color) { 2 | unsigned int count = 0; 3 | while (start < end) { 4 | if (*start == color) { 5 | if (count < limit) 6 | *(result + count) = start; 7 | count++; 8 | } 9 | start++; 10 | } 11 | return count; 12 | } -------------------------------------------------------------------------------- /source/pixelsearchall1x.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned int pixelsearchall1x(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned int color) { 4 | 5 | // Number of 32-bit integers the register can iterate over. 6 | int iter = 4; 7 | 8 | // Track number of matching searches. 9 | unsigned int count = 0; 10 | 11 | // Create a vector of four copies of the target color. 12 | __m128i vcolor = _mm_set1_epi32(color); 13 | 14 | // Loop over start pointer with a step of four unsigned integers. 15 | loop: 16 | while (start < end - 3) { 17 | 18 | // Load four unsigned integers from start into a vector. 19 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 20 | 21 | // Compare vstart and vcolor for equality. 22 | __m128i vcmp = _mm_cmpeq_epi32(vstart, vcolor); 23 | 24 | // Create a mask from each byte (using the most significant bit) in vcmp. 25 | int mask = _mm_movemask_epi8(vcmp); 26 | 27 | // If the mask is nonzero, there is at least one match. 28 | if (mask != 0) 29 | break; 30 | 31 | // Increment start by four unsigned integers. 32 | start += 4; 33 | } 34 | 35 | // Clean up any remaining elements. 36 | while (start < end) { 37 | if (iter > 0) { 38 | if (*start == color) { 39 | if (count < limit) 40 | *(result + count) = start; 41 | count++; 42 | } 43 | iter--; 44 | } 45 | else { 46 | iter = 4; 47 | goto loop; 48 | } 49 | start++; 50 | } 51 | 52 | return count; 53 | } -------------------------------------------------------------------------------- /source/pixelsearchall2.c: -------------------------------------------------------------------------------- 1 | unsigned int pixelsearchall2(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned char rh, unsigned char rl, unsigned char gh, unsigned char gl, unsigned char bh, unsigned char bl) { 2 | unsigned int count = 0; 3 | unsigned char r, g, b; 4 | while (start < end) { 5 | r = *((unsigned char *) start + 2); 6 | g = *((unsigned char *) start + 1); 7 | b = *((unsigned char *) start + 0); 8 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) { 9 | if (count < limit) 10 | *(result + count) = start; 11 | count++; 12 | } 13 | start++; 14 | } 15 | return count; 16 | } -------------------------------------------------------------------------------- /source/pixelsearchall2x.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) 4 | #define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) 5 | #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) 6 | #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) 7 | 8 | unsigned int pixelsearchall2x(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned char rh, unsigned char rl, unsigned char gh, unsigned char gl, unsigned char bh, unsigned char bl) { 9 | 10 | // Number of 32-bit integers the register can iterate over. 11 | int iter = 4; 12 | 13 | // Track number of matching searches. 14 | unsigned int count = 0; 15 | 16 | // Reconstruct ARGB from individual color channels. 17 | unsigned int h = (0xFF << 24 | rh << 16 | gh << 8 | bh << 0); 18 | unsigned int l = (0x00 << 24 | rl << 16 | gl << 8 | bl << 0); 19 | 20 | // Create a vector of four copies of the target color. 21 | __m128i vh = _mm_set1_epi32(h); 22 | __m128i vl = _mm_set1_epi32(l); 23 | __m128i vmask = _mm_set1_epi32(0xFFFFFFFF); 24 | 25 | // Loop over start pointer with a step of four unsigned integers. 26 | loop: 27 | while (start < end - 3) { 28 | 29 | // Load four unsigned integers from start into a vector. 30 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 31 | 32 | // Compare vstart <= vh and vstart >= vl. Note these are macros. 33 | __m128i v2 = _mm_cmple_epu8(vstart, vh); 34 | __m128i v3 = _mm_cmpge_epu8(vstart, vl); 35 | __m128i v1234 = _mm_and_si128(v2, v3); 36 | 37 | // Compare equality to four unsigned integers. 38 | __m128i vcmp = _mm_cmpeq_epi32(v1234, vmask); 39 | 40 | // Create a mask from each byte (using the most significant bit) in vcmp. 41 | int mask = _mm_movemask_epi8(vcmp); 42 | 43 | // If the mask is nonzero, there is at least one match. 44 | if (mask != 0) 45 | break; 46 | 47 | // Increment start by four unsigned integers. 48 | start += 4; 49 | } 50 | 51 | // Clean up any remaining elements. 52 | unsigned char r, g, b; 53 | while (start < end) { 54 | if (iter > 0) { 55 | r = *((unsigned char *) start + 2); 56 | g = *((unsigned char *) start + 1); 57 | b = *((unsigned char *) start + 0); 58 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) { 59 | if (count < limit) 60 | *(result + count) = start; 61 | count++; 62 | } 63 | iter--; 64 | } 65 | else { 66 | iter = 4; 67 | goto loop; 68 | } 69 | start++; 70 | } 71 | 72 | return count; 73 | } -------------------------------------------------------------------------------- /source/pixelsearchall3.c: -------------------------------------------------------------------------------- 1 | unsigned int pixelsearchall3(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned int * colors, unsigned int length) { 2 | unsigned int count = 0; 3 | while (start < end) { 4 | for (int i = 0; i < length; i++) 5 | if (*start == colors[i]) { 6 | if (count < limit) 7 | *(result + count) = start; 8 | count++; 9 | } 10 | start++; 11 | } 12 | return count; 13 | } -------------------------------------------------------------------------------- /source/pixelsearchall3x.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned int pixelsearchall3x(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned int * colors, unsigned int length) { 4 | 5 | // Number of 32-bit integers the register can iterate over. 6 | int iter = 4; 7 | 8 | // Track number of matching searches. 9 | unsigned int count = 0; 10 | 11 | // Save the starting pointer position. 12 | unsigned int * current = start; 13 | 14 | // Offset of pointer to stored colors. 15 | int i = 0; 16 | 17 | // Number of colors left. 18 | int n; 19 | 20 | // Somehow there are diminishing returns past running 3 checks at once. 21 | int check; 22 | 23 | // Determine how many matches to be run in sequence. 24 | enter: 25 | n = length - i; 26 | if (n == 0) 27 | return count; // Somehow goto exit creates a bigger binary in mcodeforgcc. 28 | if (n == 1) { 29 | check = 1; 30 | goto init_1; 31 | } 32 | if (n == 2) { 33 | check = 2; 34 | goto init_2; 35 | } 36 | if (n > 2) { 37 | check = 3; 38 | goto init_3; // Segue into next label. 39 | } 40 | 41 | // Create a vector of four copies of the target color. 42 | init_3: 43 | __m128i vcolor3 = _mm_set1_epi32(*(colors + i + 2)); 44 | init_2: 45 | __m128i vcolor2 = _mm_set1_epi32(*(colors + i + 1)); 46 | init_1: 47 | __m128i vcolor1 = _mm_set1_epi32(*(colors + i + 0)); 48 | 49 | // Restore starting pointer for each run. 50 | start = current; 51 | 52 | // Loop over start pointer with a step of four unsigned integers. 53 | loop: 54 | if (n == 1) 55 | goto check_1; 56 | if (n == 2) 57 | goto check_2; 58 | if (n > 2) 59 | goto check_3; // Segue into next label. 60 | 61 | check_3: 62 | while (start < end - 3) { 63 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 64 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor1, vstart)); 65 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor2, vstart)); 66 | int mask3 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor3, vstart)); 67 | if (mask1 != 0 || mask2 != 0 || mask3 != 0) 68 | goto exit; 69 | start += 4; 70 | } 71 | i += 3; 72 | goto enter; 73 | 74 | check_2: 75 | while (start < end - 3) { 76 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 77 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor1, vstart)); 78 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor2, vstart)); 79 | if (mask1 != 0 || mask2 != 0) 80 | goto exit; 81 | start += 4; 82 | } 83 | i += 2; 84 | goto enter; 85 | 86 | check_1: 87 | while (start < end - 3) { 88 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 89 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vcolor1, vstart)); 90 | if (mask1 != 0) 91 | goto exit; 92 | start += 4; 93 | } 94 | i += 1; 95 | goto enter; 96 | 97 | // Clean up any remaining elements. 98 | exit: 99 | while (start < end) { 100 | if (iter > 0) { 101 | for (int j = 0; j < check; j++) 102 | if (*start == colors[i + j]) { 103 | if (count < limit) 104 | *(result + count) = start; 105 | count++; 106 | } 107 | iter--; 108 | } 109 | else { 110 | iter = 4; 111 | goto loop; 112 | } 113 | start++; 114 | } 115 | 116 | return count; 117 | } -------------------------------------------------------------------------------- /source/pixelsearchall4.c: -------------------------------------------------------------------------------- 1 | unsigned int pixelsearchall4(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned int * high, unsigned int * low, unsigned int length) { 2 | unsigned int count = 0; 3 | unsigned char r, g, b, rh, gh, bh, rl, gl, bl; 4 | while (start < end) { 5 | 6 | r = *((unsigned char *) start + 2); 7 | g = *((unsigned char *) start + 1); 8 | b = *((unsigned char *) start + 0); 9 | 10 | for (int i = 0; i < length; i++) { 11 | 12 | rh = *((unsigned char *) high + 4*i + 2); 13 | gh = *((unsigned char *) high + 4*i + 1); 14 | bh = *((unsigned char *) high + 4*i + 0); 15 | rl = *((unsigned char *) low + 4*i + 2); 16 | gl = *((unsigned char *) low + 4*i + 1); 17 | bl = *((unsigned char *) low + 4*i + 0); 18 | 19 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) { 20 | if (count < limit) 21 | *(result + count) = start; 22 | count++; 23 | } 24 | } 25 | start++; 26 | } 27 | return count; 28 | } -------------------------------------------------------------------------------- /source/pixelsearchall4x.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) 4 | #define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) 5 | #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) 6 | #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) 7 | 8 | unsigned int pixelsearchall4x(unsigned int ** result, unsigned int limit, unsigned int * start, unsigned int * end, unsigned int * high, unsigned int * low, unsigned int length) { 9 | 10 | // Number of 32-bit integers the register can iterate over. 11 | int iter = 4; 12 | 13 | // Track number of matching searches. 14 | unsigned int count = 0; 15 | 16 | // Save the starting pointer position. 17 | unsigned int * current = start; 18 | 19 | // Offset of pointer to stored colors. 20 | int i = 0; 21 | 22 | // Number of colors left. 23 | int n; 24 | 25 | // Somehow there are diminishing returns past running 3 checks at once. 26 | int check; 27 | 28 | // Comparison mask for unsigned integers. 29 | __m128i vmask = _mm_set1_epi32(0xFFFFFFFF); 30 | 31 | // Determine how many matches to be run in sequence. 32 | enter: 33 | n = length - i; 34 | if (n == 0) 35 | return count; // Somehow goto exit creates a bigger binary in mcodeforgcc. 36 | if (n == 1) { 37 | check = 1; 38 | goto init_1; 39 | } 40 | if (n == 2) { 41 | check = 2; 42 | goto init_2; 43 | } 44 | if (n > 2) { 45 | check = 3; 46 | goto init_3; // Segue into next label. 47 | } 48 | 49 | // Create a vector of four copies of the target color. 50 | init_3: 51 | __m128i vh3 = _mm_set1_epi32(*(high + i + 2)); 52 | __m128i vl3 = _mm_set1_epi32(*(low + i + 2)); 53 | init_2: 54 | __m128i vh2 = _mm_set1_epi32(*(high + i + 1)); 55 | __m128i vl2 = _mm_set1_epi32(*(low + i + 1)); 56 | init_1: 57 | __m128i vh1 = _mm_set1_epi32(*(high + i + 0)); 58 | __m128i vl1 = _mm_set1_epi32(*(low + i + 0)); 59 | 60 | // Restore starting pointer for each run. 61 | start = current; 62 | 63 | // Loop over start pointer with a step of four unsigned integers. 64 | loop: 65 | if (n == 1) 66 | goto check_1; 67 | if (n == 2) 68 | goto check_2; 69 | if (n > 2) 70 | goto check_3; // Segue into next label. 71 | 72 | check_3: 73 | while (start < end - 3) { 74 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 75 | 76 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 77 | _mm_and_si128( 78 | _mm_cmple_epu8(vstart, vh1), 79 | _mm_cmpge_epu8(vstart, vl1)))); 80 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 81 | _mm_and_si128( 82 | _mm_cmple_epu8(vstart, vh2), 83 | _mm_cmpge_epu8(vstart, vl2)))); 84 | int mask3 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 85 | _mm_and_si128( 86 | _mm_cmple_epu8(vstart, vh3), 87 | _mm_cmpge_epu8(vstart, vl3)))); 88 | 89 | if (mask1 != 0 || mask2 != 0 || mask3 != 0) 90 | goto exit; 91 | 92 | start += 4; 93 | } 94 | i += 3; 95 | goto enter; 96 | 97 | check_2: 98 | while (start < end - 3) { 99 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 100 | 101 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 102 | _mm_and_si128( 103 | _mm_cmple_epu8(vstart, vh1), 104 | _mm_cmpge_epu8(vstart, vl1)))); 105 | int mask2 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 106 | _mm_and_si128( 107 | _mm_cmple_epu8(vstart, vh2), 108 | _mm_cmpge_epu8(vstart, vl2)))); 109 | 110 | if (mask1 != 0 || mask2 != 0) 111 | goto exit; 112 | 113 | start += 4; 114 | } 115 | i += 2; 116 | goto enter; 117 | 118 | check_1: 119 | while (start < end - 3) { 120 | __m128i vstart = _mm_loadu_si128((__m128i *) start); 121 | 122 | int mask1 = _mm_movemask_epi8(_mm_cmpeq_epi32(vmask, 123 | _mm_and_si128( 124 | _mm_cmple_epu8(vstart, vh1), 125 | _mm_cmpge_epu8(vstart, vl1)))); 126 | 127 | if (mask1 != 0) 128 | goto exit; 129 | 130 | start += 4; 131 | } 132 | i += 1; 133 | goto enter; 134 | 135 | // Clean up any remaining elements. 136 | exit: 137 | unsigned char r, g, b, rh, gh, bh, rl, gl, bl; 138 | while (start < end) { 139 | if (iter > 0) { 140 | r = *((unsigned char *) start + 2); 141 | g = *((unsigned char *) start + 1); 142 | b = *((unsigned char *) start + 0); 143 | 144 | for (int j = 0; j < check; j++) { 145 | 146 | rh = *((unsigned char *) high + 4*(i + j) + 2); 147 | gh = *((unsigned char *) high + 4*(i + j) + 1); 148 | bh = *((unsigned char *) high + 4*(i + j) + 0); 149 | rl = *((unsigned char *) low + 4*(i + j) + 2); 150 | gl = *((unsigned char *) low + 4*(i + j) + 1); 151 | bl = *((unsigned char *) low + 4*(i + j) + 0); 152 | 153 | if (rh >= r && r >= rl && gh >= g && g >= gl && bh >= b && b >= bl) { 154 | if (count < limit) 155 | *(result + count) = start; 156 | count++; 157 | } 158 | } 159 | iter--; 160 | } 161 | else { 162 | iter = 4; 163 | goto loop; 164 | } 165 | start++; 166 | } 167 | 168 | return count; 169 | } -------------------------------------------------------------------------------- /source/setalpha.c: -------------------------------------------------------------------------------- 1 | void setalpha(unsigned int * start, unsigned int * end, unsigned char alpha) { 2 | while (start < end) { 3 | *((unsigned char *) start + 3) = alpha; 4 | start++; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /source/transcolor.c: -------------------------------------------------------------------------------- 1 | void transcolor(unsigned int * start, unsigned int * end, unsigned int color, unsigned char alpha) { 2 | while (start < end) { 3 | if ((*start & 0xFFFFFF) == (color & 0xFFFFFF)) 4 | *((unsigned char *) start + 3) = alpha; 5 | start++; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/0. Show All Inputs.ahk: -------------------------------------------------------------------------------- 1 | #include *i ..\ImagePut%A_TrayMenu%.ahk 2 | #include *i ..\ImagePut (for v%true%).ahk 3 | #singleinstance force 4 | 5 | ; SUCCESS: All 27 images show up 6 | ; FAIL: An error appears 7 | 8 | A := WinExist("A") 9 | ImagePut.gdiplusStartup() 10 | 11 | ImagePutClipboard("https://raw.githubusercontent.com/iseahound/test_images/main/insta.png") 12 | ImagePutWindow(ClipboardAll, "1. ClipboardPng") 13 | 14 | ImagePutClipboard("https://picsum.photos/1200") 15 | ImagePutWindow(ClipboardAll, "2. Clipboard") 16 | 17 | safearray := ImagePutSafeArray("https://picsum.photos/1100") 18 | ImagePutWindow(safearray, "3. SafeArray") 19 | 20 | ImagePutWindow([0, 0, 1000, 1000], "4. Screenshot") 21 | 22 | hwnd := ImageShow("https://picsum.photos/900") 23 | ImagePutWindow([0, 0, 900, 900, "ahk_id" hwnd], "5. Screenshot relative to window") 24 | ImageDestroy(hwnd) 25 | 26 | hwnd := ImageShow("https://picsum.photos/800") 27 | ImagePutWindow({hwnd: hwnd}, "6. Window with hwnd property") 28 | ImageDestroy(hwnd) 29 | 30 | pBitmap := ImagePutBitmap("https://picsum.photos/700") 31 | ImagePutWindow({pBitmap: pBitmap}, "7. Object (has pBitmap property)") 32 | 33 | encodedbuf := ImagePutEncodedBuffer("https://picsum.photos/600") 34 | ImagePutWindow(encodedbuf, "8. EncodedBuffer") 35 | 36 | buf := ImagePutBuffer("https://picsum.photos/500") 37 | ImagePutWindow({ptr: buf.ptr, width: buf.width, height: buf.height}, "9. Buffer") 38 | 39 | ImagePutWindow(0, "10. All Monitors") 40 | 41 | ImagePutWindow(1, "11. Primary Monitor") 42 | 43 | ; ImagePutDesktop Broken by 24H2? Using a SVG test as a replacement for now... 44 | hwnd := ImageShow("https://raw.githubusercontent.com/iseahound/test_images/refs/heads/main/compass.svg") 45 | ;ImagePutWindow("desktop", "12. Desktop") 46 | ;ImageDestroy(hwnd) 47 | 48 | ImagePutWindow("wallpaper", "13. Wallpaper") 49 | 50 | ImagePutWindow(A_Cursor, "14. Cursor") 51 | 52 | ImagePutWindow("https://raw.githubusercontent.com/iseahound/test_images/main/pepper-creative-brochure.pdf", "15. Url") 53 | 54 | filepath := ImagePutFile("https://picsum.photos/1200") 55 | ImagePutWindow(filepath, "16. File") 56 | ImageDestroy(filepath) 57 | 58 | ImagePutWindow(A, "17. Window (Current Active Window)") 59 | 60 | sharedbuf := ImagePutSharedBuffer("https://picsum.photos/1100", "alice") 61 | ImagePutWindow("alice", "18. SharedBuffer") 62 | 63 | hex := ImagePutHex("https://picsum.photos/1000") 64 | ImagePutWindow(hex, "19. Hex") 65 | 66 | base64 := ImagePutBase64("https://picsum.photos/900") 67 | ImagePutWindow(base64, "20. Base64") 68 | 69 | hdc := ImagePutDC("https://picsum.photos/800") 70 | ImagePutWindow(hdc, "21. DC") 71 | 72 | hBitmap := ImagePutHBitmap("https://picsum.photos/700") 73 | ImagePutWindow(hBitmap, "22. HBITMAP") 74 | 75 | hIcon := ImagePutHIcon("https://picsum.photos/600") 76 | ImagePutWindow(hIcon, "23. HICON") 77 | 78 | pBitmap := ImagePutBitmap("https://picsum.photos/500") 79 | ImagePutWindow(pBitmap, "24. BITMAP") 80 | 81 | IStream := ImagePutStream("https://picsum.photos/400") 82 | ImagePutWindow(IStream, "25. IStream") 83 | 84 | IRandomAccessStream := ImagePutRandomAccessStream("https://picsum.photos/300") 85 | ImagePutWindow(IRandomAccessStream, "26. IRandomAccessStream") 86 | 87 | IWICBitmap := ImagePutWICBitmap("https://picsum.photos/200") 88 | ImagePutWindow(IWICBitmap, "27. IWICBitmap") --------------------------------------------------------------------------------