Back to index

lightning-sunbird  0.9+nobinonly
pango-coverage.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * Pango
00003  * pango-coverage.c:
00004  *
00005  * ***** BEGIN LICENSE BLOCK *****
00006  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00007  *
00008  * The contents of this file are subject to the Mozilla Public License Version
00009  * 1.1 (the "License"); you may not use this file except in compliance with
00010  * the License. You may obtain a copy of the License at
00011  * http://www.mozilla.org/MPL/
00012  *
00013  * Software distributed under the License is distributed on an "AS IS" basis,
00014  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00015  * for the specific language governing rights and limitations under the
00016  * License.
00017  *
00018  * The Original Code is the Pango Library (www.pango.org).
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Red Hat Software.
00022  * Portions created by the Initial Developer are Copyright (C) 1999
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include <string.h>
00042 
00043 #include "pango-coverage.h"
00044 
00045 typedef struct _PangoliteBlockInfo PangoliteBlockInfo;
00046 
00047 #define N_BLOCKS_INCREMENT 256
00048 
00049 /* The structure of a PangoliteCoverage object is a two-level table, 
00050    with blocks of size 256. Each block is stored as a packed array of 2 bit 
00051    values for each index, in LSB order.
00052  */
00053 
00054 struct _PangoliteBlockInfo
00055 {
00056   guchar *data;             
00057   PangoliteCoverageLevel level;    /* Used if data == NULL */
00058 };
00059 
00060 struct _PangoliteCoverage
00061 {
00062   guint ref_count;
00063   int n_blocks;
00064   int data_size;
00065   
00066   PangoliteBlockInfo *blocks;
00067 };
00068 
00077 PangoliteCoverage *
00078 pangolite_coverage_new (void)
00079 {
00080   PangoliteCoverage *coverage = g_new (PangoliteCoverage, 1);
00081 
00082   coverage->n_blocks = N_BLOCKS_INCREMENT;
00083   coverage->blocks = g_new0 (PangoliteBlockInfo, coverage->n_blocks);
00084   coverage->ref_count = 1;
00085   
00086   return coverage;
00087 }
00088 
00099 PangoliteCoverage *
00100 pangolite_coverage_copy (PangoliteCoverage *coverage)
00101 {
00102   int i;
00103   PangoliteCoverage *result;
00104 
00105   g_return_val_if_fail (coverage != NULL, NULL);
00106 
00107   result = g_new (PangoliteCoverage, 1);
00108   result->n_blocks = coverage->n_blocks;
00109   result->blocks = g_new (PangoliteBlockInfo, coverage->n_blocks);
00110   result->ref_count = 1;
00111 
00112   for (i=0; i<coverage->n_blocks; i++) {
00113     if (coverage->blocks[i].data) {
00114       result->blocks[i].data = g_new (guchar, 64);
00115       memcpy (result->blocks[i].data, coverage->blocks[i].data, 64);
00116     }
00117     else
00118       result->blocks[i].data = NULL;
00119     
00120     result->blocks[i].level = coverage->blocks[i].level;
00121   }
00122   
00123   return result;
00124 }
00125 
00134 PangoliteCoverage *
00135 pangolite_coverage_ref (PangoliteCoverage *coverage)
00136 {
00137   g_return_val_if_fail (coverage != NULL, NULL);
00138 
00139   coverage->ref_count++;
00140   return coverage;
00141 }
00142 
00150 void
00151 pangolite_coverage_unref (PangoliteCoverage *coverage)
00152 {
00153   int i;
00154   
00155   g_return_if_fail (coverage != NULL);
00156   g_return_if_fail (coverage->ref_count > 0);
00157 
00158   coverage->ref_count--;
00159 
00160   if (coverage->ref_count == 0) {
00161     for (i=0; i<coverage->n_blocks; i++) {
00162       if (coverage->blocks[i].data)
00163         g_free (coverage->blocks[i].data);
00164     }
00165     
00166     g_free (coverage->blocks);
00167     g_free (coverage);
00168   }
00169 }
00170 
00180 PangoliteCoverageLevel
00181 pangolite_coverage_get (PangoliteCoverage *coverage, int index)
00182 {
00183   int block_index;
00184   
00185   g_return_val_if_fail (coverage != NULL, PANGO_COVERAGE_NONE);
00186 
00187   block_index = index / 256;
00188 
00189   if (block_index > coverage->n_blocks)
00190     return PANGO_COVERAGE_NONE;
00191   else {
00192     guchar *data = coverage->blocks[block_index].data;
00193     if (data) {
00194       int i = index % 256;
00195       int shift = (i % 4) * 2;
00196       
00197       return (data[i/4] >> shift) & 0x3;
00198     }
00199     else
00200       return coverage->blocks[block_index].level;
00201   }
00202 }
00203 
00212 void pangolite_coverage_set (PangoliteCoverage     *coverage,
00213                          int                index,
00214                          PangoliteCoverageLevel level)
00215 {
00216   int block_index, i;
00217   guchar *data;
00218   
00219   g_return_if_fail (coverage != NULL);
00220   g_return_if_fail (level >= 0 || level <= 3);
00221 
00222   block_index = index / 256;
00223 
00224   if (block_index > coverage->n_blocks) {
00225     int old_n_blocks = coverage->n_blocks;
00226     
00227     coverage->n_blocks =
00228       N_BLOCKS_INCREMENT * ((block_index + N_BLOCKS_INCREMENT - 1) / N_BLOCKS_INCREMENT);
00229     
00230     coverage->blocks = g_renew (PangoliteBlockInfo, coverage->blocks, coverage->n_blocks);
00231     memset (coverage->blocks + old_n_blocks, 0,
00232             sizeof (PangoliteBlockInfo) * (coverage->n_blocks - old_n_blocks));
00233   }
00234   
00235   data = coverage->blocks[block_index].data;
00236   if (!data) {
00237     guchar byte;
00238     
00239     if (level == coverage->blocks[block_index].level)
00240       return;
00241     
00242     data = g_new (guchar, 64);
00243     coverage->blocks[block_index].data = data;
00244     
00245     byte = coverage->blocks[block_index].level |
00246       (coverage->blocks[block_index].level << 2) |
00247       (coverage->blocks[block_index].level << 4) |
00248       (coverage->blocks[block_index].level << 6);
00249     
00250     memset (data, byte, 64);
00251   }
00252   
00253   i = index % 256;
00254   data[i/4] |= level << ((i % 4) * 2);
00255 }
00256 
00266 void pangolite_coverage_max (PangoliteCoverage *coverage, PangoliteCoverage *other)
00267 {
00268   int block_index, i;
00269   int old_blocks;
00270   
00271   g_return_if_fail (coverage != NULL);
00272   
00273   old_blocks = MIN (coverage->n_blocks, other->n_blocks);
00274   
00275   if (other->n_blocks > coverage->n_blocks) {
00276     coverage->n_blocks = other->n_blocks;
00277     coverage->blocks = g_renew (PangoliteBlockInfo, coverage->blocks, coverage->n_blocks);
00278     
00279     for (block_index = old_blocks; block_index < coverage->n_blocks; 
00280          block_index++) {
00281       if (other->blocks[block_index].data) {
00282         coverage->blocks[block_index].data = g_new (guchar, 64);
00283              memcpy (coverage->blocks[block_index].data, 
00284                 other->blocks[block_index].data, 64);
00285            }
00286       else
00287         coverage->blocks[block_index].data = NULL;
00288       
00289       coverage->blocks[block_index].level = other->blocks[block_index].level;
00290     }
00291   }
00292   
00293   for (block_index = 0; block_index < old_blocks; block_index++) {
00294     if (!coverage->blocks[block_index].data && !other->blocks[block_index].data) {
00295       coverage->blocks[block_index].level = MAX (coverage->blocks[block_index].level, other->blocks[block_index].level);
00296     }
00297     else if (coverage->blocks[block_index].data && other->blocks[block_index].data) {
00298       guchar *data = coverage->blocks[block_index].data;
00299       
00300       for (i=0; i<64; i++) {
00301              int byte1 = data[i];
00302              int byte2 = other->blocks[block_index].data[i];
00303         
00304              /* There are almost certainly some clever logical ops to do this */
00305              data[i] =
00306           MAX (byte1 & 0x3, byte2 & 0x3) |
00307           MAX (byte1 & 0xc, byte2 & 0xc) |
00308           MAX (byte1 & 0x30, byte2 & 0x30) |
00309           MAX (byte1 & 0xc0, byte2 & 0xc00);
00310            }
00311     }
00312     else {
00313       guchar *src, *dest;
00314       int level, byte2;
00315       
00316       if (coverage->blocks[block_index].data) {
00317              src = dest = coverage->blocks[block_index].data;
00318              level = other->blocks[block_index].level;
00319            }
00320       else {
00321              src = other->blocks[block_index].data;
00322              dest = g_new (guchar, 64);
00323              coverage->blocks[block_index].data = dest;
00324              level = coverage->blocks[block_index].level;
00325            }
00326       
00327       byte2 = level | (level << 2) | (level << 4) | (level << 6);
00328       
00329       for (i=0; i<64; i++) {
00330              int byte1 = src[i];
00331         
00332              /* There are almost certainly some clever logical ops to do this */
00333              dest[i] =
00334           MAX (byte1 & 0x3, byte2 & 0x3) |
00335           MAX (byte1 & 0xc, byte2 & 0xc) |
00336           MAX (byte1 & 0x30, byte2 & 0x30) |
00337           MAX (byte1 & 0xc0, byte2 & 0xc00);
00338            }
00339     }
00340   }
00341 }
00342 
00343 #define PANGO_COVERAGE_MAGIC 0xc89dbd5e
00344 
00353 void
00354 pangolite_coverage_to_bytes(PangoliteCoverage  *coverage,
00355                         guchar        **bytes,
00356                         int            *n_bytes)
00357 {
00358   int i, j;
00359   int size = 8 + 4 * coverage->n_blocks;
00360   guchar *data;
00361   int offset;
00362   
00363   for (i=0; i<coverage->n_blocks; i++) {
00364     if (coverage->blocks[i].data)
00365       size += 64;
00366   }
00367   
00368   data = g_malloc (size);
00369   
00370   *(guint32 *)&data[0] = g_htonl (PANGO_COVERAGE_MAGIC); /* Magic */
00371   *(guint32 *)&data[4] = g_htonl (coverage->n_blocks);
00372   offset = 8;
00373   
00374   for (i=0; i<coverage->n_blocks; i++) {
00375     guint32 header_val;
00376     
00377     /* Check for solid blocks. This is a sort of random place
00378      * to do the optimization, but we care most about getting
00379      * it right when storing it somewhere persistant.
00380      */
00381     if (coverage->blocks[i].data != NULL) {
00382       guchar *data = coverage->blocks[i].data;
00383       guchar first_val = data[0];
00384       
00385       for (j = 1 ; j < 64; j++)
00386         if (data[j] != first_val)
00387           break;
00388       
00389       if (j == 64) {
00390              g_free (data);
00391              coverage->blocks[i].data = NULL;
00392              coverage->blocks[i].level = first_val & 0x3;
00393            }
00394     }
00395     
00396     if (coverage->blocks[i].data != NULL)
00397       header_val = (guint32)-1;
00398     else
00399       header_val = coverage->blocks[i].level;
00400     
00401     *(guint32 *)&data[offset] = g_htonl (header_val);
00402     offset += 4;
00403     
00404     if (coverage->blocks[i].data) {
00405       memcpy (data + offset, coverage->blocks[i].data, 64);
00406       offset += 64;
00407     }
00408   }
00409   
00410   *bytes = data;
00411   *n_bytes = size;
00412 }
00413 
00414 static guint32
00415 pangolite_coverage_get_uint32 (guchar **ptr)
00416 {
00417   guint32 val;
00418   
00419   memcpy (&val, *ptr, 4);
00420   *ptr += 4;
00421   
00422   return g_ntohl (val);
00423 }
00424 
00436 PangoliteCoverage *
00437 pangolite_coverage_from_bytes (guchar *bytes, int n_bytes)
00438 {
00439   PangoliteCoverage *coverage = g_new0 (PangoliteCoverage, 1);
00440   guchar *ptr = bytes;
00441   int i;
00442   
00443   coverage->ref_count = 1;
00444   
00445   if (n_bytes < 8)
00446     goto error;
00447   
00448   if (pangolite_coverage_get_uint32 (&ptr) != PANGO_COVERAGE_MAGIC)
00449     goto error;
00450   
00451   coverage->n_blocks = pangolite_coverage_get_uint32 (&ptr);
00452   coverage->blocks = g_new0 (PangoliteBlockInfo, coverage->n_blocks);
00453   
00454   for (i = 0; i < coverage->n_blocks; i++) {
00455     guint val;
00456     
00457     if (ptr + 4 > bytes + n_bytes)
00458       goto error;
00459     
00460     val = pangolite_coverage_get_uint32 (&ptr);
00461     if (val == (guint32)-1) {
00462       if (ptr + 64 > bytes + n_bytes)
00463         goto error;
00464       
00465       coverage->blocks[i].data = g_new (guchar, 64);
00466       memcpy (coverage->blocks[i].data, ptr, 64);
00467       ptr += 64;
00468     }
00469     else
00470       coverage->blocks[i].level = val;
00471   }
00472   
00473   return coverage;
00474   
00475  error:
00476   
00477   pangolite_coverage_unref (coverage);
00478   return NULL;
00479 }