Upload tizen 2.0 beta source
[external/pango1.0.git] / pango / pango-coverage.c
1 /* Pango
2  * pango-coverage.c: Coverage maps for fonts
3  *
4  * Copyright (C) 2000 Red Hat Software
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include "config.h"
23 #include <string.h>
24
25 #include "pango-coverage.h"
26
27 typedef struct _PangoBlockInfo PangoBlockInfo;
28
29 #define N_BLOCKS_INCREMENT 256
30
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.
33  */
34
35 struct _PangoBlockInfo
36 {
37   guchar *data;
38   PangoCoverageLevel level;     /* Used if data == NULL */
39 };
40
41 struct _PangoCoverage
42 {
43   guint ref_count;
44   int n_blocks;
45
46   PangoBlockInfo *blocks;
47 };
48
49 /**
50  * pango_coverage_new:
51  *
52  * Create a new #PangoCoverage
53  *
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().
58  **/
59 PangoCoverage *
60 pango_coverage_new (void)
61 {
62   PangoCoverage *coverage = g_slice_new (PangoCoverage);
63
64   coverage->n_blocks = N_BLOCKS_INCREMENT;
65   coverage->blocks = g_new0 (PangoBlockInfo, coverage->n_blocks);
66   coverage->ref_count = 1;
67
68   return coverage;
69 }
70
71 /**
72  * pango_coverage_copy:
73  * @coverage: a #PangoCoverage
74  *
75  * Copy an existing #PangoCoverage. (This function may now be unnecessary
76  * since we refcount the structure. File a bug if you use it.)
77  *
78  * Return value: the newly allocated #PangoCoverage,
79  *               with a reference count of one, which
80  *               should be freed with pango_coverage_unref().
81  **/
82 PangoCoverage *
83 pango_coverage_copy (PangoCoverage *coverage)
84 {
85   int i;
86   PangoCoverage *result;
87
88   g_return_val_if_fail (coverage != NULL, NULL);
89
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;
94
95   for (i=0; i<coverage->n_blocks; i++)
96     {
97       if (coverage->blocks[i].data)
98         {
99           result->blocks[i].data = g_new (guchar, 64);
100           memcpy (result->blocks[i].data, coverage->blocks[i].data, 64);
101         }
102       else
103         result->blocks[i].data = NULL;
104
105       result->blocks[i].level = coverage->blocks[i].level;
106     }
107
108   return result;
109 }
110
111 /**
112  * pango_coverage_ref:
113  * @coverage: a #PangoCoverage
114  *
115  * Increase the reference count on the #PangoCoverage by one
116  *
117  * Return value: @coverage
118  **/
119 PangoCoverage *
120 pango_coverage_ref (PangoCoverage *coverage)
121 {
122   g_return_val_if_fail (coverage != NULL, NULL);
123
124   g_atomic_int_inc ((int *) &coverage->ref_count);
125
126   return coverage;
127 }
128
129 /**
130  * pango_coverage_unref:
131  * @coverage: a #PangoCoverage
132  *
133  * Decrease the reference count on the #PangoCoverage by one.
134  * If the result is zero, free the coverage and all associated memory.
135  **/
136 void
137 pango_coverage_unref (PangoCoverage *coverage)
138 {
139   int i;
140
141   g_return_if_fail (coverage != NULL);
142   g_return_if_fail (coverage->ref_count > 0);
143
144   if (g_atomic_int_dec_and_test ((int *) &coverage->ref_count))
145     {
146       for (i=0; i<coverage->n_blocks; i++)
147         g_slice_free1 (64, coverage->blocks[i].data);
148
149       g_free (coverage->blocks);
150       g_slice_free (PangoCoverage, coverage);
151     }
152 }
153
154 /**
155  * pango_coverage_get:
156  * @coverage: a #PangoCoverage
157  * @index_: the index to check
158  *
159  * Determine whether a particular index is covered by @coverage
160  *
161  * Return value: the coverage level of @coverage for character @index_.
162  **/
163 PangoCoverageLevel
164 pango_coverage_get (PangoCoverage *coverage,
165                     int            index)
166 {
167   int block_index;
168
169   g_return_val_if_fail (coverage != NULL, PANGO_COVERAGE_NONE);
170
171   /* index should really have been defined unsigned.  Work around
172    * it by just returning NONE.
173    */
174   if (G_UNLIKELY (index < 0))
175     return PANGO_COVERAGE_NONE;
176
177   block_index = index / 256;
178
179   if (block_index >= coverage->n_blocks)
180     return PANGO_COVERAGE_NONE;
181   else
182     {
183       guchar *data = coverage->blocks[block_index].data;
184       if (data)
185         {
186           int i = index % 256;
187           int shift = (i % 4) * 2;
188
189           return (data[i/4] >> shift) & 0x3;
190         }
191       else
192         return coverage->blocks[block_index].level;
193     }
194 }
195
196 /**
197  * pango_coverage_set:
198  * @coverage: a #PangoCoverage
199  * @index_: the index to modify
200  * @level: the new level for @index_
201  *
202  * Modify a particular index within @coverage
203  **/
204 void
205 pango_coverage_set (PangoCoverage     *coverage,
206                     int                index,
207                     PangoCoverageLevel level)
208 {
209   int block_index, i;
210   guchar *data;
211
212   g_return_if_fail (coverage != NULL);
213   g_return_if_fail (index >= 0);
214   g_return_if_fail ((guint) level <= 3);
215
216   block_index = index / 256;
217
218   if (block_index >= coverage->n_blocks)
219     {
220       int old_n_blocks = coverage->n_blocks;
221
222       coverage->n_blocks =
223         N_BLOCKS_INCREMENT * ((block_index + N_BLOCKS_INCREMENT) / N_BLOCKS_INCREMENT);
224
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));
228     }
229
230   data = coverage->blocks[block_index].data;
231   if (!data)
232     {
233       guchar byte;
234
235       if (level == coverage->blocks[block_index].level)
236         return;
237
238       data = g_slice_alloc (64);
239       coverage->blocks[block_index].data = data;
240
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);
245
246       memset (data, byte, 64);
247     }
248
249   i = index % 256;
250   data[i/4] |= level << ((i % 4) * 2);
251 }
252
253 /**
254  * pango_coverage_max:
255  * @coverage: a #PangoCoverage
256  * @other: another #PangoCoverage
257  *
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.
261  **/
262 void
263 pango_coverage_max (PangoCoverage *coverage,
264                     PangoCoverage *other)
265 {
266   int block_index, i;
267   int old_blocks;
268
269   g_return_if_fail (coverage != NULL);
270
271   old_blocks = MIN (coverage->n_blocks, other->n_blocks);
272
273   if (other->n_blocks > coverage->n_blocks)
274     {
275       coverage->n_blocks = other->n_blocks;
276       coverage->blocks = g_renew (PangoBlockInfo, coverage->blocks, coverage->n_blocks);
277
278       for (block_index = old_blocks; block_index < coverage->n_blocks; block_index++)
279         {
280           if (other->blocks[block_index].data)
281             {
282               coverage->blocks[block_index].data = g_new (guchar, 64);
283               memcpy (coverage->blocks[block_index].data, other->blocks[block_index].data, 64);
284             }
285           else
286             coverage->blocks[block_index].data = NULL;
287
288           coverage->blocks[block_index].level = other->blocks[block_index].level;
289         }
290     }
291
292   for (block_index = 0; block_index < old_blocks; block_index++)
293     {
294       if (!coverage->blocks[block_index].data && !other->blocks[block_index].data)
295         {
296           coverage->blocks[block_index].level = MAX (coverage->blocks[block_index].level, other->blocks[block_index].level);
297         }
298       else if (coverage->blocks[block_index].data && other->blocks[block_index].data)
299         {
300           guchar *data = coverage->blocks[block_index].data;
301
302           for (i=0; i<64; i++)
303             {
304               int byte1 = data[i];
305               int byte2 = other->blocks[block_index].data[i];
306
307               /* There are almost certainly some clever logical ops to do this */
308               data[i] =
309                 MAX (byte1 & 0x3, byte2 & 0x3) |
310                 MAX (byte1 & 0xc, byte2 & 0xc) |
311                 MAX (byte1 & 0x30, byte2 & 0x30) |
312                 MAX (byte1 & 0xc0, byte2 & 0xc0);
313             }
314         }
315       else
316         {
317           guchar *src, *dest;
318           int level, byte2;
319
320           if (coverage->blocks[block_index].data)
321             {
322               src = dest = coverage->blocks[block_index].data;
323               level = other->blocks[block_index].level;
324             }
325           else
326             {
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;
331             }
332
333           byte2 = level | (level << 2) | (level << 4) | (level << 6);
334
335           for (i=0; i<64; i++)
336             {
337               int byte1 = src[i];
338
339               /* There are almost certainly some clever logical ops to do this */
340               dest[i] =
341                 MAX (byte1 & 0x3, byte2 & 0x3) |
342                 MAX (byte1 & 0xc, byte2 & 0xc) |
343                 MAX (byte1 & 0x30, byte2 & 0x30) |
344                 MAX (byte1 & 0xc0, byte2 & 0xc0);
345             }
346         }
347     }
348 }
349
350 #define PANGO_COVERAGE_MAGIC 0xc89dbd5e
351
352 /**
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
357  *
358  * Convert a #PangoCoverage structure into a flat binary format
359  **/
360 void
361 pango_coverage_to_bytes   (PangoCoverage  *coverage,
362                            guchar        **bytes,
363                            int            *n_bytes)
364 {
365   int i, j;
366   int size = 8 + 4 * coverage->n_blocks;
367   guchar *data;
368   int offset;
369
370   for (i=0; i<coverage->n_blocks; i++)
371     {
372       if (coverage->blocks[i].data)
373         size += 64;
374     }
375
376   data = g_malloc (size);
377
378   *(guint32 *)&data[0] = g_htonl (PANGO_COVERAGE_MAGIC); /* Magic */
379   *(guint32 *)&data[4] = g_htonl (coverage->n_blocks);
380   offset = 8;
381
382   for (i=0; i<coverage->n_blocks; i++)
383     {
384       guint32 header_val;
385
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.
389        */
390       if (coverage->blocks[i].data != NULL)
391         {
392           guchar *data = coverage->blocks[i].data;
393           guchar first_val = data[0];
394
395           if (first_val == 0 || first_val == 0xff)
396             {
397               for (j = 1 ; j < 64; j++)
398                 if (data[j] != first_val)
399                   break;
400
401               if (j == 64)
402                 {
403                   g_slice_free1 (64, data);
404                   coverage->blocks[i].data = NULL;
405                   coverage->blocks[i].level = first_val & 0x3;
406                 }
407             }
408         }
409
410       if (coverage->blocks[i].data != NULL)
411         header_val = (guint32)-1;
412       else
413         header_val = coverage->blocks[i].level;
414
415       *(guint32 *)&data[offset] = g_htonl (header_val);
416       offset += 4;
417
418       if (coverage->blocks[i].data)
419         {
420           memcpy (data + offset, coverage->blocks[i].data, 64);
421           offset += 64;
422         }
423     }
424
425   *bytes = data;
426   *n_bytes = size;
427 }
428
429 static guint32
430 pango_coverage_get_uint32 (guchar **ptr)
431 {
432   guint32 val;
433
434   memcpy (&val, *ptr, 4);
435   *ptr += 4;
436
437   return g_ntohl (val);
438 }
439
440 /**
441  * pango_coverage_from_bytes:
442  * @bytes: binary data representing a #PangoCoverage
443  * @n_bytes: the size of @bytes in bytes
444  *
445  * Convert data generated from pango_converage_to_bytes() back
446  * to a #PangoCoverage
447  *
448  * Return value: a newly allocated #PangoCoverage, or %NULL if
449  *               the data was invalid.
450  **/
451 PangoCoverage *
452 pango_coverage_from_bytes (guchar *bytes,
453                            int     n_bytes)
454 {
455   PangoCoverage *coverage = g_slice_new0 (PangoCoverage);
456   guchar *ptr = bytes;
457   int i;
458
459   coverage->ref_count = 1;
460
461   if (n_bytes < 8)
462     goto error;
463
464   if (pango_coverage_get_uint32 (&ptr) != PANGO_COVERAGE_MAGIC)
465     goto error;
466
467   coverage->n_blocks = pango_coverage_get_uint32 (&ptr);
468   coverage->blocks = g_new0 (PangoBlockInfo, coverage->n_blocks);
469
470   for (i = 0; i < coverage->n_blocks; i++)
471     {
472       guint val;
473
474       if (ptr + 4 > bytes + n_bytes)
475         goto error;
476
477       val = pango_coverage_get_uint32 (&ptr);
478       if (val == (guint32)-1)
479         {
480           if (ptr + 64 > bytes + n_bytes)
481             goto error;
482
483           coverage->blocks[i].data = g_new (guchar, 64);
484           memcpy (coverage->blocks[i].data, ptr, 64);
485           ptr += 64;
486         }
487       else
488         coverage->blocks[i].level = val;
489     }
490
491   return coverage;
492
493  error:
494
495   pango_coverage_unref (coverage);
496   return NULL;
497 }
498
499