async def image_search(
        self,
        image_path: str,
        upper_bound: Tuple[Union[int, str], Union[int, str]] = (0, 0),
        lower_bound: Optional[Tuple[Union[int, str], Union[int, str]]] = None,
        *,
        color_variation: Optional[int] = None,
        coord_mode: Optional[CoordModeRelativeTo] = None,
        scale_height: Optional[int] = None,
        scale_width: Optional[int] = None,
        transparent: Optional[str] = None,
        icon: Optional[int] = None,
        blocking: bool = True,
    ) -> Union[Coordinates, None, AsyncFutureResult[Optional[Coordinates]]]:
        """
        Analog for `ImageSearch <https://www.autohotkey.com/docs/commands/ImageSearch.htm>`_
        """

        if scale_height and not scale_width:
            scale_width = -1
        elif scale_width and not scale_height:
            scale_height = -1

        options: List[Union[str, int]] = []
        if icon:
            options.append(f'Icon{icon}')
        if color_variation is not None:
            options.append(color_variation)
        if transparent is not None:
            options.append(f'Trans{transparent}')
        if scale_width:
            options.append(f'w{scale_width}')
            options.append(f'h{scale_height}')

        x1, y1 = upper_bound
        if lower_bound:
            x2, y2 = lower_bound
        else:
            x2, y2 = ('A_ScreenWidth', 'A_ScreenHeight')

        args = [str(x1), str(y1), str(x2), str(y2)]
        if options:
            opts = ' '.join(f'*{opt}' for opt in options)
            args.append(opts + f' {image_path}')
        else:
            args.append(image_path)

        if coord_mode is not None:
            args.append(coord_mode)
        else:
            args.append('')

        resp = await self._transport.function_call('AHKImageSearch', args, blocking=blocking)
        return resp

        Analog for `ImageSearch <https://www.autohotkey.com/docs/commands/ImageSearch.htm>`_
        
ahk/_async/engine.py