Update bitmap

This commit is contained in:
vCaesar 2016-10-20 01:10:10 +08:00
parent e6daa02dfb
commit 368a40e25f
5 changed files with 431 additions and 10 deletions

26
bitmap/bitmap_class.h Normal file
View 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
View 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
View 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;
}

View File

@ -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;
}

View File

@ -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
}
/*
____ __ ____ __ .__ __. _______ ______ ____ __ ____
\ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /