· 9 years ago · Dec 21, 2016, 05:50 PM
1/*
2AutoHotkey
3
4Copyright 2003-2007 Chris Mallett (support@autohotkey.com)
5DLL conversion 2008: kangkengkingkong@hotmail.com
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; either version 2
10of the License, or (at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16*/
17
18#include "stdafx.h" // pre-compiled headers
19#include <olectl.h> // for OleLoadPicture()
20#include <Gdiplus.h> // Used by LoadPicture().
21#include <windef.h>
22#include <windows.h>
23#include <winuser.h>
24#include <malloc.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <shellapi.h>
28
29
30#define CLR_DEFAULT 0x808080
31#define ToWideChar(source, dest, dest_size_in_wchars) MultiByteToWideChar(CP_ACP, 0, source, -1, dest, dest_size_in_wchars)
32
33#define CLR_NONE 0xFFFFFFFF
34#define IS_SPACE_OR_TAB(c) (c == ' ' || c == '\t')
35
36char answer[50];
37
38HINSTANCE g_hInstance;
39
40#define SET_COLOR_RANGE \
41{\
42 red_low = (aVariation > search_red) ? 0 : search_red - aVariation;\
43 green_low = (aVariation > search_green) ? 0 : search_green - aVariation;\
44 blue_low = (aVariation > search_blue) ? 0 : search_blue - aVariation;\
45 red_high = (aVariation > 0xFF - search_red) ? 0xFF : search_red + aVariation;\
46 green_high = (aVariation > 0xFF - search_green) ? 0xFF : search_green + aVariation;\
47 blue_high = (aVariation > 0xFF - search_blue) ? 0xFF : search_blue + aVariation;\
48}
49#define bgr_to_rgb(aBGR) rgb_to_bgr(aBGR)
50
51inline COLORREF rgb_to_bgr(DWORD aRGB)
52// Fancier methods seem prone to problems due to byte alignment or compiler issues.
53{
54 return RGB(GetBValue(aRGB), GetGValue(aRGB), GetRValue(aRGB));
55}
56
57COLORREF ColorNameToBGR(char *aColorName)
58// These are the main HTML color names. Returns CLR_NONE if a matching HTML color name can't be found.
59// Returns CLR_DEFAULT only if aColorName is the word Default.
60{
61 if (!aColorName || !*aColorName) return CLR_NONE;
62 if (!_stricmp(aColorName, "Black")) return 0x000000; // These colors are all in BGR format, not RGB.
63 if (!_stricmp(aColorName, "Silver")) return 0xC0C0C0;
64 if (!_stricmp(aColorName, "Gray")) return 0x808080;
65 if (!_stricmp(aColorName, "White")) return 0xFFFFFF;
66 if (!_stricmp(aColorName, "Maroon")) return 0x000080;
67 if (!_stricmp(aColorName, "Red")) return 0x0000FF;
68 if (!_stricmp(aColorName, "Purple")) return 0x800080;
69 if (!_stricmp(aColorName, "Fuchsia"))return 0xFF00FF;
70 if (!_stricmp(aColorName, "Green")) return 0x008000;
71 if (!_stricmp(aColorName, "Lime")) return 0x00FF00;
72 if (!_stricmp(aColorName, "Olive")) return 0x008080;
73 if (!_stricmp(aColorName, "Yellow")) return 0x00FFFF;
74 if (!_stricmp(aColorName, "Navy")) return 0x800000;
75 if (!_stricmp(aColorName, "Blue")) return 0xFF0000;
76 if (!_stricmp(aColorName, "Teal")) return 0x808000;
77 if (!_stricmp(aColorName, "Aqua")) return 0xFFFF00;
78 if (!_stricmp(aColorName, "Default"))return CLR_DEFAULT;
79 return CLR_NONE;
80}
81
82inline char *StrChrAny(char *aStr, char *aCharList)
83// Returns the position of the first char in aStr that is of any one of the characters listed in aCharList.
84// Returns NULL if not found.
85// Update: Yes, this seems identical to strpbrk(). However, since the corresponding code would
86// have to be added to the EXE regardless of which was used, there doesn't seem to be much
87// advantage to switching (especially since if the two differ in behavior at all, things might
88// get broken). Another reason is the name "strpbrk()" is not as easy to remember.
89{
90 if (aStr == NULL || aCharList == NULL) return NULL;
91 if (!*aStr || !*aCharList) return NULL;
92 // Don't use strchr() because that would just find the first occurrence
93 // of the first search-char, which is not necessarily the first occurrence
94 // of *any* search-char:
95 char *look_for_this_char, char_being_analyzed;
96 for (; *aStr; ++aStr)
97 // If *aStr is any of the search char's, we're done:
98 for (char_being_analyzed = *aStr, look_for_this_char = aCharList; *look_for_this_char; ++look_for_this_char)
99 if (char_being_analyzed == *look_for_this_char)
100 return aStr; // Match found.
101 return NULL; // No match.
102}
103inline char *omit_leading_whitespace(char *aBuf) // 10/17/2006: __forceinline didn't help significantly.
104// While aBuf points to a whitespace, moves to the right and returns the first non-whitespace
105// encountered.
106{
107 for (; IS_SPACE_OR_TAB(*aBuf); ++aBuf);
108 return aBuf;
109}
110
111inline bool IsHex(char *aBuf) // 10/17/2006: __forceinline worsens performance, but physically ordering it near ATOI64() [via /ORDER] boosts by 3.5%.
112// Note: AHK support for hex ints reduces performance by only 10% for decimal ints, even in the tightest
113// of math loops that have SetBatchLines set to -1.
114{
115 // For whatever reason, omit_leading_whitespace() benches consistently faster (albeit slightly) than
116 // the same code put inline (confirmed again on 10/17/2006, though the difference is hardly anything):
117 //for (; IS_SPACE_OR_TAB(*aBuf); ++aBuf);
118 aBuf = omit_leading_whitespace(aBuf); // i.e. caller doesn't have to have ltrimmed.
119 if (!*aBuf)
120 return false;
121 if (*aBuf == '-' || *aBuf == '+')
122 ++aBuf;
123 // The "0x" prefix must be followed by at least one hex digit, otherwise it's not considered hex:
124 #define IS_HEX(buf) (*buf == '0' && (*(buf + 1) == 'x' || *(buf + 1) == 'X') && isxdigit(*(buf + 2)))
125 return IS_HEX(aBuf);
126}
127
128inline int ATOI(char *buf)
129{
130 // Below has been updated because values with leading zeros were being intepreted as
131 // octal, which is undesirable.
132 // Formerly: #define ATOI(buf) strtol(buf, NULL, 0) // Use zero as last param to support both hex & dec.
133 return IsHex(buf) ? strtol(buf, NULL, 16) : atoi(buf); // atoi() has superior performance, so use it when possible.
134}
135
136void strlcpy(char *aDst, const char *aSrc, size_t aDstSize) // Non-inline because it benches slightly faster that way.
137// Caller must ensure that aDstSize is greater than 0.
138// Caller must ensure that the entire capacity of aDst is writable, EVEN WHEN it knows that aSrc is much shorter
139// than the aDstSize. This is because the call to strncpy (which is used for its superior performance) zero-fills
140// any unused portion of aDst.
141// Description:
142// Same as strncpy() but guarantees null-termination of aDst upon return.
143// No more than aDstSize - 1 characters will be copied from aSrc into aDst
144// (leaving room for the zero terminator, which is always inserted).
145// This function is defined in some Unices but is not standard. But unlike
146// other versions, this one uses void for return value for reduced code size
147// (since it's called in so many places).
148{
149 // Disabled for performance and reduced code size:
150 //if (!aDst || !aSrc || !aDstSize) return aDstSize; // aDstSize must not be zero due to the below method.
151 // It might be worthwhile to have a custom char-copying-loop here someday so that number of characters
152 // actually copied (not including the zero terminator) can be returned to callers who want it.
153 --aDstSize; // Convert from size to length (caller has ensured that aDstSize > 0).
154 strncpy(aDst, aSrc, aDstSize); // NOTE: In spite of its zero-filling, strncpy() benchmarks considerably faster than a custom loop, probably because it uses 32-bit memory operations vs. 8-bit.
155 aDst[aDstSize] = '\0';
156}
157
158LPCOLORREF getbits(HBITMAP ahImage, HDC hdc, LONG &aWidth, LONG &aHeight, bool &aIs16Bit, int aMinColorDepth = 8)
159// Helper function used by PixelSearch below.
160// Returns an array of pixels to the caller, which it must free when done. Returns NULL on failure,
161// in which case the contents of the output parameters is indeterminate.
162{
163 HDC tdc = CreateCompatibleDC(hdc);
164 if (!tdc)
165 return NULL;
166
167 // From this point on, "goto end" will assume tdc is non-NULL, but that the below
168 // might still be NULL. Therefore, all of the following must be initialized so that the "end"
169 // label can detect them:
170 HGDIOBJ tdc_orig_select = NULL;
171 LPCOLORREF image_pixel = NULL;
172 bool success = false;
173
174 // Confirmed:
175 // Needs extra memory to prevent buffer overflow due to: "A bottom-up DIB is specified by setting
176 // the height to a positive number, while a top-down DIB is specified by setting the height to a
177 // negative number. THE BITMAP COLOR TABLE WILL BE APPENDED to the BITMAPINFO structure."
178 // Maybe this applies only to negative height, in which case the second call to GetDIBits()
179 // below uses one.
180 struct BITMAPINFO3
181 {
182 BITMAPINFOHEADER bmiHeader;
183 RGBQUAD bmiColors[260]; // v1.0.40.10: 260 vs. 3 to allow room for color table when color depth is 8-bit or less.
184 } bmi;
185
186 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
187 bmi.bmiHeader.biBitCount = 0; // i.e. "query bitmap attributes" only.
188 if (!GetDIBits(tdc, ahImage, 0, 0, NULL, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS)
189 || bmi.bmiHeader.biBitCount < aMinColorDepth) // Relies on short-circuit boolean order.
190 goto end;
191
192 // Set output parameters for caller:
193 aIs16Bit = (bmi.bmiHeader.biBitCount == 16);
194 aWidth = bmi.bmiHeader.biWidth;
195 aHeight = bmi.bmiHeader.biHeight;
196
197 int image_pixel_count = aWidth * aHeight;
198 if ( !(image_pixel = (LPCOLORREF)malloc(image_pixel_count * sizeof(COLORREF))) )
199 goto end;
200
201 // v1.0.40.10: To preserve compatibility with callers who check for transparency in icons, don't do any
202 // of the extra color table handling for 1-bpp images. Update: For code simplification, support only
203 // 8-bpp images. If ever support lower color depths, use something like "bmi.bmiHeader.biBitCount > 1
204 // && bmi.bmiHeader.biBitCount < 9";
205 bool is_8bit = (bmi.bmiHeader.biBitCount == 8);
206 if (!is_8bit)
207 bmi.bmiHeader.biBitCount = 32;
208 bmi.bmiHeader.biHeight = -bmi.bmiHeader.biHeight; // Storing a negative inside the bmiHeader struct is a signal for GetDIBits().
209
210 // Must be done only after GetDIBits() because: "The bitmap identified by the hbmp parameter
211 // must not be selected into a device context when the application calls GetDIBits()."
212 // (Although testing shows it works anyway, perhaps because GetDIBits() above is being
213 // called in its informational mode only).
214 // Note that this seems to return NULL sometimes even though everything still works.
215 // Perhaps that is normal.
216 tdc_orig_select = SelectObject(tdc, ahImage); // Returns NULL when we're called the second time?
217
218 // Appparently there is no need to specify DIB_PAL_COLORS below when color depth is 8-bit because
219 // DIB_RGB_COLORS also retrieves the color indices.
220 if ( !(GetDIBits(tdc, ahImage, 0, aHeight, image_pixel, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS)) )
221 goto end;
222
223 if (is_8bit) // This section added in v1.0.40.10.
224 {
225 // Convert the color indicies to RGB colors by going through the array in reverse order.
226 // Reverse order allows an in-place conversion of each 8-bit color index to its corresponding
227 // 32-bit RGB color.
228 LPDWORD palette = (LPDWORD)_alloca(256 * sizeof(PALETTEENTRY));
229 GetSystemPaletteEntries(tdc, 0, 256, (LPPALETTEENTRY)palette); // Even if failure can realistically happen, consequences of using uninitialized palette seem acceptable.
230 // Above: GetSystemPaletteEntries() is the only approach that provided the correct palette.
231 // The following other approaches didn't give the right one:
232 // GetDIBits(): The palette it stores in bmi.bmiColors seems completely wrong.
233 // GetPaletteEntries()+GetCurrentObject(hdc, OBJ_PAL): Returned only 20 entries rather than the expected 256.
234 // GetDIBColorTable(): I think same as above or maybe it returns 0.
235
236 // The following section is necessary because apparently each new row in the region starts on
237 // a DWORD boundary. So if the number of pixels in each row isn't an exact multiple of 4, there
238 // are between 1 and 3 zero-bytes at the end of each row.
239 int remainder = aWidth % 4;
240 int empty_bytes_at_end_of_each_row = remainder ? (4 - remainder) : 0;
241
242 // Start at the last RGB slot and the last color index slot:
243 BYTE *byte = (BYTE *)image_pixel + image_pixel_count - 1 + (aHeight * empty_bytes_at_end_of_each_row); // Pointer to 8-bit color indices.
244 DWORD *pixel = image_pixel + image_pixel_count - 1; // Pointer to 32-bit RGB entries.
245
246 int row, col;
247 for (row = 0; row < aHeight; ++row) // For each row.
248 {
249 byte -= empty_bytes_at_end_of_each_row;
250 for (col = 0; col < aWidth; ++col) // For each column.
251 *pixel-- = rgb_to_bgr(palette[*byte--]); // Caller always wants RGB vs. BGR format.
252 }
253 }
254
255 // Since above didn't "goto end", indicate success:
256 success = true;
257
258end:
259 if (tdc_orig_select) // i.e. the original call to SelectObject() didn't fail.
260 SelectObject(tdc, tdc_orig_select); // Probably necessary to prevent memory leak.
261 DeleteDC(tdc);
262 if (!success && image_pixel)
263 {
264 free(image_pixel);
265 image_pixel = NULL;
266 }
267 return image_pixel;
268}
269
270HBITMAP LoadPicture(char *aFilespec, int aWidth, int aHeight, int &aImageType, int aIconNumber
271 , bool aUseGDIPlusIfAvailable)
272// Returns NULL on failure.
273// If aIconNumber > 0, an HICON or HCURSOR is returned (both should be interchangeable), never an HBITMAP.
274// However, aIconNumber==1 is treated as a special icon upon which LoadImage is given preference over ExtractIcon
275// for .ico/.cur/.ani files.
276// Otherwise, .ico/.cur/.ani files are normally loaded as HICON (unless aUseGDIPlusIfAvailable is true or
277// something else unusual happened such as file contents not matching file's extension). This is done to preserve
278// any properties that HICONs have but HBITMAPs lack, namely the ability to be animated and perhaps other things.
279//
280// Loads a JPG/GIF/BMP/ICO/etc. and returns an HBITMAP or HICON to the caller (which it may call
281// DeleteObject()/DestroyIcon() upon, though upon program termination all such handles are freed
282// automatically). The image is scaled to the specified width and height. If zero is specified
283// for either, the image's actual size will be used for that dimension. If -1 is specified for one,
284// that dimension will be kept proportional to the other dimension's size so that the original aspect
285// ratio is retained.
286{
287 HBITMAP hbitmap = NULL;
288 aImageType = -1; // The type of image currently inside hbitmap. Set default value for output parameter as "unknown".
289
290 if (!*aFilespec) // Allow blank filename to yield NULL bitmap (and currently, some callers do call it this way).
291 return NULL;
292 if (aIconNumber < 0) // Allowed to be called this way by GUI and others (to avoid need for validation of user input there).
293 aIconNumber = 0; // Use the default behavior, which is "load icon or bitmap, whichever is most appropriate".
294
295 char *file_ext = strrchr(aFilespec, '.');
296 if (file_ext)
297 ++file_ext;
298
299
300 // v1.0.43.07: If aIconNumber is zero, caller didn't specify whether it wanted an icon or bitmap. Thus,
301 // there must be some kind of detection for whether ExtractIcon is needed instead of GDIPlus/OleLoadPicture.
302 // Although this could be done by attempting ExtractIcon only after GDIPlus/OleLoadPicture fails (or by
303 // somehow checking the internal nature of the file), for performance and code size, it seems best to not
304 // to incur this extra I/O and instead make only one attempt based on the file's extension.
305 // Must use ExtractIcon() if either of the following is true:
306 // 1) Caller gave an icon index of the second or higher icon in the file. Update for v1.0.43.05: There
307 // doesn't seem to be any reason to allow a caller to explicitly specify ExtractIcon as the method of
308 // loading the *first* icon from a .ico file since LoadImage is likely always superior. This is
309 // because unlike ExtractIcon/Ex, LoadImage: 1) Doesn't distort icons, especially 16x16 icons; 2) is
310 // capable of loading icons other than the first by means of width and height parameters.
311 // 2) The target file is of type EXE/DLL/ICL/CPL/etc. (LoadImage() is documented not to work on those file types).
312 // ICL files (v1.0.43.05): Apparently ICL files are an unofficial file format. Someone on the newsgroups
313 // said that an ICL is an "ICon Library... a renamed 16-bit Windows .DLL (an NE format executable) which
314 // typically contains nothing but a resource section. The ICL extension seems to be used by convention."
315 bool ExtractIcon_was_used = aIconNumber > 1 || (file_ext && (
316 !_stricmp(file_ext, "exe")
317 || !_stricmp(file_ext, "dll")
318 || !_stricmp(file_ext, "icl") // Icon library: Unofficial dll container, see notes above.
319 || !_stricmp(file_ext, "cpl") // Control panel extension/applet (ExtractIcon is said to work on these).
320 || !_stricmp(file_ext, "scr") // Screen saver (ExtractIcon should work since these are really EXEs).
321 // v1.0.44: Below are now omitted to reduce code size and improve performance. They are still supported
322 // indirectly because ExtractIcon is attempted whenever LoadImage() fails further below.
323 //|| !_stricmp(file_ext, "drv") // Driver (ExtractIcon is said to work on these).
324 //|| !_stricmp(file_ext, "ocx") // OLE/ActiveX Control Extension
325 //|| !_stricmp(file_ext, "vbx") // Visual Basic Extension
326 //|| !_stricmp(file_ext, "acm") // Audio Compression Manager Driver
327 //|| !_stricmp(file_ext, "bpl") // Delphi Library (like a DLL?)
328 // Not supported due to rarity, code size, performance, and uncertainty of whether ExtractIcon works on them.
329 // Update for v1.0.44: The following are now supported indirectly because ExtractIcon is attempted whenever
330 // LoadImage() fails further below.
331 //|| !_stricmp(file_ext, "nil") // Norton Icon Library
332 //|| !_stricmp(file_ext, "wlx") // Total/Windows Commander Lister Plug-in
333 //|| !_stricmp(file_ext, "wfx") // Total/Windows Commander File System Plug-in
334 //|| !_stricmp(file_ext, "wcx") // Total/Windows Commander Plug-in
335 //|| !_stricmp(file_ext, "wdx") // Total/Windows Commander Plug-in
336 ));
337 if (ExtractIcon_was_used)
338 {
339 aImageType = IMAGE_ICON;
340 hbitmap = (HBITMAP)ExtractIcon(g_hInstance, aFilespec, aIconNumber > 0 ? aIconNumber - 1 : 0);
341 // Above: Although it isn't well documented at MSDN, apparently both ExtractIcon() and LoadIcon()
342 // scale the icon to the system's large-icon size (usually 32x32) regardless of the actual size of
343 // the icon inside the file. For this reason, callers should call us in a way that allows us to
344 // give preference to LoadImage() over ExtractIcon() (unless the caller needs to retain backward
345 // compatibility with existing scripts that explicitly specify icon #1 to force the ExtractIcon
346 // method to be used).
347 if (hbitmap < (HBITMAP)2) // i.e. it's NULL or 1. Return value of 1 means "incorrect file type".
348 return NULL; // v1.0.44: Fixed to return NULL vs. hbitmap, since 1 is an invalid handle (perhaps rare since no known bugs caused by it).
349 //else continue on below so that the icon can be resized to the caller's specified dimensions.
350 }
351 else if (aIconNumber > 0) // Caller wanted HICON, never HBITMAP, so set type now to enforce that.
352 aImageType = IMAGE_ICON; // Should be suitable for cursors too, since they're interchangeable for the most part.
353 else if (file_ext) // Make an initial guess of the type of image if the above didn't already determine the type.
354 {
355 if (!_stricmp(file_ext, "ico"))
356 aImageType = IMAGE_ICON;
357 else if (!_stricmp(file_ext, "cur") || !_stricmp(file_ext, "ani"))
358 aImageType = IMAGE_CURSOR;
359 else if (!_stricmp(file_ext, "bmp"))
360 aImageType = IMAGE_BITMAP;
361 //else for other extensions, leave set to "unknown" so that the below knows to use IPic or GDI+ to load it.
362 }
363 //else same comment as above.
364
365 if ((aWidth == -1 || aHeight == -1) && (!aWidth || !aHeight))
366 aWidth = aHeight = 0; // i.e. One dimension is zero and the other is -1, which resolves to the same as "keep original size".
367 bool keep_aspect_ratio = (aWidth == -1 || aHeight == -1);
368
369 // Caller should ensure that aUseGDIPlusIfAvailable==false when aIconNumber > 0, since it makes no sense otherwise.
370 HINSTANCE hinstGDI = NULL;
371 if (aUseGDIPlusIfAvailable && !(hinstGDI = LoadLibrary("gdiplus"))) // Relies on short-circuit boolean order for performance.
372 aUseGDIPlusIfAvailable = false; // Override any original "true" value as a signal for the section below.
373
374 if (!hbitmap && aImageType > -1 && !aUseGDIPlusIfAvailable)
375 {
376 // Since image hasn't yet be loaded and since the file type appears to be one supported by
377 // LoadImage() [icon/cursor/bitmap], attempt that first. If it fails, fall back to the other
378 // methods below in case the file's internal contents differ from what the file extension indicates.
379 int desired_width, desired_height;
380 if (keep_aspect_ratio) // Load image at its actual size. It will be rescaled to retain aspect ratio later below.
381 {
382 desired_width = 0;
383 desired_height = 0;
384 }
385 else
386 {
387 desired_width = aWidth;
388 desired_height = aHeight;
389 }
390 // For LoadImage() below:
391 // LR_CREATEDIBSECTION applies only when aImageType == IMAGE_BITMAP, but seems appropriate in that case.
392 // Also, if width and height are non-zero, that will determine which icon of a multi-icon .ico file gets
393 // loaded (though I don't know the exact rules of precedence).
394 // KNOWN LIMITATIONS/BUGS:
395 // LoadImage() fails when requesting a size of 1x1 for an image whose orig/actual size is small (e.g. 1x2).
396 // Unlike CopyImage(), perhaps it detects that division by zero would occur and refuses to do the
397 // calculation rather than providing more code to do a correct calculation that doesn't divide by zero.
398 // For example:
399 // LoadImage() Success:
400 // Gui, Add, Pic, h2 w2, bitmap 1x2.bmp
401 // Gui, Add, Pic, h1 w1, bitmap 4x6.bmp
402 // LoadImage() Failure:
403 // Gui, Add, Pic, h1 w1, bitmap 1x2.bmp
404 // LoadImage() also fails on:
405 // Gui, Add, Pic, h1, bitmap 1x2.bmp
406 // And then it falls back to GDIplus, which in the particular case above appears to traumatize the
407 // parent window (or its picture control), because the GUI window hangs (but not the script) after
408 // doing a FileSelectFolder. For example:
409 // Gui, Add, Button,, FileSelectFile
410 // Gui, Add, Pic, h1, bitmap 1x2.bmp ; Causes GUI window to hang after FileSelectFolder (due to LoadImage failing then falling back to GDIplus; i.e. GDIplus is somehow triggering the problem).
411 // Gui, Show
412 // return
413 // ButtonFileSelectFile:
414 // FileSelectFile, outputvar
415 // return
416 //printf("\nloading image %s",(LPCTSTR)aFilespec);
417 if (hbitmap = (HBITMAP)LoadImage(NULL, aFilespec, aImageType, desired_width, desired_height
418 , LR_LOADFROMFILE | LR_CREATEDIBSECTION))
419 {
420 // The above might have loaded an HICON vs. an HBITMAP (it has been confirmed that LoadImage()
421 // will return an HICON vs. HBITMAP is aImageType is IMAGE_ICON/CURSOR). Note that HICON and
422 // HCURSOR are identical for most/all Windows API uses. Also note that LoadImage() will load
423 // an icon as a bitmap if the file contains an icon but IMAGE_BITMAP was passed in (at least
424 // on Windows XP).
425 //printf("\n got here");
426 if (!keep_aspect_ratio) // No further resizing is needed.
427 return hbitmap;
428 // Otherwise, continue on so that the image can be resized via a second call to LoadImage().
429 }
430 // v1.0.40.10: Abort if file doesn't exist so that GDIPlus isn't even attempted. This is done because
431 // loading GDIPlus apparently disrupts the color palette of certain games, at least old ones that use
432 // DirectDraw in 256-color depth.
433 else if (GetFileAttributes(aFilespec) == 0xFFFFFFFF) // For simplicity, we don't check if it's a directory vs. file, since that should be too rare.
434 return NULL;
435 // v1.0.43.07: Also abort if caller wanted an HICON (not an HBITMAP), since the other methods below
436 // can't yield an HICON.
437 else if (aIconNumber > 0)
438 {
439 // UPDATE for v1.0.44: Attempt ExtractIcon in case its some extension that's
440 // was recognized as an icon container (such as AutoHotkeySC.bin) and thus wasn't handled higher above.
441 hbitmap = (HBITMAP)ExtractIcon(g_hInstance, aFilespec, aIconNumber - 1);
442 if (hbitmap < (HBITMAP)2) // i.e. it's NULL or 1. Return value of 1 means "incorrect file type".
443 return NULL;
444 ExtractIcon_was_used = true;
445 }
446 //else file exists, so continue on so that the other methods are attempted in case file's contents
447 // differ from what the file extension indicates, or in case the other methods can be successful
448 // even when the above failed.
449 }
450
451 IPicture *pic = NULL; // Also used to detect whether IPic method was used to load the image.
452
453 if (!hbitmap) // Above hasn't loaded the image yet, so use the fall-back methods.
454 {
455 // At this point, regardless of the image type being loaded (even an icon), it will
456 // definitely be converted to a Bitmap below. So set the type:
457 aImageType = IMAGE_BITMAP;
458 // Find out if this file type is supported by the non-GDI+ method. This check is not foolproof
459 // since all it does is look at the file's extension, not its contents. However, it doesn't
460 // need to be 100% accurate because its only purpose is to detect whether the higher-overhead
461 // calls to GdiPlus can be avoided.
462 if (aUseGDIPlusIfAvailable || !file_ext || (_stricmp(file_ext, "jpg")
463 && _stricmp(file_ext, "jpeg") && _stricmp(file_ext, "gif"))) // Non-standard file type (BMP is already handled above).
464 if (!hinstGDI) // We don't yet have a handle from an earlier call to LoadLibary().
465 hinstGDI = LoadLibrary("gdiplus");
466 // If it is suspected that the file type isn't supported, try to use GdiPlus if available.
467 // If it's not available, fall back to the old method in case the filename doesn't properly
468 // reflect its true contents (i.e. in case it really is a JPG/GIF/BMP internally).
469 // If the below LoadLibrary() succeeds, either the OS is XP+ or the GdiPlus extensions have been
470 // installed on an older OS.
471 if (hinstGDI)
472 {
473 // LPVOID and "int" are used to avoid compiler errors caused by... namespace issues?
474 typedef int (WINAPI *GdiplusStartupType)(ULONG_PTR*, LPVOID, LPVOID);
475 typedef VOID (WINAPI *GdiplusShutdownType)(ULONG_PTR);
476 typedef int (WINGDIPAPI *GdipCreateBitmapFromFileType)(LPVOID, LPVOID);
477 typedef int (WINGDIPAPI *GdipCreateHBITMAPFromBitmapType)(LPVOID, LPVOID, DWORD);
478 typedef int (WINGDIPAPI *GdipDisposeImageType)(LPVOID);
479 GdiplusStartupType DynGdiplusStartup = (GdiplusStartupType)GetProcAddress(hinstGDI, "GdiplusStartup");
480 GdiplusShutdownType DynGdiplusShutdown = (GdiplusShutdownType)GetProcAddress(hinstGDI, "GdiplusShutdown");
481 GdipCreateBitmapFromFileType DynGdipCreateBitmapFromFile = (GdipCreateBitmapFromFileType)GetProcAddress(hinstGDI, "GdipCreateBitmapFromFile");
482 GdipCreateHBITMAPFromBitmapType DynGdipCreateHBITMAPFromBitmap = (GdipCreateHBITMAPFromBitmapType)GetProcAddress(hinstGDI, "GdipCreateHBITMAPFromBitmap");
483 GdipDisposeImageType DynGdipDisposeImage = (GdipDisposeImageType)GetProcAddress(hinstGDI, "GdipDisposeImage");
484
485 ULONG_PTR token;
486 Gdiplus::GdiplusStartupInput gdi_input;
487 Gdiplus::GpBitmap *pgdi_bitmap;
488 if (DynGdiplusStartup && DynGdiplusStartup(&token, &gdi_input, NULL) == Gdiplus::Ok)
489 {
490 WCHAR filespec_wide[MAX_PATH];
491 ToWideChar(aFilespec, filespec_wide, MAX_PATH); // Dest. size is in wchars, not bytes.
492 if (DynGdipCreateBitmapFromFile(filespec_wide, &pgdi_bitmap) == Gdiplus::Ok)
493 {
494 if (DynGdipCreateHBITMAPFromBitmap(pgdi_bitmap, &hbitmap, CLR_DEFAULT) != Gdiplus::Ok)
495 hbitmap = NULL; // Set to NULL to be sure.
496 DynGdipDisposeImage(pgdi_bitmap); // This was tested once to make sure it really returns Gdiplus::Ok.
497 }
498 // The current thought is that shutting it down every time conserves resources. If so, it
499 // seems justified since it is probably called infrequently by most scripts:
500 DynGdiplusShutdown(token);
501 }
502 FreeLibrary(hinstGDI);
503 }
504 else // Using old picture loading method.
505 {
506 // Based on code sample at http://www.codeguru.com/Cpp/G-M/bitmap/article.php/c4935/
507 HANDLE hfile = CreateFile(aFilespec, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
508 if (hfile == INVALID_HANDLE_VALUE)
509 return NULL;
510 DWORD size = GetFileSize(hfile, NULL);
511 HGLOBAL hglobal = GlobalAlloc(GMEM_MOVEABLE, size);
512 if (!hglobal)
513 {
514 CloseHandle(hfile);
515 return NULL;
516 }
517 LPVOID hlocked = GlobalLock(hglobal);
518 if (!hlocked)
519 {
520 CloseHandle(hfile);
521 GlobalFree(hglobal);
522 return NULL;
523 }
524 // Read the file into memory:
525 ReadFile(hfile, hlocked, size, &size, NULL);
526 GlobalUnlock(hglobal);
527 CloseHandle(hfile);
528 LPSTREAM stream;
529 if (FAILED(CreateStreamOnHGlobal(hglobal, FALSE, &stream)) || !stream) // Relies on short-circuit boolean order.
530 {
531 GlobalFree(hglobal);
532 return NULL;
533 }
534 // Specify TRUE to have it do the GlobalFree() for us. But since the call might fail, it seems best
535 // to free the mem ourselves to avoid uncertainy over what it does on failure:
536 if (FAILED(OleLoadPicture(stream, 0, FALSE, IID_IPicture, (void **)&pic)))
537 pic = NULL;
538 stream->Release();
539 GlobalFree(hglobal);
540 if (!pic)
541 return NULL;
542 pic->get_Handle((OLE_HANDLE *)&hbitmap);
543 // Above: MSDN: "The caller is responsible for this handle upon successful return. The variable is set
544 // to NULL on failure."
545 if (!hbitmap)
546 {
547 pic->Release();
548 return NULL;
549 }
550 // Don't pic->Release() yet because that will also destroy/invalidate hbitmap handle.
551 } // IPicture method was used.
552 } // IPicture or GDIPlus was used to load the image, not a simple LoadImage() or ExtractIcon().
553
554 // Above has ensured that hbitmap is now not NULL.
555 // Adjust things if "keep aspect ratio" is in effect:
556 if (keep_aspect_ratio)
557 {
558 HBITMAP hbitmap_to_analyze;
559 ICONINFO ii; // Must be declared at this scope level.
560 if (aImageType == IMAGE_BITMAP)
561 hbitmap_to_analyze = hbitmap;
562 else // icon or cursor
563 {
564 if (GetIconInfo((HICON)hbitmap, &ii)) // Works on cursors too.
565 hbitmap_to_analyze = ii.hbmMask; // Use Mask because MSDN implies hbmColor can be NULL for monochrome cursors and such.
566 else
567 {
568 DestroyIcon((HICON)hbitmap);
569 return NULL; // No need to call pic->Release() because since it's an icon, we know IPicture wasn't used (it only loads bitmaps).
570 }
571 }
572 // Above has ensured that hbitmap_to_analyze is now not NULL. Find bitmap's dimensions.
573 BITMAP bitmap;
574 GetObject(hbitmap_to_analyze, sizeof(BITMAP), &bitmap); // Realistically shouldn't fail at this stage.
575 if (aHeight == -1)
576 {
577 // Caller wants aHeight calculated based on the specified aWidth (keep aspect ratio).
578 if (bitmap.bmWidth) // Avoid any chance of divide-by-zero.
579 aHeight = (int)(((double)bitmap.bmHeight / bitmap.bmWidth) * aWidth + .5); // Round.
580 }
581 else
582 {
583 // Caller wants aWidth calculated based on the specified aHeight (keep aspect ratio).
584 if (bitmap.bmHeight) // Avoid any chance of divide-by-zero.
585 aWidth = (int)(((double)bitmap.bmWidth / bitmap.bmHeight) * aHeight + .5); // Round.
586 }
587 if (aImageType != IMAGE_BITMAP)
588 {
589 // It's our reponsibility to delete these two when they're no longer needed:
590 DeleteObject(ii.hbmColor);
591 DeleteObject(ii.hbmMask);
592 // If LoadImage() vs. ExtractIcon() was used originally, call LoadImage() again because
593 // I haven't found any other way to retain an animated cursor's animation (and perhaps
594 // other icon/cursor attributes) when resizing the icon/cursor (CopyImage() doesn't
595 // retain animation):
596 if (!ExtractIcon_was_used)
597 {
598 DestroyIcon((HICON)hbitmap); // Destroy the original HICON.
599 // Load a new one, but at the size newly calculated above.
600 // Due to an apparent bug in Windows 9x (at least Win98se), the below call will probably
601 // crash the program with a "divide error" if the specified aWidth and/or aHeight are
602 // greater than 90. Since I don't know whether this affects all versions of Windows 9x, and
603 // all animated cursors, it seems best just to document it here and in the help file rather
604 // than limiting the dimensions of .ani (and maybe .cur) files for certain operating systems.
605 return (HBITMAP)LoadImage(NULL, aFilespec, aImageType, aWidth, aHeight, LR_LOADFROMFILE);
606 }
607 }
608 }
609
610 ;int test=LR_COPYRETURNORG;
611
612 HBITMAP hbitmap_new; // To hold the scaled image (if scaling is needed).
613 if (pic) // IPicture method was used.
614 {
615 // The below statement is confirmed by having tested that DeleteObject(hbitmap) fails
616 // if called after pic->Release():
617 // "Copy the image. Necessary, because upon pic's release the handle is destroyed."
618 // MSDN: CopyImage(): "[If either width or height] is zero, then the returned image will have the
619 // same width/height as the original."
620 // Note also that CopyImage() seems to provide better scaling quality than using MoveWindow()
621 // (followed by redrawing the parent window) on the static control that contains it:
622 hbitmap_new = (HBITMAP)CopyImage(hbitmap, IMAGE_BITMAP, aWidth, aHeight // We know it's IMAGE_BITMAP in this case.
623 , (aWidth || aHeight) ? 0 : LR_COPYRETURNORG); // Produce original size if no scaling is needed.
624 pic->Release();
625 // No need to call DeleteObject(hbitmap), see above.
626 }
627 else // GDIPlus or a simple method such as LoadImage or ExtractIcon was used.
628 {
629 if (!aWidth && !aHeight) // No resizing needed.
630 return hbitmap;
631 // The following will also handle HICON/HCURSOR correctly if aImageType == IMAGE_ICON/CURSOR.
632 // Also, LR_COPYRETURNORG|LR_COPYDELETEORG is used because it might allow the animation of
633 // a cursor to be retained if the specified size happens to match the actual size of the
634 // cursor. This is because normally, it seems that CopyImage() omits cursor animation
635 // from the new object. MSDN: "LR_COPYRETURNORG returns the original hImage if it satisfies
636 // the criteria for the copy—that is, correct dimensions and color depth—in which case the
637 // LR_COPYDELETEORG flag is ignored. If this flag is not specified, a new object is always created."
638 // KNOWN BUG: Calling CopyImage() when the source image is tiny and the destination width/height
639 // is also small (e.g. 1) causes a divide-by-zero exception.
640 // For example:
641 // Gui, Add, Pic, h1 w-1, bitmap 1x2.bmp ; Crash (divide by zero)
642 // Gui, Add, Pic, h1 w-1, bitmap 2x3.bmp ; Crash (divide by zero)
643 // However, such sizes seem too rare to document or put in an exception handler for.
644 hbitmap_new = (HBITMAP)CopyImage(hbitmap, aImageType, aWidth, aHeight, LR_COPYRETURNORG | LR_COPYDELETEORG);
645 // Above's LR_COPYDELETEORG deletes the original to avoid cascading resource usage. MSDN's
646 // LoadImage() docs say:
647 // "When you are finished using a bitmap, cursor, or icon you loaded without specifying the
648 // LR_SHARED flag, you can release its associated memory by calling one of [the three functions]."
649 // Therefore, it seems best to call the right function even though DeleteObject might work on
650 // all of them on some or all current OSes. UPDATE: Evidence indicates that DestroyIcon()
651 // will also destroy cursors, probably because icons and cursors are literally identical in
652 // every functional way. One piece of evidence:
653 //> No stack trace, but I know the exact source file and line where the call
654 //> was made. But still, it is annoying when you see 'DestroyCursor' even though
655 //> there is 'DestroyIcon'.
656 // "Can't be helped. Icons and cursors are the same thing" (Tim Robinson (MVP, Windows SDK)).
657 //
658 // Finally, the reason this is important is that it eliminates one handle type
659 // that we would otherwise have to track. For example, if a gui window is destroyed and
660 // and recreated multiple times, its bitmap and icon handles should all be destroyed each time.
661 // Otherwise, resource usage would cascade upward until the script finally terminated, at
662 // which time all such handles are freed automatically.
663 }
664 return hbitmap_new;
665}
666
667
668HBITMAP IconToBitmap(HICON ahIcon, bool aDestroyIcon)
669// Converts HICON to an HBITMAP that has ahIcon's actual dimensions.
670// The incoming ahIcon will be destroyed if the caller passes true for aDestroyIcon.
671// Returns NULL on failure, in which case aDestroyIcon will still have taken effect.
672// If the icon contains any transparent pixels, they will be mapped to CLR_NONE within
673// the bitmap so that the caller can detect them.
674{
675 if (!ahIcon)
676 return NULL;
677
678 HBITMAP hbitmap = NULL; // Set default. This will be the value returned.
679
680 HDC hdc_desktop = GetDC(HWND_DESKTOP);
681 HDC hdc = CreateCompatibleDC(hdc_desktop); // Don't pass NULL since I think that would result in a monochrome bitmap.
682 if (hdc)
683 {
684 ICONINFO ii;
685 if (GetIconInfo(ahIcon, &ii))
686 {
687 BITMAP icon_bitmap;
688 // Find out how big the icon is and create a bitmap compatible with the desktop DC (not the memory DC,
689 // since its bits per pixel (color depth) is probably 1.
690 if (GetObject(ii.hbmColor, sizeof(BITMAP), &icon_bitmap)
691 && (hbitmap = CreateCompatibleBitmap(hdc_desktop, icon_bitmap.bmWidth, icon_bitmap.bmHeight))) // Assign
692 {
693 // To retain maximum quality in case caller needs to resize the bitmap we return, convert the
694 // icon to a bitmap that matches the icon's actual size:
695 HGDIOBJ old_object = SelectObject(hdc, hbitmap);
696 if (old_object) // Above succeeded.
697 {
698 // Use DrawIconEx() vs. DrawIcon() because someone said DrawIcon() always draws 32x32
699 // regardless of the icon's actual size.
700 // If it's ever needed, this can be extended so that the caller can pass in a background
701 // color to use in place of any transparent pixels within the icon (apparently, DrawIconEx()
702 // skips over transparent pixels in the icon when drawing to the DC and its bitmap):
703 RECT rect = {0, 0, icon_bitmap.bmWidth, icon_bitmap.bmHeight}; // Left, top, right, bottom.
704 HBRUSH hbrush = CreateSolidBrush(CLR_DEFAULT);
705 FillRect(hdc, &rect, hbrush);
706 DeleteObject(hbrush);
707 // Probably something tried and abandoned: FillRect(hdc, &rect, (HBRUSH)GetStockObject(NULL_BRUSH));
708 DrawIconEx(hdc, 0, 0, ahIcon, icon_bitmap.bmWidth, icon_bitmap.bmHeight, 0, NULL, DI_NORMAL);
709 // Debug: Find out properties of new bitmap.
710 //BITMAP b;
711 //GetObject(hbitmap, sizeof(BITMAP), &b);
712 SelectObject(hdc, old_object); // Might be needed (prior to deleting hdc) to prevent memory leak.
713 }
714 }
715 // It's our reponsibility to delete these two when they're no longer needed:
716 DeleteObject(ii.hbmColor);
717 DeleteObject(ii.hbmMask);
718 }
719 DeleteDC(hdc);
720 }
721 ReleaseDC(HWND_DESKTOP, hdc_desktop);
722 if (aDestroyIcon)
723 DestroyIcon(ahIcon);
724 return hbitmap;
725}
726
727
728
729int WINAPI ImageTest(int a)
730{
731 return a + a;
732}
733// ResultType Line::ImageSearch(int aLeft, int aTop, int aRight, int aBottom, char *aImageFile)
734char* WINAPI ImageSearch(int aLeft, int aTop, int aRight, int aBottom, char *aImageFile)
735// Author: ImageSearch was created by Aurelian Maga.
736{
737 // Many of the following sections are similar to those in PixelSearch(), so they should be
738 // maintained together.
739 //Var *output_var_x = ARGVAR1; // Ok if NULL. RAW wouldn't be safe because load-time validation actually
740 //Var *output_var_y = ARGVAR2; // requires a minimum of zero parameters so that the output-vars can be optional.
741
742 // Set default results, both ErrorLevel and output variables, in case of early return:
743 //g_ErrorLevel->Assign(ERRORLEVEL_ERROR2); // 2 means error other than "image not found".
744 //if (output_var_x)
745 // output_var_x->Assign(); // Init to empty string regardless of whether we succeed here.
746 //if (output_var_y)
747 // output_var_y->Assign(); // Same.
748 RECT rect = {0}; // Set default (for CoordMode == "screen").
749 //if (!(g.CoordMode & COORD_MODE_PIXEL)) // Using relative vs. screen coordinates.
750 //{
751 // if (!GetWindowRect(GetForegroundWindow(), &rect))
752 // return OK; // Let ErrorLevel tell the story.
753 // aLeft += rect.left;
754 // aTop += rect.top;
755 // aRight += rect.left; // Add left vs. right because we're adjusting based on the position of the window.
756 // aBottom += rect.top; // Same.
757 //}
758
759 // Options are done as asterisk+option to permit future expansion.
760 // Set defaults to be possibly overridden by any specified options:
761 int aVariation = 0; // This is named aVariation vs. variation for use with the SET_COLOR_RANGE macro.
762 COLORREF trans_color = CLR_NONE; // The default must be a value that can't occur naturally in an image.
763 int icon_number = 0; // Zero means "load icon or bitmap (doesn't matter)".
764 int width = 0, height = 0;
765 // For icons, override the default to be 16x16 because that is what is sought 99% of the time.
766 // This new default can be overridden by explicitly specifying w0 h0:
767 char *cp = strrchr(aImageFile, '.');
768 if (cp)
769 {
770 ++cp;
771 if (!(_stricmp(cp, "ico") && _stricmp(cp, "exe") && _stricmp(cp, "dll")))
772 width = GetSystemMetrics(SM_CXSMICON), height = GetSystemMetrics(SM_CYSMICON);
773 }
774
775 char color_name[32], *dp;
776 cp = omit_leading_whitespace(aImageFile); // But don't alter aImageFile yet in case it contains literal whitespace we want to retain.
777 while (*cp == '*')
778 {
779 ++cp;
780 switch (toupper(*cp))
781 {
782 case 'W': width = ATOI(cp + 1); break;
783 case 'H': height = ATOI(cp + 1); break;
784 default:
785 if (!_strnicmp(cp, "Icon", 4))
786 {
787 cp += 4; // Now it's the character after the word.
788 icon_number = ATOI(cp); // LoadPicture() correctly handles any negative value.
789 }
790 else if (!_strnicmp(cp, "Trans", 5))
791 {
792 cp += 5; // Now it's the character after the word.
793 // Isolate the color name/number for ColorNameToBGR():
794 strlcpy(color_name, cp, sizeof(color_name));
795 if (dp = StrChrAny(color_name, " \t")) // Find space or tab, if any.
796 *dp = '\0';
797 // Fix for v1.0.44.10: Treat trans_color as containing an RGB value (not BGR) so that it matches
798 // the documented behavior. In older versions, a specified color like "TransYellow" was wrong in
799 // every way (inverted) and a specified numeric color like "Trans0xFFFFAA" was treated as BGR vs. RGB.
800 trans_color = ColorNameToBGR(color_name);
801 if (trans_color == CLR_NONE) // A matching color name was not found, so assume it's in hex format.
802 // It seems strtol() automatically handles the optional leading "0x" if present:
803 trans_color = strtol(color_name, NULL, 16);
804 // if color_name did not contain something hex-numeric, black (0x00) will be assumed,
805 // which seems okay given how rare such a problem would be.
806 else
807 trans_color = bgr_to_rgb(trans_color); // v1.0.44.10: See fix/comment above.
808
809 }
810 else // Assume it's a number since that's the only other asterisk-option.
811 {
812 aVariation = ATOI(cp); // Seems okay to support hex via ATOI because the space after the number is documented as being mandatory.
813 if (aVariation < 0)
814 aVariation = 0;
815 if (aVariation > 255)
816 aVariation = 255;
817 // Note: because it's possible for filenames to start with a space (even though Explorer itself
818 // won't let you create them that way), allow exactly one space between end of option and the
819 // filename itself:
820 }
821 } // switch()
822 if ( !(cp = StrChrAny(cp, " \t")) ) // Find the first space or tab after the option.
823 return "0"; //new
824 // return OK; // Bad option/format. Let ErrorLevel tell the story.
825 // Now it's the space or tab (if there is one) after the option letter. Advance by exactly one character
826 // because only one space or tab is considered the delimiter. Any others are considered to be part of the
827 // filename (though some or all OSes might simply ignore them or tolerate them as first-try match criteria).
828 aImageFile = ++cp; // This should now point to another asterisk or the filename itself.
829 // Above also serves to reset the filename to omit the option string whenever at least one asterisk-option is present.
830 cp = omit_leading_whitespace(cp); // This is done to make it more tolerant of having more than one space/tab between options.
831 }
832
833 // Update: Transparency is now supported in icons by using the icon's mask. In addition, an attempt
834 // is made to support transparency in GIF, PNG, and possibly TIF files via the *Trans option, which
835 // assumes that one color in the image is transparent. In GIFs not loaded via GDIPlus, the transparent
836 // color might always been seen as pure white, but when GDIPlus is used, it's probably always black
837 // like it is in PNG -- however, this will not relied upon, at least not until confirmed.
838 // OLDER/OBSOLETE comment kept for background:
839 // For now, images that can't be loaded as bitmaps (icons and cursors) are not supported because most
840 // icons have a transparent background or color present, which the image search routine here is
841 // probably not equipped to handle (since the transparent color, when shown, typically reveals the
842 // color of whatever is behind it; thus screen pixel color won't match image's pixel color).
843 // So currently, only BMP and GIF seem to work reliably, though some of the other GDIPlus-supported
844 // formats might work too.
845 int image_type;
846 HBITMAP hbitmap_image = LoadPicture(aImageFile, width, height, image_type, icon_number, false);
847 // The comment marked OBSOLETE below is no longer true because the elimination of the high-byte via
848 // 0x00FFFFFF seems to have fixed it. But "true" is still not passed because that should increase
849 // consistency when GIF/BMP/ICO files are used by a script on both Win9x and other OSs (since the
850 // same loading method would be used via "false" for these formats across all OSes).
851 // OBSOLETE: Must not pass "true" with the above because that causes bitmaps and gifs to be not found
852 // by the search. In other words, nothing works. Obsolete comment: Pass "true" so that an attempt
853 // will be made to load icons as bitmaps if GDIPlus is available.
854 if (!hbitmap_image)
855 return "0"; // new
856 // return OK; // Let ErrorLevel tell the story.
857
858 HDC hdc = GetDC(NULL);
859 if (!hdc)
860 {
861 DeleteObject(hbitmap_image);
862 return "0"; // new
863 // return OK; // Let ErrorLevel tell the story.
864 }
865
866 // From this point on, "goto end" will assume hdc and hbitmap_image are non-NULL, but that the below
867 // might still be NULL. Therefore, all of the following must be initialized so that the "end"
868 // label can detect them:
869 HDC sdc = NULL;
870 HBITMAP hbitmap_screen = NULL;
871 LPCOLORREF image_pixel = NULL, screen_pixel = NULL, image_mask = NULL;
872 HGDIOBJ sdc_orig_select = NULL;
873 bool found = false; // Must init here for use by "goto end".
874
875 bool image_is_16bit;
876 LONG image_width, image_height;
877
878 if (image_type == IMAGE_ICON)
879 {
880 // Must be done prior to IconToBitmap() since it deletes (HICON)hbitmap_image:
881 ICONINFO ii;
882 if (GetIconInfo((HICON)hbitmap_image, &ii))
883 {
884 // If the icon is monochrome (black and white), ii.hbmMask will contain twice as many pixels as
885 // are actually in the icon. But since the top half of the pixels are the AND-mask, it seems
886 // okay to get all the pixels given the rarity of monochrome icons. This scenario should be
887 // handled properly because: 1) the variables image_height and image_width will be overridden
888 // further below with the correct icon dimensions; 2) Only the first half of the pixels within
889 // the image_mask array will actually be referenced by the transparency checker in the loops,
890 // and that first half is the AND-mask, which is the transparency part that is needed. The
891 // second half, the XOR part, is not needed and thus ignored. Also note that if width/height
892 // required the icon to be scaled, LoadPicture() has already done that directly to the icon,
893 // so ii.hbmMask should already be scaled to match the size of the bitmap created later below.
894 image_mask = getbits(ii.hbmMask, hdc, image_width, image_height, image_is_16bit, 1);
895 DeleteObject(ii.hbmColor); // DeleteObject() probably handles NULL okay since few MSDN/other examples ever check for NULL.
896 DeleteObject(ii.hbmMask);
897 }
898 if ( !(hbitmap_image = IconToBitmap((HICON)hbitmap_image, true)) )
899 return "0"; //new
900 // return OK; // Let ErrorLevel tell the story.
901 }
902
903 if ( !(image_pixel = getbits(hbitmap_image, hdc, image_width, image_height, image_is_16bit)) )
904 goto end;
905
906 // Create an empty bitmap to hold all the pixels currently visible on the screen that lie within the search area:
907 int search_width = aRight - aLeft + 1;
908 int search_height = aBottom - aTop + 1;
909 if ( !(sdc = CreateCompatibleDC(hdc)) || !(hbitmap_screen = CreateCompatibleBitmap(hdc, search_width, search_height)) )
910 goto end;
911
912 if ( !(sdc_orig_select = SelectObject(sdc, hbitmap_screen)) )
913 goto end;
914
915 // Copy the pixels in the search-area of the screen into the DC to be searched:
916 if ( !(BitBlt(sdc, 0, 0, search_width, search_height, hdc, aLeft, aTop, SRCCOPY)) )
917 goto end;
918
919 LONG screen_width, screen_height;
920 bool screen_is_16bit;
921 if ( !(screen_pixel = getbits(hbitmap_screen, sdc, screen_width, screen_height, screen_is_16bit)) )
922 goto end;
923
924 LONG image_pixel_count = image_width * image_height;
925 LONG screen_pixel_count = screen_width * screen_height;
926 int i, j, k, x, y; // Declaring as "register" makes no performance difference with current compiler, so let the compiler choose which should be registers.
927
928 // If either is 16-bit, convert *both* to the 16-bit-compatible 32-bit format:
929 if (image_is_16bit || screen_is_16bit)
930 {
931 if (trans_color != CLR_NONE)
932 trans_color &= 0x00F8F8F8; // Convert indicated trans-color to be compatible with the conversion below.
933 for (i = 0; i < screen_pixel_count; ++i)
934 screen_pixel[i] &= 0x00F8F8F8; // Highest order byte must be masked to zero for consistency with use of 0x00FFFFFF below.
935 for (i = 0; i < image_pixel_count; ++i)
936 image_pixel[i] &= 0x00F8F8F8; // Same.
937 }
938
939 // v1.0.44.03: The below is now done even for variation>0 mode so its results are consistent with those of
940 // non-variation mode. This is relied upon by variation=0 mode but now also by the following line in the
941 // variation>0 section:
942 // || image_pixel[j] == trans_color
943 // Without this change, there are cases where variation=0 would find a match but a higher variation
944 // (for the same search) wouldn't.
945 for (i = 0; i < image_pixel_count; ++i)
946 image_pixel[i] &= 0x00FFFFFF;
947
948 // Search the specified region for the first occurrence of the image:
949 if (aVariation < 1) // Caller wants an exact match.
950 {
951 // Concerning the following use of 0x00FFFFFF, the use of 0x00F8F8F8 above is related (both have high order byte 00).
952 // The following needs to be done only when shades-of-variation mode isn't in effect because
953 // shades-of-variation mode ignores the high-order byte due to its use of macros such as GetRValue().
954 // This transformation incurs about a 15% performance decrease (percentage is fairly constant since
955 // it is proportional to the search-region size, which tends to be much larger than the search-image and
956 // is therefore the primary determination of how long the loops take). But it definitely helps find images
957 // more successfully in some cases. For example, if a PNG file is displayed in a GUI window, this
958 // transformation allows certain bitmap search-images to be found via variation==0 when they otherwise
959 // would require variation==1 (possibly the variation==1 success is just a side-effect of it
960 // ignoring the high-order byte -- maybe a much higher variation would be needed if the high
961 // order byte were also subject to the same shades-of-variation analysis as the other three bytes [RGB]).
962 for (i = 0; i < screen_pixel_count; ++i)
963 screen_pixel[i] &= 0x00FFFFFF;
964
965 for (i = 0; i < screen_pixel_count; ++i)
966 {
967 // Unlike the variation-loop, the following one uses a first-pixel optimization to boost performance
968 // by about 10% because it's only 3 extra comparisons and exact-match mode is probably used more often.
969 // Before even checking whether the other adjacent pixels in the region match the image, ensure
970 // the image does not extend past the right or bottom edges of the current part of the search region.
971 // This is done for performance but more importantly to prevent partial matches at the edges of the
972 // search region from being considered complete matches.
973 // The following check is ordered for short-circuit performance. In addition, image_mask, if
974 // non-NULL, is used to determine which pixels are transparent within the image and thus should
975 // match any color on the screen.
976 if ((screen_pixel[i] == image_pixel[0] // A screen pixel has been found that matches the image's first pixel.
977 || image_mask && image_mask[0] // Or: It's an icon's transparent pixel, which matches any color.
978 || image_pixel[0] == trans_color) // This should be okay even if trans_color==CLR_NONE, since CLR_NONE should never occur naturally in the image.
979 && image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
980 && image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
981 {
982 // Check if this candidate region -- which is a subset of the search region whose height and width
983 // matches that of the image -- is a pixel-for-pixel match of the image.
984 for (found = true, x = 0, y = 0, j = 0, k = i; j < image_pixel_count; ++j)
985 {
986 if (!(found = (screen_pixel[k] == image_pixel[j] // At least one pixel doesn't match, so this candidate is discarded.
987 || image_mask && image_mask[j] // Or: It's an icon's transparent pixel, which matches any color.
988 || image_pixel[j] == trans_color))) // This should be okay even if trans_color==CLR_NONE, since CLR none should never occur naturally in the image.
989 break;
990 if (++x < image_width) // We're still within the same row of the image, so just move on to the next screen pixel.
991 ++k;
992 else // We're starting a new row of the image.
993 {
994 x = 0; // Return to the leftmost column of the image.
995 ++y; // Move one row downward in the image.
996 // Move to the next row within the current-candiate region (not the entire search region).
997 // This is done by moving vertically downward from "i" (which is the upper-left pixel of the
998 // current-candidate region) by "y" rows.
999 k = i + y*screen_width; // Verified correct.
1000 }
1001 }
1002 if (found) // Complete match found.
1003 break;
1004 }
1005 }
1006 }
1007 else // Allow colors to vary by aVariation shades; i.e. approximate match is okay.
1008 {
1009 // The following section is part of the first-pixel-check optimization that improves performance by
1010 // 15% or more depending on where and whether a match is found. This section and one the follows
1011 // later is commented out to reduce code size.
1012 // Set high/low range for the first pixel of the image since it is the pixel most often checked
1013 // (i.e. for performance).
1014 //BYTE search_red1 = GetBValue(image_pixel[0]); // Because it's RGB vs. BGR, the B value is fetched, not R (though it doesn't matter as long as everything is internally consistent here).
1015 //BYTE search_green1 = GetGValue(image_pixel[0]);
1016 //BYTE search_blue1 = GetRValue(image_pixel[0]); // Same comment as above.
1017 //BYTE red_low1 = (aVariation > search_red1) ? 0 : search_red1 - aVariation;
1018 //BYTE green_low1 = (aVariation > search_green1) ? 0 : search_green1 - aVariation;
1019 //BYTE blue_low1 = (aVariation > search_blue1) ? 0 : search_blue1 - aVariation;
1020 //BYTE red_high1 = (aVariation > 0xFF - search_red1) ? 0xFF : search_red1 + aVariation;
1021 //BYTE green_high1 = (aVariation > 0xFF - search_green1) ? 0xFF : search_green1 + aVariation;
1022 //BYTE blue_high1 = (aVariation > 0xFF - search_blue1) ? 0xFF : search_blue1 + aVariation;
1023 // Above relies on the fact that the 16-bit conversion higher above was already done because like
1024 // in PixelSearch, it seems more appropriate to do the 16-bit conversion prior to setting the range
1025 // of high and low colors (vs. than applying 0xF8 to each of the high/low values individually).
1026
1027 BYTE red, green, blue;
1028 BYTE search_red, search_green, search_blue;
1029 BYTE red_low, green_low, blue_low, red_high, green_high, blue_high;
1030
1031 // The following loop is very similar to its counterpart above that finds an exact match, so maintain
1032 // them together and see above for more detailed comments about it.
1033 for (i = 0; i < screen_pixel_count; ++i)
1034 {
1035 // The following is commented out to trade code size reduction for performance (see comment above).
1036 //red = GetBValue(screen_pixel[i]); // Because it's RGB vs. BGR, the B value is fetched, not R (though it doesn't matter as long as everything is internally consistent here).
1037 //green = GetGValue(screen_pixel[i]);
1038 //blue = GetRValue(screen_pixel[i]);
1039 //if ((red >= red_low1 && red <= red_high1
1040 // && green >= green_low1 && green <= green_high1
1041 // && blue >= blue_low1 && blue <= blue_high1 // All three color components are a match, so this screen pixel matches the image's first pixel.
1042 // || image_mask && image_mask[0] // Or: It's an icon's transparent pixel, which matches any color.
1043 // || image_pixel[0] == trans_color) // This should be okay even if trans_color==CLR_NONE, since CLR none should never occur naturally in the image.
1044 // && image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
1045 // && image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
1046
1047 // Instead of the above, only this abbreviated check is done:
1048 if (image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
1049 && image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
1050 {
1051 // Since the first pixel is a match, check the other pixels.
1052 for (found = true, x = 0, y = 0, j = 0, k = i; j < image_pixel_count; ++j)
1053 {
1054 search_red = GetBValue(image_pixel[j]);
1055 search_green = GetGValue(image_pixel[j]);
1056 search_blue = GetRValue(image_pixel[j]);
1057 SET_COLOR_RANGE
1058 red = GetBValue(screen_pixel[k]);
1059 green = GetGValue(screen_pixel[k]);
1060 blue = GetRValue(screen_pixel[k]);
1061
1062 if (!(found = red >= red_low && red <= red_high
1063 && green >= green_low && green <= green_high
1064 && blue >= blue_low && blue <= blue_high
1065 || image_mask && image_mask[j] // Or: It's an icon's transparent pixel, which matches any color.
1066 || image_pixel[j] == trans_color)) // This should be okay even if trans_color==CLR_NONE, since CLR_NONE should never occur naturally in the image.
1067 break; // At least one pixel doesn't match, so this candidate is discarded.
1068 if (++x < image_width) // We're still within the same row of the image, so just move on to the next screen pixel.
1069 ++k;
1070 else // We're starting a new row of the image.
1071 {
1072 x = 0; // Return to the leftmost column of the image.
1073 ++y; // Move one row downward in the image.
1074 k = i + y*screen_width; // Verified correct.
1075 }
1076 }
1077 if (found) // Complete match found.
1078 break;
1079 }
1080 }
1081 }
1082
1083 //if (!found) // Must override ErrorLevel to its new value prior to the label below.
1084 // g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // "1" indicates search completed okay, but didn't find it.
1085
1086end:
1087 // If found==false when execution reaches here, ErrorLevel is already set to the right value, so just
1088 // clean up then return.
1089 ReleaseDC(NULL, hdc);
1090 DeleteObject(hbitmap_image);
1091 if (sdc)
1092 {
1093 if (sdc_orig_select) // i.e. the original call to SelectObject() didn't fail.
1094 SelectObject(sdc, sdc_orig_select); // Probably necessary to prevent memory leak.
1095 DeleteDC(sdc);
1096 }
1097 if (hbitmap_screen)
1098 DeleteObject(hbitmap_screen);
1099 if (image_pixel)
1100 free(image_pixel);
1101 if (image_mask)
1102 free(image_mask);
1103 if (screen_pixel)
1104 free(screen_pixel);
1105
1106 if (!found) // Let ErrorLevel, which is either "1" or "2" as set earlier, tell the story.
1107 return "0";
1108
1109 // Otherwise, success. Calculate xpos and ypos of where the match was found and adjust
1110 // coords to make them relative to the position of the target window (rect will contain
1111 // zeroes if this doesn't need to be done):
1112 //if (output_var_x && !output_var_x->Assign((aLeft + i%screen_width) - rect.left))
1113 // return FAIL;
1114 //if (output_var_y && !output_var_y->Assign((aTop + i/screen_width) - rect.top))
1115 // return FAIL;
1116
1117 int locx,locy;
1118
1119 //return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
1120 if (found)
1121 {
1122 locx = (aLeft + i%screen_width) - rect.left;
1123 locy = (aTop + i/screen_width) - rect.top;
1124// printf("\nFOUND!!!!%d %d",locx,locy);
1125 sprintf_s(answer,"1|%d|%d|%d|%d",locx,locy,image_width,image_height);
1126 return answer;
1127 //return "ZZ";
1128 }
1129 return "0";
1130
1131}