mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-05-30 22:13:54 +00:00
Add capture multi screen support and update return, and remove unused files
This commit is contained in:
parent
48f1adce7e
commit
28fd0b9af6
@ -11,11 +11,13 @@ jobs:
|
||||
- checkout
|
||||
# specify any bash command here prefixed with `run: `
|
||||
- run: apt update
|
||||
- run: apt -y install gcc libc6-dev
|
||||
libx11-dev xorg-dev libxtst-dev libpng++-dev
|
||||
xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
libxkbcommon-dev
|
||||
- run:
|
||||
apt -y install gcc libc6-dev
|
||||
libx11-dev xorg-dev libxtst-dev
|
||||
xsel xclip
|
||||
# libpng++-dev
|
||||
# xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
# libxkbcommon-dev
|
||||
- run: apt -y install xvfb
|
||||
#
|
||||
# override:
|
||||
|
4
.github/workflows/go.yml
vendored
4
.github/workflows/go.yml
vendored
@ -22,10 +22,6 @@ jobs:
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
# if [ -f Gopkg.toml ]; then
|
||||
# curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
# dep ensure
|
||||
# fi
|
||||
|
||||
- name: Build
|
||||
run: go build -v .
|
||||
|
41
base/bitmap_free_c.h
Normal file
41
base/bitmap_free_c.h
Normal file
@ -0,0 +1,41 @@
|
||||
#include "MMBitmap.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
MMBitmapRef createMMBitmap_c(
|
||||
uint8_t *buffer,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t bytewidth,
|
||||
uint8_t bitsPerPixel,
|
||||
uint8_t bytesPerPixel
|
||||
){
|
||||
MMBitmapRef bitmap = malloc(sizeof(MMBitmap));
|
||||
if (bitmap == NULL) { return NULL; }
|
||||
|
||||
bitmap->imageBuffer = buffer;
|
||||
bitmap->width = width;
|
||||
bitmap->height = height;
|
||||
bitmap->bytewidth = bytewidth;
|
||||
bitmap->bitsPerPixel = bitsPerPixel;
|
||||
bitmap->bytesPerPixel = bytesPerPixel;
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
void destroyMMBitmap(MMBitmapRef bitmap) {
|
||||
assert(bitmap != NULL);
|
||||
|
||||
if (bitmap->imageBuffer != NULL) {
|
||||
free(bitmap->imageBuffer);
|
||||
bitmap->imageBuffer = NULL;
|
||||
}
|
||||
|
||||
free(bitmap);
|
||||
}
|
||||
|
||||
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint) {
|
||||
if (bitmapBuffer != NULL) {
|
||||
free(bitmapBuffer);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
// #ifndef BITMAP_CLASS_H
|
||||
// #define BITMAP_CLASS_H
|
||||
|
||||
#include "../base/MMBitmap.h"
|
||||
|
||||
/* This file defines the class "Bitmap" for dealing with raw bitmaps. */
|
||||
struct _BitmapObject {
|
||||
MMBitmapRef bitmap;
|
||||
MMPoint point; /* For iterator */
|
||||
};
|
||||
|
||||
typedef struct _BitmapObject BitmapObject;
|
||||
|
||||
// extern PyTypeObject Bitmap_Type;
|
||||
|
||||
/* Returns a newly-initialized BitmapObject from the given MMBitmap.
|
||||
* The reference to |bitmap| is "stolen"; i.e., only the pointer is copied, and
|
||||
* the reponsibility for free()'ing the buffer is given to the |BitmapObject|.
|
||||
*
|
||||
* Remember to call PyType_Ready() before using this for the first time! */
|
||||
BitmapObject BitmapObject_FromMMBitmap(MMBitmapRef bitmap);
|
||||
|
||||
// #endif /* PY_BITMAP_CLASS_H */
|
@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef BITMAP_H
|
||||
#define BITMAP_H
|
||||
|
||||
// #include "../base/types.h"
|
||||
#include "../base/MMBitmap.h"
|
||||
#include "../base/MMPointArray_c.h"
|
||||
|
||||
/* Convenience wrapper around findBitmapInRect(), where |rect| is the bounds
|
||||
* of |haystack|. */
|
||||
#define findBitmapInBitmap(needle, haystack, pointPtr, tol) \
|
||||
findBitmapInRect(needle, haystack, pointPtr, MMBitmapGetBounds(haystack), tol)
|
||||
|
||||
/* Returns 0 and sets |point| to the origin of |needle| in |haystack| if
|
||||
* |needle| was found in |haystack| inside of |rect|, or returns -1 if not.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors in the bitmaps need to match, with 0 being exact and 1 being any.
|
||||
*/
|
||||
int findBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMPoint *point, MMRect rect, float tolerance);
|
||||
|
||||
/* Convenience wrapper around findAllBitmapInRect(), where |rect| is the bounds
|
||||
* of |haystack|. */
|
||||
#define findAllBitmapInBitmap(needle, haystack, tolerance) \
|
||||
findAllBitmapInRect(needle, haystack, \
|
||||
MMBitmapGetBounds(haystack), tolerance)
|
||||
|
||||
/* Returns MMPointArray of all occurrences of |needle| in |haystack| inside of
|
||||
* |rect|. Note that an is returned regardless of whether |needle| was found;
|
||||
* check array->count to see if it actually was.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors in the bitmaps need to match, with 0 being exact and 1 being any.
|
||||
*
|
||||
* Responsibility for freeing the MMPointArray with destroyMMPointArray() is
|
||||
* given to the caller.
|
||||
*/
|
||||
MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
// #define MMRGBHexAtPoint(image, x, y) \
|
||||
// hexFromMMRGB(MMRGBColorAtPoint(image, x, y))
|
||||
|
||||
/* Convenience wrapper around countOfBitmapInRect(), where |rect| is the bounds
|
||||
* of |haystack|. */
|
||||
#define countOfBitmapInBitmap(needle, haystack, tolerance) \
|
||||
countOfBitmapInRect(needle, haystack, MMBitmapGetBounds(haystack), tolerance)
|
||||
|
||||
/* Returns the number of occurences of |needle| in |haystack| inside
|
||||
* of |rect|. */
|
||||
size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
#endif /* BITMAP_H */
|
@ -1,270 +0,0 @@
|
||||
#include "bitmap_find.h"
|
||||
#include "../base/UTHashTable_c.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* Node to be used in hash table. */
|
||||
struct shiftNode {
|
||||
UTHashNode_HEAD /* Make structure hashable */
|
||||
MMRGBHex color; /* Key */
|
||||
MMPoint offset; /* Value */
|
||||
};
|
||||
|
||||
/* --- Hash table helper functions --- */
|
||||
|
||||
/* Adds hex-color/offset pair to jump table. */
|
||||
static void addNodeToTable(UTHashTable *table, MMRGBHex color, MMPoint offset);
|
||||
|
||||
/* Returns node associated with color in jump table, or NULL if it
|
||||
* doesn't exist. */
|
||||
static struct shiftNode *nodeForColor(UTHashTable *table, MMRGBHex color);
|
||||
|
||||
/* Returns nonzero (true) if table has key, or zero (false) if not. */
|
||||
#define tableHasKey(table, color) (nodeForColor(table, color) != NULL)
|
||||
|
||||
/* --- Boyer-Moore helper functions --- */
|
||||
|
||||
/* Calculates the first table for use in a Boyer-Moore search algorithm.
|
||||
* Table is in the form [colors: shift_values], where colors are those in
|
||||
* |needle|, and the shift values are each color's distance from the rightmost
|
||||
* offset. All other colors are assumed to have a shift value equal to the
|
||||
* length of needle.
|
||||
*/
|
||||
static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle);
|
||||
|
||||
/* Frees memory occupied by calling initBadShiftTable().
|
||||
* Currently this is just an alias for destroyHashTable(). */
|
||||
#define destroyBadShiftTable(jumpTable) destroyHashTable(jumpTable)
|
||||
|
||||
/* Returns true if |needle| is found in |haystack| at |offset|. */
|
||||
static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMPoint offset, float tolerance);
|
||||
/* --- --- */
|
||||
|
||||
/* An modification of the Boyer-Moore-Horspool Algorithm, only applied to
|
||||
* bitmaps and colors instead of strings and characters.
|
||||
*
|
||||
* TODO: The Boyer-Moore algorithm (with the second jump table) would probably
|
||||
* be more efficient, but this was simpler (for now).
|
||||
*
|
||||
* The jump table (|badShiftTable|) is passed as a parameter to avoid being
|
||||
* recalculated each time. It should be a pointer to a UTHashTable init'd with
|
||||
* initBadShiftTable().
|
||||
*
|
||||
* Returns 0 and sets |point| to the starting point of |needle| in |haystack|
|
||||
* if |needle| was found in |haystack|, or returns -1 if not. */
|
||||
static int findBitmapInRectAt(MMBitmapRef needle,
|
||||
MMBitmapRef haystack,
|
||||
MMPoint *point,
|
||||
MMRect rect,
|
||||
float tolerance,
|
||||
MMPoint startPoint,
|
||||
UTHashTable *badShiftTable)
|
||||
{
|
||||
const size_t scanHeight = rect.size.height - needle->height;
|
||||
const size_t scanWidth = rect.size.width - needle->width;
|
||||
MMPoint pointOffset = startPoint;
|
||||
/* const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1); */
|
||||
|
||||
/* Sanity check */
|
||||
if (needle->height > haystack->height || needle->width > haystack->width ||
|
||||
!MMBitmapRectInBounds(haystack, rect)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(point != NULL);
|
||||
assert(needle != NULL);
|
||||
assert(needle->height > 0 && needle->width > 0);
|
||||
assert(haystack != NULL);
|
||||
assert(haystack->height > 0 && haystack->width > 0);
|
||||
assert(badShiftTable != NULL);
|
||||
|
||||
/* Search |haystack|, while |needle| can still be within it. */
|
||||
while (pointOffset.y <= scanHeight) {
|
||||
/* struct shiftNode *node = NULL;
|
||||
MMRGBHex lastColor; */
|
||||
|
||||
while (pointOffset.x <= scanWidth) {
|
||||
/* Check offset in |haystack| for |needle|. */
|
||||
if (needleAtOffset(needle, haystack, pointOffset, tolerance)) {
|
||||
// ++pointOffset.x;
|
||||
// ++pointOffset.y;
|
||||
*point = pointOffset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, calculate next x offset to check. */
|
||||
/*
|
||||
* Note that here we are getting the skip value based on the last
|
||||
* color of |needle|, no matter where we didn't match. The
|
||||
* alternative of pretending that the mismatched color was the previous
|
||||
* color is slower in the normal case.
|
||||
*/
|
||||
/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,
|
||||
pointOffset.y + lastPoint.y); */
|
||||
|
||||
/* TODO: This fails on certain edge cases (issue#7). */
|
||||
/* When a color is encountered that does not occur in |needle|, we can
|
||||
* safely skip ahead for the whole length of |needle|.
|
||||
* Otherwise, use the value stored in the jump table. */
|
||||
/* node = nodeForColor(badShiftTable, lastColor);
|
||||
pointOffset.x += (node == NULL) ? needle->width : (node->offset).x; */
|
||||
|
||||
/* For now, be naive. */
|
||||
++pointOffset.x;
|
||||
}
|
||||
|
||||
pointOffset.x = rect.origin.x;
|
||||
|
||||
/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,
|
||||
pointOffset.y + lastPoint.y);
|
||||
node = nodeForColor(badShiftTable, lastColor);
|
||||
pointOffset.y += node == NULL ? lastPoint.y : (node->offset).y; */
|
||||
|
||||
/* TODO: The above commented out code fails at certain edge cases, e.g.:
|
||||
* Needle: [B, b
|
||||
* b, b,
|
||||
* B, b]
|
||||
* Haystack: [w, w, w, w, w
|
||||
* w, w, w, w, b
|
||||
* w, w, w, b, b
|
||||
* w, w, w, w, b]
|
||||
* The previous algorithm noticed that the first 3 x 3 block had nothing
|
||||
* in common with the image, and thus, after scanning the first row,
|
||||
* skipped three blocks downward to scan the next (which didn't exist,
|
||||
* so the loop ended). However, the needle was hidden IN-BETWEEN this
|
||||
* jump -- skipping was appropriate for scanning the column but not
|
||||
* the row.
|
||||
*
|
||||
* I need to figure out a more optimal solution; temporarily I am just
|
||||
* scanning every single y coordinate, only skipping on x's. This
|
||||
* always works, but is probably not optimal.
|
||||
*/
|
||||
++pointOffset.y;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findBitmapInRect(MMBitmapRef needle,
|
||||
MMBitmapRef haystack,
|
||||
MMPoint *point,
|
||||
MMRect rect,
|
||||
float tolerance)
|
||||
{
|
||||
UTHashTable badShiftTable;
|
||||
int ret;
|
||||
|
||||
initBadShiftTable(&badShiftTable, needle);
|
||||
ret = findBitmapInRectAt(needle, haystack, point, rect,
|
||||
tolerance, MMPointZero, &badShiftTable);
|
||||
destroyBadShiftTable(&badShiftTable);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance)
|
||||
{
|
||||
MMPointArrayRef pointArray = createMMPointArray(0);
|
||||
MMPoint point = MMPointZero;
|
||||
UTHashTable badShiftTable;
|
||||
|
||||
initBadShiftTable(&badShiftTable, needle);
|
||||
while (findBitmapInRectAt(needle, haystack, &point, rect,
|
||||
tolerance, point, &badShiftTable) == 0) {
|
||||
const size_t scanWidth = (haystack->width - needle->width) + 1;
|
||||
MMPointArrayAppendPoint(pointArray, point);
|
||||
ITER_NEXT_POINT(point, scanWidth, 0);
|
||||
}
|
||||
destroyBadShiftTable(&badShiftTable);
|
||||
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance)
|
||||
{
|
||||
size_t count = 0;
|
||||
MMPoint point = MMPointZero;
|
||||
UTHashTable badShiftTable;
|
||||
|
||||
initBadShiftTable(&badShiftTable, needle);
|
||||
while (findBitmapInRectAt(needle, haystack, &point, rect,
|
||||
tolerance, point, &badShiftTable) == 0) {
|
||||
const size_t scanWidth = (haystack->width - needle->width) + 1;
|
||||
++count;
|
||||
ITER_NEXT_POINT(point, scanWidth, 0);
|
||||
}
|
||||
destroyBadShiftTable(&badShiftTable);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* --- Boyer-Moore helper functions --- */
|
||||
|
||||
static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle)
|
||||
{
|
||||
const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);
|
||||
const size_t maxColors = needle->width * needle->height;
|
||||
MMPoint scan;
|
||||
|
||||
/* Allocate max size initially to avoid a million calls to malloc(). */
|
||||
initHashTable(jumpTable, maxColors, sizeof(struct shiftNode));
|
||||
|
||||
/* Populate jumpTable with analysis of |needle|. */
|
||||
for (scan.y = lastPoint.y; ; --scan.y) {
|
||||
for (scan.x = lastPoint.x; ; --scan.x) {
|
||||
MMRGBHex color = MMRGBHexAtPoint(needle, scan.x, scan.y);
|
||||
if (!tableHasKey(jumpTable, color)) {
|
||||
addNodeToTable(jumpTable, color,
|
||||
MMPointMake(needle->width - scan.x,
|
||||
needle->height - scan.y));
|
||||
}
|
||||
|
||||
if (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */
|
||||
}
|
||||
if (scan.y == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMPoint offset, float tolerance)
|
||||
{
|
||||
const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);
|
||||
MMPoint scan;
|
||||
|
||||
/* Note that |needle| is searched backwards, in accordance with the
|
||||
* Boyer-Moore search algorithm. */
|
||||
for (scan.y = lastPoint.y; ; --scan.y) {
|
||||
for (scan.x = lastPoint.x; ; --scan.x) {
|
||||
MMRGBHex ncolor = MMRGBHexAtPoint(needle, scan.x, scan.y);
|
||||
MMRGBHex hcolor = MMRGBHexAtPoint(haystack, offset.x + scan.x,
|
||||
offset.y + scan.y);
|
||||
if (!MMRGBHexSimilarToColor(ncolor, hcolor, tolerance)) return 0;
|
||||
if (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */
|
||||
}
|
||||
if (scan.y == 0) break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* --- Hash table helper functions --- */
|
||||
|
||||
static void addNodeToTable(UTHashTable *table,
|
||||
MMRGBHex hexColor,
|
||||
MMPoint offset)
|
||||
{
|
||||
struct shiftNode *node = getNewNode(table);
|
||||
node->color = hexColor;
|
||||
node->offset = offset;
|
||||
UTHASHTABLE_ADD_INT(table, color, node, struct shiftNode);
|
||||
}
|
||||
|
||||
static struct shiftNode *nodeForColor(UTHashTable *table,
|
||||
MMRGBHex color)
|
||||
{
|
||||
struct shiftNode *uttable = table->uttable;
|
||||
struct shiftNode *node;
|
||||
HASH_FIND_INT(uttable, &color, node);
|
||||
return node;
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#include "bitmap_class.h"
|
||||
#include "bitmap_find_c.h"
|
||||
#include "../base/color_find_c.h"
|
||||
// #include "../screen/screen_c.h"
|
||||
#include "../base/file_io_c.h"
|
||||
#include "../base/pasteboard_c.h"
|
||||
#include "../base/str_io_c.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Returns false and sets error if |bitmap| is NULL. */
|
||||
bool bitmap_ready(MMBitmapRef bitmap){
|
||||
if (bitmap == NULL || bitmap->imageBuffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// void bitmap_dealloc(MMBitmapRef bitmap){
|
||||
// if (bitmap != NULL) {
|
||||
// destroyMMBitmap(bitmap);
|
||||
// bitmap = NULL;
|
||||
// }
|
||||
// }
|
||||
|
||||
bool bitmap_copy_to_pboard(MMBitmapRef bitmap){
|
||||
MMPasteError err;
|
||||
|
||||
if (!bitmap_ready(bitmap)) return false;
|
||||
if ((err = copyMMBitmapToPasteboard(bitmap)) != kMMPasteNoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MMBitmapRef bitmap_deepcopy(MMBitmapRef bitmap){
|
||||
return bitmap == NULL ? NULL : copyMMBitmap(bitmap);
|
||||
}
|
||||
|
||||
MMPoint find_bitmap(MMBitmapRef bitmap, MMBitmapRef sbit, float tolerance){
|
||||
MMPoint point = {-1, -1};
|
||||
// printf("tolenrance=%f\n", tolerance);
|
||||
if (!bitmap_ready(sbit) || !bitmap_ready(bitmap)) {
|
||||
printf("bitmap is not ready yet!\n");
|
||||
return point;
|
||||
}
|
||||
|
||||
MMRect rect = MMBitmapGetBounds(sbit);
|
||||
// printf("x=%d, y=%d, width=%d, height=%d\n", rect.origin.x,
|
||||
// rect.origin.y, rect.size.width, rect.size.height);
|
||||
|
||||
if (findBitmapInRect(bitmap, sbit, &point, rect, tolerance) == 0) {
|
||||
return point;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
MMPointArrayRef find_every_bitmap(MMBitmapRef bitmap, MMBitmapRef sbit, float tolerance, MMPoint *list){
|
||||
if (!bitmap_ready(bitmap) || !bitmap_ready(sbit)) { return NULL; }
|
||||
|
||||
MMPoint point;
|
||||
MMPointArrayRef pointArray;
|
||||
MMRect rect = MMBitmapGetBounds(sbit);
|
||||
|
||||
if (findBitmapInRect(bitmap, sbit, &point, rect, tolerance) == 0) {
|
||||
// return NULL;
|
||||
}
|
||||
|
||||
pointArray = findAllBitmapInRect(bitmap, sbit, rect, tolerance);
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
int count_of_bitmap(MMBitmapRef bitmap, MMBitmapRef sbit, float tolerance){
|
||||
if (!bitmap_ready(bitmap) || !bitmap_ready(sbit)) { return 0; }
|
||||
|
||||
MMRect rect = MMBitmapGetBounds(bitmap);
|
||||
return countOfBitmapInRect(bitmap, sbit, rect, tolerance);
|
||||
}
|
||||
|
||||
bool point_in_bounds(MMBitmapRef bitmap, MMPoint point){
|
||||
if (!bitmap_ready(bitmap)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (MMBitmapPointInBounds(bitmap, point)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MMBitmapRef bitmap_open(char *path, uint16_t ttype){
|
||||
// MMImageType type;
|
||||
MMBitmapRef bitmap;
|
||||
MMIOError err;
|
||||
|
||||
bitmap = newMMBitmapFromFile(path, ttype, &err);
|
||||
// printf("....%zd\n", bitmap->width);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
MMBitmapRef bitmap_from_string(const char *str){
|
||||
size_t len = strlen(str);
|
||||
|
||||
MMBitmapRef bitmap;
|
||||
MMBMPStringError err;
|
||||
|
||||
if ((bitmap = createMMBitmapFromString((unsigned char*)str, len, &err))
|
||||
== NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
char *bitmap_save(MMBitmapRef bitmap, char *path, uint16_t type){
|
||||
if (saveMMBitmapToFile(bitmap, path, (MMImageType) type) != 0) {
|
||||
return "Could not save image to file.";
|
||||
}
|
||||
// destroyMMBitmap(bitmap);
|
||||
return "";
|
||||
}
|
||||
|
||||
char *tostring_bitmap(MMBitmapRef bitmap){
|
||||
char *buf = NULL;
|
||||
MMBMPStringError err;
|
||||
|
||||
buf = (char *)createStringFromMMBitmap(bitmap, &err);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// char out size 200 is enough
|
||||
bool bitmap_str(MMBitmapRef bitmap, char *out){
|
||||
if (!bitmap_ready(bitmap)) { return false; }
|
||||
sprintf(out, "<Bitmap with resolution %lu%lu, \
|
||||
%u bits per pixel, and %u bytes per pixel>",
|
||||
(unsigned long)bitmap->width,
|
||||
(unsigned long)bitmap->height,
|
||||
bitmap->bitsPerPixel,
|
||||
bitmap->bytesPerPixel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MMBitmapRef get_portion(MMBitmapRef bit_map, MMRect rect){
|
||||
// MMRect rect;
|
||||
MMBitmapRef portion = NULL;
|
||||
|
||||
portion = copyMMBitmapFromPortion(bit_map, rect);
|
||||
return portion;
|
||||
}
|
||||
|
||||
MMRGBHex bitmap_get_color(MMBitmapRef bitmap, size_t x, size_t y){
|
||||
if (!bitmap_ready(bitmap)) { return 0; }
|
||||
|
||||
MMPoint point;
|
||||
point = MMPointMake(x, y);
|
||||
|
||||
if (!MMBitmapPointInBounds(bitmap, point)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return MMRGBHexAtPoint(bitmap, point.x, point.y);
|
||||
}
|
||||
|
||||
MMPoint bitmap_find_color(MMBitmapRef bitmap, MMRGBHex color, float tolerance){
|
||||
MMRect rect = MMBitmapGetBounds(bitmap);
|
||||
MMPoint point = {-1, -1};
|
||||
|
||||
if (findColorInRect(bitmap, color, &point, rect, tolerance) == 0) {
|
||||
return point;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
MMPointArrayRef bitmap_find_every_color(MMBitmapRef bitmap, MMRGBHex color, float tolerance, MMPoint *list){
|
||||
if (!bitmap_ready(bitmap)) { return NULL; }
|
||||
MMRect rect = MMBitmapGetBounds(bitmap);
|
||||
MMPointArrayRef pointArray;
|
||||
|
||||
pointArray = findAllColorInRect(bitmap, color, rect, tolerance);
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
int bitmap_count_of_color(MMBitmapRef bitmap, MMRGBHex color, float tolerance){
|
||||
if (!bitmap_ready(bitmap)) return 0;
|
||||
MMRect rect = MMBitmapGetBounds(bitmap);
|
||||
|
||||
return countOfColorsInRect(bitmap, color, rect, tolerance);
|
||||
}
|
4
go.mod
4
go.mod
@ -5,14 +5,14 @@ go 1.17
|
||||
require (
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e
|
||||
github.com/otiai10/gosseract v2.2.1+incompatible
|
||||
github.com/robotn/gohook v0.31.3
|
||||
// github.com/robotn/gohook v0.31.3
|
||||
github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934
|
||||
github.com/robotn/xgbutil v0.0.0-20190912154524-c861d6f87770
|
||||
github.com/vcaesar/gops v0.21.3
|
||||
github.com/vcaesar/imgo v0.30.0
|
||||
github.com/vcaesar/keycode v0.10.0
|
||||
github.com/vcaesar/tt v0.20.0
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
|
||||
)
|
||||
|
||||
|
2
go.sum
2
go.sum
@ -17,8 +17,6 @@ github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/robotn/gohook v0.31.3 h1:kGX8iukJ9ensVRwRKnTtdojAMQOpa6KFnXDi4OA4RaI=
|
||||
github.com/robotn/gohook v0.31.3/go.mod h1:wyGik0yb4iwCfJjDprtNkTyxkgQWuKoVPQ3hkz6+6js=
|
||||
github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934 h1:2lhSR8N3T6I30q096DT7/5AKEIcf1vvnnWAmS0wfnNY=
|
||||
github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ=
|
||||
github.com/robotn/xgbutil v0.0.0-20190912154524-c861d6f87770 h1:2uX8QRLkkxn2EpAQ6I3KhA79BkdRZfvugJUzJadiJwk=
|
||||
|
72
robotgo.go
72
robotgo.go
@ -18,6 +18,7 @@ See Requirements:
|
||||
https://github.com/go-vgo/robotgo#requirements
|
||||
|
||||
Installation:
|
||||
|
||||
With Go module support (Go 1.11+), just import:
|
||||
import "github.com/go-vgo/robotgo"
|
||||
|
||||
@ -68,7 +69,7 @@ import (
|
||||
|
||||
const (
|
||||
// Version get the robotgo version
|
||||
Version = "v0.100.0.1189, MT. Baker!"
|
||||
Version = "v1.00.0.1189, MT. Baker!"
|
||||
)
|
||||
|
||||
// GetVersion get the robotgo version
|
||||
@ -81,6 +82,9 @@ var (
|
||||
MouseSleep = 0
|
||||
// KeySleep set the key default millisecond sleep time
|
||||
KeySleep = 0
|
||||
|
||||
// DisplayID set the screen display id
|
||||
DisplayID = -1
|
||||
)
|
||||
|
||||
type (
|
||||
@ -199,17 +203,18 @@ func RgbToHex(r, g, b uint8) C.uint32_t {
|
||||
}
|
||||
|
||||
// GetPxColor get the pixel color return C.MMRGBHex
|
||||
func GetPxColor(x, y int) C.MMRGBHex {
|
||||
func GetPxColor(x, y int, displayId ...int) C.MMRGBHex {
|
||||
cx := C.int32_t(x)
|
||||
cy := C.int32_t(y)
|
||||
|
||||
color := C.get_px_color(cx, cy)
|
||||
display := displayIdx(displayId...)
|
||||
color := C.get_px_color(cx, cy, C.int32_t(display))
|
||||
return color
|
||||
}
|
||||
|
||||
// GetPixelColor get the pixel color return string
|
||||
func GetPixelColor(x, y int) string {
|
||||
return PadHex(GetPxColor(x, y))
|
||||
func GetPixelColor(x, y int, displayId ...int) string {
|
||||
return PadHex(GetPxColor(x, y, displayId...))
|
||||
}
|
||||
|
||||
// GetMouseColor get the mouse pos's color
|
||||
@ -218,6 +223,18 @@ func GetMouseColor() string {
|
||||
return GetPixelColor(x, y)
|
||||
}
|
||||
|
||||
func displayIdx(id ...int) int {
|
||||
display := -1
|
||||
if DisplayID != -1 {
|
||||
display = DisplayID
|
||||
}
|
||||
if len(id) > 0 {
|
||||
display = id[0]
|
||||
}
|
||||
|
||||
return display
|
||||
}
|
||||
|
||||
// SysScale get the sys scale
|
||||
func SysScale() float64 {
|
||||
s := C.sys_scale()
|
||||
@ -244,7 +261,7 @@ func GetScreenSize() (int, int) {
|
||||
|
||||
// GetScreenRect get the screen rect (x, y, w, h)
|
||||
func GetScreenRect(displayId ...int) Rect {
|
||||
display := 0
|
||||
display := -1
|
||||
if len(displayId) > 0 {
|
||||
display = displayId[0]
|
||||
}
|
||||
@ -274,8 +291,16 @@ func GetScaleSize() (int, int) {
|
||||
// use `defer robotgo.FreeBitmap(bitmap)` to free the bitmap
|
||||
//
|
||||
// robotgo.CaptureScreen(x, y, w, h int)
|
||||
func CaptureScreen(args ...int) C.MMBitmapRef {
|
||||
func CaptureScreen(args ...int) CBitmap {
|
||||
var x, y, w, h C.int32_t
|
||||
displayId := -1
|
||||
if DisplayID != -1 {
|
||||
displayId = DisplayID
|
||||
}
|
||||
|
||||
if len(args) > 4 {
|
||||
displayId = args[4]
|
||||
}
|
||||
|
||||
if len(args) > 3 {
|
||||
x = C.int32_t(args[0])
|
||||
@ -284,16 +309,15 @@ func CaptureScreen(args ...int) C.MMBitmapRef {
|
||||
h = C.int32_t(args[3])
|
||||
} else {
|
||||
// Get the main screen rect.
|
||||
rect := GetScreenRect()
|
||||
|
||||
rect := GetScreenRect(displayId)
|
||||
x = C.int32_t(rect.X)
|
||||
y = C.int32_t(rect.Y)
|
||||
w = C.int32_t(rect.W)
|
||||
h = C.int32_t(rect.H)
|
||||
}
|
||||
|
||||
bit := C.capture_screen(x, y, w, h)
|
||||
return bit
|
||||
bit := C.capture_screen(x, y, w, h, C.int32_t(displayId))
|
||||
return CBitmap(bit)
|
||||
}
|
||||
|
||||
// GoCaptureScreen capture the screen and return bitmap(go struct)
|
||||
@ -313,13 +337,13 @@ func CaptureImg(args ...int) image.Image {
|
||||
}
|
||||
|
||||
// FreeBitmap free and dealloc the C bitmap
|
||||
func FreeBitmap(bitmap C.MMBitmapRef) {
|
||||
func FreeBitmap(bitmap CBitmap) {
|
||||
// C.destroyMMBitmap(bitmap)
|
||||
C.bitmap_dealloc(bitmap)
|
||||
C.bitmap_dealloc(C.MMBitmapRef(bitmap))
|
||||
}
|
||||
|
||||
// ToBitmap trans C.MMBitmapRef to Bitmap
|
||||
func ToBitmap(bit C.MMBitmapRef) Bitmap {
|
||||
func ToBitmap(bit CBitmap) Bitmap {
|
||||
bitmap := Bitmap{
|
||||
ImgBuf: (*uint8)(bit.imageBuffer),
|
||||
Width: int(bit.width),
|
||||
@ -332,13 +356,27 @@ func ToBitmap(bit C.MMBitmapRef) Bitmap {
|
||||
return bitmap
|
||||
}
|
||||
|
||||
// ToCBitmap trans Bitmap to C.MMBitmapRef
|
||||
func ToCBitmap(bit Bitmap) CBitmap {
|
||||
cbitmap := C.createMMBitmap_c(
|
||||
(*C.uint8_t)(bit.ImgBuf),
|
||||
C.size_t(bit.Width),
|
||||
C.size_t(bit.Height),
|
||||
C.size_t(bit.Bytewidth),
|
||||
C.uint8_t(bit.BitsPixel),
|
||||
C.uint8_t(bit.BytesPerPixel),
|
||||
)
|
||||
|
||||
return CBitmap(cbitmap)
|
||||
}
|
||||
|
||||
// ToImage convert C.MMBitmapRef to standard image.Image
|
||||
func ToImage(bit C.MMBitmapRef) image.Image {
|
||||
func ToImage(bit CBitmap) image.Image {
|
||||
return ToRGBA(bit)
|
||||
}
|
||||
|
||||
// ToRGBA convert C.MMBitmapRef to standard image.RGBA
|
||||
func ToRGBA(bit C.MMBitmapRef) *image.RGBA {
|
||||
func ToRGBA(bit CBitmap) *image.RGBA {
|
||||
bmp1 := ToBitmap(bit)
|
||||
return ToRGBAGo(bmp1)
|
||||
}
|
||||
@ -536,7 +574,7 @@ func MoveSmooth(x, y int, args ...interface{}) bool {
|
||||
cy := C.int32_t(y)
|
||||
|
||||
var (
|
||||
mouseDelay = 5
|
||||
mouseDelay = 1
|
||||
low C.double
|
||||
high C.double
|
||||
)
|
||||
|
@ -14,7 +14,6 @@
|
||||
package robotgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/vcaesar/tt"
|
||||
@ -152,36 +151,6 @@ func TestKeyCode(t *testing.T) {
|
||||
tt.Equal(t, "=", s)
|
||||
}
|
||||
|
||||
func TestBitmap(t *testing.T) {
|
||||
bit := CaptureScreen()
|
||||
tt.NotNil(t, bit)
|
||||
e := SaveBitmap(bit, "robot_test.png")
|
||||
tt.Empty(t, e)
|
||||
|
||||
bit0 := CaptureScreen(10, 10, 20, 20)
|
||||
x, y := FindBitmap(bit0)
|
||||
fmt.Println("Find bitmap: ", x, y)
|
||||
|
||||
arr := FindAllBitmap(bit0)
|
||||
fmt.Println("Find all bitmap:", arr)
|
||||
tt.Equal(t, 1, len(arr))
|
||||
|
||||
c1 := CHex(0xAADCDC)
|
||||
x, y = FindColor(c1)
|
||||
fmt.Println("Find color: ", x, y)
|
||||
arr = FindAllColor(c1)
|
||||
fmt.Println("Find all color: ", arr)
|
||||
|
||||
img := ToImage(bit)
|
||||
err := SavePng(img, "robot_img.png")
|
||||
tt.Nil(t, err)
|
||||
|
||||
bit1 := OpenBitmap("robot_test.png")
|
||||
b := tt.TypeOf(bit, bit1)
|
||||
tt.True(t, b)
|
||||
tt.NotNil(t, bit1)
|
||||
}
|
||||
|
||||
func TestPs(t *testing.T) {
|
||||
id, err := Pids()
|
||||
tt.Not(t, "[]", id)
|
||||
|
@ -45,7 +45,7 @@ uint32_t color_rgb_to_hex(uint8_t r, uint8_t g, uint8_t b){
|
||||
return RGB_TO_HEX(r, g, b);
|
||||
}
|
||||
|
||||
MMRGBHex get_px_color(int32_t x, int32_t y){
|
||||
MMRGBHex get_px_color(int32_t x, int32_t y, int32_t display_id) {
|
||||
MMBitmapRef bitmap;
|
||||
MMRGBHex color;
|
||||
|
||||
@ -53,7 +53,7 @@ MMRGBHex get_px_color(int32_t x, int32_t y){
|
||||
return color;
|
||||
}
|
||||
|
||||
bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, 1, 1));
|
||||
bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, 1, 1), display_id);
|
||||
// bitmap = MMRectMake(x, y, 1, 1);
|
||||
|
||||
color = MMRGBHexAtPoint(bitmap, 0, 0);
|
||||
@ -62,8 +62,8 @@ MMRGBHex get_px_color(int32_t x, int32_t y){
|
||||
return color;
|
||||
}
|
||||
|
||||
char* get_pixel_color(int32_t x, int32_t y){
|
||||
MMRGBHex color = get_px_color(x, y);
|
||||
char* get_pixel_color(int32_t x, int32_t y, int32_t display_id) {
|
||||
MMRGBHex color = get_px_color(x, y, display_id);
|
||||
|
||||
char* s = pad_hex(color);
|
||||
return s;
|
||||
@ -104,16 +104,8 @@ void bitmap_dealloc(MMBitmapRef bitmap){
|
||||
}
|
||||
|
||||
// capture_screen capture screen
|
||||
MMBitmapRef capture_screen(int32_t x, int32_t y, int32_t w, int32_t h){
|
||||
// if () {
|
||||
// x = 0;
|
||||
// y = 0;
|
||||
// // Get screen size.
|
||||
// MMSize displaySize = getMainDisplaySize();
|
||||
// w = displaySize.width;
|
||||
// h = displaySize.height;
|
||||
// }
|
||||
MMBitmapRef bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, w, h));
|
||||
MMBitmapRef capture_screen(int32_t x, int32_t y, int32_t w, int32_t h, int32_t display_id) {
|
||||
MMBitmapRef bitmap = copyMMBitmapFromDisplayInRect(MMRectInt32Make(x, y, w, h), display_id);
|
||||
// printf("%s\n", bitmap);
|
||||
return bitmap;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ MMSizeInt32 getMainDisplaySize(void){
|
||||
MMRectInt32 getScreenRect(int32_t display_id) {
|
||||
#if defined(IS_MACOSX)
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID) display_id;
|
||||
if (display_id == 0) {
|
||||
if (display_id == -1) {
|
||||
displayID = CGMainDisplayID();
|
||||
}
|
||||
CGRect displayRect = CGDisplayBounds(displayID);
|
||||
|
@ -3,7 +3,8 @@
|
||||
#define SCREENGRAB_H
|
||||
|
||||
#include "../base/types.h"
|
||||
#include "../base/MMBitmap_c.h"
|
||||
// #include "../base/MMBitmap_c.h"
|
||||
#include "../base/bitmap_free_c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -12,7 +13,7 @@ extern "C"
|
||||
|
||||
/* Returns a raw bitmap of screengrab of the display (to be destroyed()'d by
|
||||
* caller), or NULL on error. */
|
||||
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect);
|
||||
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect, int32_t display_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -17,22 +17,24 @@
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect){
|
||||
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect, int32_t display_id){
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
MMBitmapRef bitmap = NULL;
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
|
||||
CGDirectDisplayID displayID = CGMainDisplayID();
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID) display_id;
|
||||
|
||||
if (displayID == -1) {
|
||||
displayID = CGMainDisplayID();
|
||||
}
|
||||
|
||||
CGImageRef image = CGDisplayCreateImageForRect(displayID,
|
||||
CGRectMake(rect.origin.x, rect.origin.y, rect.size.w, rect.size.h));
|
||||
|
||||
if (!image) { return NULL; }
|
||||
|
||||
CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(image));
|
||||
|
||||
if (!imageData) { return NULL; }
|
||||
|
||||
bufferSize = CFDataGetLength(imageData);
|
||||
@ -40,7 +42,7 @@ MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect){
|
||||
|
||||
CFDataGetBytes(imageData, CFRangeMake(0, bufferSize), buffer);
|
||||
|
||||
bitmap = createMMBitmap(buffer,
|
||||
bitmap = createMMBitmap_c(buffer,
|
||||
CGImageGetWidth(image),CGImageGetHeight(image),
|
||||
CGImageGetBytesPerRow(image), CGImageGetBitsPerPixel(image),
|
||||
CGImageGetBitsPerPixel(image) / 8);
|
||||
@ -60,7 +62,7 @@ MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect){
|
||||
XCloseDisplay(display);
|
||||
if (image == NULL) { return NULL; }
|
||||
|
||||
bitmap = createMMBitmap((uint8_t *)image->data,
|
||||
bitmap = createMMBitmap_c((uint8_t *)image->data,
|
||||
rect.size.w, rect.size.h, (size_t)image->bytes_per_line,
|
||||
(uint8_t)image->bits_per_pixel, (uint8_t)image->bits_per_pixel / 8);
|
||||
image->data = NULL; /* Steal ownership of bitmap data so we don't have to
|
||||
@ -109,7 +111,7 @@ MMBitmapRef copyMMBitmapFromDisplayInRect(MMRectInt32 rect){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bitmap = createMMBitmap(NULL, rect.size.w, rect.size.h, 4 * rect.size.w,
|
||||
bitmap = createMMBitmap_c(NULL, rect.size.w, rect.size.h, 4 * rect.size.w,
|
||||
(uint8_t)bi.bmiHeader.biBitCount, 4);
|
||||
|
||||
/* Copy the data to our pixel buffer. */
|
||||
|
Loading…
Reference in New Issue
Block a user