diff options
Diffstat (limited to 'trunk/pango/pango-coverage.c')
-rw-r--r-- | trunk/pango/pango-coverage.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/trunk/pango/pango-coverage.c b/trunk/pango/pango-coverage.c new file mode 100644 index 00000000..340ccf7c --- /dev/null +++ b/trunk/pango/pango-coverage.c @@ -0,0 +1,494 @@ +/* Pango + * pango-coverage.c: Coverage maps for fonts + * + * Copyright (C) 2000 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> + +#include "pango-coverage.h" + +typedef struct _PangoBlockInfo PangoBlockInfo; + +#define N_BLOCKS_INCREMENT 256 + +/* The structure of a PangoCoverage object is a two-level table, with blocks of size 256. + * each block is stored as a packed array of 2 bit values for each index, in LSB order. + */ + +struct _PangoBlockInfo +{ + guchar *data; + PangoCoverageLevel level; /* Used if data == NULL */ +}; + +struct _PangoCoverage +{ + guint ref_count; + int n_blocks; + int data_size; + + PangoBlockInfo *blocks; +}; + +/** + * pango_coverage_new: + * + * Create a new #PangoCoverage + * + * Return value: the newly allocated #PangoCoverage, + * initialized to %PANGO_COVERAGE_NONE + * with a reference count of one, which + * should be freed with pango_coverage_unref(). + **/ +PangoCoverage * +pango_coverage_new (void) +{ + PangoCoverage *coverage = g_slice_new (PangoCoverage); + + coverage->n_blocks = N_BLOCKS_INCREMENT; + coverage->blocks = g_new0 (PangoBlockInfo, coverage->n_blocks); + coverage->ref_count = 1; + + return coverage; +} + +/** + * pango_coverage_copy: + * @coverage: a #PangoCoverage + * + * Copy an existing #PangoCoverage. (This function may now be unnecessary + * since we refcount the structure. File a bug if you use it.) + * + * Return value: the newly allocated #PangoCoverage, + * with a reference count of one, which + * should be freed with pango_coverage_unref(). + **/ +PangoCoverage * +pango_coverage_copy (PangoCoverage *coverage) +{ + int i; + PangoCoverage *result; + + g_return_val_if_fail (coverage != NULL, NULL); + + result = g_slice_new (PangoCoverage); + result->n_blocks = coverage->n_blocks; + result->blocks = g_new (PangoBlockInfo, coverage->n_blocks); + result->ref_count = 1; + + for (i=0; i<coverage->n_blocks; i++) + { + if (coverage->blocks[i].data) + { + result->blocks[i].data = g_new (guchar, 64); + memcpy (result->blocks[i].data, coverage->blocks[i].data, 64); + } + else + result->blocks[i].data = NULL; + + result->blocks[i].level = coverage->blocks[i].level; + } + + return result; +} + +/** + * pango_coverage_ref: + * @coverage: a #PangoCoverage + * + * Increase the reference count on the #PangoCoverage by one + * + * Return value: @coverage + **/ +PangoCoverage * +pango_coverage_ref (PangoCoverage *coverage) +{ + g_return_val_if_fail (coverage != NULL, NULL); + + coverage->ref_count++; + + return coverage; +} + +/** + * pango_coverage_unref: + * @coverage: a #PangoCoverage + * + * Increase the reference count on the #PangoCoverage by one. + * if the result is zero, free the coverage and all associated memory. + **/ +void +pango_coverage_unref (PangoCoverage *coverage) +{ + int i; + + g_return_if_fail (coverage != NULL); + g_return_if_fail (coverage->ref_count > 0); + + coverage->ref_count--; + + if (coverage->ref_count == 0) + { + for (i=0; i<coverage->n_blocks; i++) + g_free (coverage->blocks[i].data); + + g_free (coverage->blocks); + g_slice_free (PangoCoverage, coverage); + } +} + +/** + * pango_coverage_get: + * @coverage: a #PangoCoverage + * @index_: the index to check + * + * Determine whether a particular index is covered by @coverage + * + * Return value: the coverage level of @coverage for character @index_. + **/ +PangoCoverageLevel +pango_coverage_get (PangoCoverage *coverage, + int index) +{ + int block_index; + + g_return_val_if_fail (coverage != NULL, PANGO_COVERAGE_NONE); + g_return_val_if_fail (index >= 0, PANGO_COVERAGE_NONE); + + block_index = index / 256; + + if (block_index >= coverage->n_blocks) + return PANGO_COVERAGE_NONE; + else + { + guchar *data = coverage->blocks[block_index].data; + if (data) + { + int i = index % 256; + int shift = (i % 4) * 2; + + return (data[i/4] >> shift) & 0x3; + } + else + return coverage->blocks[block_index].level; + } +} + +/** + * pango_coverage_set: + * @coverage: a #PangoCoverage + * @index_: the index to modify + * @level: the new level for @index_ + * + * Modify a particular index within @coverage + **/ +void +pango_coverage_set (PangoCoverage *coverage, + int index, + PangoCoverageLevel level) +{ + int block_index, i; + guchar *data; + + g_return_if_fail (coverage != NULL); + g_return_if_fail (index >= 0); + g_return_if_fail (level >= 0 && level <= 3); + + block_index = index / 256; + + if (block_index >= coverage->n_blocks) + { + int old_n_blocks = coverage->n_blocks; + + coverage->n_blocks = + N_BLOCKS_INCREMENT * ((block_index + N_BLOCKS_INCREMENT) / N_BLOCKS_INCREMENT); + + coverage->blocks = g_renew (PangoBlockInfo, coverage->blocks, coverage->n_blocks); + memset (coverage->blocks + old_n_blocks, 0, + sizeof (PangoBlockInfo) * (coverage->n_blocks - old_n_blocks)); + } + + data = coverage->blocks[block_index].data; + if (!data) + { + guchar byte; + + if (level == coverage->blocks[block_index].level) + return; + + data = g_new (guchar, 64); + coverage->blocks[block_index].data = data; + + byte = coverage->blocks[block_index].level | + (coverage->blocks[block_index].level << 2) | + (coverage->blocks[block_index].level << 4) | + (coverage->blocks[block_index].level << 6); + + memset (data, byte, 64); + } + + i = index % 256; + data[i/4] |= level << ((i % 4) * 2); +} + +/** + * pango_coverage_max: + * @coverage: a #PangoCoverage + * @other: another #PangoCoverage + * + * Set the coverage for each index in @coverage to be the max (better) + * value of the current coverage for the index and the coverage for + * the corresponding index in @other. + **/ +void +pango_coverage_max (PangoCoverage *coverage, + PangoCoverage *other) +{ + int block_index, i; + int old_blocks; + + g_return_if_fail (coverage != NULL); + + old_blocks = MIN (coverage->n_blocks, other->n_blocks); + + if (other->n_blocks > coverage->n_blocks) + { + coverage->n_blocks = other->n_blocks; + coverage->blocks = g_renew (PangoBlockInfo, coverage->blocks, coverage->n_blocks); + + for (block_index = old_blocks; block_index < coverage->n_blocks; block_index++) + { + if (other->blocks[block_index].data) + { + coverage->blocks[block_index].data = g_new (guchar, 64); + memcpy (coverage->blocks[block_index].data, other->blocks[block_index].data, 64); + } + else + coverage->blocks[block_index].data = NULL; + + coverage->blocks[block_index].level = other->blocks[block_index].level; + } + } + + for (block_index = 0; block_index < old_blocks; block_index++) + { + if (!coverage->blocks[block_index].data && !other->blocks[block_index].data) + { + coverage->blocks[block_index].level = MAX (coverage->blocks[block_index].level, other->blocks[block_index].level); + } + else if (coverage->blocks[block_index].data && other->blocks[block_index].data) + { + guchar *data = coverage->blocks[block_index].data; + + for (i=0; i<64; i++) + { + int byte1 = data[i]; + int byte2 = other->blocks[block_index].data[i]; + + /* There are almost certainly some clever logical ops to do this */ + data[i] = + MAX (byte1 & 0x3, byte2 & 0x3) | + MAX (byte1 & 0xc, byte2 & 0xc) | + MAX (byte1 & 0x30, byte2 & 0x30) | + MAX (byte1 & 0xc0, byte2 & 0xc0); + } + } + else + { + guchar *src, *dest; + int level, byte2; + + if (coverage->blocks[block_index].data) + { + src = dest = coverage->blocks[block_index].data; + level = other->blocks[block_index].level; + } + else + { + src = other->blocks[block_index].data; + dest = g_new (guchar, 64); + coverage->blocks[block_index].data = dest; + level = coverage->blocks[block_index].level; + } + + byte2 = level | (level << 2) | (level << 4) | (level << 6); + + for (i=0; i<64; i++) + { + int byte1 = src[i]; + + /* There are almost certainly some clever logical ops to do this */ + dest[i] = + MAX (byte1 & 0x3, byte2 & 0x3) | + MAX (byte1 & 0xc, byte2 & 0xc) | + MAX (byte1 & 0x30, byte2 & 0x30) | + MAX (byte1 & 0xc0, byte2 & 0xc0); + } + } + } +} + +#define PANGO_COVERAGE_MAGIC 0xc89dbd5e + +/** + * pango_coverage_to_bytes: + * @coverage: a #PangoCoverage + * @bytes: location to store result (must be freed with g_free()) + * @n_bytes: location to store size of result + * + * Convert a #PangoCoverage structure into a flat binary format + **/ +void +pango_coverage_to_bytes (PangoCoverage *coverage, + guchar **bytes, + int *n_bytes) +{ + int i, j; + int size = 8 + 4 * coverage->n_blocks; + guchar *data; + int offset; + + for (i=0; i<coverage->n_blocks; i++) + { + if (coverage->blocks[i].data) + size += 64; + } + + data = g_malloc (size); + + *(guint32 *)&data[0] = g_htonl (PANGO_COVERAGE_MAGIC); /* Magic */ + *(guint32 *)&data[4] = g_htonl (coverage->n_blocks); + offset = 8; + + for (i=0; i<coverage->n_blocks; i++) + { + guint32 header_val; + + /* Check for solid blocks. This is a sort of random place + * to do the optimization, but we care most about getting + * it right when storing it somewhere persistant. + */ + if (coverage->blocks[i].data != NULL) + { + guchar *data = coverage->blocks[i].data; + guchar first_val = data[0]; + + for (j = 1 ; j < 64; j++) + if (data[j] != first_val) + break; + + if (j == 64) + { + g_free (data); + coverage->blocks[i].data = NULL; + coverage->blocks[i].level = first_val & 0x3; + } + } + + if (coverage->blocks[i].data != NULL) + header_val = (guint32)-1; + else + header_val = coverage->blocks[i].level; + + *(guint32 *)&data[offset] = g_htonl (header_val); + offset += 4; + + if (coverage->blocks[i].data) + { + memcpy (data + offset, coverage->blocks[i].data, 64); + offset += 64; + } + } + + *bytes = data; + *n_bytes = size; +} + +static guint32 +pango_coverage_get_uint32 (guchar **ptr) +{ + guint32 val; + + memcpy (&val, *ptr, 4); + *ptr += 4; + + return g_ntohl (val); +} + +/** + * pango_coverage_from_bytes: + * @bytes: binary data representing a #PangoCoverage + * @n_bytes: the size of @bytes in bytes + * + * Convert data generated from pango_converage_to_bytes() back + * to a #PangoCoverage + * + * Return value: a newly allocated #PangoCoverage, or %NULL if + * the data was invalid. + **/ +PangoCoverage * +pango_coverage_from_bytes (guchar *bytes, + int n_bytes) +{ + PangoCoverage *coverage = g_slice_new0 (PangoCoverage); + guchar *ptr = bytes; + int i; + + coverage->ref_count = 1; + + if (n_bytes < 8) + goto error; + + if (pango_coverage_get_uint32 (&ptr) != PANGO_COVERAGE_MAGIC) + goto error; + + coverage->n_blocks = pango_coverage_get_uint32 (&ptr); + coverage->blocks = g_new0 (PangoBlockInfo, coverage->n_blocks); + + for (i = 0; i < coverage->n_blocks; i++) + { + guint val; + + if (ptr + 4 > bytes + n_bytes) + goto error; + + val = pango_coverage_get_uint32 (&ptr); + if (val == (guint32)-1) + { + if (ptr + 64 > bytes + n_bytes) + goto error; + + coverage->blocks[i].data = g_new (guchar, 64); + memcpy (coverage->blocks[i].data, ptr, 64); + ptr += 64; + } + else + coverage->blocks[i].level = val; + } + + return coverage; + + error: + + pango_coverage_unref (coverage); + return NULL; +} + + |