summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2020-06-22 10:06:35 +0100
committerVincent Sanders <vince@kyllikki.org>2020-06-22 22:45:10 +0100
commit54e06e7d582b36b8aca463cac5f63e0501c898dc (patch)
tree1be0b38860d452e5513d271c364c12faebd0ca83 /utils
parenta27f5d32cd199845ae34ffc28d23ac6a17cbaa33 (diff)
downloadnetsurf-54e06e7d582b36b8aca463cac5f63e0501c898dc.tar.gz
netsurf-54e06e7d582b36b8aca463cac5f63e0501c898dc.tar.bz2
move all the build tools to utils
move the source and make rules of the convert image and font tools to the utils directory. This puts all the rules for build tools together.
Diffstat (limited to 'utils')
-rw-r--r--utils/Makefile34
-rw-r--r--utils/convert_font.c1215
-rw-r--r--utils/convert_image.c304
3 files changed, 1551 insertions, 2 deletions
diff --git a/utils/Makefile b/utils/Makefile
index 7b20978ca..a6d2f1248 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -25,14 +25,44 @@ S_UTILS := \
S_UTILS := $(addprefix utils/,$(S_UTILS))
-# Host tool to convert file to comiled data
+
+# lib png build compiler flags
+ifeq ($(HOST),OpenBSD)
+ BUILD_LIBPNG_CFLAGS += $(shell $(PKG_CONFIG) --cflags libpng)
+ BUILD_LIBPNG_LDFLAGS += $(shell $(PKG_CONFIG) --libs libpng)
+else
+ ifeq ($(HOST),FreeBSD)
+ BUILD_LIBPNG_CFLAGS += $(shell $(PKG_CONFIG) --cflags libpng)
+ BUILD_LIBPNG_LDFLAGS += $(shell $(PKG_CONFIG) --libs libpng)
+ else
+ BUILD_LIBPNG_CFLAGS +=
+ BUILD_LIBPNG_LDFLAGS += -lpng
+ endif
+endif
+
+
+# Build tool to convert file to comiled data
#
$(TOOLROOT)/xxd: utils/xxd.c $(TOOLROOT)/created
$(VQ)echo "BUILD CC: $@"
$(Q)$(BUILD_CC) $(BUILD_CFLAGS) -o $@ $< $(BUILD_LDFLAGS)
-# Host tool to convert file to comiled data
+
+# Build tool to filter messages
#
$(TOOLROOT)/split-messages: utils/split-messages.c $(TOOLROOT)/created
$(VQ)echo "BUILD CC: $@"
$(Q)$(BUILD_CC) $(BUILD_CFLAGS) -o $@ $< $(BUILD_LDFLAGS) -lz
+
+
+# Build tool to convert image bitmaps to source code.
+#
+$(TOOLROOT)/convert_image: utils/convert_image.c $(TOOLROOT)/created
+ $(VQ)echo "BUILD CC: $@"
+ $(Q)$(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_LIBPNG_CFLAGS) -o $@ $< $(BUILD_LDFLAGS) $(BUILD_LIBPNG_LDFLAGS)
+
+
+# Build too to perform font conversion
+$(TOOLROOT)/convert_font: utils/convert_font.c $(TOOLROOT)/created
+ $(VQ)echo "BUILD CC: $@"
+ $(Q)$(BUILD_CC) $(BUILD_CFLAGS) -o $@ $<
diff --git a/utils/convert_font.c b/utils/convert_font.c
new file mode 100644
index 000000000..9f5734b71
--- /dev/null
+++ b/utils/convert_font.c
@@ -0,0 +1,1215 @@
+/*
+ * Copyright 2014 Michael Drake <tlsa@netsurf-browser.org>
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of the convert_font tool used to convert font
+ * glyph data into a compilable representation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#define GLYPH_LEN 16
+#define BUCKETS 512
+#define CHUNK_SIZE (64 * 1024)
+#define HEADER_MAX 2000
+
+#define SECTION_SIZE (sizeof(uint16_t) * 256)
+
+const char *labels[4] = {
+ " Regular",
+ " Italic",
+ " Bold",
+ "Bold & Italic"
+};
+
+const char *var_lables[4] = {
+ "fb_regular",
+ "fb_italic",
+ "fb_bold",
+ "fb_bold_italic"
+};
+
+const char *short_labels[4] = {
+ " ",
+ " i",
+ "b ",
+ "bi"
+};
+
+enum font_style {
+ REGULAR = 0,
+ ITALIC = (1 << 0),
+ BOLD = (1 << 1),
+ ITALIC_BOLD = (1 << 2)
+};
+
+enum log_level {
+ LOG_DEBUG,
+ LOG_INFO,
+ LOG_RESULT,
+ LOG_WARNING,
+ LOG_ERROR
+};
+
+enum log_level level;
+
+typedef struct glyph_entry {
+ union {
+ uint32_t u32[GLYPH_LEN / 4];
+ uint8_t u8[GLYPH_LEN];
+ } data;
+ uint32_t index;
+ struct glyph_entry *next;
+} glyph_entry;
+
+/** Scratch glyph for generated code points */
+uint8_t code_point[GLYPH_LEN];
+
+/** Hash table */
+glyph_entry *ht[BUCKETS];
+
+#define LOG(lev, fmt, ...) \
+ if (lev >= level) \
+ printf(fmt, ##__VA_ARGS__);
+
+/**
+ * Get hash for glyph data
+ * \param g Glyph data (GLYPH_LEN bytes)
+ * \return glyph's hash
+ */
+static inline uint32_t glyph_hash(const uint8_t *g)
+{
+ uint32_t hash = 0x811c9dc5;
+ unsigned int len = GLYPH_LEN;
+
+ while (len > 0) {
+ hash *= 0x01000193;
+ hash ^= *g++;
+ len--;
+ }
+
+ return hash;
+}
+
+
+/**
+ * Check whether glyphs are identical (compares glyph data)
+ *
+ * \param g1 First glyph's data (GLYPH_LEN bytes)
+ * \param g2 Second glyph's data (GLYPH_LEN bytes)
+ * \return true iff both glyphs are identical, else false
+ */
+static inline bool glyphs_match(const uint8_t *g1, const uint8_t *g2)
+{
+ return (memcmp(g1, g2, GLYPH_LEN) == 0);
+}
+
+
+/**
+ * Add a glyph to a hash chain (or free, and return pointer to existing glyph)
+ *
+ * Note that if new glyph already exists in chain, it is freed and a pointer to
+ * the existing glyph is returned. If the glyph does not exist in the chain
+ * it is added and its pointer is returned.
+ *
+ * \param head Head of hash chain
+ * \param new New glyph to add (may be freed)
+ * \return pointer to glyph in hash chain
+ */
+static glyph_entry * glyph_add_to_chain(glyph_entry **head, glyph_entry *new)
+{
+ glyph_entry *e = *head;
+
+ if (*head == NULL) {
+ new->next = NULL;
+ *head = new;
+ return new;
+ }
+
+ do {
+ if (glyphs_match(new->data.u8, e->data.u8)) {
+ free(new);
+ return e;
+ }
+ if (e->next == NULL)
+ break;
+ e = e->next;
+ } while (1);
+
+ new->next = e->next;
+ e->next = new;
+ return new;
+}
+
+
+/**
+ * Free a glyph entry chain
+ *
+ * \param head Head of hash chain
+ */
+static void free_chain(glyph_entry *head)
+{
+ glyph_entry *e = head;
+
+ if (head == NULL)
+ return;
+
+ while (e != NULL) {
+ head = e->next;
+ free(e);
+ e = head;
+ };
+}
+
+
+/**
+ * Add new glyph to hash table (or free, and return pointer to existing glyph)
+ *
+ * Note that if new glyph already exists in table, it is freed and a pointer to
+ * the existing glyph is returned. If the glyph does not exist in the table
+ * it is added and its pointer is returned.
+ *
+ * \param new New glyph to add (may be freed)
+ * \return pointer to glyph in hash table
+ */
+static glyph_entry * glyph_add_to_table(glyph_entry *new)
+{
+ uint32_t hash = glyph_hash(new->data.u8);
+
+ return glyph_add_to_chain(&ht[hash % BUCKETS], new);
+}
+
+
+/**
+ * Free glyph table.
+ */
+static void free_table(void)
+{
+ int i;
+
+ for (i = 0; i < BUCKETS; i++) {
+ free_chain(ht[i]);
+ }
+}
+
+struct parse_context {
+ enum {
+ START,
+ IN_HEADER,
+ BEFORE_ID,
+ GLYPH_ID,
+ BEFORE_GLYPH_DATA,
+ IN_GLYPH_DATA
+ } state; /**< Current parser state */
+
+ union {
+ struct {
+ bool new_line;
+ } in_header;
+ struct {
+ bool new_line;
+ bool u;
+ } before_id;
+ struct {
+ int c;
+ } g_id;
+ struct {
+ bool new_line;
+ bool prev_h;
+ bool prev_s;
+ int c;
+ } before_gd;
+ struct {
+ int line;
+ int pos;
+ int styles;
+ int line_styles;
+ glyph_entry *e[4];
+ } in_gd;
+ } data; /**< The state specific data */
+
+ int id; /**< Current ID */
+
+ int codepoints; /**< Glyphs containing codepoints */
+ int count[4]; /**< Count of glyphs in file */
+};
+
+struct font_data {
+ char header[HEADER_MAX];
+ int header_len;
+
+ uint8_t section_table[4][256];
+ uint8_t sec_count[4];
+ uint16_t *sections[4];
+
+ glyph_entry *e[0xffff];
+ int glyphs;
+};
+
+static bool generate_font_header(const char *path, struct font_data *data)
+{
+ FILE *fp;
+ int s;
+
+ fp = fopen(path, "wb");
+ if (fp == NULL) {
+ LOG(LOG_ERROR, "Couldn't open header file \"%s\"\n", path);
+ return false;
+ }
+
+ fprintf(fp, "/*\n");
+ fwrite(data->header, 1, data->header_len, fp);
+ fprintf(fp, " */\n\n");
+ fprintf(fp, "/* Don't edit this file, it was generated from the "
+ "plain text source data. */\n\n");
+
+
+ for (s = 0; s < 4; s++) {
+ fprintf(fp, "const uint8_t *%s_section_table;\n",
+ var_lables[s]);
+ fprintf(fp, "const uint16_t *%s_sections;\n",
+ var_lables[s]);
+
+ }
+
+ fprintf(fp, "const uint8_t *font_glyph_data;\n");
+
+ fprintf(fp, "\n\n");
+
+ fclose(fp);
+
+ return true;
+
+}
+
+static bool generate_font_source(const char *path, struct font_data *data)
+{
+ int s, i, y;
+ int limit;
+ FILE *fp;
+
+ fp = fopen(path, "wb");
+ if (fp == NULL) {
+ LOG(LOG_ERROR, "Couldn't open output file \"%s\"\n", path);
+ return false;
+ }
+
+ fprintf(fp, "/*\n");
+ fwrite(data->header, 1, data->header_len, fp);
+ fprintf(fp, " */\n\n");
+ fprintf(fp, "/* Don't edit this file, it was generated from the "
+ "plain text source data. */\n\n");
+
+ fprintf(fp, "#include <stdint.h>\n");
+ fprintf(fp, "\n");
+
+ for (s = 0; s < 4; s++) {
+
+ fprintf(fp, "static const uint8_t %s_section_table_c[256] = {\n",
+ var_lables[s]);
+
+ for (i = 0; i < 256; i++) {
+ if (i == 255)
+ fprintf(fp, "0x%.2X\n",
+ data->section_table[s][i]);
+ else if (i % 8 == 7)
+ fprintf(fp, "0x%.2X,\n",
+ data->section_table[s][i]);
+ else if (i % 8 == 0)
+ fprintf(fp, "\t0x%.2X, ",
+ data->section_table[s][i]);
+ else
+ fprintf(fp, "0x%.2X, ",
+ data->section_table[s][i]);
+ }
+
+ fprintf(fp, "};\nconst uint8_t *%s_section_table = &%s_section_table_c[0];\n\n",
+ var_lables[s], var_lables[s]);
+ fprintf(fp, "static const uint16_t %s_sections_c[%i] = {\n",
+ var_lables[s], data->sec_count[s] * 256);
+
+ limit = data->sec_count[s] * 256;
+ for (i = 0; i < limit; i++) {
+ uint16_t offset = data->sections[s][i];
+ if (i == limit - 1)
+ fprintf(fp, "0x%.4X\n", offset);
+ else if (i % 4 == 3)
+ fprintf(fp, "0x%.4X,\n", offset);
+ else if (i % 4 == 0)
+ fprintf(fp, "\t0x%.4X, ", offset);
+ else
+ fprintf(fp, "0x%.4X, ", offset);
+ }
+
+ fprintf(fp, "};\nconst uint16_t *%s_sections = &%s_sections_c[0];\n\n", var_lables[s], var_lables[s]);
+ }
+
+ fprintf(fp, "static const uint8_t font_glyph_data_c[%i] = {\n",
+ (data->glyphs + 1) * 16);
+
+ fprintf(fp, "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n"
+ "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n");
+
+ limit = data->glyphs;
+ for (i = 0; i < limit; i++) {
+ glyph_entry *e = data->e[i];
+
+ for (y = 0; y < 16; y++) {
+ if (i == limit - 1 && y == 15)
+ fprintf(fp, "0x%.2X\n", e->data.u8[y]);
+ else if (y % 8 == 7)
+ fprintf(fp, "0x%.2X,\n", e->data.u8[y]);
+ else if (y % 8 == 0)
+ fprintf(fp, "\t0x%.2X, ", e->data.u8[y]);
+ else
+ fprintf(fp, "0x%.2X, ", e->data.u8[y]);
+ }
+ }
+
+ fprintf(fp, "};\n");
+ fprintf(fp, "const uint8_t *font_glyph_data = &font_glyph_data_c[0];\n\n");
+
+ fclose(fp);
+
+ return true;
+}
+
+static bool add_glyph_to_data(glyph_entry *add, int id, int style,
+ struct font_data *d)
+{
+ glyph_entry *e;
+ int offset;
+ int s;
+
+ /* Find out if 'add' is unique, and get its unique table entry */
+ e = glyph_add_to_table(add);
+ if (e == add) {
+ /* Unique glyph */
+ d->e[d->glyphs++] = e;
+ e->index = d->glyphs;
+ if (d->glyphs >= 0xfffd) {
+ LOG(LOG_ERROR, " Too many glyphs for internal data "
+ "representation\n");
+ return false;
+ }
+ } else {
+ /* Duplicate glyph */
+ LOG(LOG_DEBUG, " U+%.4X (%s) is duplicate\n",
+ id, short_labels[style]);
+ }
+
+ /* Find glyph's section */
+ s = id / 256;
+
+ /* Allocate section if needed */
+ if ((s == 0 && d->sections[style] == NULL) ||
+ (s != 0 && d->section_table[style][s] == 0)) {
+ size_t size = (d->sec_count[style] + 1) * SECTION_SIZE;
+ uint16_t *temp = realloc(d->sections[style], size);
+ if (temp == NULL) {
+ LOG(LOG_ERROR, " Couldn't increase sections "
+ "allocation\n");
+ return false;
+ }
+ memset(temp + d->sec_count[style] * 256, 0,
+ SECTION_SIZE);
+ d->section_table[style][s] = d->sec_count[style];
+ d->sections[style] = temp;
+ d->sec_count[style]++;
+ }
+
+ offset = d->section_table[style][s] * 256 + (id & 0xff);
+ d->sections[style][offset] = e->index;
+
+ return true;
+}
+
+
+static bool check_glyph_data_valid(int pos, char c)
+{
+ int offset = pos % 11;
+
+ if (pos == 44) {
+ if (c != '\n') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '\\n', got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (pos < 3) {
+ if (c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting ' ', got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (offset == 0) {
+ if (c != '\n' && c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '\\n' or ' ', "
+ "got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (offset < 3) {
+ if (c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting ' ', got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ } else if (offset >= 3 && pos < 11) {
+ if (c != '.' && c != '#') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '.' or '#', "
+ "got '%c' (%i)\n",
+ c, c);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /* offset must be >=3 */
+ if (c != '.' && c != '#' && c != ' ') {
+ LOG(LOG_ERROR, " Invalid glyph data: "
+ "expecting '.', '#', or ' ', "
+ "got '%c' (%i)\n",
+ c, c);
+ return false;
+ }
+
+ return true;
+}
+
+#define SEVEN_SET ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \
+ (1 << 4) | (1 << 5) | (1 << 6))
+
+#define THREE_SSS ((1 << 0) | (1 << 1) | (1 << 2))
+#define THREE_S_S ((1 << 0) | (1 << 2))
+#define THREE__SS ((1 << 0) | (1 << 1) )
+#define THREE_SS_ ( (1 << 1) | (1 << 2))
+#define THREE_S__ (1 << 2)
+#define THREE__S_ (1 << 1)
+#define THREE___S (1 << 0)
+
+uint8_t frag[16][5] = {
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE__S_,
+ THREE_SS_,
+ THREE__S_,
+ THREE__S_,
+ THREE_SSS },
+
+ { THREE_SS_,
+ THREE___S,
+ THREE__S_,
+ THREE_S__,
+ THREE_SSS },
+
+ { THREE_SS_,
+ THREE___S,
+ THREE_SS_,
+ THREE___S,
+ THREE_SS_ },
+
+ { THREE_S_S,
+ THREE_S_S,
+ THREE_SSS,
+ THREE___S,
+ THREE___S },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SSS,
+ THREE___S,
+ THREE_SSS },
+
+ { THREE__SS,
+ THREE_S__,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE___S,
+ THREE__S_,
+ THREE__S_,
+ THREE__S_ },
+
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE_S_S,
+ THREE_SSS,
+ THREE___S,
+ THREE___S },
+
+ { THREE__S_,
+ THREE_S_S,
+ THREE_SSS,
+ THREE_S_S,
+ THREE_S_S },
+
+ { THREE_SS_,
+ THREE_S_S,
+ THREE_SS_,
+ THREE_S_S,
+ THREE_SS_ },
+
+ { THREE__S_,
+ THREE_S_S,
+ THREE_S__,
+ THREE_S_S,
+ THREE__S_ },
+
+ { THREE_SS_,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_S_S,
+ THREE_SS_ },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SS_,
+ THREE_S__,
+ THREE_SSS },
+
+ { THREE_SSS,
+ THREE_S__,
+ THREE_SS_,
+ THREE_S__,
+ THREE_S__ }
+};
+
+static void build_codepoint(int id, bool italic, uint8_t *code_point)
+{
+ int shift = 0;
+ int l;
+ int r;
+
+ if (!italic)
+ shift = 1;
+
+ l = (id >> 12);
+ r = 0xf & (id >> 8);
+
+ code_point[ 0] = 0;
+ code_point[ 1] = SEVEN_SET << shift;
+ code_point[ 2] = 0;
+
+ code_point[ 3] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
+ code_point[ 4] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
+ code_point[ 5] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
+ code_point[ 6] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
+ code_point[ 7] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
+
+ code_point[ 8] = 0;
+
+ shift = 1;
+
+ l = 0xf & (id >> 4);
+ r = 0xf & id ;
+
+ code_point[ 9] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
+ code_point[10] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
+ code_point[11] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
+ code_point[12] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
+ code_point[13] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
+
+ code_point[14] = 0;
+ code_point[15] = SEVEN_SET << shift;
+}
+
+#undef SEVEN_SET
+#undef THREE_SSS
+#undef THREE_S_S
+#undef THREE__SS
+#undef THREE_SS_
+#undef THREE_S__
+#undef THREE__S_
+#undef THREE___S
+
+static bool glyph_is_codepoint(const glyph_entry *e, int id, int style)
+{
+ bool italic = false;
+
+ if (style == 1 || style == 3) {
+ italic = true;
+ }
+
+ build_codepoint(id, italic, code_point);
+
+ return glyphs_match(code_point, e->data.u8);
+}
+
+
+static bool parse_glyph_data(struct parse_context *ctx, char c,
+ struct font_data *d)
+{
+ int glyph = ctx->data.in_gd.pos / 11;
+ int g_pos = ctx->data.in_gd.pos % 11 - 3;
+ uint8_t *row;
+ bool ok;
+ int i;
+
+ /* Check that character is valid */
+ if (check_glyph_data_valid(ctx->data.in_gd.pos, c) == false) {
+ LOG(LOG_ERROR, " Error in U+%.4X data: "
+ "glyph line: %i, pos: %i\n",
+ ctx->id,
+ ctx->data.in_gd.line,
+ ctx->data.in_gd.pos);
+ goto error;
+ }
+
+ /* Allocate glyph data if needed */
+ if (ctx->data.in_gd.line == 0 &&
+ (c == '.' || c == '#')) {
+ if (ctx->data.in_gd.e[glyph] == NULL) {
+ ctx->data.in_gd.e[glyph] =
+ calloc(sizeof(struct glyph_entry), 1);
+ if (ctx->data.in_gd.e[glyph] == NULL) {
+ LOG(LOG_ERROR, " Couldn't allocate memory for "
+ "glyph entry\n");
+ goto error;
+ }
+
+ ctx->data.in_gd.styles |= 1 << glyph;
+ }
+ }
+
+ /* Build glyph data */
+ if (c == '#') {
+ row = &ctx->data.in_gd.e[glyph]->data.u8[ctx->data.in_gd.line];
+ *row += 1 << (7 - g_pos);
+
+ ctx->data.in_gd.line_styles |= 1 << glyph;
+ } else if (c == '.') {
+ ctx->data.in_gd.line_styles |= 1 << glyph;
+ }
+
+ /* Deal with current position */
+ if (c == '\n') {
+ if (ctx->data.in_gd.line == 0) {
+ if (ctx->data.in_gd.e[0] == NULL) {
+ LOG(LOG_ERROR, " Error in U+%.4X data: "
+ "\"Regular\" glyph style must "
+ "be present\n", ctx->id);
+ goto error;
+ }
+ } else if (ctx->data.in_gd.styles !=
+ ctx->data.in_gd.line_styles) {
+ LOG(LOG_ERROR, " Error in U+%.4X data: "
+ "glyph line: %i "
+ "styles don't match first line\n",
+ ctx->id,
+ ctx->data.in_gd.line);
+ goto error;
+ }
+
+ ctx->data.in_gd.pos = 0;
+ ctx->data.in_gd.line++;
+ ctx->data.in_gd.line_styles = 0;
+ } else {
+ ctx->data.in_gd.pos++;
+ }
+
+ /* If we've got all the glyph data, tidy up and advance state */
+ if (ctx->data.in_gd.line == 16) {
+ for (i = 0; i < 4; i++) {
+ if (ctx->data.in_gd.e[i] != NULL) {
+ ctx->count[i] += 1;
+ if (glyph_is_codepoint(ctx->data.in_gd.e[i],
+ ctx->id, i)) {
+ LOG(LOG_DEBUG, " U+%.4X (%s) is "
+ "codepoint\n",
+ ctx->id,
+ short_labels[i]);
+ ctx->codepoints += 1;
+ free(ctx->data.in_gd.e[i]);
+ ctx->data.in_gd.e[i] = NULL;
+ continue;
+ }
+
+ ok = add_glyph_to_data(ctx->data.in_gd.e[i],
+ ctx->id, i, d);
+ if (!ok) {
+ goto error;
+ }
+ }
+ }
+
+ ctx->data.before_id.new_line = false;
+ ctx->data.before_id.u = false;
+ ctx->state = BEFORE_ID;
+ }
+
+ return true;
+
+error:
+
+ for (i = 0; i < 4; i++) {
+ free(ctx->data.in_gd.e[i]);
+ }
+
+ return false;
+}
+
+static void parse_init(struct parse_context *ctx)
+{
+ memset(ctx, 0, sizeof(struct parse_context));
+}
+
+static bool get_hex_digit_value(char c, int *v)
+{
+ if (c >= '0' && c <= '9')
+ *v = (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ *v = (10 + c - 'A');
+ else {
+ LOG(LOG_ERROR, "Invalid hex digit '%c' (%i)\n", c, c);
+ return false;
+ }
+
+ return true;
+}
+
+static bool assemble_codepoint(const char* c, int n, int *id)
+{
+ bool ok;
+ int v;
+
+ ok = get_hex_digit_value(*c, &v);
+ if (!ok) {
+ return false;
+ }
+
+ *id += v << (4 * (3 - n));
+
+ return true;
+}
+
+static bool parse_chunk(struct parse_context *ctx, const char *buf, size_t len,
+ struct font_data *d)
+{
+ int i;
+ bool ok;
+ int count[4];
+ const char *pos = buf;
+ const char *end = buf + len;
+
+ for (i = 0; i < 4; i++) {
+ count[i] = ctx->count[i];
+ }
+
+ while (pos < end) {
+ if (*pos == '\r') {
+ LOG(LOG_ERROR, "Detected \'\\r\': Bad line ending\n");
+ return false;
+ }
+
+ switch (ctx->state) {
+ case START:
+ if (*pos != '*') {
+ LOG(LOG_ERROR, "First character must be '*'\n");
+ printf("Got: %c (%i)\n", *pos, *pos);
+ return false;
+ }
+ d->header_len = 0;
+ ctx->data.in_header.new_line = true;
+ ctx->state = IN_HEADER;
+
+ /* Fall through */
+ case IN_HEADER:
+ if (ctx->data.in_header.new_line == true) {
+ if (*pos != '*') {
+ LOG(LOG_INFO, " Got header "
+ "(%i bytes)\n",
+ d->header_len);
+ LOG(LOG_DEBUG, " Header:\n\n%.*s\n",
+ d->header_len,
+ d->header);
+ ctx->data.before_id.new_line = false;
+ ctx->data.before_id.u = false;
+ ctx->state = BEFORE_ID;
+ continue;
+ } else if (*pos == '*') {
+ d->header[d->header_len++] = ' ';
+ }
+ ctx->data.in_header.new_line = false;
+
+ } else if (*pos == '\n') {
+ ctx->data.in_header.new_line = true;
+ }
+
+ if (d->header_len == HEADER_MAX) {
+ LOG(LOG_ERROR, " Header too long "
+ "(>%i bytes)\n",
+ d->header_len);
+ return false;
+ }
+
+ d->header[d->header_len++] = *pos;
+ break;
+
+ case BEFORE_ID:
+ if (*pos == '+' &&
+ ctx->data.before_id.new_line == true &&
+ ctx->data.before_id.u == true) {
+ ctx->data.g_id.c = 0;
+ ctx->id = 0;
+ ctx->state = GLYPH_ID;
+ break;
+
+ } else if (*pos == 'U' &&
+ ctx->data.before_id.new_line == true) {
+ ctx->data.before_id.u = true;
+
+ } else if (*pos == '\n') {
+ ctx->data.before_id.new_line = true;
+ ctx->data.before_id.u = false;
+
+ } else {
+ ctx->data.before_id.new_line = false;
+ ctx->data.before_id.u = false;
+ }
+ break;
+
+ case GLYPH_ID:
+ ok = assemble_codepoint(pos, ctx->data.g_id.c++,
+ &ctx->id);
+ if (!ok) {
+ LOG(LOG_ERROR, " Invalid glyph ID\n");
+ return false;
+ }
+
+ if (ctx->data.g_id.c == 4) {
+ ctx->data.before_gd.new_line = false;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = false;
+ ctx->data.before_gd.c = 0;
+ ctx->state = BEFORE_GLYPH_DATA;
+ break;
+ }
+ break;
+
+ case BEFORE_GLYPH_DATA:
+ /* Skip until end of dashed line */
+ if (*pos == '\n' && ctx->data.before_gd.c == 53) {
+ ctx->state = IN_GLYPH_DATA;
+ ctx->data.in_gd.e[0] = NULL;
+ ctx->data.in_gd.e[1] = NULL;
+ ctx->data.in_gd.e[2] = NULL;
+ ctx->data.in_gd.e[3] = NULL;
+ ctx->data.in_gd.line = 0;
+ ctx->data.in_gd.pos = 0;
+ ctx->data.in_gd.line_styles = 0;
+ ctx->data.in_gd.styles = 0;
+ break;
+
+ } else if (*pos == '\n') {
+ ctx->data.before_gd.new_line = true;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = false;
+ ctx->data.before_gd.c = 0;
+ } else if (*pos == '-' &&
+ ctx->data.before_gd.new_line == true) {
+ assert(ctx->data.before_gd.c == 0);
+ ctx->data.before_gd.new_line = false;
+ ctx->data.before_gd.c++;
+ ctx->data.before_gd.prev_h = true;
+ } else if (*pos == ' ' &&
+ ctx->data.before_gd.prev_h == true) {
+ assert(ctx->data.before_gd.prev_s == false);
+ ctx->data.before_gd.c++;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = true;
+ } else if (*pos == '-' &&
+ ctx->data.before_gd.prev_s == true) {
+ assert(ctx->data.before_gd.prev_h == false);
+ ctx->data.before_gd.c++;
+ ctx->data.before_gd.prev_h = true;
+ ctx->data.before_gd.prev_s = false;
+ } else {
+ ctx->data.before_gd.new_line = false;
+ ctx->data.before_gd.prev_h = false;
+ ctx->data.before_gd.prev_s = false;
+ ctx->data.before_gd.c = 0;
+ }
+ break;
+
+ case IN_GLYPH_DATA:
+ ok = parse_glyph_data(ctx, *pos, d);
+ if (!ok) {
+ return false;
+ }
+
+ break;
+ }
+
+ pos++;
+ }
+
+ for (i = 0; i < 4; i++) {
+ LOG(LOG_DEBUG, " %s: %i gylphs\n", labels[i],
+ ctx->count[i] - count[i]);
+ }
+
+ return true;
+}
+
+
+static bool load_font(const char *path, struct font_data **data)
+{
+ struct parse_context ctx;
+ struct font_data *d;
+ size_t file_len;
+ size_t done;
+ size_t len;
+ int count;
+ char *buf;
+ FILE *fp;
+ bool ok;
+ int i;
+
+ *data = NULL;
+
+ fp = fopen(path, "rb");
+ if (fp == NULL) {
+ LOG(LOG_ERROR, "Couldn't open font data file\n");
+ return false;
+ }
+
+ d = calloc(sizeof(struct font_data), 1);
+ if (d == NULL) {
+ LOG(LOG_ERROR, "Couldn't allocate memory for font data\n");
+ fclose(fp);
+ return false;
+ }
+
+ /* Find filesize */
+ fseek(fp, 0L, SEEK_END);
+ file_len = ftell(fp);
+ if ((long)file_len == -1) {
+ LOG(LOG_ERROR, "Could not size input file\n");
+ free(d);
+ fclose(fp);
+ return false;
+ }
+ fseek(fp, 0L, SEEK_SET);
+ LOG(LOG_DEBUG, "Input size: %zu bytes\n", file_len);
+
+ /* Allocate buffer for data chunks */
+ buf = malloc(CHUNK_SIZE);
+ if (buf == NULL) {
+ LOG(LOG_ERROR, "Couldn't allocate memory for input buffer\n");
+ free(d);
+ fclose(fp);
+ return false;
+ }
+
+ /* Initialise parser */
+ parse_init(&ctx);
+
+ LOG(LOG_DEBUG, "Using chunk size of %i bytes\n", CHUNK_SIZE);
+
+ /* Parse the input file in chunks */
+ for (done = 0; done < file_len; done += CHUNK_SIZE) {
+ LOG(LOG_INFO, "Parsing input chunk %zu\n", done / CHUNK_SIZE);
+
+ /* Read chunk */
+ len = fread(buf, 1, CHUNK_SIZE, fp);
+ if (file_len - done < CHUNK_SIZE &&
+ len != file_len - done) {
+ LOG(LOG_WARNING, "Last chunk has suspicious size\n");
+ } else if (file_len - done >= CHUNK_SIZE &&
+ len != CHUNK_SIZE) {
+ LOG(LOG_ERROR, "Problem reading file\n");
+ free(buf);
+ free(d);
+ fclose(fp);
+ return false;
+ }
+
+ /* Parse chunk */
+ ok = parse_chunk(&ctx, buf, len, d);
+ if (!ok) {
+ free(buf);
+ free(d);
+ fclose(fp);
+ return false;
+ }
+ LOG(LOG_DEBUG, "Parsed %zu bytes\n", done + len);
+ }
+
+ fclose(fp);
+
+ if (ctx.state != BEFORE_ID) {
+ LOG(LOG_ERROR, "Unexpected end of file\n");
+ free(buf);
+ free(d);
+ return false;
+ }
+
+ LOG(LOG_INFO, "Parsing complete:\n");
+ count = 0;
+ for (i = 0; i < 4; i++) {
+ LOG(LOG_INFO, " %s: %i gylphs\n", labels[i], ctx.count[i]);
+ count += ctx.count[i];
+ }
+
+ LOG(LOG_RESULT, " Total %i gylphs "
+ "(of which %i unique, %i codepoints, %i duplicates)\n",
+ count, d->glyphs, ctx.codepoints,
+ count - d->glyphs - ctx.codepoints);
+
+ free(buf);
+
+ *data = d;
+ return true;
+}
+
+static void log_usage(const char *argv0)
+{
+ level = LOG_INFO;
+ LOG(LOG_INFO,
+ "Usage:\n"
+ "\t%s [options] <in_file> <out_file>\n"
+ "\n"
+ "Options:\n"
+ "\t--help -h Display this text\n"
+ "\t--quiet -q Don't show warnings\n"
+ "\t--verbose -v Verbose output\n"
+ "\t--debug -d Full debug output\n",
+ argv0);
+}
+
+int main(int argc, char** argv)
+{
+ const char *in_path = NULL;
+ const char *out_path = NULL;
+ char *header_path = NULL;
+ struct font_data *data;
+ bool ok;
+ int i;
+ int opt;
+
+ level = LOG_RESULT;
+
+ /* Handle program arguments */
+ struct option long_options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "debug", no_argument, NULL, 'd' },
+ { "header", required_argument, NULL, 'H' },
+ };
+
+ while ((opt = getopt_long(argc, argv, "hqvdH:", long_options, NULL)) != -1) {
+ switch (opt) {
+ case 'q':
+ level = LOG_WARNING;
+ break;
+
+ case 'v':
+ level = LOG_INFO;
+ break;
+
+ case 'd':
+ level = LOG_DEBUG;
+ break;
+
+ case 'H':
+ header_path = strdup(optarg);
+ break;
+
+ case 'h':
+ log_usage(argv[0]);
+ free(header_path);
+ return EXIT_SUCCESS;
+
+ default:
+ log_usage(argv[0]);
+ free(header_path);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if ((argc - optind) < 2) {
+ log_usage(argv[0]);
+ free(header_path);
+ return EXIT_FAILURE;
+ }
+
+ in_path = argv[optind];
+ out_path = argv[optind + 1];
+
+ LOG(LOG_DEBUG, "Using input path: \"%s\"\n", in_path);
+ LOG(LOG_DEBUG, "Using output path: \"%s\"\n", out_path);
+
+ ok = load_font(in_path, &data);
+ if (!ok) {
+ free_table();
+ free(header_path);
+ return EXIT_FAILURE;
+ }
+
+ ok = generate_font_source(out_path, data);
+ if (ok && (header_path != NULL)) {
+ ok = generate_font_header(header_path, data);
+ }
+ free(header_path);
+ free_table();
+ for (i = 0; i < 4; i++) {
+ free(data->sections[i]);
+ }
+ free(data);
+ if (!ok) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/utils/convert_image.c b/utils/convert_image.c
new file mode 100644
index 000000000..de772fc29
--- /dev/null
+++ b/utils/convert_image.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2009 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <png.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if PNG_LIBPNG_VER < 10209
+#define png_set_expand_gray_1_2_4_to_8(png) png_set_gray_1_2_4_to_8(png)
+#endif
+
+static png_structp png;
+static png_infop info;
+static int interlace;
+static size_t rowbytes;
+static int raw_width, raw_height;
+static int rowstride;
+static unsigned char *bitmap_data;
+static bool is_cursor = true;
+static int raw_hot_x, raw_hot_y;
+
+#define WIDTH (is_cursor?raw_width-1:raw_width)
+#define HEIGHT (is_cursor?raw_height-1:raw_height)
+
+#define HOT_X (is_cursor?raw_hot_x-1:0)
+#define HOT_Y (is_cursor?raw_hot_y-1:0)
+
+#define REAL(v) (is_cursor?v+1:v)
+
+#define PPIX_AT(x,y) ((bitmap_data + (rowstride * y)) + (x * 4))
+
+#define R_OFF 2
+#define G_OFF 1
+#define B_OFF 0
+#define A_OFF 3
+
+#define R_AT(x,y) *(PPIX_AT(x,y) + R_OFF)
+#define G_AT(x,y) *(PPIX_AT(x,y) + G_OFF)
+#define B_AT(x,y) *(PPIX_AT(x,y) + B_OFF)
+#define A_AT(x,y) *(PPIX_AT(x,y) + A_OFF)
+
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: fb_convert_image input.png output.inc varname\n");
+}
+
+
+static void
+detect_hotspot(void)
+{
+ int i;
+ int greenpixels = 0;
+
+ for (i = 0; i < raw_width; ++i) {
+ if (A_AT(i, 0) == 255) {
+ if (G_AT(i, 0) == 255) {
+ greenpixels++;
+ raw_hot_x = i;
+ }
+ if ((B_AT(i, 0) != 0) || (R_AT(i, 0) != 0)) {
+ is_cursor = false;
+ return;
+ }
+ } else if (A_AT(i, 0) != 0) {
+ is_cursor = false;
+ return;
+ }
+ }
+ if (greenpixels != 1) {
+ is_cursor = false;
+ return;
+ }
+
+ for (i = 0; i < raw_height; ++i) {
+ if (A_AT(0, i) == 255) {
+ if (G_AT(0, i) == 255) {
+ greenpixels++;
+ raw_hot_y = i;
+ }
+ if ((B_AT(0, i) != 0) || (R_AT(0, i) != 0)) {
+ is_cursor = false;
+ return;
+ }
+ } else if (A_AT(0, i) != 0) {
+ is_cursor = false;
+ return;
+ }
+ }
+ if (greenpixels != 2) {
+ is_cursor = false;
+ return;
+ }
+ printf(" Pointer detected. Adjusted hotspot at %d, %d (0-based)\n",
+ raw_hot_x - 1, raw_hot_y - 1);
+}
+
+
+static void
+info_callback(png_structp png, png_infop info)
+{
+ int bit_depth, color_type, interlace, intent;
+ double gamma;
+ png_uint_32 width, height;
+
+ /* Read the PNG details */
+ png_get_IHDR(png, info, &width, &height, &bit_depth,
+ &color_type, &interlace, 0, 0);
+
+ /* Set up our transformations */
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png);
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ png_set_expand_gray_1_2_4_to_8(png);
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png);
+ if (bit_depth == 16)
+ png_set_strip_16(png);
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png);
+ if (!(color_type & PNG_COLOR_MASK_ALPHA))
+ png_set_filler(png, 0xff, PNG_FILLER_AFTER);
+ /* gamma correction - we use 2.2 as our screen gamma
+ * this appears to be correct (at least in respect to !Browse)
+ * see http://www.w3.org/Graphics/PNG/all_seven.html for a test case
+ */
+ if (png_get_sRGB(png, info, &intent))
+ png_set_gamma(png, 2.2, 0.45455);
+ else {
+ if (png_get_gAMA(png, info, &gamma))
+ png_set_gamma(png, 2.2, gamma);
+ else
+ png_set_gamma(png, 2.2, 0.45455);
+ }
+
+
+ png_read_update_info(png, info);
+
+ rowbytes = png_get_rowbytes(png, info);
+ interlace = (interlace == PNG_INTERLACE_ADAM7);
+ raw_width = width;
+ raw_height = height;
+
+ rowstride = raw_width * 4;
+ bitmap_data = malloc(rowstride * raw_height);
+}
+
+static unsigned int interlace_start[8] = {0, 16, 0, 8, 0, 4, 0};
+static unsigned int interlace_step[8] = {28, 28, 12, 12, 4, 4, 0};
+static unsigned int interlace_row_start[8] = {0, 0, 4, 0, 2, 0, 1};
+static unsigned int interlace_row_step[8] = {8, 8, 8, 4, 4, 2, 2};
+
+static void
+row_callback(png_structp png, png_bytep new_row,
+ png_uint_32 row_num, int pass)
+{
+ unsigned long i, j;
+ unsigned int start, step;
+ unsigned char *row = bitmap_data + (rowstride * row_num);
+
+ if (new_row == 0)
+ return;
+
+ if (interlace) {
+ start = interlace_start[pass];
+ step = interlace_step[pass];
+ row_num = interlace_row_start[pass] +
+ interlace_row_step[pass] * row_num;
+
+ /* Copy the data to our current row taking interlacing
+ * into consideration */
+ row = bitmap_data + (rowstride * row_num);
+ for (j = 0, i = start; i < rowbytes; i += step) {
+ row[i++] = new_row[j++];
+ row[i++] = new_row[j++];
+ row[i++] = new_row[j++];
+ row[i++] = new_row[j++];
+ }
+ } else {
+ memcpy(row, new_row, rowbytes);
+ }
+}
+
+static void
+end_callback(png_structp png, png_infop info)
+{
+}
+
+
+int
+main(int argc, char **argv)
+{
+ FILE *f;
+ unsigned char buffer[1024];
+ int br;
+ int x, y, c;
+
+ if (argc != 4) {
+ usage();
+ return 1;
+ }
+
+ printf(" CONVERT: %s (%s)\n", argv[1], argv[3]);
+
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ info = png_create_info_struct(png);
+
+ png_set_progressive_read_fn(png, NULL, info_callback, row_callback, end_callback);
+
+ f = fopen(argv[1], "rb");
+ if (f == NULL) {
+ printf(" Unable to open %s\n", argv[1]);
+ return 1;
+ }
+
+ do {
+ br = fread(buffer, 1, 1024, f);
+ if (br > 0) {
+ png_process_data(png, info, buffer, br);
+ }
+ } while (br > 0);
+
+ if (br < 0) {
+ printf("Error reading input: %s\n", strerror(errno));
+ fclose(f);
+ return 1;
+ }
+
+ fclose(f);
+
+ detect_hotspot();
+
+ f = fopen(argv[2], "w");
+ if (f == NULL) {
+ printf(" Unable to open %s\n", argv[2]);
+ return 2;
+ }
+
+ fprintf(f, "/* This file is auto-generated from %s\n", argv[1]);
+ fprintf(f, " *\n * Do not edit this file directly.\n */\n\n");
+ fprintf(f, "#include <sys/types.h>\n\n");
+ fprintf(f, "#include <stdint.h>\n\n");
+ fprintf(f, "#include <stdbool.h>\n\n");
+ fprintf(f, "#include <libnsfb.h>\n\n");
+ fprintf(f, "#include \"netsurf/plot_style.h\"\n");
+ fprintf(f, "#include \"framebuffer/gui.h\"\n");
+ fprintf(f, "#include \"framebuffer/fbtk.h\"\n\n");
+
+ fprintf(f, "static uint8_t %s_pixdata[] = {\n", argv[3]);
+ for (y = 0; y < HEIGHT; ++y) {
+ unsigned char *rowptr = bitmap_data + (rowstride * y);
+ if (is_cursor) {
+ /* If it's a cursor, skip one row and one column */
+ rowptr += rowstride + 4;
+ }
+ fprintf(f, "\t");
+ for (x = 0; x < WIDTH; ++x) {
+ for (c = 0; c < 4; ++c) {
+ unsigned char b = *rowptr++;
+ fprintf(f, "0x%02x, ", b);
+ }
+ }
+ fprintf(f, "\n");
+ }
+ fprintf(f, "};\n\n");
+
+ fprintf(f, "struct fbtk_bitmap %s = {\n", argv[3]);
+ fprintf(f, "\t.width\t\t= %d,\n", WIDTH);
+ fprintf(f, "\t.height\t\t= %d,\n", HEIGHT);
+ fprintf(f, "\t.hot_x\t\t= %d,\n", HOT_X);
+ fprintf(f, "\t.hot_y\t\t= %d,\n", HOT_Y);
+ fprintf(f, "\t.pixdata\t= %s_pixdata,\n", argv[3]);
+
+ fprintf(f, "};\n\n");
+ fclose(f);
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:8
+ * End:
+ */