mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-05-31 06:13:55 +00:00
base bitmap
This commit is contained in:
parent
5a30749dfe
commit
33c461bc15
@ -22,9 +22,14 @@ This is a work in progress.
|
||||
|
||||
####ALL:
|
||||
Golang
|
||||
//Gcc
|
||||
zlib & libpng (bitmap)
|
||||
|
||||
####For Mac OS X:
|
||||
Xcode Command Line Tools
|
||||
|
||||
brew install libpng
|
||||
brew install homebrew/dupes/zlib
|
||||
####For Windows:
|
||||
MinGW or other GCC
|
||||
####For everything else:
|
||||
|
@ -23,9 +23,14 @@ RobotGo 支持 Mac, Windows, and Linux(X11).
|
||||
|
||||
####ALL:
|
||||
Golang
|
||||
//Gcc
|
||||
zlib & libpng (bitmap)
|
||||
|
||||
####For Mac OS X:
|
||||
Xcode Command Line Tools
|
||||
|
||||
brew install libpng
|
||||
brew install homebrew/dupes/zlib
|
||||
####For Windows:
|
||||
MinGW or other GCC
|
||||
####For everything else(Linux等其他系统):
|
||||
|
@ -7,6 +7,12 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// #if defined(_MSC_VER)
|
||||
// #include "ms_stdint.h"
|
||||
// #else
|
||||
// #include <stdint.h>
|
||||
// #endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -24,6 +30,7 @@ struct _MMBitmap {
|
||||
|
||||
typedef struct _MMBitmap MMBitmap;
|
||||
typedef MMBitmap *MMBitmapRef;
|
||||
MMBitmapRef bitmap;
|
||||
|
||||
/* Creates new MMBitmap with the given values.
|
||||
* Follows the Create Rule (caller is responsible for destroy()'ing object). */
|
||||
|
33
base/MMPointArray.h
Normal file
33
base/MMPointArray.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#ifndef MMARRAY_H
|
||||
#define MMARRAY_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct _MMPointArray {
|
||||
MMPoint *array; /* Pointer to actual data. */
|
||||
size_t count; /* Number of elements in array. */
|
||||
size_t _allocedCount; /* Private; do not use outside of MMPointArray.c. */
|
||||
};
|
||||
|
||||
typedef struct _MMPointArray MMPointArray;
|
||||
typedef MMPointArray *MMPointArrayRef;
|
||||
|
||||
/* Creates array of an initial size (the maximum size is still limitless).
|
||||
* This follows the "Create" Rule; i.e., responsibility for "destroying" the
|
||||
* array is given to the caller. */
|
||||
MMPointArrayRef createMMPointArray(size_t initialCount);
|
||||
|
||||
/* Frees memory occupied by |pointArray|. Does not accept NULL. */
|
||||
void destroyMMPointArray(MMPointArrayRef pointArray);
|
||||
|
||||
/* Appends a point to an array, increasing the internal size if necessary. */
|
||||
void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point);
|
||||
|
||||
/* Retrieve point from array. */
|
||||
#define MMPointArrayGetItem(a, i) ((a)->array)[i]
|
||||
|
||||
/* Set point in array. */
|
||||
#define MMPointArraySetItem(a, i, item) ((a)->array[i] = item)
|
||||
|
||||
#endif /* MMARRAY_H */
|
41
base/MMPointArray_init.h
Normal file
41
base/MMPointArray_init.h
Normal file
@ -0,0 +1,41 @@
|
||||
#include "MMPointArray.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
MMPointArrayRef createMMPointArray(size_t initialCount)
|
||||
{
|
||||
MMPointArrayRef pointArray = calloc(1, sizeof(MMPointArray));
|
||||
|
||||
if (initialCount == 0) initialCount = 1;
|
||||
|
||||
pointArray->_allocedCount = initialCount;
|
||||
pointArray->array = malloc(pointArray->_allocedCount * sizeof(MMPoint));
|
||||
if (pointArray->array == NULL) return NULL;
|
||||
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
void destroyMMPointArray(MMPointArrayRef pointArray)
|
||||
{
|
||||
if (pointArray->array != NULL) {
|
||||
free(pointArray->array);
|
||||
pointArray->array = NULL;
|
||||
}
|
||||
|
||||
free(pointArray);
|
||||
}
|
||||
|
||||
void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point)
|
||||
{
|
||||
const size_t newCount = ++(pointArray->count);
|
||||
if (pointArray->_allocedCount < newCount) {
|
||||
do {
|
||||
/* Double size each time to avoid calls to realloc(). */
|
||||
pointArray->_allocedCount <<= 1;
|
||||
} while (pointArray->_allocedCount < newCount);
|
||||
pointArray->array = realloc(pointArray->array,
|
||||
sizeof(point) *
|
||||
pointArray->_allocedCount);
|
||||
}
|
||||
|
||||
pointArray->array[pointArray->count - 1] = point;
|
||||
}
|
83
base/UTHashTable.h
Normal file
83
base/UTHashTable.h
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#ifndef UTHASHTABLE_H
|
||||
#define UTHASHTABLE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "uthash.h"
|
||||
|
||||
/* All node structs must begin with this (note that there is NO semicolon). */
|
||||
#define UTHashNode_HEAD UT_hash_handle hh;
|
||||
|
||||
/* This file contains convenience macros and a standard struct for working with
|
||||
* uthash hash tables.
|
||||
*
|
||||
* The main purpose of this is for convenience of creating/freeing nodes. */
|
||||
struct _UTHashTable {
|
||||
void *uttable; /* The uthash table -- must start out as NULL. */
|
||||
void *nodes; /* Contiguous array of nodes. */
|
||||
size_t allocedNodeCount; /* Node count currently allocated for. */
|
||||
size_t nodeCount; /* Current node count. */
|
||||
size_t nodeSize; /* Size of each node. */
|
||||
};
|
||||
|
||||
typedef struct _UTHashTable UTHashTable;
|
||||
|
||||
/* Initiates a hash table to the default values. |table| should point to an
|
||||
* already allocated UTHashTable struct.
|
||||
*
|
||||
* If the |initialCount| argument in initHashTable is given, |nodes| is
|
||||
* allocated immediately to the maximum size and new nodes are simply slices of
|
||||
* that array. This can save calls to malloc if many nodes are to be added, and
|
||||
* the a reasonable maximum number is known ahead of time.
|
||||
*
|
||||
* If the node count goes over this maximum, or if |initialCount| is 0, the
|
||||
* array is dynamically reallocated to fit the size.
|
||||
*/
|
||||
void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize);
|
||||
|
||||
/* Frees memory occupied by a UTHashTable's members.
|
||||
*
|
||||
* Note that this does NOT free memory for the UTHashTable pointed to by
|
||||
* |table| itself; if that was allocated on the heap, you must free() it
|
||||
* yourself after calling this. */
|
||||
void destroyHashTable(UTHashTable *table);
|
||||
|
||||
/* Returns memory allocated for a new node. Responsibility for freeing this is
|
||||
* up to the destroyHashTable() macro; this should NOT be freed by the caller.
|
||||
*
|
||||
* This is intended to be used with a HASH_ADD() macro, e.g.:
|
||||
* {%
|
||||
* struct myNode *uttable = utHashTable->uttable;
|
||||
* struct myNode *node = getNewNode(utHashTable);
|
||||
* node->key = 42;
|
||||
* node->value = someValue;
|
||||
* HASH_ADD_INT(uttable, key, node);
|
||||
* utHashTable->uttable = uttable;
|
||||
* %}
|
||||
*
|
||||
* Or, use the UTHASHTABLE_ADD_INT or UTHASHTABLE_ADD_STR macros
|
||||
* for convenience (they are exactly equivalent):
|
||||
* {%
|
||||
* struct myNode *node = getNewNode(utHashTable);
|
||||
* node->key = 42;
|
||||
* node->value = someValue;
|
||||
* UTHASHTABLE_ADD_INT(utHashTable, key, node, struct myNode);
|
||||
* %}
|
||||
*/
|
||||
void *getNewNode(UTHashTable *table);
|
||||
|
||||
#define UTHASHTABLE_ADD_INT(tablePtr, keyName, node, nodeType) \
|
||||
do { \
|
||||
nodeType *uttable = (tablePtr)->uttable; \
|
||||
HASH_ADD_INT(uttable, keyName, node); \
|
||||
(tablePtr)->uttable = uttable; \
|
||||
} while (0)
|
||||
|
||||
#define UTHASHTABLE_ADD_STR(tablePtr, keyName, node, nodeType) \
|
||||
do { \
|
||||
nodeType *uttable = (tablePtr)->uttable; \
|
||||
HASH_ADD_STR(uttable, keyName, node); \
|
||||
(tablePtr)->uttable = uttable; \
|
||||
} while (0)
|
||||
|
||||
#endif /* MMHASHTABLE_H */
|
56
base/UTHashTable_init.h
Normal file
56
base/UTHashTable_init.h
Normal file
@ -0,0 +1,56 @@
|
||||
#include "UTHashTable.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Base struct class (all nodes must contain at least the elements in
|
||||
* this struct). */
|
||||
struct _UTHashNode {
|
||||
UTHashNode_HEAD
|
||||
};
|
||||
|
||||
typedef struct _UTHashNode UTHashNode;
|
||||
|
||||
void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize)
|
||||
{
|
||||
assert(table != NULL);
|
||||
assert(nodeSize >= sizeof(UTHashNode));
|
||||
|
||||
table->uttable = NULL; /* Must be set to NULL for uthash. */
|
||||
table->allocedNodeCount = (initialCount == 0) ? 1 : initialCount;
|
||||
table->nodeCount = 0;
|
||||
table->nodeSize = nodeSize;
|
||||
table->nodes = calloc(table->nodeSize, nodeSize * table->allocedNodeCount);
|
||||
}
|
||||
|
||||
void destroyHashTable(UTHashTable *table)
|
||||
{
|
||||
UTHashNode *uttable = table->uttable;
|
||||
UTHashNode *node;
|
||||
|
||||
/* Let uthash do its magic. */
|
||||
while (uttable != NULL) {
|
||||
node = uttable; /* Grab pointer to first item. */
|
||||
HASH_DEL(uttable, node); /* Delete it (table advances to next). */
|
||||
}
|
||||
|
||||
/* Only giant malloc'd block containing each node must be freed. */
|
||||
if (table->nodes != NULL) free(table->nodes);
|
||||
table->uttable = table->nodes = NULL;
|
||||
}
|
||||
|
||||
void *getNewNode(UTHashTable *table)
|
||||
{
|
||||
/* Increment node count, resizing table if necessary. */
|
||||
const size_t newNodeCount = ++(table->nodeCount);
|
||||
if (table->allocedNodeCount < newNodeCount) {
|
||||
do {
|
||||
/* Double size each time to avoid calls to realloc(). */
|
||||
table->allocedNodeCount <<= 1;
|
||||
} while (table->allocedNodeCount < newNodeCount);
|
||||
|
||||
table->nodes = realloc(table->nodes, table->nodeSize *
|
||||
table->allocedNodeCount);
|
||||
}
|
||||
|
||||
return (char *)table->nodes + (table->nodeSize * (table->nodeCount - 1));
|
||||
}
|
111
base/base64.c
Normal file
111
base/base64.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include "base64.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Encoding table as described in RFC1113. */
|
||||
const static uint8_t b64_encode_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* Decoding table. */
|
||||
const static int8_t b64_decode_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */
|
||||
};
|
||||
|
||||
uint8_t *base64decode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
int8_t digit, lastdigit;
|
||||
size_t i, j;
|
||||
uint8_t *decoded;
|
||||
const size_t maxlen = ((buflen + 3) / 4) * 3;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
|
||||
digit = lastdigit = j = 0;
|
||||
decoded = malloc(maxlen + 1);
|
||||
if (decoded == NULL) return NULL;
|
||||
for (i = 0; i < buflen; ++i) {
|
||||
if ((digit = b64_decode_table[src[i]]) != -1) {
|
||||
/* Decode block */
|
||||
switch (i % 4) {
|
||||
case 1:
|
||||
decoded[j++] = ((lastdigit << 2) | ((digit & 0x30) >> 4));
|
||||
break;
|
||||
case 2:
|
||||
decoded[j++] = (((lastdigit & 0xF) << 4) | ((digit & 0x3C) >> 2));
|
||||
break;
|
||||
case 3:
|
||||
decoded[j++] = (((lastdigit & 0x03) << 6) | digit);
|
||||
break;
|
||||
}
|
||||
lastdigit = digit;
|
||||
}
|
||||
}
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
decoded[j] = '\0';
|
||||
return decoded; /* Must be free()'d by caller */
|
||||
}
|
||||
|
||||
uint8_t *base64encode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
size_t i, j;
|
||||
const size_t maxlen = (((buflen + 3) & ~3)) * 4;
|
||||
uint8_t *encoded = malloc(maxlen + 1);
|
||||
if (encoded == NULL) return NULL;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
assert(buflen > 0);
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < buflen + 1; ++i) {
|
||||
/* Encode block */
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
encoded[j++] = b64_encode_table[src[i] >> 2];
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x03) << 4) |
|
||||
((src[i + 1] & 0xF0) >> 4)];
|
||||
break;
|
||||
case 1:
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x0F) << 2) |
|
||||
((src[i + 1] & 0xC0) >> 6)];
|
||||
break;
|
||||
case 2:
|
||||
encoded[j++] = b64_encode_table[(src[i] & 0x3F)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add padding if necessary */
|
||||
if ((j % 4) != 0) {
|
||||
const size_t with_padding = ((j + 3) & ~3); /* Align to 4 bytes */
|
||||
do {
|
||||
encoded[j++] = '=';
|
||||
} while (j < with_padding);
|
||||
}
|
||||
|
||||
assert(j <= maxlen);
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
encoded[j] = '\0';
|
||||
return encoded; /* Must be free()'d by caller */
|
||||
}
|
31
base/base64.h
Normal file
31
base/base64.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Decode a base64 encoded string discarding line breaks and noise.
|
||||
*
|
||||
* Returns a new string to be free()'d by caller, or NULL on error.
|
||||
* Returned string is guaranteed to be NUL-terminated.
|
||||
*
|
||||
* If |retlen| is not NULL, it is set to the length of the returned string
|
||||
* (minus the NUL-terminator) on successful return. */
|
||||
uint8_t *base64decode(const uint8_t *buf, const size_t buflen, size_t *retlen);
|
||||
|
||||
/* Encode a base64 encoded string without line breaks or noise.
|
||||
*
|
||||
* Returns a new string to be free()'d by caller, or NULL on error.
|
||||
* Returned string is guaranteed to be NUL-terminated with the correct padding.
|
||||
*
|
||||
* If |retlen| is not NULL, it is set to the length of the returned string
|
||||
* (minus the NUL-terminator) on successful return. */
|
||||
uint8_t *base64encode(const uint8_t *buf, const size_t buflen, size_t *retlen);
|
||||
|
||||
#endif /* BASE64_H */
|
111
base/base64_init.h
Normal file
111
base/base64_init.h
Normal file
@ -0,0 +1,111 @@
|
||||
#include "base64.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Encoding table as described in RFC1113. */
|
||||
const static uint8_t b64_encode_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* Decoding table. */
|
||||
const static int8_t b64_decode_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */
|
||||
};
|
||||
|
||||
uint8_t *base64decode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
int8_t digit, lastdigit;
|
||||
size_t i, j;
|
||||
uint8_t *decoded;
|
||||
const size_t maxlen = ((buflen + 3) / 4) * 3;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
|
||||
digit = lastdigit = j = 0;
|
||||
decoded = malloc(maxlen + 1);
|
||||
if (decoded == NULL) return NULL;
|
||||
for (i = 0; i < buflen; ++i) {
|
||||
if ((digit = b64_decode_table[src[i]]) != -1) {
|
||||
/* Decode block */
|
||||
switch (i % 4) {
|
||||
case 1:
|
||||
decoded[j++] = ((lastdigit << 2) | ((digit & 0x30) >> 4));
|
||||
break;
|
||||
case 2:
|
||||
decoded[j++] = (((lastdigit & 0xF) << 4) | ((digit & 0x3C) >> 2));
|
||||
break;
|
||||
case 3:
|
||||
decoded[j++] = (((lastdigit & 0x03) << 6) | digit);
|
||||
break;
|
||||
}
|
||||
lastdigit = digit;
|
||||
}
|
||||
}
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
decoded[j] = '\0';
|
||||
return decoded; /* Must be free()'d by caller */
|
||||
}
|
||||
|
||||
uint8_t *base64encode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
size_t i, j;
|
||||
const size_t maxlen = (((buflen + 3) & ~3)) * 4;
|
||||
uint8_t *encoded = malloc(maxlen + 1);
|
||||
if (encoded == NULL) return NULL;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
assert(buflen > 0);
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < buflen + 1; ++i) {
|
||||
/* Encode block */
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
encoded[j++] = b64_encode_table[src[i] >> 2];
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x03) << 4) |
|
||||
((src[i + 1] & 0xF0) >> 4)];
|
||||
break;
|
||||
case 1:
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x0F) << 2) |
|
||||
((src[i + 1] & 0xC0) >> 6)];
|
||||
break;
|
||||
case 2:
|
||||
encoded[j++] = b64_encode_table[(src[i] & 0x3F)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add padding if necessary */
|
||||
if ((j % 4) != 0) {
|
||||
const size_t with_padding = ((j + 3) & ~3); /* Align to 4 bytes */
|
||||
do {
|
||||
encoded[j++] = '=';
|
||||
} while (j < with_padding);
|
||||
}
|
||||
|
||||
assert(j <= maxlen);
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
encoded[j] = '\0';
|
||||
return encoded; /* Must be free()'d by caller */
|
||||
}
|
49
base/color_find.h
Normal file
49
base/color_find.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#ifndef COLOR_FIND_H
|
||||
#define COLOR_FIND_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "MMPointArray.h"
|
||||
|
||||
/* Convenience wrapper around findColorInRect(), where |rect| is the bounds of
|
||||
* the image. */
|
||||
#define findColorInImage(image, color, pointPtr, tolerance) \
|
||||
findColorInRect(image, color, pointPtr, MMBitmapGetBounds(image), tolerance)
|
||||
|
||||
/* Attempt to find a pixel with the given color in |image| inside |rect|.
|
||||
* Returns 0 on success, non-zero on failure. If the color was found and
|
||||
* |point| is not NULL, it will be initialized to the (x, y) coordinates the
|
||||
* RGB color.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors need to match, with 0 being exact and 1 being any. */
|
||||
int findColorInRect(MMBitmapRef image, MMRGBHex color, MMPoint *point,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
/* Convenience wrapper around findAllRGBInRect(), where |rect| is the bounds of
|
||||
* the image. */
|
||||
#define findAllColorInImage(image, color, tolerance) \
|
||||
findAllColorInRect(image, color, MMBitmapGetBounds(image), tolerance)
|
||||
|
||||
/* Returns MMPointArray of all pixels of given color in |image| inside of
|
||||
* |rect|. Note that an array is returned regardless of whether the color was
|
||||
* found; check array->count to see if it actually was.
|
||||
*
|
||||
* Responsibility for freeing the MMPointArray with destroyMMPointArray() is
|
||||
* given to the caller.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors need to match, with 0 being exact and 1 being any. */
|
||||
MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
/* Convenience wrapper around countOfColorsInRect, where |rect| is the bounds
|
||||
* of the image. */
|
||||
#define countOfColorsInImage(image, color, tolerance) \
|
||||
countOfColorsInRect(image, color, MMBitmapGetBounds(image), tolerance)
|
||||
|
||||
/* Returns the count of the given color in |rect| inside of |image|. */
|
||||
size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
|
||||
float tolerance);
|
||||
|
||||
#endif /* COLOR_FIND_H */
|
58
base/color_find_init.h
Normal file
58
base/color_find_init.h
Normal file
@ -0,0 +1,58 @@
|
||||
#include "color_find.h"
|
||||
// #include "../screen/screen_init.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Abstracted, general function to avoid repeated code. */
|
||||
static int findColorInRectAt(MMBitmapRef image, MMRGBHex color, MMPoint *point,
|
||||
MMRect rect, float tolerance, MMPoint startPoint)
|
||||
{
|
||||
MMPoint scan = startPoint;
|
||||
if (!MMBitmapRectInBounds(image, rect)) return -1;
|
||||
|
||||
for (; scan.y < rect.size.height; ++scan.y) {
|
||||
for (; scan.x < rect.size.width; ++scan.x) {
|
||||
MMRGBHex found = MMRGBHexAtPoint(image, scan.x, scan.y);
|
||||
if (MMRGBHexSimilarToColor(color, found, tolerance)) {
|
||||
if (point != NULL) *point = scan;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
scan.x = rect.origin.x;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findColorInRect(MMBitmapRef image, MMRGBHex color,
|
||||
MMPoint *point, MMRect rect, float tolerance)
|
||||
{
|
||||
return findColorInRectAt(image, color, point, rect, tolerance, rect.origin);
|
||||
}
|
||||
|
||||
MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
|
||||
MMRect rect, float tolerance)
|
||||
{
|
||||
MMPointArrayRef pointArray = createMMPointArray(0);
|
||||
MMPoint point = MMPointZero;
|
||||
|
||||
while (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {
|
||||
MMPointArrayAppendPoint(pointArray, point);
|
||||
ITER_NEXT_POINT(point, rect.size.width, rect.origin.x);
|
||||
}
|
||||
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
|
||||
float tolerance)
|
||||
{
|
||||
size_t count = 0;
|
||||
MMPoint point = MMPointZero;
|
||||
|
||||
while (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {
|
||||
ITER_NEXT_POINT(point, rect.size.width, rect.origin.x);
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
79
base/io_init.h
Normal file
79
base/io_init.h
Normal file
@ -0,0 +1,79 @@
|
||||
#include "io.h"
|
||||
// #include "os.h"
|
||||
// #include "bmp_io_init.h"
|
||||
#include "png_io_init.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <string.h> /* For strcmp() */
|
||||
#include <ctype.h> /* For tolower() */
|
||||
|
||||
const char *getExtension(const char *fname, size_t len)
|
||||
{
|
||||
if (fname == NULL || len <= 0) return NULL;
|
||||
|
||||
while (--len > 0 && fname[len] != '.' && fname[len] != '\0')
|
||||
;
|
||||
|
||||
return fname + len + 1;
|
||||
}
|
||||
|
||||
MMImageType imageTypeFromExtension(const char *extension)
|
||||
{
|
||||
char ext[4];
|
||||
const size_t maxlen = sizeof(ext) / sizeof(ext[0]);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; extension[i] != '\0'; ++i) {
|
||||
if (i >= maxlen) return kInvalidImageType;
|
||||
ext[i] = tolower(extension[i]);
|
||||
}
|
||||
ext[i] = '\0';
|
||||
|
||||
if (strcmp(ext, "png") == 0) {
|
||||
return kPNGImageType;
|
||||
} else if (strcmp(ext, "bmp") == 0) {
|
||||
return kBMPImageType;
|
||||
} else {
|
||||
return kInvalidImageType;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromFile(const char *path,
|
||||
MMImageType type,
|
||||
MMIOError *err)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return newMMBitmapFromBMP(path, err);
|
||||
case kPNGImageType:
|
||||
return newMMBitmapFromPNG(path, err);
|
||||
default:
|
||||
if (err != NULL) *err = kMMIOUnsupportedTypeError;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int saveMMBitmapToFile(MMBitmapRef bitmap,
|
||||
const char *path,
|
||||
MMImageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return saveMMBitmapAsBMP(bitmap, path);
|
||||
case kPNGImageType:
|
||||
return saveMMBitmapAsPNG(bitmap, path);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *MMIOErrorString(MMImageType type, MMIOError error)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return MMBMPReadErrorString(error);
|
||||
case kPNGImageType:
|
||||
return MMPNGReadErrorString(error);
|
||||
default:
|
||||
return "Unsupported image type";
|
||||
}
|
||||
}
|
28
base/pasteboard.h
Normal file
28
base/pasteboard.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#ifndef PASTEBOARD_H
|
||||
#define PASTEBOARD_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "io.h"
|
||||
|
||||
enum _MMBitmapPasteError {
|
||||
kMMPasteNoError = 0,
|
||||
kMMPasteGenericError,
|
||||
kMMPasteOpenError,
|
||||
kMMPasteClearError,
|
||||
kMMPasteDataError,
|
||||
kMMPastePasteError,
|
||||
kMMPasteUnsupportedError
|
||||
};
|
||||
|
||||
typedef MMIOError MMPasteError;
|
||||
|
||||
/* Copies |bitmap| to the pasteboard as a PNG.
|
||||
* Returns 0 on success, non-zero on error. */
|
||||
MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap);
|
||||
|
||||
/* Returns description of given MMPasteError.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMPasteErrorString(MMPasteError error);
|
||||
|
||||
#endif /* PASTEBOARD_H */
|
106
base/pasteboard_init.h
Normal file
106
base/pasteboard_init.h
Normal file
@ -0,0 +1,106 @@
|
||||
#include "pasteboard.h"
|
||||
#include "os.h"
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include "png_io.h"
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(IS_WINDOWS)
|
||||
#include "bmp_io.h"
|
||||
#endif
|
||||
|
||||
MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
PasteboardRef clipboard;
|
||||
|
||||
size_t len;
|
||||
uint8_t *pngbuf;
|
||||
CFDataRef data;
|
||||
OSStatus err;
|
||||
|
||||
if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
|
||||
return kMMPasteOpenError;
|
||||
}
|
||||
|
||||
if (PasteboardClear(clipboard) != noErr) {
|
||||
CFRelease(clipboard);
|
||||
return kMMPasteClearError;
|
||||
}
|
||||
|
||||
pngbuf = createPNGData(bitmap, &len);
|
||||
if (pngbuf == NULL) {
|
||||
CFRelease(clipboard);
|
||||
return kMMPasteDataError;
|
||||
}
|
||||
|
||||
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pngbuf, len,
|
||||
kCFAllocatorNull);
|
||||
if (data == NULL) {
|
||||
CFRelease(clipboard);
|
||||
free(pngbuf);
|
||||
return kMMPasteDataError;
|
||||
}
|
||||
|
||||
err = PasteboardPutItemFlavor(clipboard, bitmap, kUTTypePNG, data, 0);
|
||||
CFRelease(data);
|
||||
CFRelease(clipboard);
|
||||
free(pngbuf);
|
||||
return (err == noErr) ? kMMPasteNoError : kMMPastePasteError;
|
||||
#elif defined(IS_WINDOWS)
|
||||
MMPasteError ret = kMMPasteNoError;
|
||||
uint8_t *bmpData;
|
||||
size_t len;
|
||||
HGLOBAL handle;
|
||||
|
||||
if (!OpenClipboard(NULL)) return kMMPasteOpenError;
|
||||
if (!EmptyClipboard()) return kMMPasteClearError;
|
||||
|
||||
bmpData = createBitmapData(bitmap, &len);
|
||||
if (bmpData == NULL) return kMMPasteDataError;
|
||||
|
||||
/* CF_DIB does not include the BITMAPFILEHEADER struct (and displays a
|
||||
* cryptic error if it is included). */
|
||||
len -= sizeof(BITMAPFILEHEADER);
|
||||
|
||||
/* SetClipboardData() needs a "handle", not just a buffer, so we have to
|
||||
* allocate one with GlobalAlloc(). */
|
||||
if ((handle = GlobalAlloc(GMEM_MOVEABLE, len)) == NULL) {
|
||||
CloseClipboard();
|
||||
free(bmpData);
|
||||
return kMMPasteDataError;
|
||||
}
|
||||
|
||||
memcpy(GlobalLock(handle), bmpData + sizeof(BITMAPFILEHEADER), len);
|
||||
GlobalUnlock(handle);
|
||||
free(bmpData);
|
||||
|
||||
if (SetClipboardData(CF_DIB, handle) == NULL) {
|
||||
ret = kMMPastePasteError;
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
GlobalFree(handle);
|
||||
return ret;
|
||||
#elif defined(USE_X11)
|
||||
/* TODO (X11's clipboard is _weird_.) */
|
||||
return kMMPasteUnsupportedError;
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *MMPasteErrorString(MMPasteError err)
|
||||
{
|
||||
switch (err) {
|
||||
case kMMPasteOpenError:
|
||||
return "Could not open pasteboard";
|
||||
case kMMPasteClearError:
|
||||
return "Could not clear pasteboard";
|
||||
case kMMPasteDataError:
|
||||
return "Could not create image data from bitmap";
|
||||
case kMMPastePasteError:
|
||||
return "Could not paste data";
|
||||
case kMMPasteUnsupportedError:
|
||||
return "Unsupported platform";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
37
base/png_io.h
Normal file
37
base/png_io.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#ifndef PNG_IO_H
|
||||
#define PNG_IO_H
|
||||
|
||||
// #include "MMBitmap_init.h"
|
||||
// #include "io_init.h"
|
||||
|
||||
enum _PNGReadError {
|
||||
kPNGGenericError = 0,
|
||||
kPNGReadError,
|
||||
kPNGAccessError,
|
||||
kPNGInvalidHeaderError
|
||||
};
|
||||
|
||||
typedef MMIOError MMPNGReadError;
|
||||
|
||||
/* Returns description of given MMPNGReadError.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMPNGReadErrorString(MMIOError error);
|
||||
|
||||
/* Attempts to read PNG file at path; returns new MMBitmap on success, or
|
||||
* NULL on error. If |error| is non-NULL, it will be set to the error code
|
||||
* on return.
|
||||
* Responsibility for destroy()'ing returned MMBitmap is left up to caller. */
|
||||
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *error);
|
||||
|
||||
/* Attempts to write PNG at path; returns 0 on success, -1 on error. */
|
||||
int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path);
|
||||
|
||||
/* Returns a buffer containing the raw PNG file data, ready to be saved to a
|
||||
* file. |len| will be set to the number of bytes allocated in the returned
|
||||
* buffer (it cannot be NULL).
|
||||
*
|
||||
* Responsibility for free()'ing data is left up to the caller. */
|
||||
uint8_t *createPNGData(MMBitmapRef bitmap, size_t *len);
|
||||
|
||||
#endif /* PNG_IO_H */
|
339
base/png_io_init.h
Normal file
339
base/png_io_init.h
Normal file
@ -0,0 +1,339 @@
|
||||
#include "png_io.h"
|
||||
#include "os.h"
|
||||
// #include "libpng/png.c"
|
||||
#include <png.h>
|
||||
#include <stdio.h> /* fopen() */
|
||||
#include <stdlib.h> /* malloc/realloc */
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#include "ms_stdbool.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
const char *MMPNGReadErrorString(MMIOError error)
|
||||
{
|
||||
switch (error) {
|
||||
case kPNGAccessError:
|
||||
return "Could not open file";
|
||||
case kPNGReadError:
|
||||
return "Could not read file";
|
||||
case kPNGInvalidHeaderError:
|
||||
return "Not a PNG file";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *err)
|
||||
{
|
||||
FILE *fp;
|
||||
uint8_t header[8];
|
||||
png_struct *png_ptr = NULL;
|
||||
png_info *info_ptr = NULL;
|
||||
png_byte bit_depth, color_type;
|
||||
uint8_t *row, *bitmapData;
|
||||
uint8_t bytesPerPixel;
|
||||
png_uint_32 width, height, y;
|
||||
uint32_t bytewidth;
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
if (err != NULL) *err = kPNGAccessError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize error code to generic value. */
|
||||
if (err != NULL) *err = kPNGGenericError;
|
||||
|
||||
/* Validate the PNG. */
|
||||
if (fread(header, 1, sizeof header, fp) == 0) {
|
||||
if (err != NULL) *err = kPNGReadError;
|
||||
goto bail;
|
||||
} else if (png_sig_cmp(header, 0, sizeof(header)) != 0) {
|
||||
if (err != NULL) *err = kPNGInvalidHeaderError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (png_ptr == NULL) goto bail;
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL) goto bail;
|
||||
|
||||
/* Set up error handling. */
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
goto bail;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
/* Skip past the header. */
|
||||
png_set_sig_bytes(png_ptr, sizeof header);
|
||||
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
/* Convert different image types to common type to be read. */
|
||||
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
color_type = png_get_color_type(png_ptr, info_ptr);
|
||||
|
||||
/* Convert color palettes to RGB. */
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
/* Convert PNG to bit depth of 8. */
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||
} else if (bit_depth == 16) {
|
||||
png_set_strip_16(png_ptr);
|
||||
}
|
||||
|
||||
/* Convert transparency chunk to alpha channel. */
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
}
|
||||
|
||||
/* Convert gray images to RGB. */
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
/* Ignore alpha for now. */
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA) {
|
||||
png_set_strip_alpha(png_ptr);
|
||||
}
|
||||
|
||||
/* Get image attributes. */
|
||||
width = png_get_image_width(png_ptr, info_ptr);
|
||||
height = png_get_image_height(png_ptr, info_ptr);
|
||||
bytesPerPixel = 3; /* All images decompress to this size. */
|
||||
bytewidth = ADD_PADDING(width * bytesPerPixel); /* Align width. */
|
||||
|
||||
/* Decompress the PNG row by row. */
|
||||
bitmapData = calloc(1, bytewidth * height);
|
||||
row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
|
||||
if (bitmapData == NULL || row == NULL) goto bail;
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_uint_32 x;
|
||||
const uint32_t rowOffset = y * bytewidth;
|
||||
uint8_t *rowptr = row;
|
||||
png_read_row(png_ptr, (png_byte *)row, NULL);
|
||||
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t colOffset = x * bytesPerPixel;
|
||||
MMRGBColor *color = (MMRGBColor *)(bitmapData + rowOffset + colOffset);
|
||||
color->red = *rowptr++;
|
||||
color->green = *rowptr++;
|
||||
color->blue = *rowptr++;
|
||||
}
|
||||
}
|
||||
free(row);
|
||||
|
||||
/* Finish reading. */
|
||||
png_read_end(png_ptr, NULL);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
|
||||
return createMMBitmap(bitmapData, width, height,
|
||||
bytewidth, bytesPerPixel * 8, bytesPerPixel);
|
||||
|
||||
bail:
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct _PNGWriteInfo {
|
||||
png_struct *png_ptr;
|
||||
png_info *info_ptr;
|
||||
png_byte **row_pointers;
|
||||
size_t row_count;
|
||||
bool free_row_pointers;
|
||||
};
|
||||
|
||||
typedef struct _PNGWriteInfo PNGWriteInfo;
|
||||
typedef PNGWriteInfo *PNGWriteInfoRef;
|
||||
|
||||
/* Returns pointer to PNGWriteInfo struct containing data ready to be used with
|
||||
* functions such as png_write_png().
|
||||
*
|
||||
* It is the caller's responsibility to destroy() the returned structure with
|
||||
* destroyPNGWriteInfo(). */
|
||||
static PNGWriteInfoRef createPNGWriteInfo(MMBitmapRef bitmap)
|
||||
{
|
||||
PNGWriteInfoRef info = malloc(sizeof(PNGWriteInfo));
|
||||
png_uint_32 y;
|
||||
|
||||
if (info == NULL) return NULL;
|
||||
info->png_ptr = NULL;
|
||||
info->info_ptr = NULL;
|
||||
info->row_pointers = NULL;
|
||||
|
||||
assert(bitmap != NULL);
|
||||
|
||||
/* Initialize the write struct. */
|
||||
info->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL);
|
||||
if (info->png_ptr == NULL) goto bail;
|
||||
|
||||
/* Set up error handling. */
|
||||
if (setjmp(png_jmpbuf(info->png_ptr))) {
|
||||
png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Initialize the info struct. */
|
||||
info->info_ptr = png_create_info_struct(info->png_ptr);
|
||||
if (info->info_ptr == NULL) {
|
||||
png_destroy_write_struct(&(info->png_ptr), NULL);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Set image attributes. */
|
||||
png_set_IHDR(info->png_ptr,
|
||||
info->info_ptr,
|
||||
(png_uint_32)bitmap->width,
|
||||
(png_uint_32)bitmap->height,
|
||||
8,
|
||||
PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
info->row_count = bitmap->height;
|
||||
info->row_pointers = png_malloc(info->png_ptr,
|
||||
sizeof(png_byte *) * info->row_count);
|
||||
|
||||
if (bitmap->bytesPerPixel == 3) {
|
||||
/* No alpha channel; image data can be copied directly. */
|
||||
for (y = 0; y < info->row_count; ++y) {
|
||||
info->row_pointers[y] = bitmap->imageBuffer + (bitmap->bytewidth * y);
|
||||
}
|
||||
info->free_row_pointers = false;
|
||||
|
||||
/* Convert BGR to RGB if necessary. */
|
||||
if (MMRGB_IS_BGR) {
|
||||
png_set_bgr(info->png_ptr);
|
||||
}
|
||||
} else {
|
||||
/* Ignore alpha channel; copy image data row by row. */
|
||||
const size_t bytesPerPixel = 3;
|
||||
const size_t bytewidth = ADD_PADDING(bitmap->width * bytesPerPixel);
|
||||
|
||||
for (y = 0; y < info->row_count; ++y) {
|
||||
png_uint_32 x;
|
||||
png_byte *row_ptr = png_malloc(info->png_ptr, bytewidth);
|
||||
info->row_pointers[y] = row_ptr;
|
||||
for (x = 0; x < bitmap->width; ++x) {
|
||||
MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);
|
||||
row_ptr[0] = color->red;
|
||||
row_ptr[1] = color->green;
|
||||
row_ptr[2] = color->blue;
|
||||
|
||||
row_ptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
info->free_row_pointers = true;
|
||||
}
|
||||
|
||||
png_set_rows(info->png_ptr, info->info_ptr, info->row_pointers);
|
||||
return info;
|
||||
|
||||
bail:
|
||||
if (info != NULL) free(info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free memory in use by |info|. */
|
||||
static void destroyPNGWriteInfo(PNGWriteInfoRef info)
|
||||
{
|
||||
assert(info != NULL);
|
||||
if (info->row_pointers != NULL) {
|
||||
if (info->free_row_pointers) {
|
||||
size_t y;
|
||||
for (y = 0; y < info->row_count; ++y) {
|
||||
free(info->row_pointers[y]);
|
||||
}
|
||||
}
|
||||
png_free(info->png_ptr, info->row_pointers);
|
||||
}
|
||||
|
||||
png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));
|
||||
free(info);
|
||||
}
|
||||
|
||||
int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "wb");
|
||||
PNGWriteInfoRef info;
|
||||
if (fp == NULL) return -1;
|
||||
|
||||
if ((info = createPNGWriteInfo(bitmap)) == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
png_init_io(info->png_ptr, fp);
|
||||
png_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
fclose(fp);
|
||||
|
||||
destroyPNGWriteInfo(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Structure to store PNG image bytes. */
|
||||
struct io_data
|
||||
{
|
||||
uint8_t *buffer; /* Pointer to raw file data. */
|
||||
size_t size; /* Number of bytes actually written to buffer. */
|
||||
size_t allocedSize; /* Number of bytes allocated for buffer. */
|
||||
};
|
||||
|
||||
/* Called each time libpng attempts to write data in createPNGData(). */
|
||||
void png_append_data(png_struct *png_ptr,
|
||||
png_byte *new_data,
|
||||
png_size_t length)
|
||||
{
|
||||
struct io_data *data = png_get_io_ptr(png_ptr);
|
||||
data->size += length;
|
||||
|
||||
/* Allocate or grow buffer. */
|
||||
if (data->buffer == NULL) {
|
||||
data->allocedSize = data->size;
|
||||
data->buffer = png_malloc(png_ptr, data->allocedSize);
|
||||
assert(data->buffer != NULL);
|
||||
} else if (data->allocedSize < data->size) {
|
||||
do {
|
||||
/* Double size each time to avoid calls to realloc. */
|
||||
data->allocedSize <<= 1;
|
||||
} while (data->allocedSize < data->size);
|
||||
|
||||
data->buffer = realloc(data->buffer, data->allocedSize);
|
||||
}
|
||||
|
||||
/* Copy new bytes to end of buffer. */
|
||||
memcpy(data->buffer + data->size - length, new_data, length);
|
||||
}
|
||||
|
||||
uint8_t *createPNGData(MMBitmapRef bitmap, size_t *len)
|
||||
{
|
||||
PNGWriteInfoRef info = NULL;
|
||||
struct io_data data = {NULL, 0, 0};
|
||||
|
||||
assert(bitmap != NULL);
|
||||
assert(len != NULL);
|
||||
|
||||
if ((info = createPNGWriteInfo(bitmap)) == NULL) return NULL;
|
||||
|
||||
png_set_write_fn(info->png_ptr, &data, &png_append_data, NULL);
|
||||
png_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
|
||||
destroyPNGWriteInfo(info);
|
||||
|
||||
*len = data.size;
|
||||
return data.buffer;
|
||||
}
|
46
base/snprintf.h
Normal file
46
base/snprintf.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef _PORTABLE_SNPRINTF_H_
|
||||
#define _PORTABLE_SNPRINTF_H_
|
||||
|
||||
#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
|
||||
#define PORTABLE_SNPRINTF_VERSION_MINOR 2
|
||||
|
||||
#include "os.h"
|
||||
#if defined(IS_MACOSX)
|
||||
#define HAVE_SNPRINTF
|
||||
#else
|
||||
#define HAVE_SNPRINTF
|
||||
#define PREFER_PORTABLE_SNPRINTF
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SNPRINTF
|
||||
#include <stdio.h>
|
||||
#else
|
||||
extern int snprintf(char *, size_t, const char *, /*args*/ ...);
|
||||
extern int vsnprintf(char *, size_t, const char *, va_list);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)
|
||||
extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
|
||||
extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
|
||||
#define snprintf portable_snprintf
|
||||
#define vsnprintf portable_vsnprintf
|
||||
#endif
|
||||
|
||||
extern int asprintf (char **ptr, const char *fmt, /*args*/ ...);
|
||||
extern int vasprintf (char **ptr, const char *fmt, va_list ap);
|
||||
extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
|
||||
extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
1019
base/snprintf_init.h
Normal file
1019
base/snprintf_init.h
Normal file
File diff suppressed because it is too large
Load Diff
50
base/str_io.h
Normal file
50
base/str_io.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#ifndef STR_IO_H
|
||||
#define STR_IO_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "io.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
enum _MMBMPStringError {
|
||||
kMMBMPStringGenericError = 0,
|
||||
kMMBMPStringInvalidHeaderError,
|
||||
kMMBMPStringDecodeError,
|
||||
kMMBMPStringDecompressError,
|
||||
kMMBMPStringSizeError, /* Size does not match header. */
|
||||
MMMBMPStringEncodeError,
|
||||
kMMBMPStringCompressError
|
||||
};
|
||||
|
||||
typedef MMIOError MMBMPStringError;
|
||||
|
||||
/* Creates a 24-bit bitmap from a compressed, printable string.
|
||||
*
|
||||
* String should be in the format: "b[width],[height],[data]",
|
||||
* where [width] and [height] are the image width & height, and [data]
|
||||
* is the raw image data run through zlib_compress() and base64_encode().
|
||||
*
|
||||
* Returns NULL on error; follows the Create Rule (that is, the caller is
|
||||
* responsible for destroy'()ing object).
|
||||
* If |error| is non-NULL, it will be set to the error code on return.
|
||||
*/
|
||||
MMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,
|
||||
MMBMPStringError *error);
|
||||
|
||||
/* Inverse of createMMBitmapFromString().
|
||||
*
|
||||
* Creates string in the format: "b[width],[height],[data]", where [width] and
|
||||
* [height] are the image width & height, and [data] is the raw image data run
|
||||
* through zlib_compress() and base64_encode().
|
||||
*
|
||||
* Returns NULL on error, or new string on success (to be free'()d by caller).
|
||||
* If |error| is non-NULL, it will be set to the error code on return.
|
||||
*/
|
||||
uint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *error);
|
||||
|
||||
/* Returns description of given error code.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMBitmapStringErrorString(MMBMPStringError err);
|
||||
|
||||
#endif /* STR_IO_H */
|
209
base/str_io_init.h
Normal file
209
base/str_io_init.h
Normal file
@ -0,0 +1,209 @@
|
||||
#include "str_io.h"
|
||||
#include "zlib_util_init.h"
|
||||
#include "base64_init.h"
|
||||
#include "snprintf_init.h" /* snprintf() */
|
||||
#include <stdio.h> /* fputs() */
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <stdlib.h> /* atoi() */
|
||||
#include <string.h> /* strlen() */
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdbool.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#define STR_BITS_PER_PIXEL 24
|
||||
#define STR_BYTES_PER_PIXEL ((STR_BITS_PER_PIXEL) / 8)
|
||||
|
||||
#define MAX_DIMENSION_LEN 5 /* Maximum length for [width] or [height]
|
||||
* in string. */
|
||||
|
||||
const char *MMBitmapStringErrorString(MMBMPStringError err)
|
||||
{
|
||||
switch (err) {
|
||||
case kMMBMPStringInvalidHeaderError:
|
||||
return "Invalid header for string";
|
||||
case kMMBMPStringDecodeError:
|
||||
return "Error decoding string";
|
||||
case kMMBMPStringDecompressError:
|
||||
return "Error decompressing string";
|
||||
case kMMBMPStringSizeError:
|
||||
return "String not of expected size";
|
||||
case MMMBMPStringEncodeError:
|
||||
return "Error encoding string";
|
||||
case kMMBMPStringCompressError:
|
||||
return "Error compressing string";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parses beginning of string in the form of "[width],[height],*".
|
||||
*
|
||||
* If successful, |width| and |height| are set to the appropropriate values,
|
||||
* |len| is set to the length of [width] + the length of [height] + 2,
|
||||
* and true is returned; otherwise, false is returned.
|
||||
*/
|
||||
static bool getSizeFromString(const uint8_t *buf, size_t buflen,
|
||||
size_t *width, size_t *height,
|
||||
size_t *len);
|
||||
|
||||
MMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,
|
||||
MMBMPStringError *err)
|
||||
{
|
||||
uint8_t *decoded, *decompressed;
|
||||
size_t width, height;
|
||||
size_t len, bytewidth;
|
||||
|
||||
if (*buffer++ != 'b' || !getSizeFromString(buffer, --buflen,
|
||||
&width, &height, &len)) {
|
||||
if (err != NULL) *err = kMMBMPStringInvalidHeaderError;
|
||||
return NULL;
|
||||
}
|
||||
buffer += len;
|
||||
buflen -= len;
|
||||
|
||||
decoded = base64decode(buffer, buflen, NULL);
|
||||
if (decoded == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringDecodeError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decompressed = zlib_decompress(decoded, &len);
|
||||
free(decoded);
|
||||
|
||||
if (decompressed == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringDecompressError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytewidth = width * STR_BYTES_PER_PIXEL; /* Note that bytewidth is NOT
|
||||
* aligned to a padding. */
|
||||
if (height * bytewidth != len) {
|
||||
if (err != NULL) *err = kMMBMPStringSizeError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return createMMBitmap(decompressed, width, height,
|
||||
bytewidth, STR_BITS_PER_PIXEL, STR_BYTES_PER_PIXEL);
|
||||
}
|
||||
|
||||
/* Returns bitmap data suitable for encoding to a string; that is, 24-bit BGR
|
||||
* bitmap with no padding and 3 bytes per pixel.
|
||||
*
|
||||
* Caller is responsible for free()'ing returned buffer. */
|
||||
static uint8_t *createRawBitmapData(MMBitmapRef bitmap);
|
||||
|
||||
uint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *err)
|
||||
{
|
||||
uint8_t *raw, *compressed;
|
||||
uint8_t *ret, *encoded;
|
||||
size_t len, retlen;
|
||||
|
||||
assert(bitmap != NULL);
|
||||
|
||||
raw = createRawBitmapData(bitmap);
|
||||
if (raw == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringGenericError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
compressed = zlib_compress(raw,
|
||||
bitmap->width * bitmap->height *
|
||||
STR_BYTES_PER_PIXEL,
|
||||
9, &len);
|
||||
free(raw);
|
||||
if (compressed == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringCompressError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
encoded = base64encode(compressed, len - 1, &retlen);
|
||||
free(compressed);
|
||||
if (encoded == NULL) {
|
||||
if (err != NULL) *err = MMMBMPStringEncodeError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retlen += 3 + (MAX_DIMENSION_LEN * 2);
|
||||
ret = calloc(sizeof(char), (retlen + 1));
|
||||
snprintf((char *)ret, retlen, "b%lu,%lu,%s", (unsigned long)bitmap->width,
|
||||
(unsigned long)bitmap->height,
|
||||
encoded);
|
||||
ret[retlen] = '\0';
|
||||
free(encoded);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
|
||||
size_t *numlen);
|
||||
|
||||
static bool getSizeFromString(const uint8_t *buf, size_t buflen,
|
||||
size_t *width, size_t *height,
|
||||
size_t *len)
|
||||
{
|
||||
size_t numlen;
|
||||
assert(buf != NULL);
|
||||
assert(width != NULL);
|
||||
assert(height != NULL);
|
||||
|
||||
if ((*width = parseDimension(buf, buflen, &numlen)) == 0) {
|
||||
return false;
|
||||
}
|
||||
*len = numlen + 1;
|
||||
|
||||
if ((*height = parseDimension(buf + *len, buflen, &numlen)) == 0) {
|
||||
return false;
|
||||
}
|
||||
*len += numlen + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parses one dimension from string as described in getSizeFromString().
|
||||
* Returns dimension on success, or 0 on error. */
|
||||
static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
|
||||
size_t *numlen)
|
||||
{
|
||||
char num[MAX_DIMENSION_LEN + 1];
|
||||
size_t i;
|
||||
// ssize_t len;
|
||||
// size_t len;
|
||||
uint8_t * len;
|
||||
|
||||
assert(buf != NULL);
|
||||
assert(len != NULL);
|
||||
for (i = 0; i < buflen && buf[i] != ',' && buf[i] != '\0'; ++i) {
|
||||
if (!isdigit(buf[i]) || i > MAX_DIMENSION_LEN) return 0;
|
||||
num[i] = buf[i];
|
||||
}
|
||||
num[i] = '\0';
|
||||
*numlen = i;
|
||||
|
||||
return (uint32_t)atoi(num);
|
||||
}
|
||||
|
||||
static uint8_t *createRawBitmapData(MMBitmapRef bitmap)
|
||||
{
|
||||
uint8_t *raw = calloc(STR_BYTES_PER_PIXEL, bitmap->width * bitmap->height);
|
||||
size_t y;
|
||||
|
||||
for (y = 0; y < bitmap->height; ++y) {
|
||||
/* No padding is added to string bitmaps. */
|
||||
const size_t rowOffset = y * bitmap->width * STR_BYTES_PER_PIXEL;
|
||||
size_t x;
|
||||
for (x = 0; x < bitmap->width; ++x) {
|
||||
/* Copy in BGR format. */
|
||||
const size_t colOffset = x * STR_BYTES_PER_PIXEL;
|
||||
uint8_t *dest = raw + rowOffset + colOffset;
|
||||
MMRGBColor *srcColor = MMRGBColorRefAtPoint(bitmap, x, y);
|
||||
dest[0] = srcColor->blue;
|
||||
dest[1] = srcColor->green;
|
||||
dest[2] = srcColor->red;
|
||||
}
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
32
base/zlib_util.h
Normal file
32
base/zlib_util.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#ifndef ZLIB_UTIL_H
|
||||
#define ZLIB_UTIL_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Attempts to decompress given deflated NUL-terminated buffer.
|
||||
*
|
||||
* If successful and |len| is not NULL, |len| will be set to the number of
|
||||
* bytes in the returned buffer.
|
||||
* Returns new string to be free()'d by caller, or NULL on error. */
|
||||
uint8_t *zlib_decompress(const uint8_t *buf, size_t *len);
|
||||
|
||||
/* Attempt to compress given buffer.
|
||||
*
|
||||
* The compression level is passed directly to zlib: it must between 0 and 9,
|
||||
* where 1 gives best speed, 9 gives best compression, and 0 gives no
|
||||
* compression at all.
|
||||
*
|
||||
* If successful and |len| is not NULL, |len| will be set to the number of
|
||||
* bytes in the returned buffer.
|
||||
* Returns new string to be free()'d by caller, or NULL on error. */
|
||||
uint8_t *zlib_compress(const uint8_t *buf, const size_t buflen, int level,
|
||||
size_t *len);
|
||||
|
||||
#endif /* ZLIB_UTIL_H */
|
99
base/zlib_util_init.h
Normal file
99
base/zlib_util_init.h
Normal file
@ -0,0 +1,99 @@
|
||||
#include "zlib_util.h"
|
||||
#include <zlib.h>
|
||||
#include <stdio.h> /* fprintf() */
|
||||
#include <stdlib.h> /* malloc() */
|
||||
#include <assert.h>
|
||||
|
||||
#define ZLIB_CHUNK (16 * 1024)
|
||||
|
||||
uint8_t *zlib_decompress(const uint8_t *buf, size_t *len)
|
||||
{
|
||||
size_t output_size = ZLIB_CHUNK;
|
||||
uint8_t *output = malloc(output_size);
|
||||
int err;
|
||||
z_stream zst;
|
||||
|
||||
/* Sanity check */
|
||||
if (output == NULL) return NULL;
|
||||
assert(buf != NULL);
|
||||
|
||||
/* Set inflate state */
|
||||
zst.zalloc = Z_NULL;
|
||||
zst.zfree = Z_NULL;
|
||||
zst.opaque = Z_NULL;
|
||||
zst.next_out = (Byte *)output;
|
||||
zst.next_in = (Byte *)buf;
|
||||
zst.avail_out = ZLIB_CHUNK;
|
||||
|
||||
if (inflateInit(&zst) != Z_OK) goto error;
|
||||
|
||||
/* Decompress input buffer */
|
||||
do {
|
||||
if ((err = inflate(&zst, Z_NO_FLUSH)) == Z_OK) { /* Need more memory */
|
||||
zst.avail_out = (uInt)output_size;
|
||||
|
||||
/* Double size each time to avoid calls to realloc() */
|
||||
output_size <<= 1;
|
||||
output = realloc(output, output_size + 1);
|
||||
if (output == NULL) return NULL;
|
||||
|
||||
zst.next_out = (Byte *)(output + zst.avail_out);
|
||||
} else if (err != Z_STREAM_END) { /* Error decompressing */
|
||||
if (zst.msg != NULL) {
|
||||
fprintf(stderr, "Could not decompress data: %s\n", zst.msg);
|
||||
}
|
||||
inflateEnd(&zst);
|
||||
goto error;
|
||||
}
|
||||
} while (err != Z_STREAM_END);
|
||||
|
||||
if (len != NULL) *len = zst.total_out;
|
||||
if (inflateEnd(&zst) != Z_OK) goto error;
|
||||
return output; /* To be free()'d by caller */
|
||||
|
||||
error:
|
||||
if (output != NULL) free(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *zlib_compress(const uint8_t *buf, const size_t buflen, int level,
|
||||
size_t *len)
|
||||
{
|
||||
z_stream zst;
|
||||
uint8_t *output = NULL;
|
||||
|
||||
/* Sanity check */
|
||||
assert(buf != NULL);
|
||||
assert(len != NULL);
|
||||
assert(level <= 9 && level >= 0);
|
||||
|
||||
zst.avail_out = (uInt)((buflen + (buflen / 10)) + 12);
|
||||
output = malloc(zst.avail_out);
|
||||
if (output == NULL) return NULL;
|
||||
|
||||
/* Set deflate state */
|
||||
zst.zalloc = Z_NULL;
|
||||
zst.zfree = Z_NULL;
|
||||
zst.next_out = (Byte *)output;
|
||||
zst.next_in = (Byte *)buf;
|
||||
zst.avail_in = (uInt)buflen;
|
||||
|
||||
if (deflateInit(&zst, level) != Z_OK) goto error;
|
||||
|
||||
/* Compress input buffer */
|
||||
if (deflate(&zst, Z_FINISH) != Z_STREAM_END) {
|
||||
if (zst.msg != NULL) {
|
||||
fprintf(stderr, "Could not compress data: %s\n", zst.msg);
|
||||
}
|
||||
deflateEnd(&zst);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (len != NULL) *len = zst.total_out;
|
||||
if (deflateEnd(&zst) != Z_OK) goto error;
|
||||
return output; /* To be free()'d by caller */
|
||||
|
||||
error:
|
||||
if (output != NULL) free(output);
|
||||
return NULL;
|
||||
}
|
0
event/goEvent.h
Normal file
0
event/goEvent.h
Normal file
Loading…
Reference in New Issue
Block a user