2 * pango-coverage.c: Coverage maps for fonts
4 * Copyright (C) 2000 Red Hat Software
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
25 #include "pango-coverage.h"
27 typedef struct _PangoBlockInfo PangoBlockInfo;
29 #define N_BLOCKS_INCREMENT 256
31 /* The structure of a PangoCoverage object is a two-level table, with blocks of size 256.
32 * each block is stored as a packed array of 2 bit values for each index, in LSB order.
35 struct _PangoBlockInfo
38 PangoCoverageLevel level; /* Used if data == NULL */
46 PangoBlockInfo *blocks;
52 * Create a new #PangoCoverage
54 * Return value: the newly allocated #PangoCoverage,
55 * initialized to %PANGO_COVERAGE_NONE
56 * with a reference count of one, which
57 * should be freed with pango_coverage_unref().
60 pango_coverage_new (void)
62 PangoCoverage *coverage = g_slice_new (PangoCoverage);
64 coverage->n_blocks = N_BLOCKS_INCREMENT;
65 coverage->blocks = g_new0 (PangoBlockInfo, coverage->n_blocks);
66 coverage->ref_count = 1;
72 * pango_coverage_copy:
73 * @coverage: a #PangoCoverage
75 * Copy an existing #PangoCoverage. (This function may now be unnecessary
76 * since we refcount the structure. File a bug if you use it.)
78 * Return value: the newly allocated #PangoCoverage,
79 * with a reference count of one, which
80 * should be freed with pango_coverage_unref().
83 pango_coverage_copy (PangoCoverage *coverage)
86 PangoCoverage *result;
88 g_return_val_if_fail (coverage != NULL, NULL);
90 result = g_slice_new (PangoCoverage);
91 result->n_blocks = coverage->n_blocks;
92 result->blocks = g_new (PangoBlockInfo, coverage->n_blocks);
93 result->ref_count = 1;
95 for (i=0; i<coverage->n_blocks; i++)
97 if (coverage->blocks[i].data)
99 result->blocks[i].data = g_new (guchar, 64);
100 memcpy (result->blocks[i].data, coverage->blocks[i].data, 64);
103 result->blocks[i].data = NULL;
105 result->blocks[i].level = coverage->blocks[i].level;
112 * pango_coverage_ref:
113 * @coverage: a #PangoCoverage
115 * Increase the reference count on the #PangoCoverage by one
117 * Return value: @coverage
120 pango_coverage_ref (PangoCoverage *coverage)
122 g_return_val_if_fail (coverage != NULL, NULL);
124 g_atomic_int_inc ((int *) &coverage->ref_count);
130 * pango_coverage_unref:
131 * @coverage: a #PangoCoverage
133 * Decrease the reference count on the #PangoCoverage by one.
134 * If the result is zero, free the coverage and all associated memory.
137 pango_coverage_unref (PangoCoverage *coverage)
141 g_return_if_fail (coverage != NULL);
142 g_return_if_fail (coverage->ref_count > 0);
144 if (g_atomic_int_dec_and_test ((int *) &coverage->ref_count))
146 for (i=0; i<coverage->n_blocks; i++)
147 g_slice_free1 (64, coverage->blocks[i].data);
149 g_free (coverage->blocks);
150 g_slice_free (PangoCoverage, coverage);
155 * pango_coverage_get:
156 * @coverage: a #PangoCoverage
157 * @index_: the index to check
159 * Determine whether a particular index is covered by @coverage
161 * Return value: the coverage level of @coverage for character @index_.
164 pango_coverage_get (PangoCoverage *coverage,
169 g_return_val_if_fail (coverage != NULL, PANGO_COVERAGE_NONE);
171 /* index should really have been defined unsigned. Work around
172 * it by just returning NONE.
174 if (G_UNLIKELY (index < 0))
175 return PANGO_COVERAGE_NONE;
177 block_index = index / 256;
179 if (block_index >= coverage->n_blocks)
180 return PANGO_COVERAGE_NONE;
183 guchar *data = coverage->blocks[block_index].data;
187 int shift = (i % 4) * 2;
189 return (data[i/4] >> shift) & 0x3;
192 return coverage->blocks[block_index].level;
197 * pango_coverage_set:
198 * @coverage: a #PangoCoverage
199 * @index_: the index to modify
200 * @level: the new level for @index_
202 * Modify a particular index within @coverage
205 pango_coverage_set (PangoCoverage *coverage,
207 PangoCoverageLevel level)
212 g_return_if_fail (coverage != NULL);
213 g_return_if_fail (index >= 0);
214 g_return_if_fail ((guint) level <= 3);
216 block_index = index / 256;
218 if (block_index >= coverage->n_blocks)
220 int old_n_blocks = coverage->n_blocks;
223 N_BLOCKS_INCREMENT * ((block_index + N_BLOCKS_INCREMENT) / N_BLOCKS_INCREMENT);
225 coverage->blocks = g_renew (PangoBlockInfo, coverage->blocks, coverage->n_blocks);
226 memset (coverage->blocks + old_n_blocks, 0,
227 sizeof (PangoBlockInfo) * (coverage->n_blocks - old_n_blocks));
230 data = coverage->blocks[block_index].data;
235 if (level == coverage->blocks[block_index].level)
238 data = g_slice_alloc (64);
239 coverage->blocks[block_index].data = data;
241 byte = coverage->blocks[block_index].level |
242 (coverage->blocks[block_index].level << 2) |
243 (coverage->blocks[block_index].level << 4) |
244 (coverage->blocks[block_index].level << 6);
246 memset (data, byte, 64);
250 data[i/4] |= level << ((i % 4) * 2);
254 * pango_coverage_max:
255 * @coverage: a #PangoCoverage
256 * @other: another #PangoCoverage
258 * Set the coverage for each index in @coverage to be the max (better)
259 * value of the current coverage for the index and the coverage for
260 * the corresponding index in @other.
263 pango_coverage_max (PangoCoverage *coverage,
264 PangoCoverage *other)
269 g_return_if_fail (coverage != NULL);
271 old_blocks = MIN (coverage->n_blocks, other->n_blocks);
273 if (other->n_blocks > coverage->n_blocks)
275 coverage->n_blocks = other->n_blocks;
276 coverage->blocks = g_renew (PangoBlockInfo, coverage->blocks, coverage->n_blocks);
278 for (block_index = old_blocks; block_index < coverage->n_blocks; block_index++)
280 if (other->blocks[block_index].data)
282 coverage->blocks[block_index].data = g_new (guchar, 64);
283 memcpy (coverage->blocks[block_index].data, other->blocks[block_index].data, 64);
286 coverage->blocks[block_index].data = NULL;
288 coverage->blocks[block_index].level = other->blocks[block_index].level;
292 for (block_index = 0; block_index < old_blocks; block_index++)
294 if (!coverage->blocks[block_index].data && !other->blocks[block_index].data)
296 coverage->blocks[block_index].level = MAX (coverage->blocks[block_index].level, other->blocks[block_index].level);
298 else if (coverage->blocks[block_index].data && other->blocks[block_index].data)
300 guchar *data = coverage->blocks[block_index].data;
305 int byte2 = other->blocks[block_index].data[i];
307 /* There are almost certainly some clever logical ops to do this */
309 MAX (byte1 & 0x3, byte2 & 0x3) |
310 MAX (byte1 & 0xc, byte2 & 0xc) |
311 MAX (byte1 & 0x30, byte2 & 0x30) |
312 MAX (byte1 & 0xc0, byte2 & 0xc0);
320 if (coverage->blocks[block_index].data)
322 src = dest = coverage->blocks[block_index].data;
323 level = other->blocks[block_index].level;
327 src = other->blocks[block_index].data;
328 dest = g_new (guchar, 64);
329 coverage->blocks[block_index].data = dest;
330 level = coverage->blocks[block_index].level;
333 byte2 = level | (level << 2) | (level << 4) | (level << 6);
339 /* There are almost certainly some clever logical ops to do this */
341 MAX (byte1 & 0x3, byte2 & 0x3) |
342 MAX (byte1 & 0xc, byte2 & 0xc) |
343 MAX (byte1 & 0x30, byte2 & 0x30) |
344 MAX (byte1 & 0xc0, byte2 & 0xc0);
350 #define PANGO_COVERAGE_MAGIC 0xc89dbd5e
353 * pango_coverage_to_bytes:
354 * @coverage: a #PangoCoverage
355 * @bytes: location to store result (must be freed with g_free())
356 * @n_bytes: location to store size of result
358 * Convert a #PangoCoverage structure into a flat binary format
361 pango_coverage_to_bytes (PangoCoverage *coverage,
366 int size = 8 + 4 * coverage->n_blocks;
370 for (i=0; i<coverage->n_blocks; i++)
372 if (coverage->blocks[i].data)
376 data = g_malloc (size);
378 *(guint32 *)&data[0] = g_htonl (PANGO_COVERAGE_MAGIC); /* Magic */
379 *(guint32 *)&data[4] = g_htonl (coverage->n_blocks);
382 for (i=0; i<coverage->n_blocks; i++)
386 /* Check for solid blocks. This is a sort of random place
387 * to do the optimization, but we care most about getting
388 * it right when storing it somewhere persistant.
390 if (coverage->blocks[i].data != NULL)
392 guchar *data = coverage->blocks[i].data;
393 guchar first_val = data[0];
395 if (first_val == 0 || first_val == 0xff)
397 for (j = 1 ; j < 64; j++)
398 if (data[j] != first_val)
403 g_slice_free1 (64, data);
404 coverage->blocks[i].data = NULL;
405 coverage->blocks[i].level = first_val & 0x3;
410 if (coverage->blocks[i].data != NULL)
411 header_val = (guint32)-1;
413 header_val = coverage->blocks[i].level;
415 *(guint32 *)&data[offset] = g_htonl (header_val);
418 if (coverage->blocks[i].data)
420 memcpy (data + offset, coverage->blocks[i].data, 64);
430 pango_coverage_get_uint32 (guchar **ptr)
434 memcpy (&val, *ptr, 4);
437 return g_ntohl (val);
441 * pango_coverage_from_bytes:
442 * @bytes: binary data representing a #PangoCoverage
443 * @n_bytes: the size of @bytes in bytes
445 * Convert data generated from pango_converage_to_bytes() back
446 * to a #PangoCoverage
448 * Return value: a newly allocated #PangoCoverage, or %NULL if
449 * the data was invalid.
452 pango_coverage_from_bytes (guchar *bytes,
455 PangoCoverage *coverage = g_slice_new0 (PangoCoverage);
459 coverage->ref_count = 1;
464 if (pango_coverage_get_uint32 (&ptr) != PANGO_COVERAGE_MAGIC)
467 coverage->n_blocks = pango_coverage_get_uint32 (&ptr);
468 coverage->blocks = g_new0 (PangoBlockInfo, coverage->n_blocks);
470 for (i = 0; i < coverage->n_blocks; i++)
474 if (ptr + 4 > bytes + n_bytes)
477 val = pango_coverage_get_uint32 (&ptr);
478 if (val == (guint32)-1)
480 if (ptr + 64 > bytes + n_bytes)
483 coverage->blocks[i].data = g_new (guchar, 64);
484 memcpy (coverage->blocks[i].data, ptr, 64);
488 coverage->blocks[i].level = val;
495 pango_coverage_unref (coverage);