mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-06-01 06:33:56 +00:00
Update bitmap
This commit is contained in:
parent
e6daa02dfb
commit
368a40e25f
26
bitmap/bitmap_class.h
Normal file
26
bitmap/bitmap_class.h
Normal file
@ -0,0 +1,26 @@
|
||||
#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 */
|
||||
//,MMRect rect
|
||||
MMPoint afindBitmap(MMBitmapRef *needle);
|
55
bitmap/bitmap_find.h
Normal file
55
bitmap/bitmap_find.h
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#ifndef BITMAP_H
|
||||
#define BITMAP_H
|
||||
|
||||
// #include "../base/types.h"
|
||||
#include "../base/MMBitmap.h"
|
||||
#include "../base/MMPointArray_init.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 */
|
270
bitmap/bitmap_find_init.h
Normal file
270
bitmap/bitmap_find_init.h
Normal file
@ -0,0 +1,270 @@
|
||||
#include "bitmap_find.h"
|
||||
#include "../base/UTHashTable_init.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,10 +1,50 @@
|
||||
class BMP
|
||||
{
|
||||
public:
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t byteWidth;
|
||||
uint8_t bitsPerPixel;
|
||||
uint8_t bytesPerPixel;
|
||||
uint8_t *image;
|
||||
};
|
||||
// class BMP
|
||||
// {
|
||||
// public:
|
||||
// size_t width;
|
||||
// size_t height;
|
||||
// size_t byteWidth;
|
||||
// uint8_t bitsPerPixel;
|
||||
// uint8_t bytesPerPixel;
|
||||
// uint8_t *image;
|
||||
// };
|
||||
#include "bitmap_class.h"
|
||||
#include "bitmap_find_init.h"
|
||||
#include "../base/color_find_init.h"
|
||||
// #include "../screen/screen_init.h"
|
||||
#include "../base/io_init.h"
|
||||
#include "../base/pasteboard_init.h"
|
||||
#include "../base/str_io_init.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
MMBitmapRef aOpenBitmap(char *path){
|
||||
MMImageType type;
|
||||
|
||||
MMBitmapRef bitmap;
|
||||
MMIOError err;
|
||||
|
||||
bitmap = newMMBitmapFromFile(path, type, &err);
|
||||
// printf("....%zd\n",bitmap->width);
|
||||
return bitmap;
|
||||
|
||||
}
|
||||
|
||||
char *aSaveBitmap(MMBitmapRef bitmap,char *path, MMImageType type){
|
||||
if (saveMMBitmapToFile(bitmap, path, type) != 0) {
|
||||
return "Could not save image to file.";
|
||||
}else{
|
||||
saveMMBitmapToFile(bitmap, path, type);
|
||||
}
|
||||
//destroyMMBitmap(bitmap);
|
||||
return "ok";
|
||||
}
|
||||
|
||||
char *aTostringBitmap(MMBitmapRef bitmap){
|
||||
char *buf = NULL;
|
||||
MMBMPStringError err;
|
||||
|
||||
buf = (char *)createStringFromMMBitmap(bitmap, &err);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
30
robotgo.go
30
robotgo.go
@ -214,6 +214,36 @@ func SetKeyboardDelay(x C.size_t) {
|
||||
|______/ |__| |__| |__| |__| /__/ \__\ | _|
|
||||
*/
|
||||
|
||||
func OpenBitmap(gpath string) C.MMBitmapRef {
|
||||
path := C.CString(gpath)
|
||||
bit := C.aOpenBitmap(path)
|
||||
Println("opening...", bit)
|
||||
return bit
|
||||
// defer C.free(unsafe.Pointer(path))
|
||||
}
|
||||
|
||||
func SaveBitmap(args ...interface{}) {
|
||||
var mtype C.MMImageType
|
||||
Try(func() {
|
||||
mtype = args[2].(C.MMImageType)
|
||||
}, func(e interface{}) {
|
||||
Println("err:::", e)
|
||||
mtype = 1
|
||||
})
|
||||
|
||||
path := C.CString(args[1].(string))
|
||||
savebit := C.aSaveBitmap(args[0].(C.MMBitmapRef), path, mtype)
|
||||
Println("opening...", savebit)
|
||||
// return bit
|
||||
// defer C.free(unsafe.Pointer(path))
|
||||
}
|
||||
|
||||
func TostringBitmap(bit C.MMBitmapRef) *C.char {
|
||||
str_bit := C.aTostringBitmap(bit)
|
||||
// Println("...", str_bit)
|
||||
return str_bit
|
||||
}
|
||||
|
||||
/*
|
||||
____ __ ____ __ .__ __. _______ ______ ____ __ ____
|
||||
\ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /
|
||||
|
Loading…
Reference in New Issue
Block a user