fontscale.c revision 7f7f5e4e
1/* $Xorg: fontscale.c,v 1.5 2001/02/09 02:04:03 xorgcvs Exp $ */
2
3/*
4
5Copyright 1991, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall not be
24used in advertising or otherwise to promote the sale, use or other dealings
25in this Software without prior written authorization from The Open Group.
26
27*/
28/* $XFree86: xc/lib/font/fontfile/fontscale.c,v 3.9 2001/08/27 19:49:54 dawes Exp $ */
29
30/*
31 * Author:  Keith Packard, MIT X Consortium
32 */
33
34#ifdef HAVE_CONFIG_H
35#include <config.h>
36#endif
37#include    <X11/fonts/fntfilst.h>
38#include <math.h>
39
40Bool
41FontFileAddScaledInstance (FontEntryPtr entry, FontScalablePtr vals,
42			   FontPtr pFont, char *bitmapName)
43{
44    FontScalableEntryPtr    scalable;
45    FontScalableExtraPtr    extra;
46    FontScaledPtr	    new;
47    int			    newsize;
48
49    scalable = &entry->u.scalable;
50    extra = scalable->extra;
51    if (extra->numScaled == extra->sizeScaled)
52    {
53	newsize = extra->sizeScaled + 4;
54	new = realloc (extra->scaled, newsize * sizeof (FontScaledRec));
55	if (!new)
56	    return FALSE;
57	extra->sizeScaled = newsize;
58	extra->scaled = new;
59    }
60    new = &extra->scaled[extra->numScaled++];
61    new->vals = *vals;
62    new->pFont = pFont;
63    new->bitmap = (FontEntryPtr) bitmapName;
64    if (pFont)
65	pFont->fpePrivate = (pointer) entry;
66    return TRUE;
67}
68
69/* Must call this after the directory is sorted */
70
71void
72FontFileSwitchStringsToBitmapPointers (FontDirectoryPtr dir)
73{
74    int	    s;
75    int	    b;
76    int	    i;
77    FontEntryPtr	    scalable;
78    FontEntryPtr	    nonScalable;
79    FontScaledPtr	    scaled;
80    FontScalableExtraPtr    extra;
81
82    scalable = dir->scalable.entries;
83    nonScalable = dir->nonScalable.entries;
84    for (s = 0; s < dir->scalable.used; s++)
85    {
86	extra = scalable[s].u.scalable.extra;
87	scaled = extra->scaled;
88	for (i = 0; i < extra->numScaled; i++)
89	    for (b = 0; b < dir->nonScalable.used; b++)
90		if (nonScalable[b].name.name == (char *) scaled[i].bitmap)
91		    scaled[i].bitmap = &nonScalable[b];
92    }
93}
94
95void
96FontFileRemoveScaledInstance (FontEntryPtr entry, FontPtr pFont)
97{
98    FontScalableEntryPtr    scalable;
99    FontScalableExtraPtr    extra;
100    int			    i;
101
102    scalable = &entry->u.scalable;
103    extra = scalable->extra;
104    for (i = 0; i < extra->numScaled; i++)
105    {
106	if (extra->scaled[i].pFont == pFont)
107	{
108	    if (extra->scaled[i].vals.ranges)
109		free (extra->scaled[i].vals.ranges);
110	    extra->numScaled--;
111	    for (; i < extra->numScaled; i++)
112		extra->scaled[i] = extra->scaled[i+1];
113	}
114    }
115}
116
117Bool
118FontFileCompleteXLFD (FontScalablePtr vals, FontScalablePtr def)
119{
120    FontResolutionPtr res;
121    int		num_res;
122    double	sx, sy, temp_matrix[4];
123    double	pixel_setsize_adjustment = 1.0;
124    /*
125     * If two of the three vertical scale values are specified, compute the
126     * third.  If all three are specified, make sure they are consistent
127     * (within a pixel)
128     *
129     * One purpose of this procedure is to complete XLFD names in a
130     * repeatable manner.  That is, if the user partially specifies
131     * a name (say, pixelsize but not pointsize), the results generated
132     * here result in a fully specified name that will result in the
133     * same font.
134     */
135
136    res = GetClientResolutions(&num_res);
137
138    if (!(vals->values_supplied & PIXELSIZE_MASK) ||
139	!(vals->values_supplied & POINTSIZE_MASK))
140    {
141	/* If resolution(s) unspecified and cannot be computed from
142	   pixelsize and pointsize, get appropriate defaults. */
143
144	if (num_res)
145	{
146	    if (vals->x <= 0)
147		vals->x = res->x_resolution;
148	    if (vals->y <= 0)
149		vals->y = res->y_resolution;
150	}
151
152	if (vals->x <= 0)
153	    vals->x = def->x;
154	if (vals->y <= 0)
155	    vals->y = def->y;
156    }
157    else
158    {
159	/* If needed, compute resolution values from the pixel and
160	   pointsize information we were given.  This problem is
161	   overdetermined (four equations, two unknowns), but we don't
162	   check for inconsistencies here.  If they exist, they will
163	   show up in later tests for the point and pixel sizes.  */
164
165	if (vals->y <= 0)
166	{
167	    double x = hypot(vals->pixel_matrix[1], vals->pixel_matrix[3]);
168	    double y = hypot(vals->point_matrix[1], vals->point_matrix[3]);
169	    if (y < EPS) return FALSE;
170	    vals->y = (int)(x * 72.27 / y + .5);
171	}
172	if (vals->x <= 0)
173	{
174	    /* If the pixelsize was given as an array, or as a scalar that
175	       has been normalized for the pixel shape, we have enough
176	       information to compute a separate horizontal resolution */
177
178	    if ((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY ||
179	        (vals->values_supplied & PIXELSIZE_MASK) ==
180		    PIXELSIZE_SCALAR_NORMALIZED)
181	    {
182		double x = hypot(vals->pixel_matrix[0], vals->pixel_matrix[2]);
183		double y = hypot(vals->point_matrix[0], vals->point_matrix[2]);
184		if (y < EPS) return FALSE;
185		vals->x = (int)(x * 72.27 / y + .5);
186	    }
187	    else
188	    {
189		/* Not enough information in the pixelsize array.  Just
190		   assume the pixels are square. */
191		vals->x = vals->y;
192	    }
193	}
194    }
195
196    if (vals->x <= 0 || vals->y <= 0) return FALSE;
197
198    /* If neither pixelsize nor pointsize is defined, take the pointsize
199       from the defaults structure we've been passed. */
200    if (!(vals->values_supplied & PIXELSIZE_MASK) &&
201	!(vals->values_supplied & POINTSIZE_MASK))
202    {
203	if (num_res)
204	{
205	    vals->point_matrix[0] =
206	    vals->point_matrix[3] = (double)res->point_size / 10.0;
207	    vals->point_matrix[1] =
208	    vals->point_matrix[2] = 0;
209	    vals->values_supplied = (vals->values_supplied & ~POINTSIZE_MASK) |
210				    POINTSIZE_SCALAR;
211	}
212	else if (def->values_supplied & POINTSIZE_MASK)
213	{
214	    vals->point_matrix[0] = def->point_matrix[0];
215	    vals->point_matrix[1] = def->point_matrix[1];
216	    vals->point_matrix[2] = def->point_matrix[2];
217	    vals->point_matrix[3] = def->point_matrix[3];
218	    vals->values_supplied = (vals->values_supplied & ~POINTSIZE_MASK) |
219				    (def->values_supplied & POINTSIZE_MASK);
220	}
221	else return FALSE;
222    }
223
224    /* At this point, at least two of the three vertical scale values
225       should be specified.  Our job now is to compute the missing ones
226       and check for agreement between overspecified values */
227
228    /* If pixelsize was specified by a scalar, we need to fix the matrix
229       now that we know the resolutions.  */
230    if ((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_SCALAR)
231    {
232	/* pixel_setsize_adjustment used below to modify permissible
233	   error in pixel/pointsize matching, since multiplying a
234	   number rounded to integer changes the amount of the error
235	   caused by the rounding */
236
237	pixel_setsize_adjustment = (double)vals->x / (double)vals->y;
238	vals->pixel_matrix[0] *= pixel_setsize_adjustment;
239	vals->values_supplied  = (vals->values_supplied & ~PIXELSIZE_MASK) |
240				 PIXELSIZE_SCALAR_NORMALIZED;
241    }
242
243    sx = (double)vals->x / 72.27;
244    sy = (double)vals->y / 72.27;
245
246    /* If a pointsize was specified, make sure pixelsize is consistent
247       to within 1 pixel, then replace pixelsize with a consistent
248       floating-point value.  */
249
250    if (vals->values_supplied & POINTSIZE_MASK)
251    {
252    recompute_pixelsize: ;
253	temp_matrix[0] = vals->point_matrix[0] * sx;
254	temp_matrix[1] = vals->point_matrix[1] * sy;
255	temp_matrix[2] = vals->point_matrix[2] * sx;
256	temp_matrix[3] = vals->point_matrix[3] * sy;
257	if (vals->values_supplied & PIXELSIZE_MASK)
258	{
259	    if (fabs(vals->pixel_matrix[0] - temp_matrix[0]) >
260		    pixel_setsize_adjustment ||
261		fabs(vals->pixel_matrix[1] - temp_matrix[1]) > 1 ||
262		fabs(vals->pixel_matrix[2] - temp_matrix[2]) > 1 ||
263		fabs(vals->pixel_matrix[3] - temp_matrix[3]) > 1)
264		return FALSE;
265	}
266	if ((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY &&
267	    (vals->values_supplied & POINTSIZE_MASK) == POINTSIZE_SCALAR)
268	{
269	    /* In the special case that pixelsize came as an array and
270	       pointsize as a scalar, recompute the pointsize matrix
271	       from the pixelsize matrix. */
272	    goto recompute_pointsize;
273	}
274
275	/* Refresh pixel matrix with precise values computed from
276	   pointsize and resolution.  */
277	vals->pixel_matrix[0] = temp_matrix[0];
278	vals->pixel_matrix[1] = temp_matrix[1];
279	vals->pixel_matrix[2] = temp_matrix[2];
280	vals->pixel_matrix[3] = temp_matrix[3];
281
282	/* Set values_supplied for pixel to match that for point */
283	vals->values_supplied =
284	    (vals->values_supplied & ~PIXELSIZE_MASK) |
285	    (((vals->values_supplied & POINTSIZE_MASK) == POINTSIZE_ARRAY) ?
286		PIXELSIZE_ARRAY : PIXELSIZE_SCALAR_NORMALIZED);
287    }
288    else
289    {
290	/* Pointsize unspecified...  compute from pixel size and
291	   resolutions */
292    recompute_pointsize: ;
293	if (fabs(sx) < EPS || fabs(sy) < EPS) return FALSE;
294	vals->point_matrix[0] = vals->pixel_matrix[0] / sx;
295	vals->point_matrix[1] = vals->pixel_matrix[1] / sy;
296	vals->point_matrix[2] = vals->pixel_matrix[2] / sx;
297	vals->point_matrix[3] = vals->pixel_matrix[3] / sy;
298
299	/* Set values_supplied for pixel to match that for point */
300	vals->values_supplied =
301	    (vals->values_supplied & ~POINTSIZE_MASK) |
302	    (((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY) ?
303		POINTSIZE_ARRAY : POINTSIZE_SCALAR);
304
305	/* If we computed scalar pointsize from scalar pixelsize, round
306	   pointsize to decipoints and recompute pixelsize so we end up
307	   with a repeatable name */
308	if ((vals->values_supplied & POINTSIZE_MASK) == POINTSIZE_SCALAR)
309	{
310	    /* Off-diagonal elements should be zero since no matrix was
311	       specified. */
312	    vals->point_matrix[0] =
313		(double)(int)(vals->point_matrix[0] * 10.0 + .5) / 10.0;
314	    vals->point_matrix[3] =
315		(double)(int)(vals->point_matrix[3] * 10.0 + .5) / 10.0;
316	    goto recompute_pixelsize;
317	}
318    }
319
320    /* We've succeeded.  Round everything to a few decimal places
321       for repeatability. */
322
323    vals->pixel_matrix[0] = xlfd_round_double(vals->pixel_matrix[0]);
324    vals->pixel_matrix[1] = xlfd_round_double(vals->pixel_matrix[1]);
325    vals->pixel_matrix[2] = xlfd_round_double(vals->pixel_matrix[2]);
326    vals->pixel_matrix[3] = xlfd_round_double(vals->pixel_matrix[3]);
327    vals->point_matrix[0] = xlfd_round_double(vals->point_matrix[0]);
328    vals->point_matrix[1] = xlfd_round_double(vals->point_matrix[1]);
329    vals->point_matrix[2] = xlfd_round_double(vals->point_matrix[2]);
330    vals->point_matrix[3] = xlfd_round_double(vals->point_matrix[3]);
331
332    /* Fill in the deprecated fields for the benefit of rasterizers
333       that do not handle the matrices. */
334    vals->point = vals->point_matrix[3] * 10;
335    vals->pixel = vals->pixel_matrix[3];
336
337    return TRUE;
338}
339
340static Bool
341MatchScalable (FontScalablePtr a, FontScalablePtr b)
342{
343    int i;
344
345    /* Some asymmetry here:  we assume that the first argument (a) is
346       the table entry and the second (b) the item we're trying to match
347       (the key).  We'll consider the fonts matched if the relevant
348       metrics match *and* if a) the table entry doesn't have charset
349       subsetting or b) the table entry has identical charset subsetting
350       to that in the key.  We could add logic to check if the table
351       entry has a superset of the charset required by the key, but
352       we'll resist the urge for now.  */
353
354#define EQUAL(a,b) ((a)[0] == (b)[0] && \
355                    (a)[1] == (b)[1] && \
356                    (a)[2] == (b)[2] && \
357                    (a)[3] == (b)[3])
358
359    if (!(a->x == b->x &&
360	  a->y == b->y &&
361	  (a->width == b->width || a->width == 0 || b->width == 0 || b->width == -1) &&
362	  (!(b->values_supplied & PIXELSIZE_MASK) ||
363	    ((a->values_supplied & PIXELSIZE_MASK) ==
364	     (b->values_supplied & PIXELSIZE_MASK) &&
365	    EQUAL(a->pixel_matrix, b->pixel_matrix))) &&
366	  (!(b->values_supplied & POINTSIZE_MASK) ||
367	    ((a->values_supplied & POINTSIZE_MASK) ==
368	     (b->values_supplied & POINTSIZE_MASK) &&
369	    EQUAL(a->point_matrix, b->point_matrix))) &&
370	  (a->nranges == 0 || a->nranges == b->nranges)))
371      return FALSE;
372
373    for (i = 0; i < a->nranges; i++)
374	if (a->ranges[i].min_char_low != b->ranges[i].min_char_low ||
375	    a->ranges[i].min_char_high != b->ranges[i].min_char_high ||
376	    a->ranges[i].max_char_low != b->ranges[i].max_char_low ||
377	    a->ranges[i].max_char_high != b->ranges[i].max_char_high)
378		return FALSE;
379
380    return TRUE;
381}
382
383FontScaledPtr
384FontFileFindScaledInstance (FontEntryPtr entry, FontScalablePtr vals,
385			    int noSpecificSize)
386{
387    FontScalableEntryPtr    scalable;
388    FontScalableExtraPtr    extra;
389    FontScalablePtr	    mvals;
390    int			    dist, i;
391    int			    mini;
392    double		    mindist;
393    register double	    temp, sum=0.0;
394
395#define NORMDIFF(a, b) ( \
396    temp = (a)[0] - (b)[0], \
397    sum = temp * temp, \
398    temp = (a)[1] - (b)[1], \
399    sum += temp * temp, \
400    temp = (a)[2] - (b)[2], \
401    sum += temp * temp, \
402    temp = (a)[3] - (b)[3], \
403    sum + temp * temp )
404
405    scalable = &entry->u.scalable;
406    extra = scalable->extra;
407    if (noSpecificSize && extra->numScaled)
408    {
409	mini = 0;
410	mindist = NORMDIFF(extra->scaled[0].vals.point_matrix,
411			   vals->point_matrix);
412	for (i = 1; i < extra->numScaled; i++)
413	{
414	    if (extra->scaled[i].pFont &&
415		!extra->scaled[i].pFont->info.cachable) continue;
416	    mvals = &extra->scaled[i].vals;
417	    dist = NORMDIFF(mvals->point_matrix, vals->point_matrix);
418	    if (dist < mindist)
419	    {
420		mindist = dist;
421		mini = i;
422	    }
423	}
424	if (extra->scaled[mini].pFont &&
425	    !extra->scaled[mini].pFont->info.cachable) return 0;
426	return &extra->scaled[mini];
427    }
428    else
429    {
430    	/* See if we've scaled to this value yet */
431    	for (i = 0; i < extra->numScaled; i++)
432    	{
433	    if (extra->scaled[i].pFont &&
434		!extra->scaled[i].pFont->info.cachable) continue;
435	    if (MatchScalable (&extra->scaled[i].vals, vals))
436	    	return &extra->scaled[i];
437    	}
438    }
439    return 0;
440}
441