From 368a40e25f5dbc36160b822d07d43b87976116d5 Mon Sep 17 00:00:00 2001 From: vCaesar Date: Thu, 20 Oct 2016 01:10:10 +0800 Subject: [PATCH] Update bitmap --- bitmap/bitmap_class.h | 26 ++++ bitmap/bitmap_find.h | 55 ++++++++ bitmap/bitmap_find_init.h | 270 ++++++++++++++++++++++++++++++++++++++ bitmap/goBitmap.h | 60 +++++++-- robotgo.go | 30 +++++ 5 files changed, 431 insertions(+), 10 deletions(-) create mode 100644 bitmap/bitmap_class.h create mode 100644 bitmap/bitmap_find.h create mode 100644 bitmap/bitmap_find_init.h diff --git a/bitmap/bitmap_class.h b/bitmap/bitmap_class.h new file mode 100644 index 0000000..ca09930 --- /dev/null +++ b/bitmap/bitmap_class.h @@ -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); \ No newline at end of file diff --git a/bitmap/bitmap_find.h b/bitmap/bitmap_find.h new file mode 100644 index 0000000..c8793ca --- /dev/null +++ b/bitmap/bitmap_find.h @@ -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 */ diff --git a/bitmap/bitmap_find_init.h b/bitmap/bitmap_find_init.h new file mode 100644 index 0000000..ff14bd7 --- /dev/null +++ b/bitmap/bitmap_find_init.h @@ -0,0 +1,270 @@ +#include "bitmap_find.h" +#include "../base/UTHashTable_init.h" +#include + +/* 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; +} diff --git a/bitmap/goBitmap.h b/bitmap/goBitmap.h index 217c74d..9cfa9c1 100644 --- a/bitmap/goBitmap.h +++ b/bitmap/goBitmap.h @@ -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 +#include + +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; +} diff --git a/robotgo.go b/robotgo.go index 1255f92..5e7b56c 100644 --- a/robotgo.go +++ b/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 +} + /* ____ __ ____ __ .__ __. _______ ______ ____ __ ____ \ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /