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