Post by nnnik » 18 Jan 2017, 10:46
Code: Select all
if (aVariation < 1) // Caller wants an exact match.
{
// Concerning the following use of 0x00FFFFFF, the use of 0x00F8F8F8 above is related (both have high order byte 00).
// The following needs to be done only when shades-of-variation mode isn't in effect because
// shades-of-variation mode ignores the high-order byte due to its use of macros such as GetRValue().
// This transformation incurs about a 15% performance decrease (percentage is fairly constant since
// it is proportional to the search-region size, which tends to be much larger than the search-image and
// is therefore the primary determination of how long the loops take). But it definitely helps find images
// more successfully in some cases. For example, if a PNG file is displayed in a GUI window, this
// transformation allows certain bitmap search-images to be found via variation==0 when they otherwise
// would require variation==1 (possibly the variation==1 success is just a side-effect of it
// ignoring the high-order byte -- maybe a much higher variation would be needed if the high
// order byte were also subject to the same shades-of-variation analysis as the other three bytes [RGB]).
for (i = 0; i < screen_pixel_count; ++i)
screen_pixel[i] &= 0x00FFFFFF;
for (i = 0; i < screen_pixel_count; ++i)
{
// Unlike the variation-loop, the following one uses a first-pixel optimization to boost performance
// by about 10% because it's only 3 extra comparisons and exact-match mode is probably used more often.
// Before even checking whether the other adjacent pixels in the region match the image, ensure
// the image does not extend past the right or bottom edges of the current part of the search region.
// This is done for performance but more importantly to prevent partial matches at the edges of the
// search region from being considered complete matches.
// The following check is ordered for short-circuit performance. In addition, image_mask, if
// non-NULL, is used to determine which pixels are transparent within the image and thus should
// match any color on the screen.
if ((screen_pixel[i] == image_pixel[0] // A screen pixel has been found that matches the image's first pixel.
|| image_mask && image_mask[0] // Or: It's an icon's transparent pixel, which matches any color.
|| 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.
&& image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
&& image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
{
// Check if this candidate region -- which is a subset of the search region whose height and width
// matches that of the image -- is a pixel-for-pixel match of the image.
for (found = true, x = 0, y = 0, j = 0, k = i; j < image_pixel_count; ++j)
{
if (!(found = (screen_pixel[k] == image_pixel[j] // At least one pixel doesn't match, so this candidate is discarded.
|| image_mask && image_mask[j] // Or: It's an icon's transparent pixel, which matches any color.
|| 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.
break;
if (++x < image_width) // We're still within the same row of the image, so just move on to the next screen pixel.
++k;
else // We're starting a new row of the image.
{
x = 0; // Return to the leftmost column of the image.
++y; // Move one row downward in the image.
// Move to the next row within the current-candidate region (not the entire search region).
// This is done by moving vertically downward from "i" (which is the upper-left pixel of the
// current-candidate region) by "y" rows.
k = i + y*screen_width; // Verified correct.
}
}
if (found) // Complete match found.
break;
}
}
}
else // Allow colors to vary by aVariation shades; i.e. approximate match is okay.
{
// The following section is part of the first-pixel-check optimization that improves performance by
// 15% or more depending on where and whether a match is found. This section and one the follows
// later is commented out to reduce code size.
// Set high/low range for the first pixel of the image since it is the pixel most often checked
// (i.e. for performance).
//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).
//BYTE search_green1 = GetGValue(image_pixel[0]);
//BYTE search_blue1 = GetRValue(image_pixel[0]); // Same comment as above.
//BYTE red_low1 = (aVariation > search_red1) ? 0 : search_red1 - aVariation;
//BYTE green_low1 = (aVariation > search_green1) ? 0 : search_green1 - aVariation;
//BYTE blue_low1 = (aVariation > search_blue1) ? 0 : search_blue1 - aVariation;
//BYTE red_high1 = (aVariation > 0xFF - search_red1) ? 0xFF : search_red1 + aVariation;
//BYTE green_high1 = (aVariation > 0xFF - search_green1) ? 0xFF : search_green1 + aVariation;
//BYTE blue_high1 = (aVariation > 0xFF - search_blue1) ? 0xFF : search_blue1 + aVariation;
// Above relies on the fact that the 16-bit conversion higher above was already done because like
// in PixelSearch, it seems more appropriate to do the 16-bit conversion prior to setting the range
// of high and low colors (vs. than applying 0xF8 to each of the high/low values individually).
BYTE red, green, blue;
BYTE search_red, search_green, search_blue;
BYTE red_low, green_low, blue_low, red_high, green_high, blue_high;
// The following loop is very similar to its counterpart above that finds an exact match, so maintain
// them together and see above for more detailed comments about it.
for (i = 0; i < screen_pixel_count; ++i)
{
// The following is commented out to trade code size reduction for performance (see comment above).
//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).
//green = GetGValue(screen_pixel[i]);
//blue = GetRValue(screen_pixel[i]);
//if ((red >= red_low1 && red <= red_high1
// && green >= green_low1 && green <= green_high1
// && blue >= blue_low1 && blue <= blue_high1 // All three color components are a match, so this screen pixel matches the image's first pixel.
// || image_mask && image_mask[0] // Or: It's an icon's transparent pixel, which matches any color.
// || 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.
// && image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
// && image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
// Instead of the above, only this abbreviated check is done:
if (image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
&& image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
{
// Since the first pixel is a match, check the other pixels.
for (found = true, x = 0, y = 0, j = 0, k = i; j < image_pixel_count; ++j)
{
search_red = GetBValue(image_pixel[j]);
search_green = GetGValue(image_pixel[j]);
search_blue = GetRValue(image_pixel[j]);
SET_COLOR_RANGE
red = GetBValue(screen_pixel[k]);
green = GetGValue(screen_pixel[k]);
blue = GetRValue(screen_pixel[k]);
if (!(found = red >= red_low && red <= red_high
&& green >= green_low && green <= green_high
&& blue >= blue_low && blue <= blue_high
|| image_mask && image_mask[j] // Or: It's an icon's transparent pixel, which matches any color.
|| 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.
break; // At least one pixel doesn't match, so this candidate is discarded.
if (++x < image_width) // We're still within the same row of the image, so just move on to the next screen pixel.
++k;
else // We're starting a new row of the image.
{
x = 0; // Return to the leftmost column of the image.
++y; // Move one row downward in the image.
k = i + y*screen_width; // Verified correct.
}
}
if (found) // Complete match found.
break;
}
}
}
if (!found) // Must override ErrorLevel to its new value prior to the label below.
g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // "1" indicates search completed okay, but didn't find it.
end:
[code=cpp file=Untitled.cpp] if (aVariation < 1) // Caller wants an exact match.
{
// Concerning the following use of 0x00FFFFFF, the use of 0x00F8F8F8 above is related (both have high order byte 00).
// The following needs to be done only when shades-of-variation mode isn't in effect because
// shades-of-variation mode ignores the high-order byte due to its use of macros such as GetRValue().
// This transformation incurs about a 15% performance decrease (percentage is fairly constant since
// it is proportional to the search-region size, which tends to be much larger than the search-image and
// is therefore the primary determination of how long the loops take). But it definitely helps find images
// more successfully in some cases. For example, if a PNG file is displayed in a GUI window, this
// transformation allows certain bitmap search-images to be found via variation==0 when they otherwise
// would require variation==1 (possibly the variation==1 success is just a side-effect of it
// ignoring the high-order byte -- maybe a much higher variation would be needed if the high
// order byte were also subject to the same shades-of-variation analysis as the other three bytes [RGB]).
for (i = 0; i < screen_pixel_count; ++i)
screen_pixel[i] &= 0x00FFFFFF;
for (i = 0; i < screen_pixel_count; ++i)
{
// Unlike the variation-loop, the following one uses a first-pixel optimization to boost performance
// by about 10% because it's only 3 extra comparisons and exact-match mode is probably used more often.
// Before even checking whether the other adjacent pixels in the region match the image, ensure
// the image does not extend past the right or bottom edges of the current part of the search region.
// This is done for performance but more importantly to prevent partial matches at the edges of the
// search region from being considered complete matches.
// The following check is ordered for short-circuit performance. In addition, image_mask, if
// non-NULL, is used to determine which pixels are transparent within the image and thus should
// match any color on the screen.
if ((screen_pixel[i] == image_pixel[0] // A screen pixel has been found that matches the image's first pixel.
|| image_mask && image_mask[0] // Or: It's an icon's transparent pixel, which matches any color.
|| 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.
&& image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
&& image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
{
// Check if this candidate region -- which is a subset of the search region whose height and width
// matches that of the image -- is a pixel-for-pixel match of the image.
for (found = true, x = 0, y = 0, j = 0, k = i; j < image_pixel_count; ++j)
{
if (!(found = (screen_pixel[k] == image_pixel[j] // At least one pixel doesn't match, so this candidate is discarded.
|| image_mask && image_mask[j] // Or: It's an icon's transparent pixel, which matches any color.
|| 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.
break;
if (++x < image_width) // We're still within the same row of the image, so just move on to the next screen pixel.
++k;
else // We're starting a new row of the image.
{
x = 0; // Return to the leftmost column of the image.
++y; // Move one row downward in the image.
// Move to the next row within the current-candidate region (not the entire search region).
// This is done by moving vertically downward from "i" (which is the upper-left pixel of the
// current-candidate region) by "y" rows.
k = i + y*screen_width; // Verified correct.
}
}
if (found) // Complete match found.
break;
}
}
}
else // Allow colors to vary by aVariation shades; i.e. approximate match is okay.
{
// The following section is part of the first-pixel-check optimization that improves performance by
// 15% or more depending on where and whether a match is found. This section and one the follows
// later is commented out to reduce code size.
// Set high/low range for the first pixel of the image since it is the pixel most often checked
// (i.e. for performance).
//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).
//BYTE search_green1 = GetGValue(image_pixel[0]);
//BYTE search_blue1 = GetRValue(image_pixel[0]); // Same comment as above.
//BYTE red_low1 = (aVariation > search_red1) ? 0 : search_red1 - aVariation;
//BYTE green_low1 = (aVariation > search_green1) ? 0 : search_green1 - aVariation;
//BYTE blue_low1 = (aVariation > search_blue1) ? 0 : search_blue1 - aVariation;
//BYTE red_high1 = (aVariation > 0xFF - search_red1) ? 0xFF : search_red1 + aVariation;
//BYTE green_high1 = (aVariation > 0xFF - search_green1) ? 0xFF : search_green1 + aVariation;
//BYTE blue_high1 = (aVariation > 0xFF - search_blue1) ? 0xFF : search_blue1 + aVariation;
// Above relies on the fact that the 16-bit conversion higher above was already done because like
// in PixelSearch, it seems more appropriate to do the 16-bit conversion prior to setting the range
// of high and low colors (vs. than applying 0xF8 to each of the high/low values individually).
BYTE red, green, blue;
BYTE search_red, search_green, search_blue;
BYTE red_low, green_low, blue_low, red_high, green_high, blue_high;
// The following loop is very similar to its counterpart above that finds an exact match, so maintain
// them together and see above for more detailed comments about it.
for (i = 0; i < screen_pixel_count; ++i)
{
// The following is commented out to trade code size reduction for performance (see comment above).
//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).
//green = GetGValue(screen_pixel[i]);
//blue = GetRValue(screen_pixel[i]);
//if ((red >= red_low1 && red <= red_high1
// && green >= green_low1 && green <= green_high1
// && blue >= blue_low1 && blue <= blue_high1 // All three color components are a match, so this screen pixel matches the image's first pixel.
// || image_mask && image_mask[0] // Or: It's an icon's transparent pixel, which matches any color.
// || 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.
// && image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
// && image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
// Instead of the above, only this abbreviated check is done:
if (image_height <= screen_height - i/screen_width // Image is short enough to fit in the remaining rows of the search region.
&& image_width <= screen_width - i%screen_width) // Image is narrow enough not to exceed the right-side boundary of the search region.
{
// Since the first pixel is a match, check the other pixels.
for (found = true, x = 0, y = 0, j = 0, k = i; j < image_pixel_count; ++j)
{
search_red = GetBValue(image_pixel[j]);
search_green = GetGValue(image_pixel[j]);
search_blue = GetRValue(image_pixel[j]);
SET_COLOR_RANGE
red = GetBValue(screen_pixel[k]);
green = GetGValue(screen_pixel[k]);
blue = GetRValue(screen_pixel[k]);
if (!(found = red >= red_low && red <= red_high
&& green >= green_low && green <= green_high
&& blue >= blue_low && blue <= blue_high
|| image_mask && image_mask[j] // Or: It's an icon's transparent pixel, which matches any color.
|| 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.
break; // At least one pixel doesn't match, so this candidate is discarded.
if (++x < image_width) // We're still within the same row of the image, so just move on to the next screen pixel.
++k;
else // We're starting a new row of the image.
{
x = 0; // Return to the leftmost column of the image.
++y; // Move one row downward in the image.
k = i + y*screen_width; // Verified correct.
}
}
if (found) // Complete match found.
break;
}
}
}
if (!found) // Must override ErrorLevel to its new value prior to the label below.
g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // "1" indicates search completed okay, but didn't find it.
end:[/code]